From bce557cc2dc767628bed6aac87301a1be7c5431b Mon Sep 17 00:00:00 2001 From: rxliuli Date: Tue, 4 Nov 2025 05:03:50 +0800 Subject: init commit --- node_modules/@jet/environment/json/validation.js | 250 +++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 node_modules/@jet/environment/json/validation.js (limited to 'node_modules/@jet/environment/json/validation.js') 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 : ""; + 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: + // 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 [""]; + } + 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 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 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 : ""; + 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 -- cgit v1.2.3