+1058
| "use strict"; | ||
| var __defProp = Object.defineProperty; | ||
| var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
| var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
| Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); | ||
| const TYPED_ARRAY_MAP = { | ||
| uint8: Uint8Array, | ||
| uint8clamped: Uint8ClampedArray, | ||
| uint16: Uint16Array, | ||
| uint32: Uint32Array, | ||
| int8: Int8Array, | ||
| int16: Int16Array, | ||
| int32: Int32Array, | ||
| float32: Float32Array, | ||
| float64: Float64Array | ||
| }; | ||
| function getIsTypedArray(value) { | ||
| for (const type of Object.values(TYPED_ARRAY_MAP)) { | ||
| if (value instanceof type) return true; | ||
| } | ||
| return false; | ||
| } | ||
| function getTypedArrayType(value) { | ||
| for (const name of Object.keys(TYPED_ARRAY_MAP)) { | ||
| if (value instanceof TYPED_ARRAY_MAP[name]) | ||
| return name; | ||
| } | ||
| throw new Error(`Unknown typed array type: ${value}`); | ||
| } | ||
| function getTypedArrayConstructor(type) { | ||
| const TypedArrayClass = TYPED_ARRAY_MAP[type]; | ||
| if (!TypedArrayClass) { | ||
| throw new Error(`Unknown typed array type: ${type}`); | ||
| } | ||
| return TypedArrayClass; | ||
| } | ||
| function getSpecialNumberType(value) { | ||
| switch (value) { | ||
| case Infinity: | ||
| return "Infinity"; | ||
| case -Infinity: | ||
| return "-Infinity"; | ||
| case 0: | ||
| if (1 / value === -Infinity) return "-0"; | ||
| } | ||
| if (isNaN(value)) return "NaN"; | ||
| return null; | ||
| } | ||
| function decodeSpecialNumber(value) { | ||
| switch (value) { | ||
| case "NaN": | ||
| return NaN; | ||
| case "Infinity": | ||
| return Infinity; | ||
| case "-Infinity": | ||
| return -Infinity; | ||
| case "-0": | ||
| return -0; | ||
| default: | ||
| throw new Error(`Invalid special number type: ${value}`); | ||
| } | ||
| } | ||
| function resolveThunk(thunk) { | ||
| let result = thunk; | ||
| while (typeof result === "function") { | ||
| result = result(); | ||
| } | ||
| return result; | ||
| } | ||
| function getSymbolKey(symbol) { | ||
| const nativeKey = Symbol.keyFor(symbol); | ||
| if (nativeKey) return nativeKey; | ||
| const toStringResult = symbol.toString(); | ||
| return toStringResult.slice(7, -1); | ||
| } | ||
| function removeUndefinedProperties(input) { | ||
| const result = {}; | ||
| for (const key of Object.keys(input)) { | ||
| const value = input[key]; | ||
| if (value !== void 0) { | ||
| result[key] = value; | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| function createTag(name, data) { | ||
| const tag = {}; | ||
| tag[`$$${name}`] = data; | ||
| return tag; | ||
| } | ||
| const DEFAULT_PRIORITY = 10; | ||
| class CodableType { | ||
| constructor(definition) { | ||
| __publicField(this, "name"); | ||
| __publicField(this, "priority"); | ||
| __publicField(this, "dependencies"); | ||
| __publicField(this, "tagKey"); | ||
| var _a, _b; | ||
| this.definition = definition; | ||
| this.name = definition.name; | ||
| this.tagKey = `$$${this.name}`; | ||
| this.priority = ((_a = definition.options) == null ? void 0 : _a.priority) ?? DEFAULT_PRIORITY; | ||
| this.dependencies = ((_b = definition.options) == null ? void 0 : _b.dependencies) ?? null; | ||
| } | ||
| encode(value, context) { | ||
| return this.definition.encode(value, context); | ||
| } | ||
| encodeTag(value, context) { | ||
| return createTag(this.name, this.encode(value, context)); | ||
| } | ||
| decode(data, context) { | ||
| return this.definition.decode(data, context); | ||
| } | ||
| canHandle(value) { | ||
| return this.definition.canHandle(value); | ||
| } | ||
| createTag(data) { | ||
| return createTag(this.name, data); | ||
| } | ||
| } | ||
| function createCodableType(name, canHandle, encode2, decode2, options) { | ||
| return new CodableType({ | ||
| name, | ||
| canHandle, | ||
| encode: encode2, | ||
| decode: decode2, | ||
| options | ||
| }); | ||
| } | ||
| function getIsCodableType(value) { | ||
| return value instanceof CodableType; | ||
| } | ||
| function getErrorExtraProperties(error) { | ||
| const properties = {}; | ||
| let isEmpty = true; | ||
| for (const key of Object.keys(error)) { | ||
| if (key === "message" || key === "name" || key === "cause") continue; | ||
| properties[key] = error[key]; | ||
| isEmpty = false; | ||
| } | ||
| if (isEmpty) return null; | ||
| return properties; | ||
| } | ||
| function getIsValidDate(date) { | ||
| return !isNaN(date.getTime()); | ||
| } | ||
| const $$date = createCodableType( | ||
| "Date", | ||
| (value) => value instanceof Date, | ||
| (date) => { | ||
| if (!getIsValidDate(date)) return null; | ||
| return date.toISOString(); | ||
| }, | ||
| (maybeISOString) => { | ||
| if (maybeISOString === null) return /* @__PURE__ */ new Date("invalid"); | ||
| return new Date(maybeISOString); | ||
| } | ||
| ); | ||
| const $$set = createCodableType( | ||
| "Set", | ||
| (value) => value instanceof Set, | ||
| (set) => [...set], | ||
| (array) => new Set(array) | ||
| ); | ||
| const $$map = createCodableType( | ||
| "Map", | ||
| (value) => value instanceof Map, | ||
| (map) => [...map.entries()], | ||
| (entries) => new Map(entries) | ||
| ); | ||
| const $$error = createCodableType( | ||
| "Error", | ||
| (value) => value instanceof Error, | ||
| (error, context) => { | ||
| var _a; | ||
| const shouldIncludeErrorStack = ((_a = context.options) == null ? void 0 : _a.includeErrorStack) ?? false; | ||
| const extraProperties = getErrorExtraProperties(error) ?? void 0; | ||
| const name = error.name && error.name !== "Error" ? error.name : void 0; | ||
| const cause = error.cause; | ||
| const message = error.message; | ||
| const stack = shouldIncludeErrorStack ? error.stack : void 0; | ||
| if (!extraProperties && !name && !cause && !stack) { | ||
| return message; | ||
| } | ||
| return removeUndefinedProperties({ | ||
| message, | ||
| name, | ||
| cause, | ||
| properties: extraProperties, | ||
| stack | ||
| }); | ||
| }, | ||
| (messageOrData) => { | ||
| if (typeof messageOrData === "string") return new Error(messageOrData); | ||
| const { message, name, cause, properties, stack } = messageOrData; | ||
| const error = new Error(message, { cause }); | ||
| if (stack) { | ||
| error.stack = stack; | ||
| } | ||
| if (name && name !== "Error") { | ||
| error.name = name; | ||
| } | ||
| if (properties) { | ||
| Object.assign(error, properties); | ||
| } | ||
| return error; | ||
| } | ||
| ); | ||
| const $$undefined = createCodableType( | ||
| "undefined", | ||
| (value) => value === void 0, | ||
| () => null, | ||
| () => void 0 | ||
| ); | ||
| const $$bigInt = createCodableType( | ||
| "BigInt", | ||
| (value) => typeof value === "bigint", | ||
| (bigInt) => bigInt.toString(), | ||
| (string) => BigInt(string) | ||
| ); | ||
| const $$regexp = createCodableType( | ||
| "RegExp", | ||
| (value) => value instanceof RegExp, | ||
| ({ source, flags }) => { | ||
| if (flags) return [source, flags]; | ||
| return source; | ||
| }, | ||
| (sourceOrSourceAndFlags) => { | ||
| if (typeof sourceOrSourceAndFlags === "string") { | ||
| return new RegExp(sourceOrSourceAndFlags); | ||
| } | ||
| const [source, flags] = sourceOrSourceAndFlags; | ||
| return new RegExp(source, flags); | ||
| } | ||
| ); | ||
| const $$url = createCodableType( | ||
| "URL", | ||
| (value) => value instanceof URL, | ||
| (url) => url.toString(), | ||
| (string) => new URL(string) | ||
| ); | ||
| const symbolsRegistry = /* @__PURE__ */ new Map(); | ||
| const $$symbol = createCodableType( | ||
| "Symbol", | ||
| (value) => typeof value === "symbol", | ||
| (symbol) => { | ||
| const symbolKey = getSymbolKey(symbol); | ||
| symbolsRegistry.set(symbolKey, symbol); | ||
| return symbolKey; | ||
| }, | ||
| (symbolKey) => symbolsRegistry.get(symbolKey) ?? Symbol.for(symbolKey) | ||
| ); | ||
| const $$typedArray = createCodableType( | ||
| "typedArray", | ||
| getIsTypedArray, | ||
| (value) => { | ||
| return { | ||
| type: getTypedArrayType(value), | ||
| data: [...value] | ||
| }; | ||
| }, | ||
| ({ type, data }) => { | ||
| return new (getTypedArrayConstructor(type))(data); | ||
| } | ||
| ); | ||
| const $$num = createCodableType( | ||
| "num", | ||
| (value) => typeof value === "number" && !!getSpecialNumberType(value), | ||
| getSpecialNumberType, | ||
| decodeSpecialNumber | ||
| ); | ||
| const $$urlSearchParams = createCodableType( | ||
| "URLSearchParams", | ||
| (value) => value instanceof URLSearchParams, | ||
| (urlSearchParams) => urlSearchParams.toString(), | ||
| (string) => new URLSearchParams(string) | ||
| ); | ||
| const builtinTypesMap = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ | ||
| __proto__: null, | ||
| $$bigInt, | ||
| $$date, | ||
| $$error, | ||
| $$map, | ||
| $$num, | ||
| $$regexp, | ||
| $$set, | ||
| $$symbol, | ||
| $$typedArray, | ||
| $$undefined, | ||
| $$url, | ||
| $$urlSearchParams | ||
| }, Symbol.toStringTag, { value: "Module" })); | ||
| function getTagValue(tag) { | ||
| return Object.values(tag)[0]; | ||
| } | ||
| const OBJECT_PROTOTYPE = Object.prototype; | ||
| function getCodableTypeOf(input) { | ||
| if (input === null) return "primitive"; | ||
| if (input === void 0) return "undefined"; | ||
| const typeOfInput = typeof input; | ||
| switch (typeOfInput) { | ||
| case "boolean": | ||
| case "string": | ||
| return "primitive"; | ||
| case "number": | ||
| if (getSpecialNumberType(input)) return "special-number"; | ||
| return "primitive"; | ||
| case "symbol": | ||
| return "symbol"; | ||
| case "bigint": | ||
| return "bigint"; | ||
| case "function": | ||
| return "function"; | ||
| } | ||
| if (Array.isArray(input)) return "array"; | ||
| const inputPrototype = Object.getPrototypeOf(input); | ||
| if (inputPrototype === OBJECT_PROTOTYPE || inputPrototype === null) { | ||
| return "record"; | ||
| } | ||
| return "custom-object"; | ||
| } | ||
| const ANY_TAG_KEY_REGEXP = /^~*\$\$.+/; | ||
| function getDecodableTypeOf(input, context) { | ||
| switch (typeof input) { | ||
| case "boolean": | ||
| case "string": | ||
| case "number": | ||
| return "primitive"; | ||
| case "symbol": | ||
| case "bigint": | ||
| case "function": | ||
| case "undefined": | ||
| throw new Error(`undefined is not a valid JSON value`); | ||
| } | ||
| if (input === null) return "primitive"; | ||
| if (Array.isArray(input)) return "array"; | ||
| const keys = Object.keys(input); | ||
| if (keys.length !== 1) return "record"; | ||
| const key = keys[0]; | ||
| if (key.startsWith("$$")) { | ||
| if (key === "$$") return "record"; | ||
| if (key === "$$ref") { | ||
| if (typeof input.$$ref === "string") return "ref-tag"; | ||
| return "record"; | ||
| } | ||
| return key; | ||
| } | ||
| if (ANY_TAG_KEY_REGEXP.test(key)) return "escaped-tag"; | ||
| return "record"; | ||
| } | ||
| function assert(condition, message) { | ||
| if (!condition) { | ||
| if (message instanceof Error) { | ||
| throw message; | ||
| } | ||
| throw new Error(message); | ||
| } | ||
| } | ||
| function assertGet(value, message) { | ||
| assert(value !== null && value !== void 0, message); | ||
| return value; | ||
| } | ||
| function analyzeEncodedData(data, context) { | ||
| const decodableTypeOf = getDecodableTypeOf(data); | ||
| switch (decodableTypeOf) { | ||
| case "primitive": | ||
| return; | ||
| case "ref-tag": | ||
| context.presentRefAliases.add(data.$$ref); | ||
| return; | ||
| case "array": | ||
| for (let i = 0; i < data.length; i++) { | ||
| analyzeEncodedData(data[i], context); | ||
| } | ||
| return; | ||
| case "record": | ||
| for (const key of Object.keys(data)) { | ||
| analyzeEncodedData(data[key], context); | ||
| } | ||
| return; | ||
| case "escaped-tag": | ||
| context.hasEscapedTags = true; | ||
| analyzeEncodedData(getTagValue(data), context); | ||
| return; | ||
| } | ||
| context.hasCustomTypes = true; | ||
| analyzeEncodedData(data[decodableTypeOf], context); | ||
| } | ||
| class DecodeContext { | ||
| constructor(data, options) { | ||
| __publicField(this, "hasEscapedTags", false); | ||
| __publicField(this, "hasCustomTypes", false); | ||
| // Is ready instantly after analyzing the data | ||
| __publicField(this, "presentRefAliases", /* @__PURE__ */ new Set()); | ||
| // Will be filled while decoding the data | ||
| __publicField(this, "resolvedRefs", /* @__PURE__ */ new Map()); | ||
| __publicField(this, "hasRefAliases"); | ||
| __publicField(this, "externalReferencesMap"); | ||
| this.options = options; | ||
| analyzeEncodedData(data, this); | ||
| this.externalReferencesMap = new Map(Object.entries((options == null ? void 0 : options.externalReferences) ?? {})); | ||
| this.hasRefAliases = this.presentRefAliases.size > 0; | ||
| } | ||
| /** | ||
| * No custom types, no ref aliases, no escaped tags. Encoded data is regular JSON-compatible. | ||
| */ | ||
| get isPlainJSON() { | ||
| return !this.hasEscapedTags && !this.hasCustomTypes && !this.hasRefAliases; | ||
| } | ||
| /** | ||
| * Some object needed by some alias (list prepared before) is ready to be used. | ||
| */ | ||
| registerRef(path, object) { | ||
| if (!this.hasRefAliases || !this.presentRefAliases.has(path)) return; | ||
| this.resolvedRefs.set(path, object); | ||
| } | ||
| /** | ||
| * Alias requested value it referenced | ||
| */ | ||
| resolveRefAlias(path) { | ||
| return this.resolvedRefs.get(path) ?? null; | ||
| } | ||
| } | ||
| class EncodeContext { | ||
| constructor(options) { | ||
| __publicField(this, "unknownMode"); | ||
| __publicField(this, "preserveReferences"); | ||
| __publicField(this, "refFirstSeenPath", /* @__PURE__ */ new Map()); | ||
| this.options = options; | ||
| this.unknownMode = (options == null ? void 0 : options.unknownInputMode) ?? "null"; | ||
| this.preserveReferences = (options == null ? void 0 : options.preserveReferences) ?? true; | ||
| } | ||
| /** | ||
| * Call it the first time some object is seen. | ||
| */ | ||
| registerNewSeenObject(object, path) { | ||
| this.refFirstSeenPath.set(object, path); | ||
| } | ||
| /** | ||
| * Returns where the object was first seen at. | ||
| */ | ||
| getAlreadySeenObjectPath(object) { | ||
| return this.refFirstSeenPath.get(object) ?? null; | ||
| } | ||
| } | ||
| function getMetadataKey(Class) { | ||
| return Class[Symbol.metadata] ?? null; | ||
| } | ||
| if (!Symbol.metadata) { | ||
| Reflect.set(Symbol, "metadata", Symbol.for("Symbol.metadata")); | ||
| } | ||
| class PrivateMetadata { | ||
| constructor() { | ||
| __publicField(this, "registry", /* @__PURE__ */ new WeakMap()); | ||
| } | ||
| getFor(Class) { | ||
| const key = getMetadataKey(Class); | ||
| if (!key) return null; | ||
| return this.get(key); | ||
| } | ||
| get(key) { | ||
| return this.registry.get(key) ?? null; | ||
| } | ||
| getOrInit(key, initializer) { | ||
| if (this.registry.has(key)) return this.registry.get(key); | ||
| const value = initializer(); | ||
| this.registry.set(key, value); | ||
| return value; | ||
| } | ||
| has(key) { | ||
| return this.registry.has(key); | ||
| } | ||
| set(key, value) { | ||
| this.registry.set(key, value); | ||
| } | ||
| init(key, value) { | ||
| if (this.registry.has(key)) return this.registry.get(key); | ||
| this.registry.set(key, value); | ||
| return value; | ||
| } | ||
| } | ||
| const codableClassRegistry = new PrivateMetadata(); | ||
| const codableClassFieldsRegistry = new PrivateMetadata(); | ||
| const externalClassFieldsRegistry = new PrivateMetadata(); | ||
| function registerCodableClass(key, metadata) { | ||
| return codableClassRegistry.init(key, metadata); | ||
| } | ||
| function getIsCodableClass(Class) { | ||
| const key = getMetadataKey(Class); | ||
| if (!key) return false; | ||
| return codableClassRegistry.has(key); | ||
| } | ||
| function getCodableClassType(Class) { | ||
| var _a; | ||
| const key = getMetadataKey(Class); | ||
| if (!key) return null; | ||
| const codableType = (_a = codableClassRegistry.get(key)) == null ? void 0 : _a.codableType; | ||
| if (!codableType) return null; | ||
| return codableType; | ||
| } | ||
| class ExternalReference { | ||
| constructor(key, isOptional = false) { | ||
| this.key = key; | ||
| this.isOptional = isOptional; | ||
| } | ||
| } | ||
| function externalReference(key, isOptional = false) { | ||
| return new ExternalReference(key, isOptional); | ||
| } | ||
| const $$externalReference = createCodableType( | ||
| "external", | ||
| (value) => value instanceof ExternalReference, | ||
| (ref) => { | ||
| return { key: ref.key, isOptional: ref.isOptional }; | ||
| }, | ||
| ({ key, isOptional }, context) => { | ||
| if (!context.externalReferencesMap.has(key)) { | ||
| if (isOptional) return void 0; | ||
| throw new Error(`External reference "${key}" not found`); | ||
| } | ||
| return context.externalReferencesMap.get(key); | ||
| } | ||
| ); | ||
| function getIsForbiddenProperty(property) { | ||
| switch (property) { | ||
| case "__proto__": | ||
| case "prototype": | ||
| case "constructor": | ||
| return true; | ||
| default: | ||
| return false; | ||
| } | ||
| } | ||
| function getIsRecord(value) { | ||
| if (!value) return false; | ||
| const valuePrototype = Object.getPrototypeOf(value); | ||
| return valuePrototype === Object.prototype || valuePrototype === null; | ||
| } | ||
| function getIsObject(value) { | ||
| return typeof value === "object" && value !== null; | ||
| } | ||
| function getIsNotNull(value) { | ||
| return value !== null; | ||
| } | ||
| function copyJSON(json) { | ||
| if (Array.isArray(json)) { | ||
| const result = []; | ||
| for (let index = 0; index < json.length; index++) { | ||
| result[index] = copyJSON(json[index]); | ||
| } | ||
| return result; | ||
| } else if (getIsRecord(json)) { | ||
| const result = {}; | ||
| for (const key of Object.keys(json)) { | ||
| if (getIsForbiddenProperty(key)) continue; | ||
| result[key] = copyJSON(json[key]); | ||
| } | ||
| return result; | ||
| } | ||
| return json; | ||
| } | ||
| function escapePathSegment(segment) { | ||
| if (typeof segment === "number") return `${segment}`; | ||
| return segment.replaceAll("~", "~0").replaceAll("/", "~1"); | ||
| } | ||
| function addPathSegment(currentPointer, newSegment) { | ||
| switch (currentPointer) { | ||
| case "/": | ||
| case "": | ||
| return `/${escapePathSegment(newSegment)}`; | ||
| default: | ||
| return `${currentPointer}/${escapePathSegment(newSegment)}`; | ||
| } | ||
| } | ||
| function resolveRefAlias(input, context, currentPath) { | ||
| const referencedObject = context.resolveRefAlias(input.$$ref); | ||
| if (!referencedObject) { | ||
| console.warn(`Reference could not be resolved while decoding (${input.$$ref}) at ${currentPath}`); | ||
| return null; | ||
| } | ||
| return referencedObject; | ||
| } | ||
| function resolveTypeTag(tag, tagKey, context, coder, path) { | ||
| const typeName = tagKey.slice(2); | ||
| const matchingType = coder.getTypeByName(typeName); | ||
| if (!matchingType) { | ||
| console.warn(`Unknown custom type: ${typeName} at ${path}. Returning the raw value.`); | ||
| return tag[tagKey]; | ||
| } | ||
| const decodedTypeInput = decodeInput(tag[tagKey], context, coder, addPathSegment(path, tagKey)); | ||
| return matchingType.decode(decodedTypeInput, context); | ||
| } | ||
| function decodeArray(input, context, coder, path) { | ||
| const result = []; | ||
| context.registerRef(path, result); | ||
| for (let key = 0; key < input.length; key++) { | ||
| const fullPath = addPathSegment(path, key); | ||
| const decoded = decodeInput(input[key], context, coder, fullPath); | ||
| result[key] = decoded; | ||
| if (context.hasRefAliases && getIsObject(decoded)) { | ||
| context.registerRef(fullPath, decoded); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| function decodeRecord(input, context, coder, path) { | ||
| const result = {}; | ||
| context.registerRef(path, result); | ||
| for (const key of Object.keys(input)) { | ||
| if (getIsForbiddenProperty(key)) continue; | ||
| const fullPath = addPathSegment(path, key); | ||
| const decoded = decodeInput(input[key], context, coder, fullPath); | ||
| result[key] = decoded; | ||
| if (context.hasRefAliases && getIsObject(decoded)) { | ||
| context.registerRef(fullPath, decoded); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| function unescapeTag(input) { | ||
| const key = Object.keys(input)[0]; | ||
| return { | ||
| [key.slice(1)]: input[key] | ||
| }; | ||
| } | ||
| function decodeInput(input, context, coder, path) { | ||
| let decodableTypeOf = getDecodableTypeOf(input); | ||
| switch (decodableTypeOf) { | ||
| case "escaped-tag": { | ||
| input = unescapeTag(input); | ||
| decodableTypeOf = "record"; | ||
| } | ||
| case "primitive": { | ||
| return input; | ||
| } | ||
| case "ref-tag": { | ||
| return resolveRefAlias(input, context, path); | ||
| } | ||
| case "array": { | ||
| return decodeArray(input, context, coder, path); | ||
| } | ||
| case "record": { | ||
| return decodeRecord(input, context, coder, path); | ||
| } | ||
| } | ||
| return resolveTypeTag(input, decodableTypeOf, context, coder, path); | ||
| } | ||
| function getShouldEscapeKey(key) { | ||
| return /^~*\$\$/.test(key); | ||
| } | ||
| function encodeInput(input, encodeContext, coder, path) { | ||
| const codableTypeOf = getCodableTypeOf(input); | ||
| switch (codableTypeOf) { | ||
| case "primitive": | ||
| return input; | ||
| case "special-number": | ||
| return $$num.encodeTag(input, encodeContext); | ||
| case "symbol": | ||
| return $$symbol.encodeTag(input, encodeContext); | ||
| case "bigint": | ||
| return $$bigInt.encodeTag(input, encodeContext); | ||
| case "undefined": | ||
| return $$undefined.encodeTag(input, encodeContext); | ||
| case "function": | ||
| return null; | ||
| } | ||
| if (encodeContext.preserveReferences) { | ||
| const alreadySeenAtPath = encodeContext.getAlreadySeenObjectPath(input); | ||
| if (alreadySeenAtPath !== null) { | ||
| return createTag("ref", alreadySeenAtPath); | ||
| } | ||
| encodeContext.registerNewSeenObject(input, path); | ||
| } | ||
| if (codableTypeOf === "custom-object") { | ||
| const matchingType = coder.getMatchingTypeFor(input); | ||
| if (!matchingType) { | ||
| switch (encodeContext.unknownMode) { | ||
| case "unchanged": | ||
| return input; | ||
| case "null": | ||
| return null; | ||
| case "throw": | ||
| throw new Error("Not able to encode - no matching type found", input); | ||
| } | ||
| } | ||
| return matchingType.createTag( | ||
| encodeInput( | ||
| matchingType.encode(input, encodeContext), | ||
| encodeContext, | ||
| coder, | ||
| addPathSegment(path, matchingType.tagKey) | ||
| ) | ||
| ); | ||
| } | ||
| if (codableTypeOf === "array") { | ||
| const result2 = []; | ||
| for (let i = 0; i < input.length; i++) { | ||
| result2[i] = encodeInput(input[i], encodeContext, coder, addPathSegment(path, i)); | ||
| } | ||
| return result2; | ||
| } | ||
| const keys = Object.keys(input); | ||
| if (keys.length === 1 && getShouldEscapeKey(keys[0])) { | ||
| input = { [`~${keys[0]}`]: input[keys[0]] }; | ||
| keys[0] = `~${keys[0]}`; | ||
| } | ||
| const result = {}; | ||
| for (const key of keys) { | ||
| if (getIsForbiddenProperty(key)) continue; | ||
| result[key] = encodeInput(input[key], encodeContext, coder, addPathSegment(path, key)); | ||
| } | ||
| return result; | ||
| } | ||
| function resolveDependency(dependency) { | ||
| if (getIsCodableType(dependency)) return dependency; | ||
| const codableClassType = getCodableClassType(dependency); | ||
| if (!codableClassType) return null; | ||
| return codableClassType; | ||
| } | ||
| function getDirectDependencies(dependency) { | ||
| if (getIsCodableType(dependency)) { | ||
| return resolveThunk(dependency.dependencies); | ||
| } | ||
| const codableClassType = getCodableClassType(dependency); | ||
| if (!codableClassType) return null; | ||
| return resolveThunk(codableClassType.dependencies); | ||
| } | ||
| function addToSet(source, values) { | ||
| for (const value of values) { | ||
| source.add(value); | ||
| } | ||
| } | ||
| function resolveCodableDependencies(dependency) { | ||
| const resolvedDependencies = /* @__PURE__ */ new Set(); | ||
| let dependenciesToCheck = /* @__PURE__ */ new Set([dependency]); | ||
| while (true) { | ||
| if (dependenciesToCheck.size === 0) break; | ||
| for (const dependency2 of dependenciesToCheck) { | ||
| dependenciesToCheck.delete(dependency2); | ||
| if (resolvedDependencies.has(dependency2)) continue; | ||
| resolvedDependencies.add(dependency2); | ||
| const nestedDependencies = getDirectDependencies(dependency2); | ||
| if (!nestedDependencies) continue; | ||
| addToSet(resolvedDependencies, nestedDependencies); | ||
| } | ||
| } | ||
| resolvedDependencies.delete(dependency); | ||
| const resolvedTypes = Array.from(resolvedDependencies).map(resolveDependency).filter(getIsNotNull); | ||
| return new Set(resolvedTypes); | ||
| } | ||
| const DEFAULT_TYPES = [...Object.values(builtinTypesMap), $$externalReference].filter(getIsCodableType); | ||
| function getSortedTypes(types) { | ||
| return types.sort((a, b) => { | ||
| return b.priority - a.priority; | ||
| }); | ||
| } | ||
| function createTypesMap(types) { | ||
| const sortedTypes = getSortedTypes(types); | ||
| const map = /* @__PURE__ */ new Map(); | ||
| for (const type of sortedTypes) { | ||
| if (map.has(type.name)) { | ||
| throw new Error(`Coder type "${type.name}" already registered`); | ||
| } | ||
| map.set(type.name, type); | ||
| } | ||
| return map; | ||
| } | ||
| function updateTypesOrderByPriority(currentTypes) { | ||
| const sortedTypes = getSortedTypes([...currentTypes.values()]); | ||
| const needsReordering = sortedTypes.some((type) => type.priority !== 0); | ||
| if (!needsReordering) return; | ||
| currentTypes.clear(); | ||
| for (const type of sortedTypes) { | ||
| currentTypes.set(type.name, type); | ||
| } | ||
| } | ||
| class Coder { | ||
| constructor(extraTypes = []) { | ||
| __publicField(this, "typesMap", /* @__PURE__ */ new Map()); | ||
| this.typesMap = createTypesMap([...DEFAULT_TYPES]); | ||
| this.register(...extraTypes); | ||
| } | ||
| reorderTypes() { | ||
| updateTypesOrderByPriority(this.typesMap); | ||
| } | ||
| getTypeByName(name) { | ||
| return this.typesMap.get(name) ?? null; | ||
| } | ||
| getHasType(type) { | ||
| const existingType = this.getTypeByName(type.name); | ||
| return type === existingType; | ||
| } | ||
| registerSingleType(type) { | ||
| if (this.isDefault) { | ||
| throw new Error( | ||
| "Cannot register types on the default coder. Create a custom coder instance using `new Coder()` and register types on that instance." | ||
| ); | ||
| } | ||
| if (this.getHasType(type)) return; | ||
| if (this.typesMap.has(type.name)) { | ||
| throw new Error(`Other codable type with name "${type.name}" already registered`); | ||
| } | ||
| this.typesMap.set(type.name, type); | ||
| const dependencies = resolveCodableDependencies(type); | ||
| for (const dependency of dependencies) { | ||
| this.registerSingleType(dependency); | ||
| } | ||
| this.reorderTypes(); | ||
| } | ||
| registerType(...types) { | ||
| for (const type of types) { | ||
| this.registerSingleType(type); | ||
| } | ||
| } | ||
| registerSingle(typeOrClass) { | ||
| const typeToAdd = getIsCodableClass(typeOrClass) ? assertGet(getCodableClassType(typeOrClass), `Codable class "${typeOrClass.name}" not registered`) : typeOrClass; | ||
| return this.registerSingleType(typeToAdd); | ||
| } | ||
| register(...typesOrClasses) { | ||
| for (const typeOrClass of typesOrClasses) { | ||
| this.registerSingle(typeOrClass); | ||
| } | ||
| } | ||
| /** | ||
| * Typescript-sugar over `.registerType()` with better type inference. | ||
| */ | ||
| addType(name, canEncode, encode2, decode2, options) { | ||
| return this.registerType(createCodableType(name, canEncode, encode2, decode2, options)); | ||
| } | ||
| encode(value, options) { | ||
| const encodeContext = new EncodeContext(options); | ||
| return encodeInput(value, encodeContext, this, "/"); | ||
| } | ||
| decode(value, options) { | ||
| const context = new DecodeContext(value, options); | ||
| if (context.isPlainJSON) return copyJSON(value); | ||
| return decodeInput(value, context, this, "/"); | ||
| } | ||
| stringify(value) { | ||
| return JSON.stringify(this.encode(value)); | ||
| } | ||
| parse(value) { | ||
| return this.decode(JSON.parse(value)); | ||
| } | ||
| copy(value) { | ||
| return this.decode(this.encode(value)); | ||
| } | ||
| getMatchingTypeFor(input) { | ||
| for (const type of this.typesMap.values()) { | ||
| if (type.canHandle(input)) { | ||
| return type; | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
| get isDefault() { | ||
| return this === defaultCoder; | ||
| } | ||
| } | ||
| function createCoder(extraTypes = []) { | ||
| return new Coder(extraTypes); | ||
| } | ||
| const defaultCoder = createCoder(); | ||
| function decode(value, options) { | ||
| return defaultCoder.decode(value, options); | ||
| } | ||
| function encode(value) { | ||
| return defaultCoder.encode(value); | ||
| } | ||
| function stringify(value) { | ||
| return defaultCoder.stringify(value); | ||
| } | ||
| function parse(value) { | ||
| return defaultCoder.parse(value); | ||
| } | ||
| function getRegisteredCodableFields(Class) { | ||
| return codableClassFieldsRegistry.getFor(Class); | ||
| } | ||
| function resolveCodableOptions(options) { | ||
| if (typeof options === "string") { | ||
| return { encodeAs: options }; | ||
| } | ||
| return options ?? null; | ||
| } | ||
| function codable(optionsInput) { | ||
| const options = resolveCodableOptions(optionsInput); | ||
| return function codable2(initialValue, context) { | ||
| var _a; | ||
| if (context.kind !== "accessor" && context.kind !== "field") { | ||
| throw new Error("Codable decorator can only be used on fields or accessors"); | ||
| } | ||
| const isSymbolName = typeof context.name === "symbol"; | ||
| if (isSymbolName) throw new Error("Symbol property names are not supported"); | ||
| if ((_a = externalClassFieldsRegistry.get(context.metadata)) == null ? void 0 : _a.has(context.name)) { | ||
| throw new Error("Codable decorator cannot be used on external properties"); | ||
| } | ||
| const fieldsMap = codableClassFieldsRegistry.getOrInit(context.metadata, () => /* @__PURE__ */ new Map()); | ||
| fieldsMap.set(context.name, removeUndefinedProperties({ encodeAs: options == null ? void 0 : options.encodeAs })); | ||
| }; | ||
| } | ||
| function* iteratePrototypeChain(Class) { | ||
| let current = Class; | ||
| while (current !== null && current !== Function.prototype && current !== Object.prototype) { | ||
| yield current; | ||
| current = Object.getPrototypeOf(current); | ||
| } | ||
| } | ||
| function getPrototypeChainLength(Class) { | ||
| return [...iteratePrototypeChain(Class)].length; | ||
| } | ||
| function collectRegisteredCodableFields(Class, keysMap) { | ||
| for (const ClassInPrototype of iteratePrototypeChain(Class)) { | ||
| const registeredKeysMap = getRegisteredCodableFields(ClassInPrototype); | ||
| if (!registeredKeysMap) continue; | ||
| for (const [key, metadata] of registeredKeysMap.entries()) { | ||
| keysMap.set(key, metadata); | ||
| } | ||
| } | ||
| if (keysMap.size === 0) return null; | ||
| return keysMap; | ||
| } | ||
| function getCodableProperties(Class) { | ||
| const keysMap = /* @__PURE__ */ new Map(); | ||
| collectRegisteredCodableFields(Class, keysMap); | ||
| return keysMap; | ||
| } | ||
| function getExternalProperties(Class) { | ||
| const externalFieldsMap = externalClassFieldsRegistry.getFor(Class); | ||
| if (!externalFieldsMap) return null; | ||
| return externalFieldsMap; | ||
| } | ||
| function mergeMaps(map1, map2) { | ||
| const result = new Map(map1); | ||
| for (const [key, value] of map2.entries()) { | ||
| result.set(key, value); | ||
| } | ||
| return result; | ||
| } | ||
| function getFieldsInfo(Class, fieldsFromOptions) { | ||
| const fieldsFromProperties = getCodableProperties(Class); | ||
| return mergeMaps(fieldsFromProperties, fieldsFromOptions); | ||
| } | ||
| function createDefaultClassEncoder(Class, fieldsFromOptions) { | ||
| let fields = null; | ||
| return (instance) => { | ||
| const externalKeysMap = getExternalProperties(Class); | ||
| const data = {}; | ||
| if (!fields) { | ||
| fields = getFieldsInfo(Class, fieldsFromOptions); | ||
| } | ||
| for (const [instanceKey, metadata] of fields.entries()) { | ||
| const encodeAs = metadata.encodeAs ?? instanceKey; | ||
| data[encodeAs] = instance[instanceKey]; | ||
| } | ||
| if (externalKeysMap) { | ||
| for (const [key, externalRef] of externalKeysMap.entries()) { | ||
| data[key] = externalReference(externalRef.key, externalRef.isOptional); | ||
| } | ||
| } | ||
| return [data]; | ||
| }; | ||
| } | ||
| function createRemappingMap(fields) { | ||
| const remappingMap = /* @__PURE__ */ new Map(); | ||
| for (const [classKey, fieldMeta] of fields.entries()) { | ||
| const encodedAsKey = fieldMeta.encodeAs ?? classKey; | ||
| if (encodedAsKey !== classKey) { | ||
| remappingMap.set(encodedAsKey, classKey); | ||
| } | ||
| } | ||
| return remappingMap; | ||
| } | ||
| function unmapKeys(data, remappingMap) { | ||
| const unmappedData = {}; | ||
| for (const [classOrEncodedKey, value] of Object.entries(data)) { | ||
| const instanceKey = remappingMap.get(classOrEncodedKey) ?? classOrEncodedKey; | ||
| unmappedData[instanceKey] = value; | ||
| } | ||
| return unmappedData; | ||
| } | ||
| function createClassDecoder(Class, isDefaultEncoder, keysFromOptions) { | ||
| let fields = null; | ||
| let remappingMap = null; | ||
| return (data) => { | ||
| if (!isDefaultEncoder) { | ||
| return new Class(...data); | ||
| } | ||
| let [dataInput] = data; | ||
| if (!fields) { | ||
| fields = getFieldsInfo(Class, keysFromOptions); | ||
| } | ||
| if (remappingMap === null) { | ||
| remappingMap = createRemappingMap(fields); | ||
| } | ||
| if (remappingMap.size > 0) { | ||
| dataInput = unmapKeys(dataInput, remappingMap); | ||
| } | ||
| const instance = new Class(dataInput); | ||
| Object.assign(instance, dataInput); | ||
| return instance; | ||
| }; | ||
| } | ||
| function resolveKeys(keys) { | ||
| if (Array.isArray(keys)) { | ||
| const entries2 = keys.map((key) => [key, {}]); | ||
| return new Map(entries2); | ||
| } | ||
| const entries = Object.entries(keys).map(([key, value]) => { | ||
| return [ | ||
| // | ||
| key, | ||
| { encodeAs: value } | ||
| ]; | ||
| }); | ||
| return new Map(entries); | ||
| } | ||
| function codableClass(...[name, maybeOptions]) { | ||
| return (Class, context) => { | ||
| const isUsingDefaultEncoder = (maybeOptions == null ? void 0 : maybeOptions.encode) === void 0; | ||
| const keysFromOptions = resolveKeys((maybeOptions == null ? void 0 : maybeOptions.keys) ?? []); | ||
| const encoder = (maybeOptions == null ? void 0 : maybeOptions.encode) ?? createDefaultClassEncoder(Class, keysFromOptions); | ||
| const decoder = createClassDecoder(Class, isUsingDefaultEncoder, keysFromOptions); | ||
| const type = createCodableType( | ||
| name, | ||
| (value) => value instanceof Class, | ||
| encoder, | ||
| decoder, | ||
| /** | ||
| * If we have Foo and Bar extending Foo, Bar should always be the first to try to match. | ||
| * As Foo is the parent class, it will also match Bar, resulting in incorrect data being decoded. | ||
| */ | ||
| { | ||
| priority: getPrototypeChainLength(Class), | ||
| dependencies: maybeOptions == null ? void 0 : maybeOptions.dependencies | ||
| } | ||
| ); | ||
| registerCodableClass(context.metadata, { | ||
| name, | ||
| codableType: type | ||
| }); | ||
| const fieldsMap = codableClassFieldsRegistry.getOrInit(context.metadata, () => /* @__PURE__ */ new Map()); | ||
| for (const [key, metadata] of keysFromOptions.entries()) { | ||
| if (fieldsMap.has(key)) continue; | ||
| fieldsMap.set(key, metadata); | ||
| } | ||
| }; | ||
| } | ||
| exports.CodableType = CodableType; | ||
| exports.Coder = Coder; | ||
| exports.codable = codable; | ||
| exports.codableClass = codableClass; | ||
| exports.createCodableType = createCodableType; | ||
| exports.decode = decode; | ||
| exports.encode = encode; | ||
| exports.getIsCodableType = getIsCodableType; | ||
| exports.parse = parse; | ||
| exports.stringify = stringify; | ||
| //# sourceMappingURL=index.cjs.map |
Sorry, the diff of this file is too big to display
+1058
| var __defProp = Object.defineProperty; | ||
| var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
| var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
| const TYPED_ARRAY_MAP = { | ||
| uint8: Uint8Array, | ||
| uint8clamped: Uint8ClampedArray, | ||
| uint16: Uint16Array, | ||
| uint32: Uint32Array, | ||
| int8: Int8Array, | ||
| int16: Int16Array, | ||
| int32: Int32Array, | ||
| float32: Float32Array, | ||
| float64: Float64Array | ||
| }; | ||
| function getIsTypedArray(value) { | ||
| for (const type of Object.values(TYPED_ARRAY_MAP)) { | ||
| if (value instanceof type) return true; | ||
| } | ||
| return false; | ||
| } | ||
| function getTypedArrayType(value) { | ||
| for (const name of Object.keys(TYPED_ARRAY_MAP)) { | ||
| if (value instanceof TYPED_ARRAY_MAP[name]) | ||
| return name; | ||
| } | ||
| throw new Error(`Unknown typed array type: ${value}`); | ||
| } | ||
| function getTypedArrayConstructor(type) { | ||
| const TypedArrayClass = TYPED_ARRAY_MAP[type]; | ||
| if (!TypedArrayClass) { | ||
| throw new Error(`Unknown typed array type: ${type}`); | ||
| } | ||
| return TypedArrayClass; | ||
| } | ||
| function getSpecialNumberType(value) { | ||
| switch (value) { | ||
| case Infinity: | ||
| return "Infinity"; | ||
| case -Infinity: | ||
| return "-Infinity"; | ||
| case 0: | ||
| if (1 / value === -Infinity) return "-0"; | ||
| } | ||
| if (isNaN(value)) return "NaN"; | ||
| return null; | ||
| } | ||
| function decodeSpecialNumber(value) { | ||
| switch (value) { | ||
| case "NaN": | ||
| return NaN; | ||
| case "Infinity": | ||
| return Infinity; | ||
| case "-Infinity": | ||
| return -Infinity; | ||
| case "-0": | ||
| return -0; | ||
| default: | ||
| throw new Error(`Invalid special number type: ${value}`); | ||
| } | ||
| } | ||
| function resolveThunk(thunk) { | ||
| let result = thunk; | ||
| while (typeof result === "function") { | ||
| result = result(); | ||
| } | ||
| return result; | ||
| } | ||
| function getSymbolKey(symbol) { | ||
| const nativeKey = Symbol.keyFor(symbol); | ||
| if (nativeKey) return nativeKey; | ||
| const toStringResult = symbol.toString(); | ||
| return toStringResult.slice(7, -1); | ||
| } | ||
| function removeUndefinedProperties(input) { | ||
| const result = {}; | ||
| for (const key of Object.keys(input)) { | ||
| const value = input[key]; | ||
| if (value !== void 0) { | ||
| result[key] = value; | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| function createTag(name, data) { | ||
| const tag = {}; | ||
| tag[`$$${name}`] = data; | ||
| return tag; | ||
| } | ||
| const DEFAULT_PRIORITY = 10; | ||
| class CodableType { | ||
| constructor(definition) { | ||
| __publicField(this, "name"); | ||
| __publicField(this, "priority"); | ||
| __publicField(this, "dependencies"); | ||
| __publicField(this, "tagKey"); | ||
| var _a, _b; | ||
| this.definition = definition; | ||
| this.name = definition.name; | ||
| this.tagKey = `$$${this.name}`; | ||
| this.priority = ((_a = definition.options) == null ? void 0 : _a.priority) ?? DEFAULT_PRIORITY; | ||
| this.dependencies = ((_b = definition.options) == null ? void 0 : _b.dependencies) ?? null; | ||
| } | ||
| encode(value, context) { | ||
| return this.definition.encode(value, context); | ||
| } | ||
| encodeTag(value, context) { | ||
| return createTag(this.name, this.encode(value, context)); | ||
| } | ||
| decode(data, context) { | ||
| return this.definition.decode(data, context); | ||
| } | ||
| canHandle(value) { | ||
| return this.definition.canHandle(value); | ||
| } | ||
| createTag(data) { | ||
| return createTag(this.name, data); | ||
| } | ||
| } | ||
| function createCodableType(name, canHandle, encode2, decode2, options) { | ||
| return new CodableType({ | ||
| name, | ||
| canHandle, | ||
| encode: encode2, | ||
| decode: decode2, | ||
| options | ||
| }); | ||
| } | ||
| function getIsCodableType(value) { | ||
| return value instanceof CodableType; | ||
| } | ||
| function getErrorExtraProperties(error) { | ||
| const properties = {}; | ||
| let isEmpty = true; | ||
| for (const key of Object.keys(error)) { | ||
| if (key === "message" || key === "name" || key === "cause") continue; | ||
| properties[key] = error[key]; | ||
| isEmpty = false; | ||
| } | ||
| if (isEmpty) return null; | ||
| return properties; | ||
| } | ||
| function getIsValidDate(date) { | ||
| return !isNaN(date.getTime()); | ||
| } | ||
| const $$date = createCodableType( | ||
| "Date", | ||
| (value) => value instanceof Date, | ||
| (date) => { | ||
| if (!getIsValidDate(date)) return null; | ||
| return date.toISOString(); | ||
| }, | ||
| (maybeISOString) => { | ||
| if (maybeISOString === null) return /* @__PURE__ */ new Date("invalid"); | ||
| return new Date(maybeISOString); | ||
| } | ||
| ); | ||
| const $$set = createCodableType( | ||
| "Set", | ||
| (value) => value instanceof Set, | ||
| (set) => [...set], | ||
| (array) => new Set(array) | ||
| ); | ||
| const $$map = createCodableType( | ||
| "Map", | ||
| (value) => value instanceof Map, | ||
| (map) => [...map.entries()], | ||
| (entries) => new Map(entries) | ||
| ); | ||
| const $$error = createCodableType( | ||
| "Error", | ||
| (value) => value instanceof Error, | ||
| (error, context) => { | ||
| var _a; | ||
| const shouldIncludeErrorStack = ((_a = context.options) == null ? void 0 : _a.includeErrorStack) ?? false; | ||
| const extraProperties = getErrorExtraProperties(error) ?? void 0; | ||
| const name = error.name && error.name !== "Error" ? error.name : void 0; | ||
| const cause = error.cause; | ||
| const message = error.message; | ||
| const stack = shouldIncludeErrorStack ? error.stack : void 0; | ||
| if (!extraProperties && !name && !cause && !stack) { | ||
| return message; | ||
| } | ||
| return removeUndefinedProperties({ | ||
| message, | ||
| name, | ||
| cause, | ||
| properties: extraProperties, | ||
| stack | ||
| }); | ||
| }, | ||
| (messageOrData) => { | ||
| if (typeof messageOrData === "string") return new Error(messageOrData); | ||
| const { message, name, cause, properties, stack } = messageOrData; | ||
| const error = new Error(message, { cause }); | ||
| if (stack) { | ||
| error.stack = stack; | ||
| } | ||
| if (name && name !== "Error") { | ||
| error.name = name; | ||
| } | ||
| if (properties) { | ||
| Object.assign(error, properties); | ||
| } | ||
| return error; | ||
| } | ||
| ); | ||
| const $$undefined = createCodableType( | ||
| "undefined", | ||
| (value) => value === void 0, | ||
| () => null, | ||
| () => void 0 | ||
| ); | ||
| const $$bigInt = createCodableType( | ||
| "BigInt", | ||
| (value) => typeof value === "bigint", | ||
| (bigInt) => bigInt.toString(), | ||
| (string) => BigInt(string) | ||
| ); | ||
| const $$regexp = createCodableType( | ||
| "RegExp", | ||
| (value) => value instanceof RegExp, | ||
| ({ source, flags }) => { | ||
| if (flags) return [source, flags]; | ||
| return source; | ||
| }, | ||
| (sourceOrSourceAndFlags) => { | ||
| if (typeof sourceOrSourceAndFlags === "string") { | ||
| return new RegExp(sourceOrSourceAndFlags); | ||
| } | ||
| const [source, flags] = sourceOrSourceAndFlags; | ||
| return new RegExp(source, flags); | ||
| } | ||
| ); | ||
| const $$url = createCodableType( | ||
| "URL", | ||
| (value) => value instanceof URL, | ||
| (url) => url.toString(), | ||
| (string) => new URL(string) | ||
| ); | ||
| const symbolsRegistry = /* @__PURE__ */ new Map(); | ||
| const $$symbol = createCodableType( | ||
| "Symbol", | ||
| (value) => typeof value === "symbol", | ||
| (symbol) => { | ||
| const symbolKey = getSymbolKey(symbol); | ||
| symbolsRegistry.set(symbolKey, symbol); | ||
| return symbolKey; | ||
| }, | ||
| (symbolKey) => symbolsRegistry.get(symbolKey) ?? Symbol.for(symbolKey) | ||
| ); | ||
| const $$typedArray = createCodableType( | ||
| "typedArray", | ||
| getIsTypedArray, | ||
| (value) => { | ||
| return { | ||
| type: getTypedArrayType(value), | ||
| data: [...value] | ||
| }; | ||
| }, | ||
| ({ type, data }) => { | ||
| return new (getTypedArrayConstructor(type))(data); | ||
| } | ||
| ); | ||
| const $$num = createCodableType( | ||
| "num", | ||
| (value) => typeof value === "number" && !!getSpecialNumberType(value), | ||
| getSpecialNumberType, | ||
| decodeSpecialNumber | ||
| ); | ||
| const $$urlSearchParams = createCodableType( | ||
| "URLSearchParams", | ||
| (value) => value instanceof URLSearchParams, | ||
| (urlSearchParams) => urlSearchParams.toString(), | ||
| (string) => new URLSearchParams(string) | ||
| ); | ||
| const builtinTypesMap = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ | ||
| __proto__: null, | ||
| $$bigInt, | ||
| $$date, | ||
| $$error, | ||
| $$map, | ||
| $$num, | ||
| $$regexp, | ||
| $$set, | ||
| $$symbol, | ||
| $$typedArray, | ||
| $$undefined, | ||
| $$url, | ||
| $$urlSearchParams | ||
| }, Symbol.toStringTag, { value: "Module" })); | ||
| function getTagValue(tag) { | ||
| return Object.values(tag)[0]; | ||
| } | ||
| const OBJECT_PROTOTYPE = Object.prototype; | ||
| function getCodableTypeOf(input) { | ||
| if (input === null) return "primitive"; | ||
| if (input === void 0) return "undefined"; | ||
| const typeOfInput = typeof input; | ||
| switch (typeOfInput) { | ||
| case "boolean": | ||
| case "string": | ||
| return "primitive"; | ||
| case "number": | ||
| if (getSpecialNumberType(input)) return "special-number"; | ||
| return "primitive"; | ||
| case "symbol": | ||
| return "symbol"; | ||
| case "bigint": | ||
| return "bigint"; | ||
| case "function": | ||
| return "function"; | ||
| } | ||
| if (Array.isArray(input)) return "array"; | ||
| const inputPrototype = Object.getPrototypeOf(input); | ||
| if (inputPrototype === OBJECT_PROTOTYPE || inputPrototype === null) { | ||
| return "record"; | ||
| } | ||
| return "custom-object"; | ||
| } | ||
| const ANY_TAG_KEY_REGEXP = /^~*\$\$.+/; | ||
| function getDecodableTypeOf(input, context) { | ||
| switch (typeof input) { | ||
| case "boolean": | ||
| case "string": | ||
| case "number": | ||
| return "primitive"; | ||
| case "symbol": | ||
| case "bigint": | ||
| case "function": | ||
| case "undefined": | ||
| throw new Error(`undefined is not a valid JSON value`); | ||
| } | ||
| if (input === null) return "primitive"; | ||
| if (Array.isArray(input)) return "array"; | ||
| const keys = Object.keys(input); | ||
| if (keys.length !== 1) return "record"; | ||
| const key = keys[0]; | ||
| if (key.startsWith("$$")) { | ||
| if (key === "$$") return "record"; | ||
| if (key === "$$ref") { | ||
| if (typeof input.$$ref === "string") return "ref-tag"; | ||
| return "record"; | ||
| } | ||
| return key; | ||
| } | ||
| if (ANY_TAG_KEY_REGEXP.test(key)) return "escaped-tag"; | ||
| return "record"; | ||
| } | ||
| function assert(condition, message) { | ||
| if (!condition) { | ||
| if (message instanceof Error) { | ||
| throw message; | ||
| } | ||
| throw new Error(message); | ||
| } | ||
| } | ||
| function assertGet(value, message) { | ||
| assert(value !== null && value !== void 0, message); | ||
| return value; | ||
| } | ||
| function analyzeEncodedData(data, context) { | ||
| const decodableTypeOf = getDecodableTypeOf(data); | ||
| switch (decodableTypeOf) { | ||
| case "primitive": | ||
| return; | ||
| case "ref-tag": | ||
| context.presentRefAliases.add(data.$$ref); | ||
| return; | ||
| case "array": | ||
| for (let i = 0; i < data.length; i++) { | ||
| analyzeEncodedData(data[i], context); | ||
| } | ||
| return; | ||
| case "record": | ||
| for (const key of Object.keys(data)) { | ||
| analyzeEncodedData(data[key], context); | ||
| } | ||
| return; | ||
| case "escaped-tag": | ||
| context.hasEscapedTags = true; | ||
| analyzeEncodedData(getTagValue(data), context); | ||
| return; | ||
| } | ||
| context.hasCustomTypes = true; | ||
| analyzeEncodedData(data[decodableTypeOf], context); | ||
| } | ||
| class DecodeContext { | ||
| constructor(data, options) { | ||
| __publicField(this, "hasEscapedTags", false); | ||
| __publicField(this, "hasCustomTypes", false); | ||
| // Is ready instantly after analyzing the data | ||
| __publicField(this, "presentRefAliases", /* @__PURE__ */ new Set()); | ||
| // Will be filled while decoding the data | ||
| __publicField(this, "resolvedRefs", /* @__PURE__ */ new Map()); | ||
| __publicField(this, "hasRefAliases"); | ||
| __publicField(this, "externalReferencesMap"); | ||
| this.options = options; | ||
| analyzeEncodedData(data, this); | ||
| this.externalReferencesMap = new Map(Object.entries((options == null ? void 0 : options.externalReferences) ?? {})); | ||
| this.hasRefAliases = this.presentRefAliases.size > 0; | ||
| } | ||
| /** | ||
| * No custom types, no ref aliases, no escaped tags. Encoded data is regular JSON-compatible. | ||
| */ | ||
| get isPlainJSON() { | ||
| return !this.hasEscapedTags && !this.hasCustomTypes && !this.hasRefAliases; | ||
| } | ||
| /** | ||
| * Some object needed by some alias (list prepared before) is ready to be used. | ||
| */ | ||
| registerRef(path, object) { | ||
| if (!this.hasRefAliases || !this.presentRefAliases.has(path)) return; | ||
| this.resolvedRefs.set(path, object); | ||
| } | ||
| /** | ||
| * Alias requested value it referenced | ||
| */ | ||
| resolveRefAlias(path) { | ||
| return this.resolvedRefs.get(path) ?? null; | ||
| } | ||
| } | ||
| class EncodeContext { | ||
| constructor(options) { | ||
| __publicField(this, "unknownMode"); | ||
| __publicField(this, "preserveReferences"); | ||
| __publicField(this, "refFirstSeenPath", /* @__PURE__ */ new Map()); | ||
| this.options = options; | ||
| this.unknownMode = (options == null ? void 0 : options.unknownInputMode) ?? "null"; | ||
| this.preserveReferences = (options == null ? void 0 : options.preserveReferences) ?? true; | ||
| } | ||
| /** | ||
| * Call it the first time some object is seen. | ||
| */ | ||
| registerNewSeenObject(object, path) { | ||
| this.refFirstSeenPath.set(object, path); | ||
| } | ||
| /** | ||
| * Returns where the object was first seen at. | ||
| */ | ||
| getAlreadySeenObjectPath(object) { | ||
| return this.refFirstSeenPath.get(object) ?? null; | ||
| } | ||
| } | ||
| function getMetadataKey(Class) { | ||
| return Class[Symbol.metadata] ?? null; | ||
| } | ||
| if (!Symbol.metadata) { | ||
| Reflect.set(Symbol, "metadata", Symbol.for("Symbol.metadata")); | ||
| } | ||
| class PrivateMetadata { | ||
| constructor() { | ||
| __publicField(this, "registry", /* @__PURE__ */ new WeakMap()); | ||
| } | ||
| getFor(Class) { | ||
| const key = getMetadataKey(Class); | ||
| if (!key) return null; | ||
| return this.get(key); | ||
| } | ||
| get(key) { | ||
| return this.registry.get(key) ?? null; | ||
| } | ||
| getOrInit(key, initializer) { | ||
| if (this.registry.has(key)) return this.registry.get(key); | ||
| const value = initializer(); | ||
| this.registry.set(key, value); | ||
| return value; | ||
| } | ||
| has(key) { | ||
| return this.registry.has(key); | ||
| } | ||
| set(key, value) { | ||
| this.registry.set(key, value); | ||
| } | ||
| init(key, value) { | ||
| if (this.registry.has(key)) return this.registry.get(key); | ||
| this.registry.set(key, value); | ||
| return value; | ||
| } | ||
| } | ||
| const codableClassRegistry = new PrivateMetadata(); | ||
| const codableClassFieldsRegistry = new PrivateMetadata(); | ||
| const externalClassFieldsRegistry = new PrivateMetadata(); | ||
| function registerCodableClass(key, metadata) { | ||
| return codableClassRegistry.init(key, metadata); | ||
| } | ||
| function getIsCodableClass(Class) { | ||
| const key = getMetadataKey(Class); | ||
| if (!key) return false; | ||
| return codableClassRegistry.has(key); | ||
| } | ||
| function getCodableClassType(Class) { | ||
| var _a; | ||
| const key = getMetadataKey(Class); | ||
| if (!key) return null; | ||
| const codableType = (_a = codableClassRegistry.get(key)) == null ? void 0 : _a.codableType; | ||
| if (!codableType) return null; | ||
| return codableType; | ||
| } | ||
| class ExternalReference { | ||
| constructor(key, isOptional = false) { | ||
| this.key = key; | ||
| this.isOptional = isOptional; | ||
| } | ||
| } | ||
| function externalReference(key, isOptional = false) { | ||
| return new ExternalReference(key, isOptional); | ||
| } | ||
| const $$externalReference = createCodableType( | ||
| "external", | ||
| (value) => value instanceof ExternalReference, | ||
| (ref) => { | ||
| return { key: ref.key, isOptional: ref.isOptional }; | ||
| }, | ||
| ({ key, isOptional }, context) => { | ||
| if (!context.externalReferencesMap.has(key)) { | ||
| if (isOptional) return void 0; | ||
| throw new Error(`External reference "${key}" not found`); | ||
| } | ||
| return context.externalReferencesMap.get(key); | ||
| } | ||
| ); | ||
| function getIsForbiddenProperty(property) { | ||
| switch (property) { | ||
| case "__proto__": | ||
| case "prototype": | ||
| case "constructor": | ||
| return true; | ||
| default: | ||
| return false; | ||
| } | ||
| } | ||
| function getIsRecord(value) { | ||
| if (!value) return false; | ||
| const valuePrototype = Object.getPrototypeOf(value); | ||
| return valuePrototype === Object.prototype || valuePrototype === null; | ||
| } | ||
| function getIsObject(value) { | ||
| return typeof value === "object" && value !== null; | ||
| } | ||
| function getIsNotNull(value) { | ||
| return value !== null; | ||
| } | ||
| function copyJSON(json) { | ||
| if (Array.isArray(json)) { | ||
| const result = []; | ||
| for (let index = 0; index < json.length; index++) { | ||
| result[index] = copyJSON(json[index]); | ||
| } | ||
| return result; | ||
| } else if (getIsRecord(json)) { | ||
| const result = {}; | ||
| for (const key of Object.keys(json)) { | ||
| if (getIsForbiddenProperty(key)) continue; | ||
| result[key] = copyJSON(json[key]); | ||
| } | ||
| return result; | ||
| } | ||
| return json; | ||
| } | ||
| function escapePathSegment(segment) { | ||
| if (typeof segment === "number") return `${segment}`; | ||
| return segment.replaceAll("~", "~0").replaceAll("/", "~1"); | ||
| } | ||
| function addPathSegment(currentPointer, newSegment) { | ||
| switch (currentPointer) { | ||
| case "/": | ||
| case "": | ||
| return `/${escapePathSegment(newSegment)}`; | ||
| default: | ||
| return `${currentPointer}/${escapePathSegment(newSegment)}`; | ||
| } | ||
| } | ||
| function resolveRefAlias(input, context, currentPath) { | ||
| const referencedObject = context.resolveRefAlias(input.$$ref); | ||
| if (!referencedObject) { | ||
| console.warn(`Reference could not be resolved while decoding (${input.$$ref}) at ${currentPath}`); | ||
| return null; | ||
| } | ||
| return referencedObject; | ||
| } | ||
| function resolveTypeTag(tag, tagKey, context, coder, path) { | ||
| const typeName = tagKey.slice(2); | ||
| const matchingType = coder.getTypeByName(typeName); | ||
| if (!matchingType) { | ||
| console.warn(`Unknown custom type: ${typeName} at ${path}. Returning the raw value.`); | ||
| return tag[tagKey]; | ||
| } | ||
| const decodedTypeInput = decodeInput(tag[tagKey], context, coder, addPathSegment(path, tagKey)); | ||
| return matchingType.decode(decodedTypeInput, context); | ||
| } | ||
| function decodeArray(input, context, coder, path) { | ||
| const result = []; | ||
| context.registerRef(path, result); | ||
| for (let key = 0; key < input.length; key++) { | ||
| const fullPath = addPathSegment(path, key); | ||
| const decoded = decodeInput(input[key], context, coder, fullPath); | ||
| result[key] = decoded; | ||
| if (context.hasRefAliases && getIsObject(decoded)) { | ||
| context.registerRef(fullPath, decoded); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| function decodeRecord(input, context, coder, path) { | ||
| const result = {}; | ||
| context.registerRef(path, result); | ||
| for (const key of Object.keys(input)) { | ||
| if (getIsForbiddenProperty(key)) continue; | ||
| const fullPath = addPathSegment(path, key); | ||
| const decoded = decodeInput(input[key], context, coder, fullPath); | ||
| result[key] = decoded; | ||
| if (context.hasRefAliases && getIsObject(decoded)) { | ||
| context.registerRef(fullPath, decoded); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| function unescapeTag(input) { | ||
| const key = Object.keys(input)[0]; | ||
| return { | ||
| [key.slice(1)]: input[key] | ||
| }; | ||
| } | ||
| function decodeInput(input, context, coder, path) { | ||
| let decodableTypeOf = getDecodableTypeOf(input); | ||
| switch (decodableTypeOf) { | ||
| case "escaped-tag": { | ||
| input = unescapeTag(input); | ||
| decodableTypeOf = "record"; | ||
| } | ||
| case "primitive": { | ||
| return input; | ||
| } | ||
| case "ref-tag": { | ||
| return resolveRefAlias(input, context, path); | ||
| } | ||
| case "array": { | ||
| return decodeArray(input, context, coder, path); | ||
| } | ||
| case "record": { | ||
| return decodeRecord(input, context, coder, path); | ||
| } | ||
| } | ||
| return resolveTypeTag(input, decodableTypeOf, context, coder, path); | ||
| } | ||
| function getShouldEscapeKey(key) { | ||
| return /^~*\$\$/.test(key); | ||
| } | ||
| function encodeInput(input, encodeContext, coder, path) { | ||
| const codableTypeOf = getCodableTypeOf(input); | ||
| switch (codableTypeOf) { | ||
| case "primitive": | ||
| return input; | ||
| case "special-number": | ||
| return $$num.encodeTag(input, encodeContext); | ||
| case "symbol": | ||
| return $$symbol.encodeTag(input, encodeContext); | ||
| case "bigint": | ||
| return $$bigInt.encodeTag(input, encodeContext); | ||
| case "undefined": | ||
| return $$undefined.encodeTag(input, encodeContext); | ||
| case "function": | ||
| return null; | ||
| } | ||
| if (encodeContext.preserveReferences) { | ||
| const alreadySeenAtPath = encodeContext.getAlreadySeenObjectPath(input); | ||
| if (alreadySeenAtPath !== null) { | ||
| return createTag("ref", alreadySeenAtPath); | ||
| } | ||
| encodeContext.registerNewSeenObject(input, path); | ||
| } | ||
| if (codableTypeOf === "custom-object") { | ||
| const matchingType = coder.getMatchingTypeFor(input); | ||
| if (!matchingType) { | ||
| switch (encodeContext.unknownMode) { | ||
| case "unchanged": | ||
| return input; | ||
| case "null": | ||
| return null; | ||
| case "throw": | ||
| throw new Error("Not able to encode - no matching type found", input); | ||
| } | ||
| } | ||
| return matchingType.createTag( | ||
| encodeInput( | ||
| matchingType.encode(input, encodeContext), | ||
| encodeContext, | ||
| coder, | ||
| addPathSegment(path, matchingType.tagKey) | ||
| ) | ||
| ); | ||
| } | ||
| if (codableTypeOf === "array") { | ||
| const result2 = []; | ||
| for (let i = 0; i < input.length; i++) { | ||
| result2[i] = encodeInput(input[i], encodeContext, coder, addPathSegment(path, i)); | ||
| } | ||
| return result2; | ||
| } | ||
| const keys = Object.keys(input); | ||
| if (keys.length === 1 && getShouldEscapeKey(keys[0])) { | ||
| input = { [`~${keys[0]}`]: input[keys[0]] }; | ||
| keys[0] = `~${keys[0]}`; | ||
| } | ||
| const result = {}; | ||
| for (const key of keys) { | ||
| if (getIsForbiddenProperty(key)) continue; | ||
| result[key] = encodeInput(input[key], encodeContext, coder, addPathSegment(path, key)); | ||
| } | ||
| return result; | ||
| } | ||
| function resolveDependency(dependency) { | ||
| if (getIsCodableType(dependency)) return dependency; | ||
| const codableClassType = getCodableClassType(dependency); | ||
| if (!codableClassType) return null; | ||
| return codableClassType; | ||
| } | ||
| function getDirectDependencies(dependency) { | ||
| if (getIsCodableType(dependency)) { | ||
| return resolveThunk(dependency.dependencies); | ||
| } | ||
| const codableClassType = getCodableClassType(dependency); | ||
| if (!codableClassType) return null; | ||
| return resolveThunk(codableClassType.dependencies); | ||
| } | ||
| function addToSet(source, values) { | ||
| for (const value of values) { | ||
| source.add(value); | ||
| } | ||
| } | ||
| function resolveCodableDependencies(dependency) { | ||
| const resolvedDependencies = /* @__PURE__ */ new Set(); | ||
| let dependenciesToCheck = /* @__PURE__ */ new Set([dependency]); | ||
| while (true) { | ||
| if (dependenciesToCheck.size === 0) break; | ||
| for (const dependency2 of dependenciesToCheck) { | ||
| dependenciesToCheck.delete(dependency2); | ||
| if (resolvedDependencies.has(dependency2)) continue; | ||
| resolvedDependencies.add(dependency2); | ||
| const nestedDependencies = getDirectDependencies(dependency2); | ||
| if (!nestedDependencies) continue; | ||
| addToSet(resolvedDependencies, nestedDependencies); | ||
| } | ||
| } | ||
| resolvedDependencies.delete(dependency); | ||
| const resolvedTypes = Array.from(resolvedDependencies).map(resolveDependency).filter(getIsNotNull); | ||
| return new Set(resolvedTypes); | ||
| } | ||
| const DEFAULT_TYPES = [...Object.values(builtinTypesMap), $$externalReference].filter(getIsCodableType); | ||
| function getSortedTypes(types) { | ||
| return types.sort((a, b) => { | ||
| return b.priority - a.priority; | ||
| }); | ||
| } | ||
| function createTypesMap(types) { | ||
| const sortedTypes = getSortedTypes(types); | ||
| const map = /* @__PURE__ */ new Map(); | ||
| for (const type of sortedTypes) { | ||
| if (map.has(type.name)) { | ||
| throw new Error(`Coder type "${type.name}" already registered`); | ||
| } | ||
| map.set(type.name, type); | ||
| } | ||
| return map; | ||
| } | ||
| function updateTypesOrderByPriority(currentTypes) { | ||
| const sortedTypes = getSortedTypes([...currentTypes.values()]); | ||
| const needsReordering = sortedTypes.some((type) => type.priority !== 0); | ||
| if (!needsReordering) return; | ||
| currentTypes.clear(); | ||
| for (const type of sortedTypes) { | ||
| currentTypes.set(type.name, type); | ||
| } | ||
| } | ||
| class Coder { | ||
| constructor(extraTypes = []) { | ||
| __publicField(this, "typesMap", /* @__PURE__ */ new Map()); | ||
| this.typesMap = createTypesMap([...DEFAULT_TYPES]); | ||
| this.register(...extraTypes); | ||
| } | ||
| reorderTypes() { | ||
| updateTypesOrderByPriority(this.typesMap); | ||
| } | ||
| getTypeByName(name) { | ||
| return this.typesMap.get(name) ?? null; | ||
| } | ||
| getHasType(type) { | ||
| const existingType = this.getTypeByName(type.name); | ||
| return type === existingType; | ||
| } | ||
| registerSingleType(type) { | ||
| if (this.isDefault) { | ||
| throw new Error( | ||
| "Cannot register types on the default coder. Create a custom coder instance using `new Coder()` and register types on that instance." | ||
| ); | ||
| } | ||
| if (this.getHasType(type)) return; | ||
| if (this.typesMap.has(type.name)) { | ||
| throw new Error(`Other codable type with name "${type.name}" already registered`); | ||
| } | ||
| this.typesMap.set(type.name, type); | ||
| const dependencies = resolveCodableDependencies(type); | ||
| for (const dependency of dependencies) { | ||
| this.registerSingleType(dependency); | ||
| } | ||
| this.reorderTypes(); | ||
| } | ||
| registerType(...types) { | ||
| for (const type of types) { | ||
| this.registerSingleType(type); | ||
| } | ||
| } | ||
| registerSingle(typeOrClass) { | ||
| const typeToAdd = getIsCodableClass(typeOrClass) ? assertGet(getCodableClassType(typeOrClass), `Codable class "${typeOrClass.name}" not registered`) : typeOrClass; | ||
| return this.registerSingleType(typeToAdd); | ||
| } | ||
| register(...typesOrClasses) { | ||
| for (const typeOrClass of typesOrClasses) { | ||
| this.registerSingle(typeOrClass); | ||
| } | ||
| } | ||
| /** | ||
| * Typescript-sugar over `.registerType()` with better type inference. | ||
| */ | ||
| addType(name, canEncode, encode2, decode2, options) { | ||
| return this.registerType(createCodableType(name, canEncode, encode2, decode2, options)); | ||
| } | ||
| encode(value, options) { | ||
| const encodeContext = new EncodeContext(options); | ||
| return encodeInput(value, encodeContext, this, "/"); | ||
| } | ||
| decode(value, options) { | ||
| const context = new DecodeContext(value, options); | ||
| if (context.isPlainJSON) return copyJSON(value); | ||
| return decodeInput(value, context, this, "/"); | ||
| } | ||
| stringify(value) { | ||
| return JSON.stringify(this.encode(value)); | ||
| } | ||
| parse(value) { | ||
| return this.decode(JSON.parse(value)); | ||
| } | ||
| copy(value) { | ||
| return this.decode(this.encode(value)); | ||
| } | ||
| getMatchingTypeFor(input) { | ||
| for (const type of this.typesMap.values()) { | ||
| if (type.canHandle(input)) { | ||
| return type; | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
| get isDefault() { | ||
| return this === defaultCoder; | ||
| } | ||
| } | ||
| function createCoder(extraTypes = []) { | ||
| return new Coder(extraTypes); | ||
| } | ||
| const defaultCoder = createCoder(); | ||
| function decode(value, options) { | ||
| return defaultCoder.decode(value, options); | ||
| } | ||
| function encode(value) { | ||
| return defaultCoder.encode(value); | ||
| } | ||
| function stringify(value) { | ||
| return defaultCoder.stringify(value); | ||
| } | ||
| function parse(value) { | ||
| return defaultCoder.parse(value); | ||
| } | ||
| function getRegisteredCodableFields(Class) { | ||
| return codableClassFieldsRegistry.getFor(Class); | ||
| } | ||
| function resolveCodableOptions(options) { | ||
| if (typeof options === "string") { | ||
| return { encodeAs: options }; | ||
| } | ||
| return options ?? null; | ||
| } | ||
| function codable(optionsInput) { | ||
| const options = resolveCodableOptions(optionsInput); | ||
| return function codable2(initialValue, context) { | ||
| var _a; | ||
| if (context.kind !== "accessor" && context.kind !== "field") { | ||
| throw new Error("Codable decorator can only be used on fields or accessors"); | ||
| } | ||
| const isSymbolName = typeof context.name === "symbol"; | ||
| if (isSymbolName) throw new Error("Symbol property names are not supported"); | ||
| if ((_a = externalClassFieldsRegistry.get(context.metadata)) == null ? void 0 : _a.has(context.name)) { | ||
| throw new Error("Codable decorator cannot be used on external properties"); | ||
| } | ||
| const fieldsMap = codableClassFieldsRegistry.getOrInit(context.metadata, () => /* @__PURE__ */ new Map()); | ||
| fieldsMap.set(context.name, removeUndefinedProperties({ encodeAs: options == null ? void 0 : options.encodeAs })); | ||
| }; | ||
| } | ||
| function* iteratePrototypeChain(Class) { | ||
| let current = Class; | ||
| while (current !== null && current !== Function.prototype && current !== Object.prototype) { | ||
| yield current; | ||
| current = Object.getPrototypeOf(current); | ||
| } | ||
| } | ||
| function getPrototypeChainLength(Class) { | ||
| return [...iteratePrototypeChain(Class)].length; | ||
| } | ||
| function collectRegisteredCodableFields(Class, keysMap) { | ||
| for (const ClassInPrototype of iteratePrototypeChain(Class)) { | ||
| const registeredKeysMap = getRegisteredCodableFields(ClassInPrototype); | ||
| if (!registeredKeysMap) continue; | ||
| for (const [key, metadata] of registeredKeysMap.entries()) { | ||
| keysMap.set(key, metadata); | ||
| } | ||
| } | ||
| if (keysMap.size === 0) return null; | ||
| return keysMap; | ||
| } | ||
| function getCodableProperties(Class) { | ||
| const keysMap = /* @__PURE__ */ new Map(); | ||
| collectRegisteredCodableFields(Class, keysMap); | ||
| return keysMap; | ||
| } | ||
| function getExternalProperties(Class) { | ||
| const externalFieldsMap = externalClassFieldsRegistry.getFor(Class); | ||
| if (!externalFieldsMap) return null; | ||
| return externalFieldsMap; | ||
| } | ||
| function mergeMaps(map1, map2) { | ||
| const result = new Map(map1); | ||
| for (const [key, value] of map2.entries()) { | ||
| result.set(key, value); | ||
| } | ||
| return result; | ||
| } | ||
| function getFieldsInfo(Class, fieldsFromOptions) { | ||
| const fieldsFromProperties = getCodableProperties(Class); | ||
| return mergeMaps(fieldsFromProperties, fieldsFromOptions); | ||
| } | ||
| function createDefaultClassEncoder(Class, fieldsFromOptions) { | ||
| let fields = null; | ||
| return (instance) => { | ||
| const externalKeysMap = getExternalProperties(Class); | ||
| const data = {}; | ||
| if (!fields) { | ||
| fields = getFieldsInfo(Class, fieldsFromOptions); | ||
| } | ||
| for (const [instanceKey, metadata] of fields.entries()) { | ||
| const encodeAs = metadata.encodeAs ?? instanceKey; | ||
| data[encodeAs] = instance[instanceKey]; | ||
| } | ||
| if (externalKeysMap) { | ||
| for (const [key, externalRef] of externalKeysMap.entries()) { | ||
| data[key] = externalReference(externalRef.key, externalRef.isOptional); | ||
| } | ||
| } | ||
| return [data]; | ||
| }; | ||
| } | ||
| function createRemappingMap(fields) { | ||
| const remappingMap = /* @__PURE__ */ new Map(); | ||
| for (const [classKey, fieldMeta] of fields.entries()) { | ||
| const encodedAsKey = fieldMeta.encodeAs ?? classKey; | ||
| if (encodedAsKey !== classKey) { | ||
| remappingMap.set(encodedAsKey, classKey); | ||
| } | ||
| } | ||
| return remappingMap; | ||
| } | ||
| function unmapKeys(data, remappingMap) { | ||
| const unmappedData = {}; | ||
| for (const [classOrEncodedKey, value] of Object.entries(data)) { | ||
| const instanceKey = remappingMap.get(classOrEncodedKey) ?? classOrEncodedKey; | ||
| unmappedData[instanceKey] = value; | ||
| } | ||
| return unmappedData; | ||
| } | ||
| function createClassDecoder(Class, isDefaultEncoder, keysFromOptions) { | ||
| let fields = null; | ||
| let remappingMap = null; | ||
| return (data) => { | ||
| if (!isDefaultEncoder) { | ||
| return new Class(...data); | ||
| } | ||
| let [dataInput] = data; | ||
| if (!fields) { | ||
| fields = getFieldsInfo(Class, keysFromOptions); | ||
| } | ||
| if (remappingMap === null) { | ||
| remappingMap = createRemappingMap(fields); | ||
| } | ||
| if (remappingMap.size > 0) { | ||
| dataInput = unmapKeys(dataInput, remappingMap); | ||
| } | ||
| const instance = new Class(dataInput); | ||
| Object.assign(instance, dataInput); | ||
| return instance; | ||
| }; | ||
| } | ||
| function resolveKeys(keys) { | ||
| if (Array.isArray(keys)) { | ||
| const entries2 = keys.map((key) => [key, {}]); | ||
| return new Map(entries2); | ||
| } | ||
| const entries = Object.entries(keys).map(([key, value]) => { | ||
| return [ | ||
| // | ||
| key, | ||
| { encodeAs: value } | ||
| ]; | ||
| }); | ||
| return new Map(entries); | ||
| } | ||
| function codableClass(...[name, maybeOptions]) { | ||
| return (Class, context) => { | ||
| const isUsingDefaultEncoder = (maybeOptions == null ? void 0 : maybeOptions.encode) === void 0; | ||
| const keysFromOptions = resolveKeys((maybeOptions == null ? void 0 : maybeOptions.keys) ?? []); | ||
| const encoder = (maybeOptions == null ? void 0 : maybeOptions.encode) ?? createDefaultClassEncoder(Class, keysFromOptions); | ||
| const decoder = createClassDecoder(Class, isUsingDefaultEncoder, keysFromOptions); | ||
| const type = createCodableType( | ||
| name, | ||
| (value) => value instanceof Class, | ||
| encoder, | ||
| decoder, | ||
| /** | ||
| * If we have Foo and Bar extending Foo, Bar should always be the first to try to match. | ||
| * As Foo is the parent class, it will also match Bar, resulting in incorrect data being decoded. | ||
| */ | ||
| { | ||
| priority: getPrototypeChainLength(Class), | ||
| dependencies: maybeOptions == null ? void 0 : maybeOptions.dependencies | ||
| } | ||
| ); | ||
| registerCodableClass(context.metadata, { | ||
| name, | ||
| codableType: type | ||
| }); | ||
| const fieldsMap = codableClassFieldsRegistry.getOrInit(context.metadata, () => /* @__PURE__ */ new Map()); | ||
| for (const [key, metadata] of keysFromOptions.entries()) { | ||
| if (fieldsMap.has(key)) continue; | ||
| fieldsMap.set(key, metadata); | ||
| } | ||
| }; | ||
| } | ||
| export { | ||
| CodableType, | ||
| Coder, | ||
| codable, | ||
| codableClass, | ||
| createCodableType, | ||
| decode, | ||
| encode, | ||
| getIsCodableType, | ||
| parse, | ||
| stringify | ||
| }; | ||
| //# sourceMappingURL=index.mjs.map |
Sorry, the diff of this file is too big to display
| export declare const $$date: import("./CodableType").CodableType<Date, string | null>; | ||
| export declare const $$set: import("./CodableType").CodableType<Set<any>, any[]>; | ||
| export declare const $$map: import("./CodableType").CodableType<Map<any, any>, [any, any][]>; | ||
| export declare const $$error: import("./CodableType").CodableType<Error, string | { | ||
| message: string; | ||
| name: string | undefined; | ||
| cause: unknown; | ||
| properties: Record<string, unknown> | undefined; | ||
| stack: string | undefined; | ||
| }>; | ||
| export declare const $$undefined: import("./CodableType").CodableType<undefined, null>; | ||
| export declare const $$bigInt: import("./CodableType").CodableType<bigint, string>; | ||
| export declare const $$regexp: import("./CodableType").CodableType<RegExp, string | readonly [string, string]>; | ||
| export declare const $$url: import("./CodableType").CodableType<URL, string>; | ||
| export declare const $$symbol: import("./CodableType").CodableType<symbol, string>; | ||
| export declare const $$typedArray: import("./CodableType").CodableType<import("./utils/typedArrays").TypedArray, { | ||
| readonly type: "uint8" | "uint8clamped" | "uint16" | "uint32" | "int8" | "int16" | "int32" | "float32" | "float64"; | ||
| readonly data: readonly number[]; | ||
| }>; | ||
| /** | ||
| * Handles special numbers like NaN, Infinity, -Infinity, -0 that are not correctly serialized by | ||
| * regular JSON | ||
| */ | ||
| export declare const $$num: import("./CodableType").CodableType<number, "Infinity" | "-Infinity" | "-0" | "NaN" | null>; | ||
| export declare const $$urlSearchParams: import("./CodableType").CodableType<URLSearchParams, string>; |
| import { Tag, TagKey } from "./format"; | ||
| import { CodableDependencies } from "./dependencies"; | ||
| import { DecodeContext } from "./DecodeContext"; | ||
| import { EncodeContext } from "./EncodeContext"; | ||
| export interface CodableTypeOptions { | ||
| priority?: number; | ||
| dependencies?: CodableDependencies; | ||
| } | ||
| interface CodableTypeDefinition<Item, Data> { | ||
| name: string; | ||
| canHandle: (value: unknown) => value is Item; | ||
| encode: (data: Item, context: EncodeContext) => Data; | ||
| decode: (data: Data, context: DecodeContext) => Item; | ||
| options?: CodableTypeOptions; | ||
| } | ||
| export declare function createTag<Name extends string, Data>(name: Name, data: Data): Tag<Data, Name>; | ||
| export declare class CodableType<Item = any, Data = any> { | ||
| readonly definition: CodableTypeDefinition<Item, Data>; | ||
| constructor(definition: CodableTypeDefinition<Item, Data>); | ||
| readonly name: string; | ||
| readonly priority: number; | ||
| readonly dependencies: CodableDependencies | null; | ||
| readonly tagKey: TagKey<typeof this.name>; | ||
| encode(value: Item, context: EncodeContext): Data; | ||
| encodeTag(value: Item, context: EncodeContext): Tag<Data, typeof this.name>; | ||
| decode(data: Data, context: DecodeContext): Item; | ||
| canHandle(value: unknown): value is Item; | ||
| createTag(data: Data): Tag<Data, typeof this.name>; | ||
| } | ||
| export declare function createCodableType<Item, Data>(name: string, canHandle: (value: unknown) => value is Item, encode: (data: Item, context: EncodeContext) => Data, decode: (data: Data, context: DecodeContext) => Item, options?: CodableTypeOptions): CodableType<Item, Data>; | ||
| export declare function getIsCodableType(value: unknown): value is CodableType; | ||
| export {}; |
| import { JSONValue } from "./types"; | ||
| import { CodableType, CodableTypeOptions } from "./CodableType"; | ||
| import { DecodeOptions } from "./DecodeContext"; | ||
| import { EncodeOptions } from "./EncodeContext"; | ||
| import { AnyClass } from "./decorators/types"; | ||
| type CodableTypeOrClass = CodableType | AnyClass; | ||
| export declare class Coder { | ||
| private readonly typesMap; | ||
| constructor(extraTypes?: CodableTypeOrClass[]); | ||
| private reorderTypes; | ||
| getTypeByName(name: string): CodableType | null; | ||
| private getHasType; | ||
| private registerSingleType; | ||
| registerType(...types: Array<CodableType>): void; | ||
| private registerSingle; | ||
| register(...typesOrClasses: CodableTypeOrClass[]): void; | ||
| /** | ||
| * Typescript-sugar over `.registerType()` with better type inference. | ||
| */ | ||
| addType<Item, Data>(name: string, canEncode: (value: unknown) => value is Item, encode: (data: Item) => Data, decode: (data: Data) => Item, options?: CodableTypeOptions): void; | ||
| encode<T>(value: T, options?: EncodeOptions): JSONValue; | ||
| decode<T>(value: JSONValue, options?: DecodeOptions): T; | ||
| stringify<T>(value: T): string; | ||
| parse<T>(value: string): T; | ||
| copy<T>(value: T): T; | ||
| getMatchingTypeFor(input: unknown): CodableType | null; | ||
| get isDefault(): boolean; | ||
| } | ||
| export declare function createCoder(extraTypes?: CodableType[]): Coder; | ||
| export declare const defaultCoder: Coder; | ||
| export declare function decode<T>(value: JSONValue, options?: DecodeOptions): T; | ||
| export declare function encode<T>(value: T): JSONValue; | ||
| export declare function stringify<T>(value: T): string; | ||
| export declare function parse<T>(value: string): T; | ||
| export declare function copy<T>(value: T): T; | ||
| export {}; |
| import { JSONValue } from "./types"; | ||
| import { Coder } from "./Coder"; | ||
| import { DecodeContext } from "./DecodeContext"; | ||
| export declare function decodeInput<T>(input: JSONValue, context: DecodeContext, coder: Coder, path: string): T; |
| import { JSONValue } from "./types"; | ||
| export interface DecodeOptions { | ||
| externalReferences?: Record<string, unknown>; | ||
| } | ||
| export declare function analyzeEncodedData(data: JSONValue, context: DecodeContext): void; | ||
| export declare class DecodeContext { | ||
| readonly options?: DecodeOptions | undefined; | ||
| hasEscapedTags: boolean; | ||
| hasCustomTypes: boolean; | ||
| presentRefAliases: Set<string>; | ||
| resolvedRefs: Map<string, object>; | ||
| readonly hasRefAliases: boolean; | ||
| /** | ||
| * No custom types, no ref aliases, no escaped tags. Encoded data is regular JSON-compatible. | ||
| */ | ||
| get isPlainJSON(): boolean; | ||
| /** | ||
| * Some object needed by some alias (list prepared before) is ready to be used. | ||
| */ | ||
| registerRef(path: string, object: object): void; | ||
| /** | ||
| * Alias requested value it referenced | ||
| */ | ||
| resolveRefAlias(path: string): object | null; | ||
| readonly externalReferencesMap: Map<string, unknown>; | ||
| constructor(data: JSONValue, options?: DecodeOptions | undefined); | ||
| } |
| import { AnyClass } from "./types"; | ||
| type CodableFieldDecoratorContext<T, V> = ClassFieldDecoratorContext<T, V> | ClassAccessorDecoratorContext<T, V>; | ||
| export declare function getRegisteredCodableFields(Class: AnyClass): import("./registry").CodableClassFieldsMap<AnyClass> | null; | ||
| interface CodableOptions { | ||
| encodeAs?: string; | ||
| } | ||
| type CodableOptionsInput = string | CodableOptions; | ||
| export declare function codable<T, V>(optionsInput?: CodableOptionsInput): <T_1, V_1>(initialValue: any, context: CodableFieldDecoratorContext<T_1, V_1>) => void; | ||
| export {}; |
| import { AnyClass, ClassDecorator, MakeRequired, MemberwiseClass } from "./types"; | ||
| import { ClassEncoder } from "./encode"; | ||
| import { CodableClassFieldsMap } from "./registry"; | ||
| import { CodableDependencies } from "../dependencies"; | ||
| export type CodableClassKeys<T extends AnyClass> = Array<keyof InstanceType<T>> | CodableClassFieldsMap<T>; | ||
| type CodableClassKeysInput<T extends AnyClass> = Array<keyof InstanceType<T>> | Record<keyof InstanceType<T>, string>; | ||
| interface CodableClassOptions<T extends AnyClass> { | ||
| dependencies?: CodableDependencies; | ||
| encode?: ClassEncoder<T>; | ||
| keys?: CodableClassKeysInput<T>; | ||
| } | ||
| type CodableClassDecoratorArgs<T extends AnyClass> = T extends MemberwiseClass<T> ? [ | ||
| string, | ||
| CodableClassOptions<T> | void | ||
| ] : [ | ||
| string, | ||
| MakeRequired<CodableClassOptions<T>, "encode"> | ||
| ]; | ||
| export declare function codableClass<T extends AnyClass>(...[name, maybeOptions]: CodableClassDecoratorArgs<T>): ClassDecorator<T>; | ||
| export {}; |
| import { AnyClass } from "./types"; | ||
| import { CodableClassFieldsMap } from "./registry"; | ||
| export type ClassEncoder<T extends AnyClass> = (instance: InstanceType<T>) => ConstructorParameters<T>; | ||
| export declare function createDefaultClassEncoder<T extends AnyClass>(Class: T, fieldsFromOptions: CodableClassFieldsMap<T>): ClassEncoder<T>; | ||
| export type ClassDecoder<T extends AnyClass> = (data: ConstructorParameters<T>) => InstanceType<T>; | ||
| export declare function createClassDecoder<T extends AnyClass>(Class: T, isDefaultEncoder: boolean, keysFromOptions: CodableClassFieldsMap<T>): ClassDecoder<T>; |
| export {}; |
| import { AnyClass } from "./types"; | ||
| type CodableFieldDecoratorContext<T, V> = ClassFieldDecoratorContext<T, V> | ClassAccessorDecoratorContext<T, V>; | ||
| export declare function getRegisteredCodableFields(Class: AnyClass): import("./registry").CodableClassFieldsMap<AnyClass> | null; | ||
| export declare function external<T, V>(key: string, isOptional?: boolean): <T_1, V_1>(initialValue: any, context: CodableFieldDecoratorContext<T_1, V_1>) => void; | ||
| export {}; |
| export * from "./codable"; | ||
| export * from "./codableClass"; |
| export declare function getMetadataKey<T extends object>(Class: T): DecoratorMetadata | null; | ||
| export declare class PrivateMetadata<T> { | ||
| private registry; | ||
| getFor(Class: object): T | null; | ||
| get(key: DecoratorMetadata): T | null; | ||
| getOrInit(key: DecoratorMetadata, initializer: () => T): T; | ||
| has(key: DecoratorMetadata): boolean; | ||
| set(key: DecoratorMetadata, value: T): void; | ||
| init(key: DecoratorMetadata, value: T): T; | ||
| } |
| import { CodableClassFieldsMap } from "./registry"; | ||
| import { AnyClass } from "./types"; | ||
| export declare function getCodableProperties(Class: AnyClass): CodableClassFieldsMap; | ||
| export declare function getExternalProperties(Class: AnyClass): Map<string, { | ||
| key: string; | ||
| isOptional: boolean; | ||
| }> | null; |
| import { AnyClass } from "./types"; | ||
| export declare function iteratePrototypeChain(Class: object): Generator<object>; | ||
| export declare function getPrototypeChainLength(Class: AnyClass): number; |
| import { PrivateMetadata } from "./PrivateMetadata"; | ||
| import { AnyClass } from "./types"; | ||
| import { CodableType } from "../CodableType"; | ||
| export interface FieldMetadata { | ||
| encodeAs?: string; | ||
| } | ||
| export type ClassCodableType<T extends AnyClass> = CodableType<InstanceType<T>, ConstructorParameters<T>>; | ||
| export type CodableClassFieldsMap<T extends AnyClass = AnyClass> = Map<keyof InstanceType<T>, FieldMetadata>; | ||
| export interface CodableClassMetadata<T extends AnyClass = AnyClass> { | ||
| name: string; | ||
| codableType: ClassCodableType<T>; | ||
| } | ||
| export declare const codableClassRegistry: PrivateMetadata<CodableClassMetadata<AnyClass>>; | ||
| export declare const codableClassFieldsRegistry: PrivateMetadata<CodableClassFieldsMap<AnyClass>>; | ||
| export declare const externalClassFieldsRegistry: PrivateMetadata<Map<string, { | ||
| key: string; | ||
| isOptional: boolean; | ||
| }>>; | ||
| export declare function registerCodableClass<T extends AnyClass>(key: DecoratorMetadata, metadata: CodableClassMetadata<T>): CodableClassMetadata<AnyClass>; | ||
| export declare function getIsCodableClass<T extends AnyClass>(Class: object): Class is AnyClass; | ||
| export declare function getCodableClassType<T extends AnyClass>(Class: T): ClassCodableType<T> | null; |
| export type AnyClass = new (...args: any) => any; | ||
| export type AtLeastOne<T> = [T, ...T[]]; | ||
| export type AnyClassWithArgs = new (...args: AtLeastOne<any>) => any; | ||
| export type MemberwiseClass<T extends AnyClass> = ConstructorParameters<T> extends [] | [Partial<InstanceType<T>>] ? T : never; | ||
| export type IsMemberwiseClass<T extends AnyClass> = ConstructorParameters<T> extends [Partial<InstanceType<T>>] ? true : false; | ||
| export type If<Condition, True, False> = Condition extends true ? True : False; | ||
| export type ClassDecorator<T extends AnyClass> = (Class: T, context: ClassDecoratorContext<T>) => void; | ||
| export type Voidable<T> = T | void; | ||
| export type VoidableIf<T, Condition> = Condition extends true ? Voidable<T> : T; | ||
| export type MakeRequired<T, K extends keyof T> = T & Required<Pick<T, K>>; |
| type PropDecorator = (value: any, context: any) => any; | ||
| export declare function combineDecorators(...decorators: PropDecorator[]): PropDecorator; | ||
| export {}; |
| import { CodableType } from "./CodableType"; | ||
| import { Thunk } from "./utils/misc"; | ||
| import { AnyClass } from "./decorators/types"; | ||
| export type CodableDependency = AnyClass | CodableType; | ||
| export type CodableDependencies = Thunk<CodableDependency[]>; | ||
| export declare function resolveCodableDependencies(dependency: CodableDependency): Set<CodableType>; |
| import { JSONValue } from "./types"; | ||
| import { Coder } from "./Coder"; | ||
| import { EncodeContext } from "./EncodeContext"; | ||
| export declare function encodeInput(input: unknown, encodeContext: EncodeContext, coder: Coder, path: string): JSONValue; |
| type UnknownMode = "unchanged" | "null" | "throw"; | ||
| export interface EncodeOptions { | ||
| /** | ||
| * What should happen if some custom class instance is passed to encode, but no | ||
| * matching type is found. | ||
| * | ||
| * - "unchanged": Pass the input as is to the encoded output | ||
| * - "null": Replace the input with null in the encoded output | ||
| * - "throw": Throw an error if no matching type is found | ||
| * | ||
| * @default "null" | ||
| */ | ||
| unknownInputMode?: UnknownMode; | ||
| /** | ||
| * Should encoder detect if the same object is seen multiple times in the input so it can | ||
| * re-create those references later, when decoding the data? | ||
| * | ||
| * Note: if disabled, and your input contains circular references, the encoder will throw an error. | ||
| * | ||
| * @default true | ||
| */ | ||
| preserveReferences?: boolean; | ||
| /** | ||
| * @default false | ||
| */ | ||
| includeErrorStack?: boolean; | ||
| } | ||
| export declare class EncodeContext { | ||
| readonly options?: EncodeOptions | undefined; | ||
| constructor(options?: EncodeOptions | undefined); | ||
| readonly unknownMode: UnknownMode; | ||
| readonly preserveReferences: boolean; | ||
| private refFirstSeenPath; | ||
| /** | ||
| * Call it the first time some object is seen. | ||
| */ | ||
| registerNewSeenObject(object: object, path: string): void; | ||
| /** | ||
| * Returns where the object was first seen at. | ||
| */ | ||
| getAlreadySeenObjectPath(object: object): string | null; | ||
| } | ||
| export {}; |
| export declare class ExternalReference<T> { | ||
| readonly key: string; | ||
| readonly isOptional: boolean; | ||
| constructor(key: string, isOptional?: boolean); | ||
| } | ||
| export declare function externalReference<T>(key: string, isOptional?: boolean): T; | ||
| export declare const $$externalReference: import("./CodableType").CodableType<ExternalReference<any>, { | ||
| key: string; | ||
| isOptional: boolean; | ||
| }>; |
| export type TagKey<T extends string = string> = `$$${T}`; | ||
| export type Tag<V = unknown, T extends string = string> = { | ||
| [key in TagKey<T>]: V; | ||
| }; | ||
| export type RefAlias = Tag<string, "ref">; | ||
| export declare function getTagValue<T>(tag: Tag<T>): T; |
| export { Coder, encode, decode, parse, stringify } from "./Coder"; | ||
| export { codableClass } from "./decorators/codableClass"; | ||
| export { codable } from "./decorators/codable"; | ||
| export { createCodableType, getIsCodableType, CodableType } from "./CodableType"; |
| import { JSONPrimitive } from "./types"; | ||
| export declare function getIsJSONPrimitive(value: unknown): value is JSONPrimitive; | ||
| /** | ||
| * Returns true only if the value is POJO (Plain Old JavaScript Object) | ||
| * | ||
| * Returns false for instances of classes, functions, etc. | ||
| */ | ||
| export declare function getIsRecord(value: unknown): value is Record<string, unknown>; | ||
| export declare function getIsObject(value: unknown): value is object; | ||
| export declare function getIsNotNull<T>(value: T | null): value is T; |
| export {}; |
| export {}; |
| export {}; |
| export {}; |
| export {}; |
| export {}; |
| export interface GenerateDataOptions { | ||
| sameReferences?: boolean; | ||
| i?: number; | ||
| j?: number; | ||
| } | ||
| export declare function generateData(options?: GenerateDataOptions): { | ||
| createdAt: Date | Map<string, string> | Set<Date> | Set<number> | Set<number>[]; | ||
| updatedAt: Date | Map<string, string> | Set<Date> | Set<number> | Set<number>[]; | ||
| nested1: { | ||
| createdAt: Date | Map<string, string> | Set<Date> | Set<number> | Set<number>[]; | ||
| updatedAt: Date | Map<string, string> | Set<Date> | Set<number> | Set<number>[]; | ||
| innerNested: { | ||
| createdAt: Date | Map<string, string> | Set<Date> | Set<number> | Set<number>[]; | ||
| updatedAt: Date | Map<string, string> | Set<Date> | Set<number> | Set<number>[]; | ||
| }; | ||
| }[]; | ||
| nested2: { | ||
| createdAt: Date | Map<string, string> | Set<Date> | Set<number> | Set<number>[]; | ||
| updatedAt: Date | Map<string, string> | Set<Date> | Set<number> | Set<number>[]; | ||
| innerNested: { | ||
| createdAt: Date | Map<string, string> | Set<Date> | Set<number> | Set<number>[]; | ||
| updatedAt: Date | Map<string, string> | Set<Date> | Set<number> | Set<number>[]; | ||
| }; | ||
| }[]; | ||
| }[]; |
| export {}; |
| export {}; |
| /** | ||
| * Example: | ||
| * ``` | ||
| * it("some test", () => { | ||
| * using _ = captureWarnings(); | ||
| * | ||
| * // some code that will warn | ||
| * expect(console.warn).toHaveBeenCalledWith("Warning message"); | ||
| * }); | ||
| * ``` | ||
| */ | ||
| export declare function captureWarnings(): { | ||
| [Symbol.dispose]: () => void; | ||
| }; |
| export type JSONPrimitive = string | number | boolean | null; | ||
| export type JSONObject = { | ||
| [key: string]: JSONValue; | ||
| }; | ||
| export type JSONArray = JSONValue[]; | ||
| export type JSONValue = JSONPrimitive | JSONObject | JSONArray; | ||
| export type Primitive = string | number | boolean | null | undefined; | ||
| export type ClassConstructor<T> = new (...args: any) => T; | ||
| export type AtLeastOne<T> = [T, ...T[]]; | ||
| /** | ||
| * A class that can be instantiated without any arguments. | ||
| */ | ||
| export type MemberwiseClass<T> = new (input?: Partial<T>) => T; | ||
| export type ClassWithoutInput<T> = new () => T; | ||
| /** | ||
| * A class that requires arguments to be known in order to be instantiated. | ||
| */ | ||
| export type ManuallyCodableClass<T> = new (...args: AtLeastOne<any>) => T; | ||
| export type AnyCodableClass<T> = MemberwiseClass<T> | ManuallyCodableClass<T>; |
| export declare function assert(condition: boolean, message: string | Error): asserts condition; | ||
| export declare function assertGet<T>(value: T | null | undefined, message: string | Error): T; | ||
| export declare function narrowType<T>(value: unknown): asserts value is T; |
| export declare function getErrorExtraProperties(error: Error): Record<string, unknown> | null; |
| import { JSONValue } from "../types"; | ||
| export declare function copyJSON(json: JSONValue): JSONValue; |
| export declare function addPathSegment(currentPointer: string, newSegment: string | number): string; |
| export type Thunk<T> = T | (() => Thunk<T>); | ||
| export declare function resolveThunk<T>(thunk: Thunk<T>): T; | ||
| export declare function getSymbolKey(symbol: symbol): string; | ||
| export declare function removeUndefinedProperties<T extends Record<string, unknown>>(input: T): T; |
| export declare function getSpecialNumberType(value: number): "Infinity" | "-Infinity" | "-0" | "NaN" | null; | ||
| type SpecialNumberType = ReturnType<typeof getSpecialNumberType>; | ||
| export declare function decodeSpecialNumber(value: SpecialNumberType): number; | ||
| export {}; |
| export declare function getIsForbiddenProperty(property: string): boolean; |
| declare const TYPED_ARRAY_MAP: { | ||
| readonly uint8: Uint8ArrayConstructor; | ||
| readonly uint8clamped: Uint8ClampedArrayConstructor; | ||
| readonly uint16: Uint16ArrayConstructor; | ||
| readonly uint32: Uint32ArrayConstructor; | ||
| readonly int8: Int8ArrayConstructor; | ||
| readonly int16: Int16ArrayConstructor; | ||
| readonly int32: Int32ArrayConstructor; | ||
| readonly float32: Float32ArrayConstructor; | ||
| readonly float64: Float64ArrayConstructor; | ||
| }; | ||
| export type TypedArrayTypeName = keyof typeof TYPED_ARRAY_MAP; | ||
| export type TypedArray = InstanceType<(typeof TYPED_ARRAY_MAP)[TypedArrayTypeName]>; | ||
| export declare function getIsTypedArray(value: unknown): value is TypedArray; | ||
| export declare function getTypedArrayType(value: TypedArray): TypedArrayTypeName; | ||
| export declare function getTypedArrayConstructor(type: TypedArrayTypeName): Uint8ArrayConstructor | Uint8ClampedArrayConstructor | Uint16ArrayConstructor | Uint32ArrayConstructor | Int8ArrayConstructor | Int16ArrayConstructor | Int32ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor; | ||
| export {}; |
| import { DecodeContext } from "../DecodeContext"; | ||
| import { JSONValue } from "../types"; | ||
| /** | ||
| * The goal here is to geather as much information at once, so later we avoid doing unnecessary checks | ||
| * such as `typeof input` or `Array.isArray(input)` etc. | ||
| */ | ||
| export declare function getCodableTypeOf(input: unknown): "function" | "primitive" | "undefined" | "special-number" | "symbol" | "bigint" | "array" | "record" | "custom-object"; | ||
| export type CodableTypeOf = ReturnType<typeof getCodableTypeOf>; | ||
| export type CodablePrimitive = boolean | string | undefined | number; | ||
| export declare function getDecodableTypeOf(input: JSONValue, context: DecodeContext): `$$${string}` | "primitive" | "array" | "record" | "ref-tag" | "escaped-tag"; | ||
| export type DecodableTypeOf = ReturnType<typeof getDecodableTypeOf>; |
| declare const _default: import("vite").UserConfig; | ||
| export default _default; |
| declare const _default: import("vite").UserConfig; | ||
| export default _default; |
+61
-2
| { | ||
| "name": "codables", | ||
| "version": "0.0.1", | ||
| "version": "0.1.0", | ||
| "description": "High-performance, type-safe JSON serialization library that extends JSON to support complex JavaScript types including Date, BigInt, Map, Set, RegExp, Symbol, typed arrays, circular references, and custom classes.", | ||
| "type": "module", | ||
| "main": "./dist/index.cjs", | ||
| "module": "./dist/index.mjs", | ||
| "types": "./dist/types/index.d.ts", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/types/index.d.ts", | ||
| "import": "./dist/index.mjs", | ||
| "require": "./dist/index.cjs" | ||
| } | ||
| }, | ||
| "files": [ | ||
| "dist" | ||
| ], | ||
| "keywords": [ | ||
| "json", | ||
| "serialization", | ||
| "typescript", | ||
| "date", | ||
| "bigint", | ||
| "map", | ||
| "set", | ||
| "regexp", | ||
| "symbol", | ||
| "circular-references", | ||
| "decorators", | ||
| "class-serialization", | ||
| "declarative-serialization", | ||
| "typed-arrays", | ||
| "url", | ||
| "error-serialization", | ||
| "reference-preservation", | ||
| "superjson-alternative", | ||
| "full-stack", | ||
| "nextjs", | ||
| "remix", | ||
| "api-serialization" | ||
| ], | ||
| "author": { | ||
| "name": "Adam Pietrasiak", | ||
| "email": "adam@pietrasiak.com" | ||
| }, | ||
| "license": "MIT", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/pie6k/codables.git" | ||
| }, | ||
| "bugs": { | ||
| "url": "https://github.com/pie6k/codables/issues" | ||
| }, | ||
| "homepage": "https://github.com/pie6k/codables#readme", | ||
| "scripts": { | ||
| "build": "vite build && tsc --emitDeclarationOnly", | ||
| "clean": "rm -rf dist", | ||
| "test": "vitest", | ||
| "test:watch": "vitest --watch" | ||
| "test:watch": "vitest --watch", | ||
| "bench": "vitest bench tests/benchmark.bench.ts" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/jsdom": "^27", | ||
| "@types/node": "^24.9.1", | ||
| "@vitest/coverage-v8": "4.0.1", | ||
| "jsdom": "^27.0.1", | ||
| "superjson": "^2.2.3", | ||
| "typescript": "^5.9.3", | ||
| "vite": "^5.0.0", | ||
| "vitest": "^4.0.1" | ||
| } | ||
| } |
-173
| import { createCoderType } from "./CoderType"; | ||
| export const $$undefined = createCoderType( | ||
| "undefined", | ||
| (value) => value === undefined, | ||
| () => null, | ||
| () => undefined | ||
| ); | ||
| function getIsValidDate(date: Date): boolean { | ||
| return !isNaN(date.getTime()); | ||
| } | ||
| export const $$date = createCoderType( | ||
| "date", | ||
| (value) => value instanceof Date, | ||
| (date) => (getIsValidDate(date) ? date.toISOString() : null), | ||
| (isoString) => { | ||
| if (isoString === null) return new Date("invalid"); | ||
| return new Date(isoString); | ||
| } | ||
| ); | ||
| export const $$bigInt = createCoderType( | ||
| "bigInt", | ||
| (value) => typeof value === "bigint", | ||
| (bigInt) => bigInt.toString(), | ||
| (string) => BigInt(string) | ||
| ); | ||
| export const $$set = createCoderType( | ||
| "set", | ||
| (value) => value instanceof Set, | ||
| (set) => Array.from(set), | ||
| (array) => new Set(array) | ||
| ); | ||
| export const $$map = createCoderType( | ||
| "map", | ||
| (value) => value instanceof Map, | ||
| (map) => Array.from(map.entries()), | ||
| (entries) => new Map(entries) | ||
| ); | ||
| export const $$regexp = createCoderType( | ||
| "regexp", | ||
| (value) => value instanceof RegExp, | ||
| ({ source, flags }) => [source, flags] as const, | ||
| ([source, flags]) => new RegExp(source, flags) | ||
| ); | ||
| export const $$error = createCoderType( | ||
| "error", | ||
| (value) => value instanceof Error, | ||
| (error) => ({ | ||
| message: error.message, | ||
| name: error.name, | ||
| cause: error.cause, | ||
| }), | ||
| ({ message, name, cause }) => { | ||
| const error = new Error(message, { cause }); | ||
| if (name) { | ||
| error.name = name; | ||
| } | ||
| return error; | ||
| } | ||
| ); | ||
| export const $$url = createCoderType( | ||
| "url", | ||
| (value) => value instanceof URL, | ||
| (url) => url.toString(), | ||
| (string) => new URL(string) | ||
| ); | ||
| const symbolsRegistry = new Map<string, symbol>(); | ||
| function getSymbolName(symbol: symbol): string { | ||
| const nativeKey = Symbol.keyFor(symbol); | ||
| if (nativeKey) return nativeKey; | ||
| const toStringResult = symbol.toString(); // eg "Symbol(foo)" | ||
| return toStringResult.slice(7, -1); // "foo" | ||
| } | ||
| function registerSymbol(symbol: symbol) { | ||
| const name = getSymbolName(symbol); | ||
| symbolsRegistry.set(name, symbol); | ||
| } | ||
| function getSymbol(symbolKey: string): symbol { | ||
| return symbolsRegistry.get(symbolKey) ?? Symbol.for(symbolKey); | ||
| } | ||
| export const $$symbol = createCoderType( | ||
| "symbol", | ||
| (value) => typeof value === "symbol", | ||
| (symbol) => { | ||
| registerSymbol(symbol); | ||
| return getSymbolName(symbol); | ||
| }, | ||
| (symbolKey) => getSymbol(symbolKey) | ||
| ); | ||
| const TYPED_ARRAY_MAP = { | ||
| uint8: Uint8Array, | ||
| Uint16Array, | ||
| uint32: Uint32Array, | ||
| int8: Int8Array, | ||
| int16: Int16Array, | ||
| int32: Int32Array, | ||
| float32: Float32Array, | ||
| float64: Float64Array, | ||
| } as const; | ||
| type TypedArrayTypeName = keyof typeof TYPED_ARRAY_MAP; | ||
| type TypedArray = InstanceType<(typeof TYPED_ARRAY_MAP)[TypedArrayTypeName]>; | ||
| function getIsTypedArray(value: unknown): value is TypedArray { | ||
| for (const [name, type] of Object.entries(TYPED_ARRAY_MAP)) { | ||
| if (value instanceof type) return true; | ||
| } | ||
| return false; | ||
| } | ||
| function getTypedArrayType(value: unknown): TypedArrayTypeName | null { | ||
| for (const [name, type] of Object.entries(TYPED_ARRAY_MAP)) { | ||
| if (value instanceof type) return name as TypedArrayTypeName; | ||
| } | ||
| return null; | ||
| } | ||
| export const $$typedArray = createCoderType( | ||
| "typedArray", | ||
| getIsTypedArray, | ||
| (value) => { | ||
| const type = getTypedArrayType(value)!; | ||
| return { | ||
| type, | ||
| data: Array.from(value), | ||
| }; | ||
| }, | ||
| ({ type, data }) => new TYPED_ARRAY_MAP[type as TypedArrayTypeName](data) | ||
| ); | ||
| function getSpecialNumberType( | ||
| value: number | ||
| ): "NaN" | "Infinity" | "-Infinity" | null { | ||
| if (isNaN(value)) return "NaN"; | ||
| if (value === Infinity) return "Infinity"; | ||
| if (value === -Infinity) return "-Infinity"; | ||
| return null; | ||
| } | ||
| export const $$num = createCoderType( | ||
| "num", | ||
| (value): value is number => | ||
| typeof value === "number" && !!getSpecialNumberType(value), | ||
| (value) => getSpecialNumberType(value), | ||
| (value) => { | ||
| if (value === "NaN") return NaN; | ||
| if (value === "Infinity") return Infinity; | ||
| if (value === "-Infinity") return -Infinity; | ||
| throw new Error(`Invalid special number type: ${value}`); | ||
| } | ||
| ); |
| import { CoderType, createCoderType } from "./CoderType"; | ||
| import { AnyCodableClass } from "./types"; | ||
| const IS_CODABLE = Symbol.for("IS_CODABLE"); | ||
| const CODABLE_CLASS_TYPE = Symbol.for("CODABLE_CLASS_TYPE"); | ||
| interface CodableClassMetadata { | ||
| [IS_CODABLE]: boolean; | ||
| [CODABLE_CLASS_TYPE]: CoderType<any, any>; | ||
| } | ||
| if (!Symbol.metadata) { | ||
| Reflect.set(Symbol, "metadata", Symbol.for("Symbol.metadata")); | ||
| } | ||
| function getMetadata<T extends object>(Class: T): CodableClassMetadata | null { | ||
| return ( | ||
| (Reflect.get(Class, Symbol.metadata) as CodableClassMetadata | null) ?? null | ||
| ); | ||
| } | ||
| export function getIsCodableClass<T extends AnyCodableClass<any>>( | ||
| Class: T | ||
| ): boolean { | ||
| return getMetadata(Class)?.[IS_CODABLE] ?? false; | ||
| } | ||
| export function getCodableClassType<T extends AnyCodableClass<any>>( | ||
| Class: T | ||
| ): CoderType<any, any> | null { | ||
| return getMetadata(Class)?.[CODABLE_CLASS_TYPE] ?? null; | ||
| } | ||
| export function createCodableClassType<T extends AnyCodableClass<any>>( | ||
| Class: T | ||
| ): CoderType<any, any> { | ||
| return createCoderType( | ||
| Class.name, | ||
| (value): value is T => value instanceof Class, | ||
| (value) => value, | ||
| (value) => value | ||
| ); | ||
| } | ||
| export function codableClass<T extends AnyCodableClass<any>>(name: string) { | ||
| return (Class: T, context: ClassDecoratorContext<T>) => { | ||
| context.metadata[IS_CODABLE] = true; | ||
| const codableClassType = createCodableClassType(Class); | ||
| context.metadata[CODABLE_CLASS_TYPE] = codableClassType; | ||
| }; | ||
| } | ||
| type CodableFieldDecoratorContext<T, V> = | ||
| | ClassFieldDecoratorContext<T, V> | ||
| | ClassAccessorDecoratorContext<T, V>; | ||
| export function codable<T, V>( | ||
| initialValue: any, | ||
| context: CodableFieldDecoratorContext<T, V> | ||
| ) { | ||
| const isSymbolName = typeof context.name === "symbol"; | ||
| if (isSymbolName) throw new Error("Symbol property names are not supported"); | ||
| } |
-132
| import * as builtinTypesMap from "./builtin"; | ||
| import { AnyCodableClass, JSONValue } from "./types"; | ||
| import { CoderType, createCoderType, getIsCoderType } from "./CoderType"; | ||
| import { encodeInput, finalizeEncodeWithCircularRefs } from "./encode"; | ||
| import { getCodableClassType, getIsCodableClass } from "./codableClass"; | ||
| import { CircularRefsManager } from "./refs"; | ||
| import { decodeInput } from "./decode"; | ||
| import { parseMaybeCustomTypeWrapper } from "./parseUtils"; | ||
| const DEFAULT_TYPES = [...Object.values(builtinTypesMap)].filter( | ||
| getIsCoderType | ||
| ); | ||
| export class Coder { | ||
| private typesMap = new Map<string, CoderType>( | ||
| DEFAULT_TYPES.map((type) => [type.name, type]) | ||
| ); | ||
| getTypeByName(name: string): CoderType | null { | ||
| return this.typesMap.get(name) ?? null; | ||
| } | ||
| registerType<Item, Data>(type: CoderType<Item, Data>) { | ||
| if (this.isDefault) { | ||
| throw new Error( | ||
| "Cannot register types on the default coder. Create a custom coder instance using `new Coder()` and register types on that instance." | ||
| ); | ||
| } | ||
| if (this.typesMap.has(type.name)) { | ||
| throw new Error(`Coder type "${type.name}" already registered`); | ||
| } | ||
| this.typesMap.set(type.name, type); | ||
| return type; | ||
| } | ||
| /** | ||
| * Typescript-sugar over `.registerType()` with better type inference. | ||
| */ | ||
| addType<Item, Data>( | ||
| name: string, | ||
| canEncode: (value: unknown) => value is Item, | ||
| encode: (data: Item) => Data, | ||
| decode: (data: Data) => Item | ||
| ) { | ||
| return this.registerType(createCoderType(name, canEncode, encode, decode)); | ||
| } | ||
| encode<T>(value: T): JSONValue { | ||
| const circularRefsManager = new CircularRefsManager(); | ||
| const result = encodeInput(value, circularRefsManager, this, []); | ||
| if (!circularRefsManager.hasCircularRefs) return result; | ||
| return finalizeEncodeWithCircularRefs(result, circularRefsManager); | ||
| } | ||
| decode<T>(value: JSONValue): T { | ||
| const circularRefsMap = new Map<number, unknown>(); | ||
| return decodeInput<T>(value, circularRefsMap, this, []); | ||
| } | ||
| stringify<T>(value: T): string { | ||
| return JSON.stringify(this.encode(value)); | ||
| } | ||
| parse<T>(value: string): T { | ||
| return this.decode(JSON.parse(value)); | ||
| } | ||
| getMatchingTypeFor(input: unknown): CoderType | null { | ||
| for (const type of this.typesMap.values()) { | ||
| if (type.canHandle(input)) { | ||
| return type; | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
| parseMaybeCustomTypeWrapper(input: unknown) { | ||
| return parseMaybeCustomTypeWrapper(input, this); | ||
| } | ||
| get isDefault() { | ||
| return this === coder; | ||
| } | ||
| registerClass<T extends AnyCodableClass<any>>(Class: T) { | ||
| if (this.isDefault) { | ||
| throw new Error( | ||
| "Cannot register classes on the default coder. Create a custom coder instance using `new Coder()` and register classes on that instance." | ||
| ); | ||
| } | ||
| if (!getIsCodableClass(Class)) { | ||
| throw new Error(`Class "${Class.name}" is not codable`); | ||
| } | ||
| const codableClassType = getCodableClassType(Class); | ||
| if (!codableClassType) { | ||
| throw new Error(`Class "${Class.name}" is not codable`); | ||
| } | ||
| return this.registerType(codableClassType); | ||
| } | ||
| } | ||
| export const coder = new Coder(); | ||
| export function decode<T>(value: JSONValue): T { | ||
| return coder.decode(value); | ||
| } | ||
| export function encode<T>(value: T): JSONValue { | ||
| return coder.encode(value); | ||
| } | ||
| export function stringify<T>(value: T): string { | ||
| return coder.stringify(value); | ||
| } | ||
| export function parse<T>(value: string): T { | ||
| return coder.parse(value); | ||
| } |
-70
| import { Coder } from "./Coder"; | ||
| import { getIsRecord } from "./is"; | ||
| interface CoderTypeDefinition<Item, Data> { | ||
| name: string; | ||
| encode: (data: Item) => Data; | ||
| decode: (data: Data) => Item; | ||
| canHandle: (value: unknown) => value is Item; | ||
| } | ||
| type CustomTypeKey<Name extends string> = `$$${Name}`; | ||
| type CustomTypeWrapper<Name extends string, Type> = { | ||
| [key in CustomTypeKey<Name>]: Type; | ||
| }; | ||
| function wrapAsCustomType<Name extends string, Data>( | ||
| name: Name, | ||
| data: Data | ||
| ): CustomTypeWrapper<Name, Data> { | ||
| const wrapper = { | ||
| [`$$${name}`]: data, | ||
| } as CustomTypeWrapper<Name, Data>; | ||
| return wrapper; | ||
| } | ||
| export class CoderType<Item = any, Data = any> { | ||
| constructor(readonly definition: CoderTypeDefinition<Item, Data>) {} | ||
| get name() { | ||
| return this.definition.name; | ||
| } | ||
| get encoder() { | ||
| return this.definition.encode; | ||
| } | ||
| get decoder() { | ||
| return this.definition.decode; | ||
| } | ||
| encode(value: Item): CustomTypeWrapper<string, Data> { | ||
| const encodedData = this.encoder(value); | ||
| return wrapAsCustomType(this.name, encodedData); | ||
| } | ||
| canHandle(value: unknown): value is Item { | ||
| return this.definition.canHandle(value); | ||
| } | ||
| } | ||
| export function createCoderType<Item, Data>( | ||
| name: string, | ||
| canHandle: (value: unknown) => value is Item, | ||
| encode: (data: Item) => Data, | ||
| decode: (data: Data) => Item | ||
| ): CoderType<Item, Data> { | ||
| return new CoderType({ | ||
| name, | ||
| canHandle, | ||
| encode, | ||
| decode, | ||
| }); | ||
| } | ||
| export function getIsCoderType(value: unknown): value is CoderType { | ||
| return value instanceof CoderType; | ||
| } |
| export const CUSTOM_TYPE_INDICATOR_PREFIX = "$$"; |
-61
| import { Coder } from "./Coder"; | ||
| import { JSONValue } from "./types"; | ||
| import { getIsRecord } from "./is"; | ||
| import { parseMaybeCircularRefInfo } from "./refs"; | ||
| import { sanitizePath } from "./utils"; | ||
| type CircularRefsMap = Map<number, unknown>; | ||
| export function decodeInput<T>( | ||
| input: JSONValue, | ||
| circularRefsMap: CircularRefsMap, | ||
| coder: Coder, | ||
| path: string[] | ||
| ): T { | ||
| const maybeCustomTypeWrapper = coder.parseMaybeCustomTypeWrapper(input); | ||
| if (maybeCustomTypeWrapper) { | ||
| const decodedData = decodeInput( | ||
| maybeCustomTypeWrapper.data, | ||
| circularRefsMap, | ||
| coder, | ||
| path | ||
| ); | ||
| return maybeCustomTypeWrapper.type.decoder(decodedData) as T; | ||
| } | ||
| const maybeCircularRefInfo = parseMaybeCircularRefInfo(input); | ||
| if (maybeCircularRefInfo) { | ||
| if (maybeCircularRefInfo.type === "source") { | ||
| circularRefsMap.set(maybeCircularRefInfo.id, maybeCircularRefInfo.source); | ||
| } | ||
| if (maybeCircularRefInfo.type === "alias") { | ||
| // TODO: Validate | ||
| return circularRefsMap.get(maybeCircularRefInfo.id)! as T; | ||
| } | ||
| } | ||
| if (Array.isArray(input)) { | ||
| const result: unknown[] = []; | ||
| for (const [index, item] of input.entries()) { | ||
| result[index] = decodeInput(item, circularRefsMap, coder, [ | ||
| ...path, | ||
| index.toString(), | ||
| ]); | ||
| } | ||
| return result as T; | ||
| } | ||
| if (getIsRecord(input)) { | ||
| const result: Record<string, unknown> = {}; | ||
| for (const [key, value] of Object.entries(input)) { | ||
| result[key] = decodeInput(value, circularRefsMap, coder, [...path, key]); | ||
| } | ||
| return result as T; | ||
| } | ||
| return input as T; | ||
| } |
| import { Coder } from "./Coder"; | ||
| import { codableClass } from "./codableClass"; | ||
| describe("decorators", () => { | ||
| it("should register a class as codable", () => { | ||
| @codableClass("Foo") | ||
| class Foo { | ||
| foo!: string; | ||
| } | ||
| const coder = new Coder(); | ||
| coder.registerClass(Foo); | ||
| const foo = new Foo(); | ||
| foo.foo = "bar"; | ||
| const encoded = coder.encode(foo); | ||
| expect(encoded).toEqual({ | ||
| foo: "bar", | ||
| }); | ||
| const decoded = coder.decode(encoded); | ||
| expect(decoded).toEqual(foo); | ||
| }); | ||
| }); |
-78
| import { changeInJSONByMutation, sanitizePath } from "./utils"; | ||
| import { getIsJSONPrimitive, getIsObject, getIsRecord } from "./is"; | ||
| import { CircularRefsManager } from "./refs"; | ||
| import { Coder } from "./Coder"; | ||
| import { JSONValue } from "./types"; | ||
| export function finalizeEncodeWithCircularRefs( | ||
| output: JSONValue, | ||
| circularRefsManager: CircularRefsManager | ||
| ): JSONValue { | ||
| for (const [ | ||
| circularRefId, | ||
| path, | ||
| ] of circularRefsManager.iterateCircularRefsSourcePaths()) { | ||
| output = changeInJSONByMutation(output, path, (current) => { | ||
| return { | ||
| [`$$ref:${circularRefId}`]: current, | ||
| }; | ||
| }); | ||
| } | ||
| return output; | ||
| } | ||
| export function encodeInput( | ||
| input: unknown, | ||
| circularRefsManager: CircularRefsManager, | ||
| coder: Coder, | ||
| path: string[] | ||
| ): JSONValue { | ||
| if (getIsJSONPrimitive(input)) { | ||
| return input; | ||
| } | ||
| if (getIsObject(input)) { | ||
| circularRefsManager.handleNewRef(input, path); | ||
| const circularRefAlias = circularRefsManager.getCircularRefAlias(input); | ||
| if (circularRefAlias) return circularRefAlias; | ||
| } | ||
| if (Array.isArray(input)) { | ||
| const result: JSONValue = []; | ||
| for (const [index, item] of input.entries()) { | ||
| result[index] = encodeInput(item, circularRefsManager, coder, [ | ||
| ...path, | ||
| index.toString(), | ||
| ]); | ||
| } | ||
| return result; | ||
| } | ||
| if (getIsRecord(input)) { | ||
| const result: JSONValue = {}; | ||
| for (const [key, value] of Object.entries(input)) { | ||
| result[key] = encodeInput(value, circularRefsManager, coder, [ | ||
| ...path, | ||
| key, | ||
| ]); | ||
| } | ||
| return result; | ||
| } | ||
| const customType = coder.getMatchingTypeFor(input); | ||
| if (customType) { | ||
| const encoded = customType.encode(input); | ||
| return encodeInput(encoded, circularRefsManager, coder, path); | ||
| } | ||
| return null; | ||
| } |
-37
| import { JSONArray, JSONPrimitive, Primitive } from "./types"; | ||
| export function getIsJSONPrimitive(value: unknown): value is JSONPrimitive { | ||
| if (value === null) return true; | ||
| if (typeof value === "string") return true; | ||
| if (typeof value === "boolean") return true; | ||
| if (typeof value === "number") { | ||
| if (isNaN(value)) return false; | ||
| if (value === Infinity) return false; | ||
| if (value === -Infinity) return false; | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| /** | ||
| * Returns true only if the value is POJO (Plain Old JavaScript Object) | ||
| * | ||
| * Returns false for instances of classes, functions, etc. | ||
| */ | ||
| export function getIsRecord(value: unknown): value is Record<string, unknown> { | ||
| if (typeof value !== "object" || value === null) { | ||
| return false; | ||
| } | ||
| return value.constructor === Object; | ||
| } | ||
| export function getIsPrimitive(value: unknown): value is Primitive { | ||
| return getIsJSONPrimitive(value) || value === undefined; | ||
| } | ||
| export function getIsObject(value: unknown): value is object { | ||
| return typeof value === "object" && value !== null; | ||
| } |
| import { CUSTOM_TYPE_INDICATOR_PREFIX } from "./consts"; | ||
| import { Coder } from "./Coder"; | ||
| import { JSONValue } from "./types"; | ||
| import { getIsRecord } from "./is"; | ||
| function parseCustomTypeIndicatorKey(key: string) { | ||
| if (!key.startsWith(CUSTOM_TYPE_INDICATOR_PREFIX)) { | ||
| return null; | ||
| } | ||
| return key.slice(CUSTOM_TYPE_INDICATOR_PREFIX.length); | ||
| } | ||
| export function parseMaybeCustomTypeWrapper(input: unknown, coder: Coder) { | ||
| if (!getIsRecord(input)) { | ||
| return null; | ||
| } | ||
| const key = Object.keys(input); | ||
| if (key.length !== 1) return null; | ||
| const [customTypeIndicatorKey] = key; | ||
| const customTypeName = parseCustomTypeIndicatorKey(customTypeIndicatorKey); | ||
| if (!customTypeName) return null; | ||
| const customType = coder.getTypeByName(customTypeName); | ||
| if (!customType) return null; | ||
| const data = input[customTypeIndicatorKey] as JSONValue; | ||
| return { | ||
| name: customTypeName, | ||
| type: customType, | ||
| data, | ||
| coder, | ||
| }; | ||
| } | ||
| export type ParsedCustomTypeWrapper = NonNullable< | ||
| ReturnType<typeof parseMaybeCustomTypeWrapper> | ||
| >; |
-133
| import { getIsRecord } from "./is"; | ||
| type RefSourceKey = `$$ref:${number}`; | ||
| export type CircularRefSource<T extends object> = { | ||
| [key in RefSourceKey]: T; | ||
| }; | ||
| export type CircularRefAlias = { | ||
| $$ref: number; | ||
| }; | ||
| export type CircularRefInfo = | ||
| | { | ||
| type: "source"; | ||
| id: number; | ||
| source: object; | ||
| } | ||
| | { | ||
| type: "alias"; | ||
| id: number; | ||
| }; | ||
| export function parseMaybeCircularRefInfo( | ||
| input: unknown | ||
| ): CircularRefInfo | null { | ||
| if (!getIsRecord(input)) { | ||
| return null; | ||
| } | ||
| const key = Object.keys(input); | ||
| if (key.length !== 1) return null; | ||
| const [refKey] = key; | ||
| if (refKey === "$$ref") { | ||
| return { | ||
| type: "alias", | ||
| id: input[refKey] as number, | ||
| }; | ||
| } | ||
| if (refKey.startsWith("$$ref:")) { | ||
| const id = parseInt(refKey.slice("$$ref:".length)); | ||
| return { | ||
| type: "source", | ||
| id, | ||
| source: input[refKey] as object, | ||
| }; | ||
| } | ||
| return null; | ||
| } | ||
| export class CircularRefsManager { | ||
| private refFirstSeenPath = new WeakMap<object, string[]>(); | ||
| private circularRefIds = new Map<object, number>(); | ||
| private circularRefIdRef = new Map<number, object>(); | ||
| *iterateCircularRefsSourcePaths() { | ||
| for (const [refId, ref] of this.circularRefIdRef) { | ||
| const path = this.refFirstSeenPath.get(ref)!; | ||
| yield [refId, path] as const; | ||
| } | ||
| } | ||
| registerKnownRef(value: object, path: string[]): void { | ||
| this.refFirstSeenPath.set(value, path); | ||
| } | ||
| hasKnownRef(value: object): boolean { | ||
| return this.refFirstSeenPath.has(value); | ||
| } | ||
| getCircularRefId(value: object): number | null { | ||
| return this.circularRefIds.get(value) ?? null; | ||
| } | ||
| getIsCircularRef(value: object): boolean { | ||
| return this.circularRefIds.has(value); | ||
| } | ||
| registerCircularRef(value: object): number { | ||
| const existingRef = this.getCircularRefId(value); | ||
| if (existingRef !== null) { | ||
| return existingRef; | ||
| } | ||
| const refId = this.circularRefIds.size; | ||
| this.circularRefIds.set(value, refId); | ||
| this.circularRefIdRef.set(refId, value); | ||
| return refId; | ||
| } | ||
| handleNewRef(value: object, path: string[]) { | ||
| if (!this.refFirstSeenPath.has(value)) { | ||
| this.registerKnownRef(value, path); | ||
| return null; | ||
| } | ||
| // Did already see this ref, so it's circular | ||
| return this.registerCircularRef(value); | ||
| } | ||
| getCircularRefAlias(value: object): CircularRefAlias | null { | ||
| const ref = this.getCircularRefId(value); | ||
| if (ref === null) { | ||
| return null; | ||
| } | ||
| return { $$ref: ref }; | ||
| } | ||
| get hasCircularRefs(): boolean { | ||
| return this.circularRefIds.size > 0; | ||
| } | ||
| getRefIdFirstSeenPath(refId: number): string[] | null { | ||
| const ref = this.circularRefIdRef.get(refId); | ||
| if (!ref) return null; | ||
| return this.refFirstSeenPath.get(ref) ?? null; | ||
| } | ||
| getRefId(ref: object): number | null { | ||
| return this.circularRefIds.get(ref) ?? null; | ||
| } | ||
| } |
-299
| import { Coder, coder } from "./Coder"; | ||
| import { describe, expect, it } from "vitest"; | ||
| import { JSONValue } from "./types"; | ||
| function createUInt8Array(length: number) { | ||
| const array = new Uint8Array(length); | ||
| for (let i = 0; i < length; i++) { | ||
| array[i] = i % 256; | ||
| } | ||
| return array; | ||
| } | ||
| function expectSerializeAndDeserialize( | ||
| value: unknown, | ||
| encodedShape?: JSONValue | ||
| ) { | ||
| const encoded = coder.encode(value); | ||
| if (encodedShape) { | ||
| expect(encoded).toEqual(encodedShape); | ||
| } | ||
| const decoded = coder.decode(encoded); | ||
| expect(decoded).toEqual(value); | ||
| } | ||
| describe("basic", () => { | ||
| it("should encode a simple object", () => { | ||
| const date = new Date("2025-01-01T00:00:00.000Z"); | ||
| const foo = coder.encode(date); | ||
| expect(coder.encode(date)).toEqual({ $$date: date.toISOString() }); | ||
| expect(coder.decode({ $$date: date.toISOString() })).toEqual(date); | ||
| expect(coder.encode(/bar/g)).toEqual({ $$regexp: ["bar", "g"] }); | ||
| expect(coder.decode({ $$regexp: ["bar", "g"] })).toEqual(/bar/g); | ||
| expect(coder.encode(new Map([["foo", "bar"]]))).toEqual({ | ||
| $$map: [["foo", "bar"]], | ||
| }); | ||
| expect(coder.decode({ $$map: [["foo", "bar"]] })).toEqual( | ||
| new Map([["foo", "bar"]]) | ||
| ); | ||
| expect(coder.encode(new Set(["foo", "bar"]))).toEqual({ | ||
| $$set: ["foo", "bar"], | ||
| }); | ||
| expect(coder.decode({ $$set: ["foo", "bar"] })).toEqual( | ||
| new Set(["foo", "bar"]) | ||
| ); | ||
| expect(coder.encode([undefined])).toEqual([{ $$undefined: null }]); | ||
| expect(coder.decode([{ $$undefined: null }])).toEqual([undefined]); | ||
| expect(coder.encode(createUInt8Array(10))).toEqual({ | ||
| $$typedArray: { type: "uint8", data: Array.from(createUInt8Array(10)) }, | ||
| }); | ||
| expect( | ||
| coder.decode({ | ||
| $$typedArray: { type: "uint8", data: Array.from(createUInt8Array(10)) }, | ||
| }) | ||
| ).toEqual(createUInt8Array(10)); | ||
| expect(coder.encode(NaN)).toEqual({ $$num: "NaN" }); | ||
| expect(coder.decode({ $$num: "NaN" })).toEqual(NaN); | ||
| }); | ||
| it("more coding examples", () => { | ||
| expectSerializeAndDeserialize(new Date("2025-01-01T00:00:00.000Z")); | ||
| expectSerializeAndDeserialize(NaN, { $$num: "NaN" }); | ||
| expectSerializeAndDeserialize(new Date("invalid"), { $$date: null }); | ||
| expectSerializeAndDeserialize(Infinity, { $$num: "Infinity" }); | ||
| expectSerializeAndDeserialize(-Infinity, { $$num: "-Infinity" }); | ||
| expectSerializeAndDeserialize(0.5); | ||
| expectSerializeAndDeserialize(123n, { $$bigInt: "123" }); | ||
| expectSerializeAndDeserialize(BigInt(123), { $$bigInt: "123" }); | ||
| expectSerializeAndDeserialize(Symbol.for("foo"), { $$symbol: "foo" }); | ||
| expectSerializeAndDeserialize(Symbol("foo"), { $$symbol: "foo" }); | ||
| expectSerializeAndDeserialize( | ||
| { foo: undefined }, | ||
| { foo: { $$undefined: null } } | ||
| ); | ||
| expectSerializeAndDeserialize(/bar/g, { $$regexp: ["bar", "g"] }); | ||
| }); | ||
| it("should encode nested objects", () => { | ||
| expect(coder.encode(new Set([new Date("2025-01-01T00:00:00.000Z")]))) | ||
| .toMatchInlineSnapshot(` | ||
| { | ||
| "$$set": [ | ||
| { | ||
| "$$date": "2025-01-01T00:00:00.000Z", | ||
| }, | ||
| ], | ||
| } | ||
| `); | ||
| }); | ||
| it("should decode nested objects", () => { | ||
| expect( | ||
| coder.decode({ | ||
| $$set: [{ $$date: "2025-01-01T00:00:00.000Z" }], | ||
| }) | ||
| ).toEqual(new Set([new Date("2025-01-01T00:00:00.000Z")])); | ||
| }); | ||
| }); | ||
| describe("circular references", () => { | ||
| it("should encode circular references (top)", () => { | ||
| const foo = { text: "foo", self: null as any }; | ||
| foo.self = foo; | ||
| expect(coder.encode(foo)).toEqual({ | ||
| "$$ref:0": { | ||
| text: "foo", | ||
| self: { $$ref: 0 }, | ||
| }, | ||
| }); | ||
| }); | ||
| it("should encode circular references", () => { | ||
| const foo = { foo: "foo", bar: null as any }; | ||
| const bar = { foo: foo }; | ||
| foo.bar = bar; | ||
| expect(coder.encode(foo)).toEqual({ | ||
| "$$ref:0": { | ||
| foo: "foo", | ||
| bar: { foo: { $$ref: 0 } }, | ||
| }, | ||
| }); | ||
| }); | ||
| it.todo("should properly encode same level circular references", () => { | ||
| const foo: any = {}; | ||
| const bar: any = {}; | ||
| foo.bar = bar; | ||
| bar.foo = foo; | ||
| expect(coder.encode([foo, bar])).toEqual([{ $$ref: 0 }, { $$ref: 1 }]); | ||
| }); | ||
| }); | ||
| describe("custom types", () => { | ||
| it("should encode custom types", () => { | ||
| const coder = new Coder(); | ||
| class Foo { | ||
| constructor(public name: string) {} | ||
| } | ||
| coder.addType( | ||
| "Foo", | ||
| (value) => value instanceof Foo, | ||
| (value) => value.name, | ||
| (name) => new Foo(name) | ||
| ); | ||
| expect(coder.encode(new Foo("bar"))).toEqual({ | ||
| $$Foo: "bar", | ||
| }); | ||
| expect(coder.decode({ $$Foo: "bar" })).toEqual(new Foo("bar")); | ||
| }); | ||
| it("custom types can have nested custom objects", () => { | ||
| const coder = new Coder(); | ||
| class User { | ||
| constructor(readonly logins: Set<Date>) {} | ||
| } | ||
| coder.addType( | ||
| "User", | ||
| (value) => value instanceof User, | ||
| (value) => value.logins, | ||
| (logins) => new User(logins) | ||
| ); | ||
| expect( | ||
| coder.encode(new User(new Set([new Date("2025-01-01T00:00:00.000Z")]))) | ||
| ).toEqual({ | ||
| $$User: { $$set: [{ $$date: "2025-01-01T00:00:00.000Z" }] }, | ||
| }); | ||
| expect( | ||
| coder.decode({ | ||
| $$User: { $$set: [{ $$date: "2025-01-01T00:00:00.000Z" }] }, | ||
| }) | ||
| ).toEqual(new User(new Set([new Date("2025-01-01T00:00:00.000Z")]))); | ||
| }); | ||
| }); | ||
| describe("plain json", () => { | ||
| it("serializes to json the same way as normal json", () => { | ||
| const a: JSONValue = [ | ||
| 1, | ||
| 2, | ||
| 3, | ||
| { foo: "bar" }, | ||
| { baz: false, qux: null, quux: true }, | ||
| { a: 1, b: [4, 5, 6, null] }, | ||
| ]; | ||
| expect(coder.encode(a)).toEqual(a); | ||
| expect(coder.decode(a)).toEqual(a); | ||
| expect(coder.encode(null)).toEqual(null); | ||
| expect(coder.decode(true)).toEqual(true); | ||
| expect(coder.decode(false)).toEqual(false); | ||
| expect(coder.decode(1)).toEqual(1); | ||
| expect(coder.decode(2.5)).toEqual(2.5); | ||
| expect(coder.decode("foo")).toEqual("foo"); | ||
| expect(coder.encode([])).toEqual([]); | ||
| expect(coder.encode({})).toEqual({}); | ||
| }); | ||
| }); | ||
| describe("errors", () => { | ||
| it("should throw an error if a type is registered twice", () => { | ||
| const coder = new Coder(); | ||
| coder.addType( | ||
| "foo", | ||
| (value) => typeof value === "string", | ||
| (value) => value, | ||
| (value) => value | ||
| ); | ||
| expect(() => { | ||
| coder.addType( | ||
| "foo", | ||
| (value) => typeof value === "string", | ||
| (value) => value, | ||
| (value) => value | ||
| ); | ||
| }).toThrowErrorMatchingInlineSnapshot( | ||
| `[Error: Coder type "foo" already registered]` | ||
| ); | ||
| }); | ||
| }); | ||
| describe("stringify/parse", () => { | ||
| it("should stringify and parse the same way as normal json", () => { | ||
| const a = [ | ||
| 1, | ||
| 2, | ||
| 3, | ||
| { foo: "bar" }, | ||
| { baz: false, qux: null, quux: true }, | ||
| { a: 1, b: [4, 5, 6, null] }, | ||
| ]; | ||
| expect(coder.stringify(a)).toEqual(JSON.stringify(a)); | ||
| expect(coder.parse(coder.stringify(a))).toEqual(a); | ||
| }); | ||
| it("should stringify and parse custom types", () => { | ||
| const coder = new Coder(); | ||
| class Foo { | ||
| constructor(public name: string) {} | ||
| } | ||
| coder.addType( | ||
| "Foo", | ||
| (value) => value instanceof Foo, | ||
| (value) => value.name, | ||
| (name) => new Foo(name) | ||
| ); | ||
| expect(coder.stringify(new Foo("bar"))).toEqual( | ||
| JSON.stringify({ $$Foo: "bar" }) | ||
| ); | ||
| expect(coder.parse(JSON.stringify({ $$Foo: "bar" }))).toEqual( | ||
| new Foo("bar") | ||
| ); | ||
| }); | ||
| }); | ||
| describe("isDefault", () => { | ||
| it("should return true if the coder is the default coder", () => { | ||
| expect(coder.isDefault).toBe(true); | ||
| const otherCoder = new Coder(); | ||
| expect(otherCoder.isDefault).toBe(false); | ||
| }); | ||
| it("should throw an error if a type is registered on the default coder", () => { | ||
| expect(() => { | ||
| coder.addType( | ||
| "foo", | ||
| (value) => typeof value === "string", | ||
| (value) => value, | ||
| (value) => value | ||
| ); | ||
| }).toThrowErrorMatchingInlineSnapshot( | ||
| `[Error: Cannot register types on the default coder. Create a custom coder instance using \`new Coder()\` and register types on that instance.]` | ||
| ); | ||
| }); | ||
| }); |
| { | ||
| "compilerOptions": { | ||
| "target": "ESNext", | ||
| "module": "ESNext", | ||
| "moduleResolution": "node", | ||
| "strict": true, | ||
| "esModuleInterop": true, | ||
| "skipLibCheck": true, | ||
| "forceConsistentCasingInFileNames": true, | ||
| "declaration": true, | ||
| "outDir": "./dist", | ||
| "rootDir": "./", | ||
| "types": ["vitest/globals", "node"] | ||
| }, | ||
| "include": ["**/*.ts"], | ||
| "exclude": ["node_modules", "dist"] | ||
| } |
-22
| export type JSONPrimitive = string | number | boolean | null; | ||
| export type JSONObject = { [key: string]: JSONValue }; | ||
| export type JSONArray = JSONValue[]; | ||
| export type JSONValue = JSONPrimitive | JSONObject | JSONArray; | ||
| export type Primitive = string | number | boolean | null | undefined; | ||
| export type ClassConstructor<T> = new (...args: any) => T; | ||
| export type AtLeastOne<T> = [T, ...T[]]; | ||
| /** | ||
| * A class that can be instantiated without any arguments. | ||
| */ | ||
| export type AutoCodableClass<T> = new () => T; | ||
| /** | ||
| * A class that requires arguments to be known in order to be instantiated. | ||
| */ | ||
| export type ManuallyCodableClass<T> = new (...args: AtLeastOne<any>) => T; | ||
| export type AnyCodableClass<T> = AutoCodableClass<T> | ManuallyCodableClass<T>; |
-53
| import { JSONObject, JSONValue } from "./types"; | ||
| type SanitizablePathSegment = string | number; | ||
| function sanitizePathSegment(segment: SanitizablePathSegment): string { | ||
| if (typeof segment === "number") { | ||
| return `${segment}`; | ||
| } | ||
| return segment; | ||
| } | ||
| export type PropertyPath = string[]; | ||
| export function sanitizePath(path: SanitizablePathSegment[]): PropertyPath { | ||
| return path.map(sanitizePathSegment); | ||
| } | ||
| export function changeInJSONByMutation( | ||
| input: JSONValue, | ||
| propertyPath: PropertyPath, | ||
| valueChanger: (current: JSONValue) => JSONValue | ||
| ): JSONValue { | ||
| if (propertyPath.length === 0) { | ||
| return valueChanger(input); | ||
| } | ||
| let pointer = input; | ||
| for (let i = 0; i < propertyPath.length; i++) { | ||
| const propertyName = propertyPath[i]; | ||
| const isLastProperty = i === propertyPath.length - 1; | ||
| const propertyValue = Reflect.get( | ||
| pointer as JSONObject, | ||
| propertyName | ||
| ) as JSONObject; | ||
| if (!isLastProperty) { | ||
| pointer = propertyValue; | ||
| continue; | ||
| } | ||
| Reflect.set( | ||
| pointer as JSONObject, | ||
| propertyName, | ||
| valueChanger(propertyValue) | ||
| ); | ||
| return pointer; | ||
| } | ||
| return pointer; | ||
| } |
| import { defineConfig } from "vitest/config"; | ||
| export default defineConfig({ | ||
| test: { | ||
| globals: true, | ||
| environment: "node", | ||
| }, | ||
| }); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No License Found
LicenseLicense information could not be found.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
236358
666.72%51
183.33%0
-100%2535
156.84%0
-100%1
-50%0
-100%8
166.67%1
Infinity%