| | |
| | | const SetObjectSerializer = require("./SetObjectSerializer"); |
| | | |
| | | /** @typedef {import("../logging/Logger").Logger} Logger */ |
| | | /** @typedef {typeof import("../util/Hash")} Hash */ |
| | | /** @typedef {import("../util/Hash").HashFunction} HashFunction */ |
| | | /** @typedef {import("./SerializerMiddleware").LazyOptions} LazyOptions */ |
| | | /** @typedef {import("./types").ComplexSerializableType} ComplexSerializableType */ |
| | | /** @typedef {import("./types").PrimitiveSerializableType} PrimitiveSerializableType */ |
| | |
| | | */ |
| | | |
| | | /** |
| | | * Defines the object serializer snapshot type used by this module. |
| | | * @typedef {object} ObjectSerializerSnapshot |
| | | * @property {number} length |
| | | * @property {number} cycleStackSize |
| | |
| | | /** @typedef {EXPECTED_OBJECT | string} ReferenceableItem */ |
| | | |
| | | /** |
| | | * Defines the object serializer context type used by this module. |
| | | * @typedef {object} ObjectSerializerContext |
| | | * @property {(value: EXPECTED_ANY) => void} write |
| | | * @property {(value: ReferenceableItem) => void} setCircularReference |
| | |
| | | */ |
| | | |
| | | /** |
| | | * Defines the object deserializer context type used by this module. |
| | | * @typedef {object} ObjectDeserializerContext |
| | | * @property {() => EXPECTED_ANY} read |
| | | * @property {(value: ReferenceableItem) => void} setCircularReference |
| | | */ |
| | | |
| | | /** |
| | | * Defines the object serializer type used by this module. |
| | | * @typedef {object} ObjectSerializer |
| | | * @property {(value: EXPECTED_ANY, context: ObjectSerializerContext) => void} serialize |
| | | * @property {(context: ObjectDeserializerContext) => EXPECTED_ANY} deserialize |
| | | */ |
| | | |
| | | /** |
| | | * Updates set size using the provided set. |
| | | * @template T |
| | | * @param {Set<T>} set set |
| | | * @param {number} size count of items to keep |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Updates map size using the provided map. |
| | | * @template K, X |
| | | * @param {Map<K, X>} map map |
| | | * @param {number} size count of items to keep |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Returns hash. |
| | | * @param {Buffer} buffer buffer |
| | | * @param {string | Hash} hashFunction hash function to use |
| | | * @param {HashFunction} hashFunction hash function to use |
| | | * @returns {string} hash |
| | | */ |
| | | const toHash = (buffer, hashFunction) => { |
| | |
| | | jsTypes.set(SyntaxError, new ErrorObjectSerializer(SyntaxError)); |
| | | jsTypes.set(TypeError, new ErrorObjectSerializer(TypeError)); |
| | | |
| | | // @ts-expect-error ES2018 doesn't `AggregateError`, but it can be used by developers |
| | | // eslint-disable-next-line n/no-unsupported-features/es-builtins, n/no-unsupported-features/es-syntax |
| | | if (typeof AggregateError !== "undefined") { |
| | | jsTypes.set( |
| | | // @ts-expect-error ES2018 doesn't `AggregateError`, but it can be used by developers |
| | | // eslint-disable-next-line n/no-unsupported-features/es-builtins, n/no-unsupported-features/es-syntax |
| | | AggregateError, |
| | | new AggregateErrorSerializer() |
| | |
| | | /** @typedef {PrimitiveSerializableType[]} SerializedType */ |
| | | /** @typedef {{ logger: Logger }} Context */ |
| | | |
| | | /** @typedef {(context: ObjectSerializerContext | ObjectDeserializerContext) => void} ExtendContext */ |
| | | |
| | | /** |
| | | * Represents ObjectMiddleware. |
| | | * @extends {SerializerMiddleware<DeserializedType, SerializedType, Context>} |
| | | */ |
| | | class ObjectMiddleware extends SerializerMiddleware { |
| | | /** |
| | | * @param {(context: ObjectSerializerContext | ObjectDeserializerContext) => void} extendContext context extensions |
| | | * @param {string | Hash} hashFunction hash function to use |
| | | * Creates an instance of ObjectMiddleware. |
| | | * @param {ExtendContext} extendContext context extensions |
| | | * @param {HashFunction} hashFunction hash function to use |
| | | */ |
| | | constructor(extendContext, hashFunction = DEFAULTS.HASH_FUNCTION) { |
| | | super(); |
| | | /** @type {ExtendContext} */ |
| | | this.extendContext = extendContext; |
| | | /** @type {HashFunction} */ |
| | | this._hashFunction = hashFunction; |
| | | } |
| | | |
| | | /** |
| | | * Processes the provided reg exp. |
| | | * @param {RegExp} regExp RegExp for which the request is tested |
| | | * @param {(request: string) => boolean} loader loader to load the request, returns true when successful |
| | | * @returns {void} |
| | |
| | | } |
| | | |
| | | /** |
| | | * Processes the provided constructor. |
| | | * @param {Constructor} Constructor the constructor |
| | | * @param {string} request the request which will be required when deserializing |
| | | * @param {string | null} name the name to make multiple serializer unique when sharing a request |
| | |
| | | } |
| | | |
| | | /** |
| | | * Register not serializable. |
| | | * @param {Constructor} Constructor the constructor |
| | | * @returns {void} |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * @param {Constructor} object for serialization |
| | | * Gets serializer for. |
| | | * @param {EXPECTED_ANY} object for serialization |
| | | * @returns {SerializerConfigWithSerializer} Serializer config |
| | | */ |
| | | static getSerializerFor(object) { |
| | | const proto = Object.getPrototypeOf(object); |
| | | /** @type {null | Constructor} */ |
| | | let c; |
| | | if (proto === null) { |
| | | // Object created with Object.create(null) |
| | |
| | | } |
| | | const config = serializers.get(c); |
| | | |
| | | if (!config) throw new Error(`No serializer registered for ${c.name}`); |
| | | if (!config) { |
| | | throw new Error( |
| | | `No serializer registered for ${/** @type {Constructor} */ (c).name}` |
| | | ); |
| | | } |
| | | if (config === NOT_SERIALIZABLE) throw NOT_SERIALIZABLE; |
| | | |
| | | return /** @type {SerializerConfigWithSerializer} */ (config); |
| | | } |
| | | |
| | | /** |
| | | * Gets deserializer for. |
| | | * @param {string} request request |
| | | * @param {string} name name |
| | | * @returns {ObjectSerializer} serializer |
| | |
| | | } |
| | | |
| | | /** |
| | | * Get deserializer for without error. |
| | | * @param {string} request request |
| | | * @param {string} name name |
| | | * @returns {ObjectSerializer | undefined} serializer |
| | |
| | | } |
| | | |
| | | /** |
| | | * Serializes this instance into the provided serializer context. |
| | | * @param {DeserializedType} data data |
| | | * @param {Context} context context object |
| | | * @returns {SerializedType | Promise<SerializedType> | null} serialized data |
| | |
| | | /** @type {Map<ReferenceableItem, number>} */ |
| | | let referenceable = new Map(); |
| | | /** |
| | | * Adds referenceable. |
| | | * @param {ReferenceableItem} item referenceable item |
| | | */ |
| | | const addReferenceable = (item) => { |
| | | referenceable.set(item, currentPos++); |
| | | }; |
| | | /** @type {Map<number, Buffer | [Buffer, Buffer] | Map<string, Buffer>>} */ |
| | | let bufferDedupeMap = new Map(); |
| | | /** |
| | | * Returns deduped buffer. |
| | | * @param {Buffer} buf buffer |
| | | * @returns {Buffer} deduped buffer |
| | | */ |
| | |
| | | return buf; |
| | | } |
| | | const hash = toHash(entry, this._hashFunction); |
| | | /** @type {Map<string, Buffer>} */ |
| | | const newMap = new Map(); |
| | | newMap.set(hash, entry); |
| | | bufferDedupeMap.set(len, newMap); |
| | |
| | | entry.push(buf); |
| | | return buf; |
| | | } |
| | | /** @type {Map<string, Buffer>} */ |
| | | const newMap = new Map(); |
| | | const hash = toHash(buf, this._hashFunction); |
| | | /** @type {undefined | Buffer} */ |
| | | let found; |
| | | for (const item of entry) { |
| | | const itemHash = toHash(item, this._hashFunction); |
| | |
| | | return buf; |
| | | }; |
| | | let currentPosTypeLookup = 0; |
| | | /** @type {Map<ComplexSerializableType, number>} */ |
| | | let objectTypeLookup = new Map(); |
| | | /** @type {Set<ComplexSerializableType>} */ |
| | | const cycleStack = new Set(); |
| | | /** |
| | | * Returns stack. |
| | | * @param {ComplexSerializableType} item item to stack |
| | | * @returns {string} stack |
| | | */ |
| | |
| | | if (item.constructor === Object) { |
| | | return `Object { ${Object.keys(item).join(", ")} }`; |
| | | } |
| | | if (item.constructor === Map) return `Map { ${item.size} items }`; |
| | | if (item.constructor === Array) { |
| | | return `Array { ${item.length} items }`; |
| | | if (item.constructor === Map) { |
| | | return `Map { ${/** @type {Map<EXPECTED_ANY, EXPECTED_ANY>} */ (item).size} items }`; |
| | | } |
| | | if (item.constructor === Set) return `Set { ${item.size} items }`; |
| | | if (item.constructor === RegExp) return item.toString(); |
| | | if (item.constructor === Array) { |
| | | return `Array { ${/** @type {EXPECTED_ANY[]} */ (item).length} items }`; |
| | | } |
| | | if (item.constructor === Set) { |
| | | return `Set { ${/** @type {Set<EXPECTED_ANY>} */ (item).size} items }`; |
| | | } |
| | | if (item.constructor === RegExp) { |
| | | return /** @type {RegExp} */ (item).toString(); |
| | | } |
| | | return `${item.constructor.name}`; |
| | | } |
| | | return `Object [null prototype] { ${Object.keys(item).join( |
| | |
| | | }) |
| | | .join(" -> "); |
| | | }; |
| | | /** @type {WeakSet<Error>} */ |
| | | /** @type {undefined | WeakSet<Error>} */ |
| | | let hasDebugInfoAttached; |
| | | /** @type {ObjectSerializerContext} */ |
| | | let ctx = { |
| | |
| | | }; |
| | | this.extendContext(ctx); |
| | | /** |
| | | * Processes the provided item. |
| | | * @param {ComplexSerializableType} item item to serialize |
| | | */ |
| | | const process = (item) => { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Restores this instance from the provided deserializer context. |
| | | * @param {SerializedType} data data |
| | | * @param {Context} context context object |
| | | * @returns {DeserializedType | Promise<DeserializedType>} deserialized data |
| | |
| | | /** @type {ReferenceableItem[]} */ |
| | | let referenceable = []; |
| | | /** |
| | | * Adds referenceable. |
| | | * @param {ReferenceableItem} item referenceable item |
| | | */ |
| | | const addReferenceable = (item) => { |
| | |
| | | }; |
| | | this.extendContext(ctx); |
| | | /** |
| | | * Decodes the provided value. |
| | | * @returns {ComplexSerializableType} deserialize value |
| | | */ |
| | | const decodeValue = () => { |
| | |
| | | ); |
| | | } else { |
| | | const request = nextItem; |
| | | /** @type {undefined | ObjectSerializer} */ |
| | | let serializer; |
| | | |
| | | if (typeof request === "number") { |
| | |
| | | // As this is only for error handling, we omit creating a Map for |
| | | // faster access to this information, as this would affect performance |
| | | // in the good case |
| | | /** @type {undefined | [Constructor | null, SerializerConfig]} */ |
| | | let serializerEntry; |
| | | for (const entry of serializers) { |
| | | if (entry[1].serializer === serializer) { |