From c345fdd74d7cf6feacc0f31277a4bc0ab3f31198 Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Mon, 19 Jul 2021 03:07:21 +0200 Subject: [PATCH] WIP --- lib/Connection.js | 337 +++++++++++++++++++++++---------- lib/util/ensure-array.js | 7 + lib/util/ensure-object.js | 7 + lib/util/named-tree-builder.js | 8 +- package.json | 5 + yarn.lock | 236 +++++++++++++++++++++++ 6 files changed, 499 insertions(+), 101 deletions(-) create mode 100644 lib/util/ensure-array.js create mode 100644 lib/util/ensure-object.js diff --git a/lib/Connection.js b/lib/Connection.js index fda9dc0..5a712e2 100644 --- a/lib/Connection.js +++ b/lib/Connection.js @@ -6,10 +6,17 @@ const asExpression = require("as-expression"); const matchValue = require("match-value"); const unreachable = require("@joepie91/unreachable"); +const { validateArguments } = require("@validatem/core"); +const required = require("@validatem/required"); +const anyProperty = require("@validatem/any-property"); +const isString = require("@validatem/is-string"); +const isFunction = require("@validatem/is-function"); + const { command, unsafeRaw, already7Bit } = require("./util/command"); const pInterval = require("./util/p-interval"); const createFetchTaskTracker = require("./util/fetch-task"); const createBoxTreeBuilder = require("./util/box-tree-builder"); +const ensureArray = require("./util/ensure-array"); var tls = require('tls'), Socket = require('net').Socket, @@ -1238,56 +1245,155 @@ Object.defineProperty(Connection.prototype, 'seq', { get: function() { }; }}); -function createCommandHandlers(rules) { +function parseTypes(typeString) { + if (typeString === NoRequest || typeString === AnyRequest) { + return [ typeString ]; + } else { + return typeString + .split(/\s*,\s*/) + .map((type) => type.toUpperCase()); + } +} + +function createCommandHandlers(_rules) { + let [ rules ] = validateArguments(arguments, { + rules: anyProperty({ + key: [ required ], + value: [ required, { + untagged: anyProperty({ + key: [ isString ], + value: [ required, isFunction ] + }), + tagged: [ isFunction ] + }] + }) + }); + let untaggedHandlers = new Map(); let taggedHandlers = new Map(); - for (let [ types, options ] of Object.entries(rules)) { - let parsedTypes = types - .split(/\s*\s*/) - .map((type) => type.toUpperCase()); + function setUntaggedHandler(requestType, responseType, handler) { + if (!untaggedHandlers.has(requestType)) { + untaggedHandlers.set(requestType, new Map()); + } + + let handlers = untaggedHandlers.get(requestType); + + if (!handlers.has(responseType)) { + handlers.set(responseType, handler); + } else { + throw new Error(`Duplicate handler definition for untagged type '${requestType}' -> '${responseType}'`); + } + } + + function setTaggedHandler(requestType, handler) { + if (!taggedHandlers.has(requestType)) { + taggedHandlers.set(requestType, handler); + } else { + throw new Error(`Duplicate handler definition for tagged type`); + } + } + + function getRequestType(request) { + return (request != null) + ? request.type.toUpperCase() + : NoRequest; + } + + function getTaggedHandler(request) { + return taggedHandlers.get(getRequestType(request)); + } + + function getUntaggedHandler(request, responseType) { + let typeHandlers = untaggedHandlers.get(getRequestType(request)); + let anyRequestHandlers = untaggedHandlers.get(AnyRequest); + + // console.log({ + // requestType: getRequestType(request), + // responseType: responseType, + // anyHandler: anyRequestHandlers.get(responseType) + // }); + + if (typeHandlers != null && typeHandlers.has(responseType)) { + return typeHandlers.get(responseType); + } else if (anyRequestHandlers != null && anyRequestHandlers.has(responseType)) { + return anyRequestHandlers.get(responseType); + } + } - for (let type of parsedTypes) { + for (let [ typeString, options ] of allEntries(rules)) { + for (let type of parseTypes(typeString)) { if (options.untagged != null) { - untaggedHandlers.set(type, options.untagged); + for (let [ responseTypeString, handler ] of allEntries(options.untagged)) { + for (let responseType of parseTypes(responseTypeString)) { + setUntaggedHandler(type, responseType, handler); + } + } } - + if (options.tagged != null) { - taggedHandlers.set(type, options.tagged); + setTaggedHandler(type, options.tagged); } } } // REFACTOR: Eventually remove `.call(this` hackery + // REFACTOR: Remove all the toUpperCase stuff and normalize this in the core instead, so that we can just *assume* it to be upper-case here return { - canHandleUntagged: function (request) { - return untaggedHandlers.has(request.type.toUpperCase()); + canHandleUntagged: function (request, data) { + return (getUntaggedHandler(request, data.type.toUpperCase()) != null); }, - canHandleTagged: function (request) { - return taggedHandlers.has(request.type.toUpperCase()); + canHandleTagged: function (request, _data) { + return (getTaggedHandler(request) != null); }, handleUntagged: function (request, data) { - let handler = untaggedHandlers.get(request.type.toUpperCase()); + let handler = getUntaggedHandler(request, data.type.toUpperCase()); + return handler.call(this, request, data); }, handleTagged: function (request, data) { - let handler = taggedHandlers.get(request.type.toUpperCase()); + let handler = getTaggedHandler(request); + return handler.call(this, request, data); } }; } +function allEntries(object) { + // The equivalent of Object.entries but also including Symbol keys + return Reflect.ownKeys(object).map((key) => { + return [ key, object[key] ]; + }); +} + +let NoRequest = Symbol("NoRequest"); +let AnyRequest = Symbol("AnyRequest"); + +// FIXME: Strip "UID" prefix from command names before matching, as these only affect the output format, but not the type of response +// Exception: EXPUNGE is allowed during UID commands, but not during their non-UID equivalents: https://datatracker.ietf.org/doc/html/rfc3501#page-73 let commandHandlers = createCommandHandlers({ + [AnyRequest]: { + untagged: { + "BYE": function (_request, _) { + this._sock.end(); + }, + "CAPABILITY": function (_request, { payload }) { + this._caps = payload.map((v) => v.toUpperCase()); + } + } + }, "LIST, XLIST, LSUB": { - untagged: function (request, { payload }) { - if (request.delimiter === undefined) { - request.delimiter = payload.delimiter; - } else { - if (request.boxBuilder == null) { - request.boxBuilder = createBoxTreeBuilder(); + untagged: { + "LIST, XLIST, LSUB": function (request, { payload }) { + if (request.delimiter === undefined) { + request.delimiter = payload.delimiter; + } else { + if (request.boxBuilder == null) { + request.boxBuilder = createBoxTreeBuilder(); + } + + request.boxBuilder.add(payload); } - - request._curReq.boxBuilder.add(payload); } }, tagged: function (request, _) { @@ -1301,16 +1407,114 @@ let commandHandlers = createCommandHandlers({ } }, "ID": { - untagged: function (request, { payload }) { - // https://datatracker.ietf.org/doc/html/rfc2971 - // Used for communicating server/client name, version, etc. - request.responseData.serverVersion = payload; - request.legacyArgs.push(payload); + // https://datatracker.ietf.org/doc/html/rfc2971 + // Used for communicating server/client name, version, etc. + untagged: { + "ID": function (request, { payload }) { + request.responseData.serverVersion = payload; + request.legacyArgs.push(payload); + } } - } + }, + "ESEARCH": { + // https://datatracker.ietf.org/doc/html/rfc4731 / https://datatracker.ietf.org/doc/html/rfc7377 + untagged: { + "ESEARCH": function (request, { payload }) { + Object.assign(request.responseData, payload); // Protocol-defined attributes. TODO: Improve the key names for this? Or is there extensibility? + request.legacyArgs.push(payload); + } + } + }, + "SORT": { + // https://datatracker.ietf.org/doc/html/rfc5256 + untagged: { + "SORT": function (request, { payload }) { + request.responseData.UIDs = payload; + request.legacyArgs.push(payload); + } + } + }, + "THREAD": { + // https://datatracker.ietf.org/doc/html/rfc5256 + untagged: { + "THREAD": function (request, { payload }) { + request.responseData.threads = payload; // FIXME: Work out the exact format + request.legacyArgs.push(payload); + } + } + }, + "SEARCH": { + untagged: { + "SEARCH": function (request, { payload }) { + if (payload.results !== undefined) { + // CONDSTORE-modified search results + request.legacyArgs.push(payload.results); + request.legacyArgs.push(payload.modseq); + } else { + request.legacyArgs.push(payload); + } + } + } + }, + "GETQUOTA, GETQUOTAROOT": { + // https://datatracker.ietf.org/doc/html/rfc2087 + untagged: { + "QUOTA": function (request, { payload }) { + ensureArray(request.responseData, "quota"); + request.responseData.quota.push(payload); + + ensureArray(request.legacyArgs, 0); + request.legacyArgs[0].push(payload); + }, + "QUOTAROOT": function (_request, _) { + throw new Error(`Not implemented`); + } + } + }, + "SELECT, EXAMINE": { + untagged: { + "RECENT": function (_request, { sequenceNumber }) { + this._ensureBox(); + + // FIXME: This conditional is always true? + if (this._box) { + this._box.messages.new = sequenceNumber; + } + }, + "FLAGS": function (_request, { payload }) { + this._ensureBox(); + + // FIXME: This conditional is always true? + if (this._box) { + this._box.flags = payload; + } + }, + "EXISTS": function (_request, { sequenceNumber }) { + this._ensureBox(); + + // FIXME: This conditional is always true? + if (this._box) { + var prev = this._box.messages.total, now = sequenceNumber; + this._box.messages.total = now; + if (now > prev && this.state === 'authenticated') { + this._box.messages.new = now - prev; + this.emit('mail', this._box.messages.new); + } + } + }, + } + }, }); - +Connection.prototype._ensureBox = function () { + if (!this._box) { + if (RE_OPENBOX.test(this._curReq.type)) { + this._resetCurrentBox(); + } else { + throw new Error(`Received a box-related response while not processing a box-related request`); + } + } +}; @@ -1324,15 +1528,13 @@ Connection.prototype._resUntagged = function({ type, num: sequenceNumber, textCo // console.log("resUntagged", { type, num: sequenceNumber, payload, textCode: responseData }); var i, len, box, destinationKey; - if (this._curReq != null && commandHandlers.canHandleUntagged(this._curReq)) { + let response = { type, sequenceNumber, payload }; + + if (commandHandlers.canHandleUntagged(this._curReq, response)) { // FIXME: Include other fields - commandHandlers.handleUntagged.call(this, this._curReq, { sequenceNumber, payload }); - } else if (type === 'bye') { - this._sock.end(); + commandHandlers.handleUntagged.call(this, this._curReq, response); } else if (type === 'namespace') { this.namespaces = payload; - } else if (type === 'capability') { - this._caps = payload.map((v) => v.toUpperCase()); } else if (type === 'preauth') { this.state = 'authenticated'; } else if (type === 'expunge') { @@ -1387,50 +1589,6 @@ Connection.prototype._resUntagged = function({ type, num: sequenceNumber, textCo } else if (typeof responseData === 'string' && responseData.toUpperCase() === 'UIDVALIDITY') { this.emit('uidvalidity', payload); } - } else if (type === "esearch") { - // https://datatracker.ietf.org/doc/html/rfc4731 / https://datatracker.ietf.org/doc/html/rfc7377 - Object.assign(this._curReq.responseData, payload); // Protocol-defined attributes. TODO: Improve the key names for this? Or is there extensibility? - this._curReq.cbargs.push(payload); - } else if (type === "sort") { - // https://datatracker.ietf.org/doc/html/rfc5256 - this._curReq.responseData.UIDs = payload; - this._curReq.cbargs.push(payload); - } else if (type === 'thread') { - // https://datatracker.ietf.org/doc/html/rfc5256 - this._curReq.responseData.threads = payload; // FIXME: Work out the exact format - this._curReq.cbargs.push(payload); - } else if (type === 'search') { - if (payload.results !== undefined) { - // CONDSTORE-modified search results - this._curReq.cbargs.push(payload.results); - this._curReq.cbargs.push(payload.modseq); - } else { - this._curReq.cbargs.push(payload); - } - } else if (type === 'quota') { - let responseData = this._curReq.responseData; - if (responseData.quota == null) { responseData.quota = []; }; - responseData.quota.push(payload); - - let cbargs = this._curReq.cbargs; - if (cbargs.length === 0) { cbargs.push([]); } - cbargs[0].push(payload); - } else if (type === 'recent') { - if (!this._box && RE_OPENBOX.test(this._curReq.type)) { - this._resetCurrentBox(); - } - - if (this._box) { - this._box.messages.new = sequenceNumber; - } - } else if (type === 'flags') { - if (!this._box && RE_OPENBOX.test(this._curReq.type)) { - this._resetCurrentBox(); - } - - if (this._box) { - this._box.flags = payload; - } } else if (type === 'bad' || type === 'no') { if (this.state === 'connected' && !this._curReq) { clearTimeout(this._connectionTimeout); @@ -1440,19 +1598,6 @@ Connection.prototype._resUntagged = function({ type, num: sequenceNumber, textCo this.emit('error', err); this._sock.end(); } - } else if (type === 'exists') { - if (!this._box && RE_OPENBOX.test(this._curReq.type)) { - this._resetCurrentBox(); - } - - if (this._box) { - var prev = this._box.messages.total, now = sequenceNumber; - this._box.messages.total = now; - if (now > prev && this.state === 'authenticated') { - this._box.messages.new = now - prev; - this.emit('mail', this._box.messages.new); - } - } } else if (type === 'status') { let attrs = defaultValue(payload.attrs, {}); @@ -1565,9 +1710,11 @@ Connection.prototype._resTagged = function({ type, tagnum, text: payload, textCo }); } - if (commandHandlers.canHandleTagged(request)) { + let response = { type, payload }; + + if (commandHandlers.canHandleTagged(request, response)) { // FIXME: Add other fields with a sensible name - commandHandlers.handleTagged.call(this, request, { payload }); + commandHandlers.handleTagged.call(this, request, response); } // console.dir({ done: request.cbargs }, { depth: null, colors: true }); diff --git a/lib/util/ensure-array.js b/lib/util/ensure-array.js new file mode 100644 index 0000000..4c86aa5 --- /dev/null +++ b/lib/util/ensure-array.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = function ensureArrau(object, property) { + if (object[property] == null) { + object[property] = []; + } +}; diff --git a/lib/util/ensure-object.js b/lib/util/ensure-object.js new file mode 100644 index 0000000..7618a47 --- /dev/null +++ b/lib/util/ensure-object.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = function ensureObject(object, property) { + if (object[property] == null) { + object[property] = {}; + } +}; diff --git a/lib/util/named-tree-builder.js b/lib/util/named-tree-builder.js index dcd9da4..12229a3 100644 --- a/lib/util/named-tree-builder.js +++ b/lib/util/named-tree-builder.js @@ -3,13 +3,9 @@ const defaultValue = require("default-value"); const asExpression = require("as-expression"); -// FIXME: Move to stand-alone package, clearly document that it is not order-sensitive +const ensureObject = require("./ensure-object"); -function ensureObject(object, property) { - if (object[property] == null) { - object[property] = {}; - } -} +// FIXME: Move to stand-alone package, clearly document that it is not order-sensitive module.exports = function createNamedTreeBuilder(options = {}) { let parentKey = options.parentKey; diff --git a/package.json b/package.json index 73f0782..8bbd840 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,11 @@ "main": "./lib/Connection", "dependencies": { "@joepie91/unreachable": "^1.0.0", + "@validatem/any-property": "^0.1.3", + "@validatem/core": "^0.3.15", + "@validatem/is-function": "^0.1.0", + "@validatem/is-string": "^1.0.0", + "@validatem/required": "^0.1.1", "as-expression": "^1.0.0", "bluebird": "^3.7.2", "default-value": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index 164f1c0..677d4c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -350,6 +350,152 @@ resolved "https://registry.yarnpkg.com/@types/yoga-layout/-/yoga-layout-1.9.2.tgz#efaf9e991a7390dc081a0b679185979a83a9639a" integrity sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw== +"@validatem/annotate-errors@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@validatem/annotate-errors/-/annotate-errors-0.1.2.tgz#fa9152bb30f4f42b69496b527e38f0c31ff605a9" + integrity sha512-EuX7pzdYI/YpTmZcgdPG481Oi3elAg8JWh/LYXuE1h6MaZk3A8eP5DD33/l7EoKzrysn6y8nCsqNa1ngei562w== + dependencies: + "@validatem/match-validation-error" "^0.1.0" + +"@validatem/any-property@^0.1.0", "@validatem/any-property@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@validatem/any-property/-/any-property-0.1.3.tgz#fc7768c1922a8bacff9369ae48913672e5350f52" + integrity sha512-jYWxif5ff9pccu7566LIQ/4+snlApXEJUimBywzAriBgS3r4eDBbz3oZFHuiPmhxNK/NNof5YUS+L6Sk3zaMfg== + dependencies: + "@validatem/annotate-errors" "^0.1.2" + "@validatem/combinator" "^0.1.0" + "@validatem/error" "^1.0.0" + "@validatem/validation-result" "^0.1.1" + "@validatem/virtual-property" "^0.1.0" + default-value "^1.0.0" + +"@validatem/combinator@^0.1.0": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@validatem/combinator/-/combinator-0.1.2.tgz#eab893d55f1643b9c6857eaf6ff7ed2a728e89ff" + integrity sha512-vE8t1tNXknmN62FlN6LxQmA2c6TwVKZ+fl/Wit3H2unFdOhu7SZj2kRPGjAXdK/ARh/3svYfUBeD75pea0j1Sw== + +"@validatem/core@^0.3.15": + version "0.3.15" + resolved "https://registry.yarnpkg.com/@validatem/core/-/core-0.3.15.tgz#645a0734dbc6efa3a5c39c62c5f2d8fa773f89f3" + integrity sha512-4nBLGzgpPrPsZ5DDXDXwL5p+GUEvpAFt6I3/YUHoah+ckYmKNh9qwmWKkFZHxJVdRrTewGFRj0FPw5fqje1yxA== + dependencies: + "@validatem/annotate-errors" "^0.1.2" + "@validatem/any-property" "^0.1.0" + "@validatem/error" "^1.0.0" + "@validatem/match-validation-error" "^0.1.0" + "@validatem/match-versioned-special" "^0.1.0" + "@validatem/match-virtual-property" "^0.1.0" + "@validatem/normalize-rules" "^0.1.0" + "@validatem/required" "^0.1.0" + "@validatem/validation-result" "^0.1.1" + "@validatem/virtual-property" "^0.1.0" + as-expression "^1.0.0" + assure-array "^1.0.0" + create-error "^0.3.1" + default-value "^1.0.0" + execall "^2.0.0" + flatten "^1.0.3" + indent-string "^4.0.0" + is-arguments "^1.0.4" + supports-color "^7.1.0" + syncpipe "^1.0.0" + +"@validatem/error@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@validatem/error/-/error-1.1.0.tgz#bef46e7066c39761b494ebe3eec2ecdc7348f4ed" + integrity sha512-gZJEoZq1COi/8/5v0fVKQ9uX54x5lb5HbV7mzIOhY6dqjmLNfxdQmpECZPQrCAOpcRkRMJ7zaFhq4UTslpY9yA== + +"@validatem/has-shape@^0.1.0": + version "0.1.8" + resolved "https://registry.yarnpkg.com/@validatem/has-shape/-/has-shape-0.1.8.tgz#dff0f0449c12b96d150091b7a980154d810ae63d" + integrity sha512-x2i8toW1uraFF2Vl6WBl4CScbBeg5alrtoCKMyXbJkHf2B5QxL/ftUh2RQRcBzx6U0i7KUb8vdShcWAa+fehRQ== + dependencies: + "@validatem/annotate-errors" "^0.1.2" + "@validatem/combinator" "^0.1.0" + "@validatem/error" "^1.0.0" + "@validatem/validation-result" "^0.1.1" + array-union "^2.1.0" + as-expression "^1.0.0" + assure-array "^1.0.0" + default-value "^1.0.0" + flatten "^1.0.3" + +"@validatem/is-function@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@validatem/is-function/-/is-function-0.1.0.tgz#15a2e95259dc5e32256e8c21872455661437d069" + integrity sha512-UtVrwTGhaIdIJ0mPG5XkAmYZUeWgRoMP1G9ZEHbKvAZJ4+SXf/prC0jPgE0pw+sPjdQG4hblsXSfo/9Bf3PGdQ== + dependencies: + "@validatem/error" "^1.0.0" + is-callable "^1.1.5" + +"@validatem/is-plain-object@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@validatem/is-plain-object/-/is-plain-object-0.1.1.tgz#b7a3ef8ef960882c7c41e84ed709fa0bfb932e93" + integrity sha512-aNGbNIbKRpYI0lRBczlTBbiA+nqN52ADAASdySKg2/QeSCVtYS4uOIeCNIJRAgXe/5sUnLTuL4pgq628uAl7Kw== + dependencies: + "@validatem/error" "^1.0.0" + is-plain-obj "^2.1.0" + +"@validatem/is-string@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@validatem/is-string/-/is-string-1.0.0.tgz#cc4a464f3bbc797aa7c7e124d11e5959b67636fd" + integrity sha512-j6fXuTgOrq94RbjSWeOzeKEcZ2ftnrG2ZHU16cGwC+gYn/st8JECXEpxSeZG8PJpn7V+PFaZeCKW9sJhonE1pA== + dependencies: + "@validatem/error" "^1.0.0" + is-string "^1.0.5" + +"@validatem/match-special@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@validatem/match-special/-/match-special-0.1.0.tgz#4e0c28f1aee5bf53c1ef30bbf8c755d4946ae0ff" + integrity sha512-TFiq9Wk/1Hoja4PK85WwNYnwBXk3+Lgoj59ZIMxm2an1qmNYp8j+BnSvkKBflba451yIn6V1laU9NJf+/NYZgw== + +"@validatem/match-validation-error@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@validatem/match-validation-error/-/match-validation-error-0.1.0.tgz#fa87f5f1836e7c1d9bf6b75b2addf0a5b21e4c1e" + integrity sha512-6akGTk7DdulOreyqDiGdikwRSixQz/AlvARSX18dcWaTFc79KxCLouL2hyoFcor9IIUhu5RTY4/i756y4T1yxA== + dependencies: + "@validatem/match-versioned-special" "^0.1.0" + +"@validatem/match-versioned-special@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@validatem/match-versioned-special/-/match-versioned-special-0.1.0.tgz#2eacc48debecdbbe7e3d02f0c0a665afaea9bedf" + integrity sha512-xoOTY0bdA2ELj+ntcDVJ8YyMEFIJpjZ4HNPL9lGcbnRFwJBhQcHUAhUpZwkMxu02zH9wkNM1FvYGHxPz40745Q== + +"@validatem/match-virtual-property@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@validatem/match-virtual-property/-/match-virtual-property-0.1.0.tgz#4de2de1075987b5f3b356d3f2bcf6c0be5b5fb83" + integrity sha512-ssd3coFgwbLuqvZftLZTy3eHN0TFST8oTS2XTViQdXJPXVoJmwEKBpFhXgwnb5Ly1CE037R/KWpjhd1TP/56kQ== + +"@validatem/normalize-rules@^0.1.0": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@validatem/normalize-rules/-/normalize-rules-0.1.3.tgz#59fd6193b1091ff97b5c723b32c9bb1fe2a9dc9c" + integrity sha512-HHPceAP2ce9NWymIZrgLCTzpdwXNRBCCB5H6ZPc5ggOrbmh4STpT83fLazleHtvYNlqgXZ4GjQOvCwrjaM+qEA== + dependencies: + "@validatem/has-shape" "^0.1.0" + "@validatem/is-plain-object" "^0.1.0" + "@validatem/match-special" "^0.1.0" + assure-array "^1.0.0" + default-value "^1.0.0" + flatten "^1.0.3" + is-plain-obj "^2.1.0" + +"@validatem/required@^0.1.0", "@validatem/required@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@validatem/required/-/required-0.1.1.tgz#64f4a87333fc5955511634036b7f8948ed269170" + integrity sha512-vI4NzLfay4RFAzp7xyU34PHb8sAo6w/3frrNh1EY9Xjnw2zxjY5oaxwmbFP1jVevBE6QQEnKogtzUHz/Zuvh6g== + +"@validatem/validation-result@^0.1.1": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@validatem/validation-result/-/validation-result-0.1.2.tgz#4e75cfd87305fc78f8d05ac84921a2c99a0348e0" + integrity sha512-okmP8JarIwIgfpaVcvZGuQ1yOsLKT3Egt49Ynz6h1MAeGsP/bGHXkkXtbiWOVsk5Tzku5vDVFSrFnF+5IEHKxw== + dependencies: + default-value "^1.0.0" + +"@validatem/virtual-property@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@validatem/virtual-property/-/virtual-property-0.1.0.tgz#880540dfd149f98ecf1095d93912e34443381fe4" + integrity sha512-JUUvWtdqoSkOwlsl20oB3qFHYIL05a/TAfdY4AJcs55QeVTiX5iI1b8IoQW644sIWWooBuLv+XwoxjRsQFczlQ== + acorn-jsx@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" @@ -471,6 +617,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + arrify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" @@ -623,6 +774,14 @@ caching-transform@^4.0.0: package-hash "^4.0.0" write-file-atomic "^3.0.0" +call-bind@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + caller-callsite@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" @@ -768,6 +927,13 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +clone-regexp@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f" + integrity sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q== + dependencies: + is-regexp "^2.0.0" + code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" @@ -847,6 +1013,11 @@ coveralls@^3.0.11: minimist "^1.2.5" request "^2.88.2" +create-error@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/create-error/-/create-error-0.3.1.tgz#69810245a629e654432bf04377360003a5351a23" + integrity sha1-aYECRaYp5lRDK/BDdzYAA6U1GiM= + cross-spawn@^7.0.0, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -1094,6 +1265,13 @@ events-to-array@^1.0.1: resolved "https://registry.yarnpkg.com/events-to-array/-/events-to-array-1.1.2.tgz#2d41f563e1fe400ed4962fe1a4d5c6a7539df7f6" integrity sha1-LUH1Y+H+QA7Uli/hpNXGp1Od9/Y= +execall@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/execall/-/execall-2.0.0.tgz#16a06b5fe5099df7d00be5d9c06eecded1663b45" + integrity sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow== + dependencies: + clone-regexp "^2.1.0" + extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" @@ -1173,6 +1351,11 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== +flatten@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" + integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== + foreground-child@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" @@ -1215,6 +1398,11 @@ fsevents@~2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + function-loop@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/function-loop/-/function-loop-2.0.1.tgz#799c56ced01698cf12a1b80e4802e9dafc2ebada" @@ -1235,6 +1423,15 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-intrinsic@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -1325,6 +1522,18 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-symbols@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + hasha@^5.0.0: version "5.2.2" resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.2.tgz#a48477989b3b327aea3c04f53096d816d97522a1" @@ -1427,6 +1636,13 @@ ink@^2.6.0, ink@^2.7.1: wrap-ansi "^6.2.0" yoga-layout-prebuilt "^1.9.3" +is-arguments@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" + integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== + dependencies: + call-bind "^1.0.0" + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -1434,6 +1650,11 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-callable@^1.1.5: + version "1.2.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -1475,11 +1696,26 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-regexp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d" + integrity sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA== + is-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== +is-string@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" + integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== + is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"