diff options
Diffstat (limited to 'node_modules/@jet/environment/json')
| -rw-r--r-- | node_modules/@jet/environment/json/index.js | 19 | ||||
| -rw-r--r-- | node_modules/@jet/environment/json/reader/coercion.js | 139 | ||||
| -rw-r--r-- | node_modules/@jet/environment/json/reader/index.js | 20 | ||||
| -rw-r--r-- | node_modules/@jet/environment/json/reader/key-path.js | 143 | ||||
| -rw-r--r-- | node_modules/@jet/environment/json/reader/object-cursor.js | 119 | ||||
| -rw-r--r-- | node_modules/@jet/environment/json/reader/object-reader.js | 428 | ||||
| -rw-r--r-- | node_modules/@jet/environment/json/reader/traverse.js | 36 | ||||
| -rw-r--r-- | node_modules/@jet/environment/json/validation.js | 250 |
8 files changed, 1154 insertions, 0 deletions
diff --git a/node_modules/@jet/environment/json/index.js b/node_modules/@jet/environment/json/index.js new file mode 100644 index 0000000..285e4bb --- /dev/null +++ b/node_modules/@jet/environment/json/index.js @@ -0,0 +1,19 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./reader"), exports); +__exportStar(require("./validation"), exports); +//# sourceMappingURL=index.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/json/reader/coercion.js b/node_modules/@jet/environment/json/reader/coercion.js new file mode 100644 index 0000000..6f84fcf --- /dev/null +++ b/node_modules/@jet/environment/json/reader/coercion.js @@ -0,0 +1,139 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.valueAsNumber = exports.valueAsString = exports.valueAsBoolean = void 0; +const optional_1 = require("../../types/optional"); +const validation = require("../validation"); +/** + * Attempt to coerce the given value to a boolean. + * + * @see asBoolean + * @param value - the value to coerce + * @param policy - determines when validation errors are added to the current validation context + * @param path - an optional string appended to validation errors to identify where this value originated + * @returns a boolean if the value was a boolean or coercible to a boolean, otherwise null + */ +function valueAsBoolean(value, policy = "coercible", path) { + if (!(0, optional_1.isSome)(value)) { + return value; + } + if (typeof value === "boolean") { + return value; + } + // Handle string coercion + if (typeof value === "string") { + if (value === "true") { + return true; + } + else if (value === "false") { + return false; + } + } + // Else coerce. + const coercedValue = Boolean(value); + switch (policy) { + case "strict": { + validation.context("asBoolean", () => { + validation.unexpectedType("coercedValue", "boolean", value, path); + }); + break; + } + case "coercible": { + if ((0, optional_1.isNothing)(coercedValue)) { + validation.context("asBoolean", () => { + validation.unexpectedType("coercedValue", "boolean", value, path); + }); + return null; + } + break; + } + case "none": + default: { + break; + } + } + return coercedValue; +} +exports.valueAsBoolean = valueAsBoolean; +/** + * Attempt to coerce the given value to a string. + * + * @see asString + * @param value - the value to coerce + * @param policy - determines when validation errors are added to the current validation context + * @param path - an optional string appended to validation errors to identify where this value originated + * @returns a string if the value was a string or coercible to a string, otherwise null + */ +function valueAsString(value, policy = "coercible", path) { + if (!(0, optional_1.isSome)(value)) { + return value; + } + if (typeof value === "string") { + return value; + } + // We don't consider arbitrary objects as convertable to strings even through they will result in some value + const coercedValue = typeof value === "object" ? null : String(value); + switch (policy) { + case "strict": { + validation.context("asString", () => { + validation.unexpectedType("coercedValue", "string", value, path); + }); + break; + } + case "coercible": { + if ((0, optional_1.isNothing)(coercedValue)) { + validation.context("asString", () => { + validation.unexpectedType("coercedValue", "string", value, path); + }); + } + break; + } + case "none": + default: { + break; + } + } + return coercedValue; +} +exports.valueAsString = valueAsString; +/** + * Attempt to coerce the given value to a number. + * + * @see asNumber + * @param value - the value to coerce + * @param policy - determines when validation errors are added to the current validation context + * @param path - an optional string appended to validation errors to identify where this value originated + * @returns a number if the value was a number or coercible to a number, otherwise null + */ +function valueAsNumber(value, policy = "coercible", path) { + if (!(0, optional_1.isSome)(value)) { + return value; + } + if (typeof value === "number") { + return value; + } + const coercedValue = Number(value); + switch (policy) { + case "strict": { + validation.context("asNumber", () => { + validation.unexpectedType("coercedValue", "number", value, path); + }); + break; + } + case "coercible": { + if (isNaN(coercedValue)) { + validation.context("asNumber", () => { + validation.unexpectedType("coercedValue", "number", value, path); + }); + return null; + } + break; + } + case "none": + default: { + break; + } + } + return coercedValue; +} +exports.valueAsNumber = valueAsNumber; +//# sourceMappingURL=coercion.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/json/reader/index.js b/node_modules/@jet/environment/json/reader/index.js new file mode 100644 index 0000000..3f23caa --- /dev/null +++ b/node_modules/@jet/environment/json/reader/index.js @@ -0,0 +1,20 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +__exportStar(require("./object-reader"), exports); +__exportStar(require("./key-path"), exports); +__exportStar(require("./coercion"), exports); +//# sourceMappingURL=index.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/json/reader/key-path.js b/node_modules/@jet/environment/json/reader/key-path.js new file mode 100644 index 0000000..1bb25ff --- /dev/null +++ b/node_modules/@jet/environment/json/reader/key-path.js @@ -0,0 +1,143 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.keyPathContains = exports.keyPathEndsWith = exports.keyPathStartsWith = exports.keyPathsEqual = exports.isKeyPathThis = exports.thisKeyPath = exports.keysOf = void 0; +const optional_1 = require("../../types/optional"); +/** + * A global cache containing parsed string key paths + * with components separated by a dot. + */ +const parsedKeyPaths = {}; +/** + * Extract the individual keys from a key path in order + * to traverse into an object to access a specific value. + * + * @param keyPath - A key path to extract the keys from. + * @returns An array containing the keys making up `keyPath`. + */ +function keysOf(keyPath) { + // TODO: Normalizing into an array is potentially a bottleneck. + // Do we want to do this differently for slower environments? + if (Array.isArray(keyPath)) { + return keyPath; + } + else { + switch (typeof keyPath) { + case "string": { + const existingKeyPath = parsedKeyPaths[keyPath]; + if ((0, optional_1.isSome)(existingKeyPath)) { + return existingKeyPath; + } + else { + const newKeyPath = Object.freeze(keyPath.split(".")); + parsedKeyPaths[keyPath] = newKeyPath; + return newKeyPath; + } + } + case "number": { + return [keyPath]; + } + case "symbol": { + return [keyPath]; + } + default: { + throw new TypeError(`${keyPath.toString()} is not a KeyPath`); + } + } + } +} +exports.keysOf = keysOf; +/** + * A key path representing an object itself. + */ +exports.thisKeyPath = Object.freeze([]); +/** + * Determine whether a given key path is the `this` (identity) key path. + * @param keyPath - A key path to test. + */ +function isKeyPathThis(keyPath) { + return Array.isArray(keyPath) && keyPath.length === 0; +} +exports.isKeyPathThis = isKeyPathThis; +/** + * Determines whether two key paths are equivalent taking into account + * that the key paths may have different representations. + * + * @param lhs - A key path to compare. + * @param rhs - Another key path to compare. + */ +function keyPathsEqual(lhs, rhs) { + // 1. Are the key paths equal through value semantics? + if (lhs === rhs) { + return true; + } + const lhsKeys = keysOf(lhs); + const rhsKeys = keysOf(rhs); + // 2. Do we have the same number of keys in each path? + if (lhsKeys.length !== rhsKeys.length) { + return false; + } + // 3. Do any of the keys in our paths differ? + for (let index = 0, length = lhsKeys.length; index < length; index += 1) { + if (lhsKeys[index] !== rhsKeys[index]) { + return false; + } + } + // 4. We have passed all checks and are considered equal. + return true; +} +exports.keyPathsEqual = keyPathsEqual; +/** + * Determine whether a given key path starts with a specified key. + * + * @param haystack - A key path to perform a prefix check on. + * @param needle - The key to check for. + */ +function keyPathStartsWith(haystack, needle) { + if (haystack === needle) { + return true; + } + else { + const keys = keysOf(haystack); + if (keys.length === 0) { + return false; + } + return keys[0] === needle; + } +} +exports.keyPathStartsWith = keyPathStartsWith; +/** + * Determine whether a given key path ends with a specified key. + * + * @param haystack - A key path to perform a suffix check on. + * @param needle - The key to check for. + */ +function keyPathEndsWith(haystack, needle) { + if (haystack === needle) { + return true; + } + else { + const keys = keysOf(haystack); + if (keys.length === 0) { + return false; + } + return keys[keys.length - 1] === needle; + } +} +exports.keyPathEndsWith = keyPathEndsWith; +/** + * Determine whether a given key path contains a specified key. + * + * @param haystack - A key path to search. + * @param needle - The key to search for. + */ +function keyPathContains(haystack, needle) { + if (haystack === needle) { + return true; + } + else { + const keys = keysOf(haystack); + return keys.includes(needle); + } +} +exports.keyPathContains = keyPathContains; +//# sourceMappingURL=key-path.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/json/reader/object-cursor.js b/node_modules/@jet/environment/json/reader/object-cursor.js new file mode 100644 index 0000000..8ac84f2 --- /dev/null +++ b/node_modules/@jet/environment/json/reader/object-cursor.js @@ -0,0 +1,119 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ObjectCursor = void 0; +const optional_1 = require("../../types/optional"); +const clone_1 = require("../../util/clone"); +const key_path_1 = require("./key-path"); +const traverse_1 = require("./traverse"); +class ObjectCursor { + /** + * Create a cursor for an object. + * + * @param root - An object to traverse. + */ + constructor(root) { + this.values = [root]; + this.keyPaths = [key_path_1.thisKeyPath]; + this.savedDepths = []; + } + /** + * The current value this cursor is pointing at. + */ + get currentValue() { + return this.values[this.values.length - 1]; + } + /** + * The key path of the value this cursor is pointing at. + */ + get currentKeyPath() { + return this.keyPaths[this.keyPaths.length - 1]; + } + /** + * Advance this cursor to a given value and the key path which + * was used to reach it. + * + * Use this method to override the internal traversal logic of + * the cursor as needed. Like `moveTo`, calls to this method can + * be balanced with calls to `back`. + * + * @param value - The new value for the cursor to represent. + * @param keyPath - The key path used to reach the value. + */ + interject(value, keyPath) { + this.values.push(value); + this.keyPaths.push(keyPath); + } + /** + * Reconfigure this cursor to traverse a given object. + * + * @param newRoot - The new root object to traverse. + * @param keyPath - The key path specifying where the root object came from. + * Typically this should be `thisKeyPath` (the default value for this parameter.) + */ + reuse(newRoot, keyPath = key_path_1.thisKeyPath) { + this.values.length = 0; + this.values.push(newRoot); + this.keyPaths.length = 0; + this.keyPaths.push(keyPath); + this.savedDepths.length = 0; + } + /** + * Advance this cursor to a new position in the object it is traversing, + * saving its previous position so that the cursor may be moved back. + * + * @param keyPath - A key path referring to a location in the cursor's current value. + * @returns The new current value of the cursor. + */ + moveTo(keyPath) { + const newValue = (0, traverse_1.traverse)(this.currentValue, keyPath); + this.values.push(newValue); + this.keyPaths.push(keyPath); + return newValue; + } + /** + * Rewind this cursor to its previous position in the object it is traversing. + */ + moveBack() { + const currentDepth = this.values.length; + if (currentDepth === 1) { + throw new Error("Cannot move back past the root of a cursor"); + } + const numberOfSaves = this.savedDepths.length; + if (numberOfSaves > 0 && currentDepth <= this.savedDepths[numberOfSaves - 1]) { + throw new Error("Cannot move back past the most recent saved state"); + } + this.values.pop(); + this.keyPaths.pop(); + } + /** + * Save the current position of this cursor so that it may be restored later. + * + * Calls to this method must be balanced with a call to `restoreState`. + */ + saveState() { + this.savedDepths.push(this.values.length); + } + /** + * Restore this cursor's position to a previously saved state. + * + * Use this method to balance a previous call to `saveState`. + */ + restoreState() { + const savedLength = this.savedDepths.pop(); + if ((0, optional_1.isNothing)(savedLength)) { + throw new Error("Calls to restoreState must balance previous calls to saveState"); + } + this.values.length = savedLength; + this.keyPaths.length = savedLength; + } + // section Cloneable + clone() { + const copy = (0, clone_1.shallowCloneOf)(this); + copy.values = this.values.slice(); + copy.keyPaths = this.keyPaths.slice(); + copy.savedDepths = this.savedDepths.slice(); + return copy; + } +} +exports.ObjectCursor = ObjectCursor; +//# sourceMappingURL=object-cursor.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/json/reader/object-reader.js b/node_modules/@jet/environment/json/reader/object-reader.js new file mode 100644 index 0000000..f31a222 --- /dev/null +++ b/node_modules/@jet/environment/json/reader/object-reader.js @@ -0,0 +1,428 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ObjectReader = void 0; +const optional_1 = require("../../types/optional"); +const clone_1 = require("../../util/clone"); +const coercion_1 = require("./coercion"); +const key_path_1 = require("./key-path"); +const object_cursor_1 = require("./object-cursor"); +const traverse_1 = require("./traverse"); +/* eslint-disable no-underscore-dangle */ +/** + * Map which holds any object readers recycled, divided by constructor. + */ +// eslint-disable-next-line @typescript-eslint/ban-types +const scrapReaders = new Map(); +/** + * A type which allows efficient and type-safe traversal of untyped objects. + */ +class ObjectReader { + /** + * Create a reader to traverse the contents of an untyped + * object safely and efficiently. + * + * @param object - An object to efficiently traverse with a reader. + */ + constructor(object) { + this._cursor = new object_cursor_1.ObjectCursor(object); + } + // endsection + // section Structure + /** + * Current key path which operations on this reader are relative to. + */ + get currentKeyPath() { + return this._cursor.currentKeyPath; + } + /** + * Determines whether a value exists for a given key + * relative to the reader's current location. + * + * @param key - The key to test for the existence of. + * @returns `true` if a value exists for `key`; `false` otherwise. + */ + has(key) { + return (0, key_path_1.keyPathEndsWith)(this._cursor.currentKeyPath, key) || (0, optional_1.isSome)(this.get(key)); + } + /** + * Make all operations on this reader be relative to a given key path. + * + * Consecutive calls to `select` with the same key path are idempotent. + * You may repeatedly call this method with the same key path and only + * the first call will change what operations are relative to on this reader. + * + * To allow repeated paths in consecutive `select` calls set the optional + * `allowRepeatedKeyPath` argument to `true`. + * + * You must balance calls to this method with matching calls to `deselect`. + * + * @param keyPath - The key path to make this reader's operations relative to. + * @param allowRepeatedKeyPath - The Boolean indicating whether repeated key path + * like 'value.value' should be accepted by the reader. + * Some JSON objects can have nested properties stored under the same key path. + * @returns The reader this method was called on. + */ + select(keyPath, allowRepeatedKeyPath = false) { + if (allowRepeatedKeyPath || !(0, key_path_1.keyPathsEqual)(this._cursor.currentKeyPath, keyPath)) { + this._cursor.moveTo(keyPath); + } + return this; + } + /** + * Make all operations on this reader be relative to the previously selected key path. + * + * If no key path was previously selected, this method has the effect of making + * operations relative to the media response the reader was created to work on. + * + * Use this method to balance previous calls to a method in the `select` family. + * + * @returns The reader this method was called on. + */ + deselect() { + this._cursor.moveBack(); + return this; + } + /** + * Save the current selection of this reader so that it can be restored later. + * + * Calls to this method should be balanced with a call to `restoreSelection`. + */ + saveSelection() { + this._cursor.saveState(); + return this; + } + /** + * Restore a previous selection of this reader. + * + * Use this method to balance a previous call to `saveSelection`. + */ + restoreSelection() { + this._cursor.restoreState(); + return this; + } + // endsection + // section Scalars + /** + * Access an untyped value in this reader's contents. + * + * @param keyPath - A key path specifying where to find the value in this reader's contents. + * @returns An optional untyped value. + */ + get(keyPath = key_path_1.thisKeyPath) { + if ((0, key_path_1.isKeyPathThis)(keyPath)) { + return this._cursor.currentValue; + } + else { + return (0, traverse_1.traverse)(this._cursor.currentValue, keyPath); + } + } + /** + * Access a boolean value in this reader's contents. + * + * @param keyPath - A key path specifying where to find the value in this reader's contents. + * @returns An optional boolean value. + */ + asBoolean(keyPath = key_path_1.thisKeyPath, policy = "coercible") { + return (0, coercion_1.valueAsBoolean)(this.get(keyPath), policy, String(keyPath)); + } + /** + * Access a number value in this reader's contents. + * + * @param keyPath - A key path specifying where to find the value in this reader's contents. + * @returns An optional number value. + */ + asNumber(keyPath = key_path_1.thisKeyPath, policy = "coercible") { + return (0, coercion_1.valueAsNumber)(this.get(keyPath), policy, String(keyPath)); + } + /** + * Access a string value in this reader's contents. + * + * @param keyPath - A key path specifying where to find the value in this reader's contents. + * @returns An optional string value. + */ + asString(keyPath = key_path_1.thisKeyPath, policy = "coercible") { + return (0, coercion_1.valueAsString)(this.get(keyPath), policy, String(keyPath)); + } + // endsection + // section Sequences + /** + * Create an iterator for the contents of this reader. + * + * If the current reader's contents are `undefined` or `null`, + * the returned iterator yields nothing. + * + * If the current reader's contents is an array, the returned + * iterator will yield a reader for each element in that array. + * + * Otherwise, the iterator will yield a single reader for + * the current reader's contents. + * + * __Important:__ The readers yielded by this iterator must not + * be allowed to escape your `for`-loop. For efficiency, readers + * may be reused. + * + * An iterator consumer (`for...of` loop) may safely call select + * methods on the reader without balancing them with deselect + * calls before the getting the next reader from the iterator. + */ + *[Symbol.iterator]() { + const iteratee = this.get(); + if ((0, optional_1.isNothing)(iteratee)) { + return; + } + const iterationReader = ObjectReader._clone(this); + if (Array.isArray(iteratee)) { + let index = 0; + for (const value of iteratee) { + iterationReader.saveSelection(); + iterationReader._cursor.interject(value, index); + yield iterationReader; + iterationReader.restoreSelection(); + index += 1; + } + } + else { + yield iterationReader; + } + ObjectReader._recycle(iterationReader); + } + /** + * Returns the result of combining the contents of this reader + * using a given function. + * + * If the current reader's contents are `undefined` or `null`, + * the `initialValue` is returned unchanged. + * + * If the current reader's contents is an array, the `reducer` + * will be called with a reader for each element in that array. + * + * Otherwise, the `reducer` function will be called once with + * a reader for the current reader's contents. + * + * __Important:__ The `reducer` function must not allow the passed in + * reader to escape its body. For efficiency, readers may be reused. + * The function may safely perform call select methods without balancing + * them with matching deselect calls. + * + * @param initialValue - The value to use as the initial accumulating value. + * @param reducer - A function that combines an accumulating value and an element from this reader's contents + * into a new accumulating value, to be used in the next call of this function or returned to the caller. + */ + reduce(initialValue, reducer) { + const iteratee = this.get(); + if ((0, optional_1.isNothing)(iteratee)) { + return initialValue; + } + if (Array.isArray(iteratee)) { + try { + let value = initialValue; + for (let index = 0, length = iteratee.length; index < length; index += 1) { + this.saveSelection(); + this._cursor.interject(iteratee[index], index); + value = reducer(value, this); + this.restoreSelection(); + } + return value; + } + catch (e) { + this.restoreSelection(); + throw e; + } + } + else { + return reducer(initialValue, this); + } + } + /** + * Create an array by applying a function to the contents of this reader. + * + * If the current reader's contents are `undefined` or `null`, + * an empty array will be returned without calling `transformer`. + * + * If the current reader's contents is an array, the function will + * be called with a reader for each element from that array. + * + * Otherwise, the function will be called once with a reader for + * the current reader's contents. + * + * __Important:__ The function must not allow the passed in reader + * to escape its body. For efficiency, readers may be reused. + * The function may safely perform call select methods without balancing + * them with matching deselect calls. + * + * @param transformer - A function which derives a value from a reader. + * @returns An array containing the accumulated results of calling `transformer`. + */ + map(transformer) { + return this.reduce(new Array(), (acc, reader) => { + acc.push(transformer(reader)); + return acc; + }); + } + /** + * Create an array by applying a function to the contents of this reader, + * discarding `undefined` and `null` values returned by the function. + * + * If the current reader's contents are `undefined` or `null`, + * an empty array will be returned without calling `transformer`. + * + * If the current reader's contents is an array, the function will + * be called with a reader for each element from that array. + * + * Otherwise, the function will be called once with a reader for + * the current reader's contents. + * + * __Important:__ The function must not allow the passed in reader + * to escape its body. For efficiency, readers may be reused. + * The function may safely perform call select methods without balancing + * them with matching deselect calls. + * + * @param transformer - A function which derives a value from a reader, + * or returns a nully value if none can be derived. + * @returns An array containing the accumulated results of calling `transformer`. + */ + compactMap(transformer) { + return this.reduce(new Array(), (acc, reader) => { + const value = transformer(reader); + if ((0, optional_1.isSome)(value)) { + acc.push(value); + } + return acc; + }); + } + // endsection + // section Builders + /** + * Call a function with this reader and any number of additional parameters, + * rolling back any reader selection changes the function makes. + * + * Use this method to work with closures and top level functions which use + * an object reader to do work. Prefer `#callOn` for object methods. + * + * @param body - A function which takes a reader and any number of additional parameters. + * @param rest - The parameters to pass to `body` after this reader. + * @returns The result of `body`, if any. + */ + applyTo(body, ...rest) { + this.saveSelection(); + try { + const result = body(this, ...rest); + this.restoreSelection(); + return result; + } + catch (e) { + this.restoreSelection(); + throw e; + } + } + /** + * Call an object method with this reader and any number of additional parameters, + * rolling back any reader selection changes the method makes. + * + * Use this method to work with object methods which use an object reader to do work. + * Prefer `#applyTo` for closures and top level functions. + * + * @param method - A method which takes a reader and any number of additional parameters. + * @param thisArg - The object to be used as the current object. + * @param rest - The parameters to pass to `method` after this reader. + * @returns The result of `method`, if any. + */ + callOn(method, thisArg, ...rest) { + this.saveSelection(); + try { + const result = method.call(thisArg, this, ...rest); + this.restoreSelection(); + return result; + } + catch (e) { + this.restoreSelection(); + throw e; + } + } + // endsection + // section Cloneable + clone() { + const copy = (0, clone_1.shallowCloneOf)(this); + copy._cursor = this._cursor.clone(); + return copy; + } + // endsection + // section Reuse + /** + * Reduce allocations required when iterating with this object reader + * up to a specified depth. + * + * Each subclass of `ObjectReader` should call this method on itself + * after the module containing the subclass is loaded. + * + * @param depth - The expected iteration depth of this object reader type. + */ + static optimizeIterationUpToDepth(depth) { + for (let index = 0; index < depth; index += 1) { + ObjectReader._recycle(new ObjectReader(undefined)); + } + } + /** + * Clone a given object reader, reusing a previously created instance + * of the same constructor if one is available. + * + * @param reader - The object reader to efficiently clone. + * @returns A new reader which can be treated as clone of `reader`. + */ + static _clone(reader) { + const scrap = scrapReaders.get(reader.constructor); + if ((0, optional_1.isSome)(scrap)) { + const reclaimedReader = scrap.pop(); + if ((0, optional_1.isSome)(reclaimedReader)) { + reclaimedReader.onReuseToIterate(reader); + return reclaimedReader; + } + } + return reader.clone(); + } + /** + * Informs an object reader it is about to be reused as the value + * of another object reader which is being treated as an iterator. + * + * Subclasses _must_ call `super` when overriding this method. + * + * @param other - The reader this instance is being used to assist. + */ + onReuseToIterate(other) { + const cursorToMirror = other._cursor; + this._cursor.reuse(cursorToMirror.currentValue, cursorToMirror.currentKeyPath); + } + /** + * Recycle an object reader which was used as the value of another + * object reader being treated as an iterator. + * + * @param reader - A reader which was used for iteration and is no longer + * needed for that role. + */ + static _recycle(reader) { + const ctor = reader.constructor; + const existingScrap = scrapReaders.get(ctor); + if ((0, optional_1.isSome)(existingScrap)) { + if (existingScrap.length >= 5) { + return; + } + reader.onRecycleForIteration(); + existingScrap.push(reader); + } + else { + reader.onRecycleForIteration(); + scrapReaders.set(ctor, [reader]); + } + } + /** + * Informs an object reader it is being recycled after being used as + * the value of another object reader which was treated as an iterator. + * + * Subclasses _must_ call `super` when overriding this method. + */ + onRecycleForIteration() { + this._cursor.reuse(undefined); + } +} +exports.ObjectReader = ObjectReader; +//# sourceMappingURL=object-reader.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/json/reader/traverse.js b/node_modules/@jet/environment/json/reader/traverse.js new file mode 100644 index 0000000..9f5d1ad --- /dev/null +++ b/node_modules/@jet/environment/json/reader/traverse.js @@ -0,0 +1,36 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.traverse = void 0; +const optional_1 = require("../../types/optional"); +const key_path_1 = require("./key-path"); +function traverse(object, keyPath) { + if (typeof object !== "object") { + return object; + } + if (!(0, optional_1.isSome)(object)) { + return object; + } + const keys = (0, key_path_1.keysOf)(keyPath); + switch (keys.length) { + case 0: + return object; + case 1: + return object[keys[0]]; + default: + // eslint-disable-next-line no-case-declarations + let currentObject = object; + for (const key of keys) { + const currentValue = currentObject[key]; + if (typeof currentValue !== "object") { + return currentValue; + } + if (!(0, optional_1.isSome)(currentValue)) { + return currentValue; + } + currentObject = currentValue; + } + return currentObject; + } +} +exports.traverse = traverse; +//# sourceMappingURL=traverse.js.map
\ No newline at end of file diff --git a/node_modules/@jet/environment/json/validation.js b/node_modules/@jet/environment/json/validation.js new file mode 100644 index 0000000..b338c4e --- /dev/null +++ b/node_modules/@jet/environment/json/validation.js @@ -0,0 +1,250 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.unexpectedNull = exports.catchingContext = exports.context = exports.recordValidationIncidents = exports.endContext = exports.getContextNames = exports.beginContext = exports.messageForRecoveryAction = exports.isValidatable = exports.unexpectedType = exports.extendedTypeof = void 0; +const optional_1 = require("../types/optional"); +/** + * Returns a string containing the type of a given value. + * This function augments the built in `typeof` operator + * to return sensible values for arrays and null values. + * + * @privateRemarks + * This function is exported for testing. + * + * @param value - The value to find the type of. + * @returns A string containing the type of `value`. + */ +function extendedTypeof(value) { + if (Array.isArray(value)) { + return "array"; + } + else if (value === null) { + return "null"; + } + else { + return typeof value; + } +} +exports.extendedTypeof = extendedTypeof; +/** + * Reports a non-fatal validation failure, logging a message to the console. + * @param recovery - The recovery action taken when the bad type was found. + * @param expected - The expected type of the value. + * @param actual - The actual value. + * @param pathString - A string containing the path to the value on the object which failed type validation. + */ +function unexpectedType(recovery, expected, actual, pathString) { + const actualType = extendedTypeof(actual); + const prettyPath = (0, optional_1.isSome)(pathString) && pathString.length > 0 ? pathString : "<this>"; + trackIncident({ + type: "badType", + expected: expected, + // Our test assertions are matching the string interpolation of ${actual} value. + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + actual: `${actualType} (${actual})`, + objectPath: prettyPath, + contextNames: getContextNames(), + recoveryAction: recovery, + stack: new Error().stack, + }); +} +exports.unexpectedType = unexpectedType; +// endregion +/** + * Determines if a given object conforms to the Validatable interface + * @param possibleValidatable - An object that might be considered validatable + * + * @returns `true` if it is an instance of Validatable, `false` if not + */ +function isValidatable(possibleValidatable) { + if ((0, optional_1.isNothing)(possibleValidatable)) { + return false; + } + // MAINTAINER'S NOTE: We must check for either the existence of a pre-existing incidents + // property *or* the ability to add one. Failure to do so will cause + // problems for clients that either a) use interfaces to define their + // view models; or b) return collections from their service routes. + return (Object.prototype.hasOwnProperty.call(possibleValidatable, "$incidents") || + Object.isExtensible(possibleValidatable)); +} +exports.isValidatable = isValidatable; +/** + * Returns a developer-readable diagnostic message for a given recovery action. + * @param action - The recovery action to get the message for. + * @returns The message for `action`. + */ +function messageForRecoveryAction(action) { + switch (action) { + case "coercedValue": + return "Coerced format"; + case "defaultValue": + return "Default value used"; + case "ignoredValue": + return "Ignored value"; + default: + return "Unknown"; + } +} +exports.messageForRecoveryAction = messageForRecoveryAction; +// region Contexts +/** + * Shared validation context "stack". + * + * Because validation incidents propagate up the context stack, + * the representation used here is optimized for memory usage. + * A more literal representation of this would be a singly linked + * list describing a basic stack, but that will produce a large + * amount of unnecessary garbage and require copying `incidents` + * arrays backwards. + */ +const contextState = { + /// The names of each validation context on the stack. + nameStack: Array(), + /// All incidents reported so far. Cleared when the + /// context stack is emptied. + incidents: Array(), + // TODO: Removal of this is being tracked here: + // <rdar://problem/35015460> Intro Pricing: Un-suppress missing parent 'offers' error when server address missing key + /// The paths for incidents we wish to forgo tracking. + suppressedIncidentPaths: Array(), +}; +/** + * Begin a new validation context with a given name, + * pushing it onto the validation context stack. + * @param name - The name for the validation context. + */ +function beginContext(name) { + contextState.nameStack.push(name); +} +exports.beginContext = beginContext; +/** + * Traverses the validation context stack and collects all of the context names. + * @returns The names of all validation contexts on the stack, from oldest to newest. + */ +function getContextNames() { + if (contextState.nameStack.length === 0) { + return ["<empty stack>"]; + } + return contextState.nameStack.slice(0); +} +exports.getContextNames = getContextNames; +/** + * Ends the current validation context + */ +function endContext() { + if (contextState.nameStack.length === 0) { + console.warn("endContext() called without active validation context, ignoring"); + } + contextState.nameStack.pop(); +} +exports.endContext = endContext; +/** + * Records validation incidents back into an object that implements Validatable. + * + * Note: This method has a side-effect that the incident queue and name stack are cleared + * to prepare for the next thread's invocation. + * + * @param possibleValidatable - An object that may conform to Validatable, onto which we + * want to stash our validation incidents + */ +function recordValidationIncidents(possibleValidatable) { + if (isValidatable(possibleValidatable)) { + possibleValidatable.$incidents = contextState.incidents; + } + contextState.incidents = []; + contextState.nameStack = []; + contextState.suppressedIncidentPaths = []; +} +exports.recordValidationIncidents = recordValidationIncidents; +/** + * Create a transient validation context, and call a function that will return a value. + * + * Prefer this function over manually calling begin/endContext, + * it is exception safe. + * + * @param name - The name of the context + * @param producer - A function that produces a result + * @returns <Result> The resulting type + */ +function context(name, producer, suppressingPath) { + let suppressingName = null; + if ((0, optional_1.isSome)(suppressingPath) && suppressingPath.length > 0) { + suppressingName = name; + contextState.suppressedIncidentPaths.push(suppressingPath); + } + let result; + try { + beginContext(name); + result = producer(); + } + catch (e) { + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions + if (!e.hasThrown) { + unexpectedType("defaultValue", "no exception", e.message); + e.hasThrown = true; + } + throw e; + } + finally { + if (name === suppressingName) { + contextState.suppressedIncidentPaths.pop(); + } + endContext(); + } + return result; +} +exports.context = context; +/** + * Create a transient validation context, that catches errors and returns null + * + * @param name - The name of the context + * @param producer - A function that produces a result + * @param caught - An optional handler to provide a value when an error is caught + * @returns <Result> The resulting type + */ +function catchingContext(name, producer, caught) { + let result = null; + try { + result = context(name, producer); + } + catch (e) { + result = null; + if ((0, optional_1.isSome)(caught)) { + result = caught(e); + } + } + return result; +} +exports.catchingContext = catchingContext; +/** + * Track an incident within the current validation context. + * @param incident - An incident object describing the problem. + */ +function trackIncident(incident) { + if (contextState.suppressedIncidentPaths.includes(incident.objectPath)) { + return; + } + contextState.incidents.push(incident); +} +// endregion +// region Nullability +/** + * Reports a non-fatal error indicating a value was unexpectedly null. + * @param recovery - The recovery action taken when the null value was found. + * @param expected - The expected type of the value. + * @param pathString - A string containing the path to the value on the object which was null. + */ +function unexpectedNull(recovery, expected, pathString) { + const prettyPath = (0, optional_1.isSome)(pathString) && pathString.length > 0 ? pathString : "<this>"; + trackIncident({ + type: "nullValue", + expected: expected, + actual: "null", + objectPath: prettyPath, + contextNames: getContextNames(), + recoveryAction: recovery, + stack: new Error().stack, + }); +} +exports.unexpectedNull = unexpectedNull; +// endregion +//# sourceMappingURL=validation.js.map
\ No newline at end of file |
