| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-14 06:31:28 +08:00
										 |  |  | const createHash = require("../util/createHash"); | 
					
						
							| 
									
										
										
										
											2020-01-28 15:55:22 +08:00
										 |  |  | const ArraySerializer = require("./ArraySerializer"); | 
					
						
							| 
									
										
										
										
											2020-06-02 22:09:48 +08:00
										 |  |  | const DateObjectSerializer = require("./DateObjectSerializer"); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | const ErrorObjectSerializer = require("./ErrorObjectSerializer"); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | const MapObjectSerializer = require("./MapObjectSerializer"); | 
					
						
							| 
									
										
										
										
											2018-12-26 05:07:51 +08:00
										 |  |  | const NullPrototypeObjectSerializer = require("./NullPrototypeObjectSerializer"); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | const PlainObjectSerializer = require("./PlainObjectSerializer"); | 
					
						
							| 
									
										
										
										
											2018-10-18 18:34:54 +08:00
										 |  |  | const RegExpObjectSerializer = require("./RegExpObjectSerializer"); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | const SerializerMiddleware = require("./SerializerMiddleware"); | 
					
						
							|  |  |  | const SetObjectSerializer = require("./SetObjectSerializer"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | /** @typedef {typeof import("../util/Hash")} Hash */ | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | /** @typedef {import("./types").ComplexSerializableType} ComplexSerializableType */ | 
					
						
							|  |  |  | /** @typedef {import("./types").PrimitiveSerializableType} PrimitiveSerializableType */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | /** @typedef {new (...params: any[]) => any} Constructor */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Format: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | File -> Section* | 
					
						
							|  |  |  | Section -> ObjectSection | ReferenceSection | EscapeSection | OtherSection | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ObjectSection -> ESCAPE ( | 
					
						
							| 
									
										
										
										
											2018-10-10 15:55:06 +08:00
										 |  |  | 	number:relativeOffset (number > 0) | | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	string:request (string|null):export | 
					
						
							|  |  |  | ) Section:value* ESCAPE ESCAPE_END_OBJECT | 
					
						
							| 
									
										
										
										
											2018-10-10 15:55:06 +08:00
										 |  |  | ReferenceSection -> ESCAPE number:relativeOffset (number < 0) | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | EscapeSection -> ESCAPE ESCAPE_ESCAPE_VALUE (escaped value ESCAPE) | 
					
						
							|  |  |  | EscapeSection -> ESCAPE ESCAPE_UNDEFINED (escaped value ESCAPE) | 
					
						
							|  |  |  | OtherSection -> any (except ESCAPE) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Why using null as escape value? | 
					
						
							|  |  |  | Multiple null values can merged by the BinaryMiddleware, which makes it very efficient | 
					
						
							|  |  |  | Technically any value can be used. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} ObjectSerializerContext | 
					
						
							|  |  |  |  * @property {function(any): void} write | 
					
						
							| 
									
										
										
										
											2023-04-22 07:30:02 +08:00
										 |  |  |  * @property {function(any): void} setCircularReference | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} ObjectDeserializerContext | 
					
						
							|  |  |  |  * @property {function(): any} read | 
					
						
							| 
									
										
										
										
											2023-04-22 07:30:02 +08:00
										 |  |  |  * @property {function(any): void} setCircularReference | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} ObjectSerializer | 
					
						
							|  |  |  |  * @property {function(any, ObjectSerializerContext): void} serialize | 
					
						
							|  |  |  |  * @property {function(ObjectDeserializerContext): any} deserialize | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 02:32:23 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @template T | 
					
						
							|  |  |  |  * @param {Set<T>} set set | 
					
						
							|  |  |  |  * @param {number} size count of items to keep | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-23 17:31:20 +08:00
										 |  |  | const setSetSize = (set, size) => { | 
					
						
							|  |  |  | 	let i = 0; | 
					
						
							|  |  |  | 	for (const item of set) { | 
					
						
							|  |  |  | 		if (i++ >= size) { | 
					
						
							|  |  |  | 			set.delete(item); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 02:32:23 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @template K, X | 
					
						
							|  |  |  |  * @param {Map<K, X>} map map | 
					
						
							|  |  |  |  * @param {number} size count of items to keep | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-23 17:31:20 +08:00
										 |  |  | const setMapSize = (map, size) => { | 
					
						
							|  |  |  | 	let i = 0; | 
					
						
							|  |  |  | 	for (const item of map.keys()) { | 
					
						
							|  |  |  | 		if (i++ >= size) { | 
					
						
							|  |  |  | 			map.delete(item); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @param {Buffer} buffer buffer | 
					
						
							|  |  |  |  * @param {string | Hash} hashFunction hash function to use | 
					
						
							|  |  |  |  * @returns {string} hash | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const toHash = (buffer, hashFunction) => { | 
					
						
							|  |  |  | 	const hash = createHash(hashFunction); | 
					
						
							| 
									
										
										
										
											2020-12-14 06:31:28 +08:00
										 |  |  | 	hash.update(buffer); | 
					
						
							|  |  |  | 	return /** @type {string} */ (hash.digest("latin1")); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | const ESCAPE = null; | 
					
						
							| 
									
										
										
										
											2018-10-10 15:55:06 +08:00
										 |  |  | const ESCAPE_ESCAPE_VALUE = null; | 
					
						
							|  |  |  | const ESCAPE_END_OBJECT = true; | 
					
						
							|  |  |  | const ESCAPE_UNDEFINED = false; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-26 05:07:51 +08:00
										 |  |  | const CURRENT_VERSION = 2; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-12 22:21:21 +08:00
										 |  |  | /** @type {Map<Constructor, { request?: string, name?: string | number | null, serializer?: ObjectSerializer }>} */ | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | const serializers = new Map(); | 
					
						
							| 
									
										
										
										
											2023-05-23 02:32:23 +08:00
										 |  |  | /** @type {Map<string | number, ObjectSerializer>} */ | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | const serializerInversed = new Map(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 02:32:23 +08:00
										 |  |  | /** @type {Set<string>} */ | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | const loadedRequests = new Set(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-18 21:52:22 +08:00
										 |  |  | const NOT_SERIALIZABLE = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-25 04:23:35 +08:00
										 |  |  | const jsTypes = new Map(); | 
					
						
							| 
									
										
										
										
											2020-01-28 15:55:22 +08:00
										 |  |  | jsTypes.set(Object, new PlainObjectSerializer()); | 
					
						
							|  |  |  | jsTypes.set(Array, new ArraySerializer()); | 
					
						
							| 
									
										
										
										
											2018-12-26 05:07:51 +08:00
										 |  |  | jsTypes.set(null, new NullPrototypeObjectSerializer()); | 
					
						
							| 
									
										
										
										
											2018-10-25 04:23:35 +08:00
										 |  |  | jsTypes.set(Map, new MapObjectSerializer()); | 
					
						
							|  |  |  | jsTypes.set(Set, new SetObjectSerializer()); | 
					
						
							| 
									
										
										
										
											2020-06-02 22:09:48 +08:00
										 |  |  | jsTypes.set(Date, new DateObjectSerializer()); | 
					
						
							| 
									
										
										
										
											2018-10-25 04:23:35 +08:00
										 |  |  | jsTypes.set(RegExp, new RegExpObjectSerializer()); | 
					
						
							|  |  |  | jsTypes.set(Error, new ErrorObjectSerializer(Error)); | 
					
						
							|  |  |  | jsTypes.set(EvalError, new ErrorObjectSerializer(EvalError)); | 
					
						
							|  |  |  | jsTypes.set(RangeError, new ErrorObjectSerializer(RangeError)); | 
					
						
							|  |  |  | jsTypes.set(ReferenceError, new ErrorObjectSerializer(ReferenceError)); | 
					
						
							|  |  |  | jsTypes.set(SyntaxError, new ErrorObjectSerializer(SyntaxError)); | 
					
						
							|  |  |  | jsTypes.set(TypeError, new ErrorObjectSerializer(TypeError)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | // If in a sandboxed environment (e. g. jest), this escapes the sandbox and registers
 | 
					
						
							|  |  |  | // real Object and Array types to. These types may occur in the wild too, e. g. when
 | 
					
						
							|  |  |  | // using Structured Clone in postMessage.
 | 
					
						
							|  |  |  | if (exports.constructor !== Object) { | 
					
						
							|  |  |  | 	const Obj = /** @type {typeof Object} */ (exports.constructor); | 
					
						
							|  |  |  | 	const Fn = /** @type {typeof Function} */ (Obj.constructor); | 
					
						
							|  |  |  | 	for (const [type, config] of Array.from(jsTypes)) { | 
					
						
							|  |  |  | 		if (type) { | 
					
						
							|  |  |  | 			const Type = new Fn(`return ${type.name};`)(); | 
					
						
							|  |  |  | 			jsTypes.set(Type, config); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-25 04:23:35 +08:00
										 |  |  | { | 
					
						
							|  |  |  | 	let i = 1; | 
					
						
							|  |  |  | 	for (const [type, serializer] of jsTypes) { | 
					
						
							|  |  |  | 		serializers.set(type, { | 
					
						
							|  |  |  | 			request: "", | 
					
						
							|  |  |  | 			name: i++, | 
					
						
							|  |  |  | 			serializer | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | for (const { request, name, serializer } of serializers.values()) { | 
					
						
							| 
									
										
										
										
											2023-06-12 22:21:21 +08:00
										 |  |  | 	serializerInversed.set( | 
					
						
							|  |  |  | 		`${request}/${name}`, | 
					
						
							|  |  |  | 		/** @type {ObjectSerializer} */ (serializer) | 
					
						
							|  |  |  | 	); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-20 16:05:32 +08:00
										 |  |  | /** @type {Map<RegExp, (request: string) => boolean>} */ | 
					
						
							|  |  |  | const loaders = new Map(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {ComplexSerializableType[]} DeserializedType | 
					
						
							|  |  |  |  * @typedef {PrimitiveSerializableType[]} SerializedType | 
					
						
							|  |  |  |  * @extends {SerializerMiddleware<DeserializedType, SerializedType>} | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | class ObjectMiddleware extends SerializerMiddleware { | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {function(any): void} extendContext context extensions | 
					
						
							|  |  |  | 	 * @param {string | Hash} hashFunction hash function to use | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	constructor(extendContext, hashFunction = "md4") { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		super(); | 
					
						
							|  |  |  | 		this.extendContext = extendContext; | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | 		this._hashFunction = hashFunction; | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-12-20 16:05:32 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {RegExp} regExp RegExp for which the request is tested | 
					
						
							|  |  |  | 	 * @param {function(string): boolean} loader loader to load the request, returns true when successful | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	static registerLoader(regExp, loader) { | 
					
						
							|  |  |  | 		loaders.set(regExp, loader); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Constructor} Constructor the constructor | 
					
						
							|  |  |  | 	 * @param {string} request the request which will be required when deserializing | 
					
						
							| 
									
										
										
										
											2023-06-12 22:21:21 +08:00
										 |  |  | 	 * @param {string | null} name the name to make multiple serializer unique when sharing a request | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	 * @param {ObjectSerializer} serializer the serializer | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	static register(Constructor, request, name, serializer) { | 
					
						
							|  |  |  | 		const key = request + "/" + name; | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		if (serializers.has(Constructor)) { | 
					
						
							|  |  |  | 			throw new Error( | 
					
						
							| 
									
										
										
										
											2019-06-13 16:51:12 +08:00
										 |  |  | 				`ObjectMiddleware.register: serializer for ${Constructor.name} is already registered` | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		if (serializerInversed.has(key)) { | 
					
						
							|  |  |  | 			throw new Error( | 
					
						
							|  |  |  | 				`ObjectMiddleware.register: serializer for ${key} is already registered` | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		serializers.set(Constructor, { | 
					
						
							|  |  |  | 			request, | 
					
						
							|  |  |  | 			name, | 
					
						
							|  |  |  | 			serializer | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		serializerInversed.set(key, serializer); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-18 21:52:22 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Constructor} Constructor the constructor | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	static registerNotSerializable(Constructor) { | 
					
						
							|  |  |  | 		if (serializers.has(Constructor)) { | 
					
						
							|  |  |  | 			throw new Error( | 
					
						
							| 
									
										
										
										
											2019-06-13 16:51:12 +08:00
										 |  |  | 				`ObjectMiddleware.registerNotSerializable: serializer for ${Constructor.name} is already registered` | 
					
						
							| 
									
										
										
										
											2018-10-18 21:52:22 +08:00
										 |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-18 21:52:22 +08:00
										 |  |  | 		serializers.set(Constructor, NOT_SERIALIZABLE); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	static getSerializerFor(object) { | 
					
						
							| 
									
										
										
										
											2021-02-07 23:35:04 +08:00
										 |  |  | 		const proto = Object.getPrototypeOf(object); | 
					
						
							|  |  |  | 		let c; | 
					
						
							|  |  |  | 		if (proto === null) { | 
					
						
							|  |  |  | 			// Object created with Object.create(null)
 | 
					
						
							|  |  |  | 			c = null; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			c = proto.constructor; | 
					
						
							|  |  |  | 			if (!c) { | 
					
						
							| 
									
										
										
										
											2018-12-26 05:07:51 +08:00
										 |  |  | 				throw new Error( | 
					
						
							|  |  |  | 					"Serialization of objects with prototype without valid constructor property not possible" | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		const config = serializers.get(c); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		if (!config) throw new Error(`No serializer registered for ${c.name}`); | 
					
						
							| 
									
										
										
										
											2018-10-18 21:52:22 +08:00
										 |  |  | 		if (config === NOT_SERIALIZABLE) throw NOT_SERIALIZABLE; | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		return config; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 02:32:23 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} request request | 
					
						
							|  |  |  | 	 * @param {TODO} name name | 
					
						
							|  |  |  | 	 * @returns {ObjectSerializer} serializer | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	static getDeserializerFor(request, name) { | 
					
						
							|  |  |  | 		const key = request + "/" + name; | 
					
						
							|  |  |  | 		const serializer = serializerInversed.get(key); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		if (serializer === undefined) { | 
					
						
							|  |  |  | 			throw new Error(`No deserializer registered for ${key}`); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		return serializer; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-23 02:32:23 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} request request | 
					
						
							|  |  |  | 	 * @param {TODO} name name | 
					
						
							|  |  |  | 	 * @returns {ObjectSerializer} serializer | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2021-02-15 02:50:10 +08:00
										 |  |  | 	static _getDeserializerForWithoutError(request, name) { | 
					
						
							|  |  |  | 		const key = request + "/" + name; | 
					
						
							|  |  |  | 		const serializer = serializerInversed.get(key); | 
					
						
							|  |  |  | 		return serializer; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | 	 * @param {DeserializedType} data data | 
					
						
							|  |  |  | 	 * @param {Object} context context object | 
					
						
							|  |  |  | 	 * @returns {SerializedType|Promise<SerializedType>} serialized data | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	 */ | 
					
						
							|  |  |  | 	serialize(data, context) { | 
					
						
							|  |  |  | 		/** @type {any[]} */ | 
					
						
							| 
									
										
										
										
											2021-03-25 20:24:52 +08:00
										 |  |  | 		let result = [CURRENT_VERSION]; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		let currentPos = 0; | 
					
						
							| 
									
										
										
										
											2021-03-25 20:24:52 +08:00
										 |  |  | 		let referenceable = new Map(); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		const addReferenceable = item => { | 
					
						
							|  |  |  | 			referenceable.set(item, currentPos++); | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2021-03-25 20:24:52 +08:00
										 |  |  | 		let bufferDedupeMap = new Map(); | 
					
						
							| 
									
										
										
										
											2020-12-14 06:31:28 +08:00
										 |  |  | 		const dedupeBuffer = buf => { | 
					
						
							|  |  |  | 			const len = buf.length; | 
					
						
							|  |  |  | 			const entry = bufferDedupeMap.get(len); | 
					
						
							|  |  |  | 			if (entry === undefined) { | 
					
						
							|  |  |  | 				bufferDedupeMap.set(len, buf); | 
					
						
							|  |  |  | 				return buf; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (Buffer.isBuffer(entry)) { | 
					
						
							|  |  |  | 				if (len < 32) { | 
					
						
							|  |  |  | 					if (buf.equals(entry)) { | 
					
						
							|  |  |  | 						return entry; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					bufferDedupeMap.set(len, [entry, buf]); | 
					
						
							|  |  |  | 					return buf; | 
					
						
							|  |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | 					const hash = toHash(entry, this._hashFunction); | 
					
						
							| 
									
										
										
										
											2020-12-14 06:31:28 +08:00
										 |  |  | 					const newMap = new Map(); | 
					
						
							|  |  |  | 					newMap.set(hash, entry); | 
					
						
							|  |  |  | 					bufferDedupeMap.set(len, newMap); | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | 					const hashBuf = toHash(buf, this._hashFunction); | 
					
						
							| 
									
										
										
										
											2020-12-14 06:31:28 +08:00
										 |  |  | 					if (hash === hashBuf) { | 
					
						
							|  |  |  | 						return entry; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return buf; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else if (Array.isArray(entry)) { | 
					
						
							|  |  |  | 				if (entry.length < 16) { | 
					
						
							|  |  |  | 					for (const item of entry) { | 
					
						
							|  |  |  | 						if (buf.equals(item)) { | 
					
						
							|  |  |  | 							return item; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					entry.push(buf); | 
					
						
							|  |  |  | 					return buf; | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					const newMap = new Map(); | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | 					const hash = toHash(buf, this._hashFunction); | 
					
						
							| 
									
										
										
										
											2020-12-14 06:31:28 +08:00
										 |  |  | 					let found; | 
					
						
							|  |  |  | 					for (const item of entry) { | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | 						const itemHash = toHash(item, this._hashFunction); | 
					
						
							| 
									
										
										
										
											2020-12-14 06:31:28 +08:00
										 |  |  | 						newMap.set(itemHash, item); | 
					
						
							|  |  |  | 						if (found === undefined && itemHash === hash) found = item; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					bufferDedupeMap.set(len, newMap); | 
					
						
							|  |  |  | 					if (found === undefined) { | 
					
						
							|  |  |  | 						newMap.set(hash, buf); | 
					
						
							|  |  |  | 						return buf; | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						return found; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | 				const hash = toHash(buf, this._hashFunction); | 
					
						
							| 
									
										
										
										
											2020-12-14 06:31:28 +08:00
										 |  |  | 				const item = entry.get(hash); | 
					
						
							|  |  |  | 				if (item !== undefined) { | 
					
						
							|  |  |  | 					return item; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				entry.set(hash, buf); | 
					
						
							|  |  |  | 				return buf; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		let currentPosTypeLookup = 0; | 
					
						
							| 
									
										
										
										
											2021-03-25 20:24:52 +08:00
										 |  |  | 		let objectTypeLookup = new Map(); | 
					
						
							| 
									
										
										
										
											2018-10-17 20:04:38 +08:00
										 |  |  | 		const cycleStack = new Set(); | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		const stackToString = item => { | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | 			const arr = Array.from(cycleStack); | 
					
						
							|  |  |  | 			arr.push(item); | 
					
						
							|  |  |  | 			return arr | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 				.map(item => { | 
					
						
							|  |  |  | 					if (typeof item === "string") { | 
					
						
							|  |  |  | 						if (item.length > 100) { | 
					
						
							|  |  |  | 							return `String ${JSON.stringify(item.slice(0, 100)).slice( | 
					
						
							|  |  |  | 								0, | 
					
						
							|  |  |  | 								-1 | 
					
						
							|  |  |  | 							)}..."`;
 | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						return `String ${JSON.stringify(item)}`; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | 					try { | 
					
						
							|  |  |  | 						const { request, name } = ObjectMiddleware.getSerializerFor(item); | 
					
						
							|  |  |  | 						if (request) { | 
					
						
							|  |  |  | 							return `${request}${name ? `.${name}` : ""}`; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} catch (e) { | 
					
						
							|  |  |  | 						// ignore -> fallback
 | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-06-02 22:09:48 +08:00
										 |  |  | 					if (typeof item === "object" && item !== null) { | 
					
						
							|  |  |  | 						if (item.constructor) { | 
					
						
							|  |  |  | 							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 === Set) return `Set { ${item.size} items }`; | 
					
						
							|  |  |  | 							if (item.constructor === RegExp) return item.toString(); | 
					
						
							|  |  |  | 							return `${item.constructor.name}`; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						return `Object [null prototype] { ${Object.keys(item).join( | 
					
						
							|  |  |  | 							", " | 
					
						
							|  |  |  | 						)} }`;
 | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2023-05-30 10:18:06 +08:00
										 |  |  | 					if (typeof item === "bigint") { | 
					
						
							|  |  |  | 						return `BigInt ${item}n`; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-06-02 22:09:48 +08:00
										 |  |  | 					try { | 
					
						
							|  |  |  | 						return `${item}`; | 
					
						
							|  |  |  | 					} catch (e) { | 
					
						
							|  |  |  | 						return `(${e.message})`; | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 				.join(" -> "); | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | 		let hasDebugInfoAttached; | 
					
						
							| 
									
										
										
										
											2021-03-25 20:24:52 +08:00
										 |  |  | 		let ctx = { | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | 			write(value, key) { | 
					
						
							|  |  |  | 				try { | 
					
						
							|  |  |  | 					process(value); | 
					
						
							|  |  |  | 				} catch (e) { | 
					
						
							| 
									
										
										
										
											2021-01-21 23:32:49 +08:00
										 |  |  | 					if (e !== NOT_SERIALIZABLE) { | 
					
						
							|  |  |  | 						if (hasDebugInfoAttached === undefined) | 
					
						
							|  |  |  | 							hasDebugInfoAttached = new WeakSet(); | 
					
						
							|  |  |  | 						if (!hasDebugInfoAttached.has(e)) { | 
					
						
							|  |  |  | 							e.message += `\nwhile serializing ${stackToString(value)}`; | 
					
						
							|  |  |  | 							hasDebugInfoAttached.add(e); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					throw e; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-06-19 19:16:05 +08:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2021-04-14 14:55:43 +08:00
										 |  |  | 			setCircularReference(ref) { | 
					
						
							|  |  |  | 				addReferenceable(ref); | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2019-06-19 19:16:05 +08:00
										 |  |  | 			snapshot() { | 
					
						
							|  |  |  | 				return { | 
					
						
							|  |  |  | 					length: result.length, | 
					
						
							|  |  |  | 					cycleStackSize: cycleStack.size, | 
					
						
							|  |  |  | 					referenceableSize: referenceable.size, | 
					
						
							|  |  |  | 					currentPos, | 
					
						
							|  |  |  | 					objectTypeLookupSize: objectTypeLookup.size, | 
					
						
							|  |  |  | 					currentPosTypeLookup | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			rollback(snapshot) { | 
					
						
							|  |  |  | 				result.length = snapshot.length; | 
					
						
							|  |  |  | 				setSetSize(cycleStack, snapshot.cycleStackSize); | 
					
						
							|  |  |  | 				setMapSize(referenceable, snapshot.referenceableSize); | 
					
						
							|  |  |  | 				currentPos = snapshot.currentPos; | 
					
						
							|  |  |  | 				setMapSize(objectTypeLookup, snapshot.objectTypeLookupSize); | 
					
						
							|  |  |  | 				currentPosTypeLookup = snapshot.currentPosTypeLookup; | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2019-06-19 19:16:05 +08:00
										 |  |  | 			...context | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		this.extendContext(ctx); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		const process = item => { | 
					
						
							| 
									
										
										
										
											2018-10-18 04:16:15 +08:00
										 |  |  | 			if (Buffer.isBuffer(item)) { | 
					
						
							| 
									
										
										
										
											2021-03-23 03:28:08 +08:00
										 |  |  | 				// check if we can emit a reference
 | 
					
						
							|  |  |  | 				const ref = referenceable.get(item); | 
					
						
							|  |  |  | 				if (ref !== undefined) { | 
					
						
							|  |  |  | 					result.push(ESCAPE, ref - currentPos); | 
					
						
							|  |  |  | 					return; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-12-14 06:31:28 +08:00
										 |  |  | 				const alreadyUsedBuffer = dedupeBuffer(item); | 
					
						
							|  |  |  | 				if (alreadyUsedBuffer !== item) { | 
					
						
							| 
									
										
										
										
											2020-12-15 06:05:40 +08:00
										 |  |  | 					const ref = referenceable.get(alreadyUsedBuffer); | 
					
						
							| 
									
										
										
										
											2020-12-14 06:31:28 +08:00
										 |  |  | 					if (ref !== undefined) { | 
					
						
							| 
									
										
										
										
											2020-12-15 06:05:40 +08:00
										 |  |  | 						referenceable.set(item, ref); | 
					
						
							| 
									
										
										
										
											2020-12-14 06:31:28 +08:00
										 |  |  | 						result.push(ESCAPE, ref - currentPos); | 
					
						
							|  |  |  | 						return; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-12-15 06:05:40 +08:00
										 |  |  | 					item = alreadyUsedBuffer; | 
					
						
							| 
									
										
										
										
											2020-12-14 06:31:28 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-10-18 04:16:15 +08:00
										 |  |  | 				addReferenceable(item); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-18 04:16:15 +08:00
										 |  |  | 				result.push(item); | 
					
						
							| 
									
										
										
										
											2021-03-23 03:28:08 +08:00
										 |  |  | 			} else if (item === ESCAPE) { | 
					
						
							|  |  |  | 				result.push(ESCAPE, ESCAPE_ESCAPE_VALUE); | 
					
						
							|  |  |  | 			} else if ( | 
					
						
							|  |  |  | 				typeof item === "object" | 
					
						
							|  |  |  | 				// We don't have to check for null as ESCAPE is null and this has been checked before
 | 
					
						
							|  |  |  | 			) { | 
					
						
							|  |  |  | 				// check if we can emit a reference
 | 
					
						
							|  |  |  | 				const ref = referenceable.get(item); | 
					
						
							|  |  |  | 				if (ref !== undefined) { | 
					
						
							|  |  |  | 					result.push(ESCAPE, ref - currentPos); | 
					
						
							|  |  |  | 					return; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 20:04:38 +08:00
										 |  |  | 				if (cycleStack.has(item)) { | 
					
						
							| 
									
										
										
										
											2021-04-14 14:55:43 +08:00
										 |  |  | 					throw new Error( | 
					
						
							|  |  |  | 						`This is a circular references. To serialize circular references use 'setCircularReference' somewhere in the circle during serialize and deserialize.` | 
					
						
							|  |  |  | 					); | 
					
						
							| 
									
										
										
										
											2018-10-17 20:04:38 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-11 15:31:46 +08:00
										 |  |  | 				const { request, name, serializer } = | 
					
						
							|  |  |  | 					ObjectMiddleware.getSerializerFor(item); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				const key = `${request}/${name}`; | 
					
						
							|  |  |  | 				const lastIndex = objectTypeLookup.get(key); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				if (lastIndex === undefined) { | 
					
						
							|  |  |  | 					objectTypeLookup.set(key, currentPosTypeLookup++); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 					result.push(ESCAPE, request, name); | 
					
						
							|  |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2018-10-10 15:55:06 +08:00
										 |  |  | 					result.push(ESCAPE, currentPosTypeLookup - lastIndex); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 20:04:38 +08:00
										 |  |  | 				cycleStack.add(item); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | 				try { | 
					
						
							|  |  |  | 					serializer.serialize(item, ctx); | 
					
						
							|  |  |  | 				} finally { | 
					
						
							|  |  |  | 					cycleStack.delete(item); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				result.push(ESCAPE, ESCAPE_END_OBJECT); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				addReferenceable(item); | 
					
						
							|  |  |  | 			} else if (typeof item === "string") { | 
					
						
							| 
									
										
										
										
											2020-12-14 06:31:51 +08:00
										 |  |  | 				if (item.length > 1) { | 
					
						
							|  |  |  | 					// short strings are shorter when not emitting a reference (this saves 1 byte per empty string)
 | 
					
						
							| 
									
										
										
										
											2021-03-23 03:28:08 +08:00
										 |  |  | 					// check if we can emit a reference
 | 
					
						
							|  |  |  | 					const ref = referenceable.get(item); | 
					
						
							|  |  |  | 					if (ref !== undefined) { | 
					
						
							|  |  |  | 						result.push(ESCAPE, ref - currentPos); | 
					
						
							|  |  |  | 						return; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-10-10 16:24:44 +08:00
										 |  |  | 					addReferenceable(item); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 				if (item.length > 102400 && context.logger) { | 
					
						
							|  |  |  | 					context.logger.warn( | 
					
						
							|  |  |  | 						`Serializing big strings (${Math.round( | 
					
						
							|  |  |  | 							item.length / 1024 | 
					
						
							| 
									
										
										
										
											2020-01-28 15:42:49 +08:00
										 |  |  | 						)}kiB) impacts deserialization performance (consider using Buffer instead and decode when needed)`
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				result.push(item); | 
					
						
							|  |  |  | 			} else if (typeof item === "function") { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 				if (!SerializerMiddleware.isLazy(item)) | 
					
						
							|  |  |  | 					throw new Error("Unexpected function " + item); | 
					
						
							|  |  |  | 				/** @type {SerializedType} */ | 
					
						
							| 
									
										
										
										
											2021-05-11 15:31:46 +08:00
										 |  |  | 				const serializedData = | 
					
						
							|  |  |  | 					SerializerMiddleware.getLazySerializedValue(item); | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 				if (serializedData !== undefined) { | 
					
						
							|  |  |  | 					if (typeof serializedData === "function") { | 
					
						
							|  |  |  | 						result.push(serializedData); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						throw new Error("Not implemented"); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} else if (SerializerMiddleware.isLazy(item, this)) { | 
					
						
							|  |  |  | 					throw new Error("Not implemented"); | 
					
						
							|  |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2021-09-08 19:45:27 +08:00
										 |  |  | 					const data = SerializerMiddleware.serializeLazy(item, data => | 
					
						
							|  |  |  | 						this.serialize([data], context) | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 					); | 
					
						
							| 
									
										
										
										
											2021-09-08 19:45:27 +08:00
										 |  |  | 					SerializerMiddleware.setLazySerializedValue(item, data); | 
					
						
							|  |  |  | 					result.push(data); | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 			} else if (item === undefined) { | 
					
						
							|  |  |  | 				result.push(ESCAPE, ESCAPE_UNDEFINED); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				result.push(item); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-18 21:52:22 +08:00
										 |  |  | 		try { | 
					
						
							|  |  |  | 			for (const item of data) { | 
					
						
							|  |  |  | 				process(item); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-03-25 20:24:52 +08:00
										 |  |  | 			return result; | 
					
						
							| 
									
										
										
										
											2018-10-18 21:52:22 +08:00
										 |  |  | 		} catch (e) { | 
					
						
							|  |  |  | 			if (e === NOT_SERIALIZABLE) return null; | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-18 21:52:22 +08:00
										 |  |  | 			throw e; | 
					
						
							| 
									
										
										
										
											2021-03-25 20:24:52 +08:00
										 |  |  | 		} finally { | 
					
						
							|  |  |  | 			// Get rid of these references to avoid leaking memory
 | 
					
						
							|  |  |  | 			// This happens because the optimized code v8 generates
 | 
					
						
							|  |  |  | 			// is optimized for our "ctx.write" method so it will reference
 | 
					
						
							|  |  |  | 			// it from e. g. Dependency.prototype.serialize -(IC)-> ctx.write
 | 
					
						
							| 
									
										
										
										
											2021-05-11 15:31:46 +08:00
										 |  |  | 			data = | 
					
						
							|  |  |  | 				result = | 
					
						
							|  |  |  | 				referenceable = | 
					
						
							|  |  |  | 				bufferDedupeMap = | 
					
						
							|  |  |  | 				objectTypeLookup = | 
					
						
							|  |  |  | 				ctx = | 
					
						
							|  |  |  | 					undefined; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | 	 * @param {SerializedType} data data | 
					
						
							|  |  |  | 	 * @param {Object} context context object | 
					
						
							|  |  |  | 	 * @returns {DeserializedType|Promise<DeserializedType>} deserialized data | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	 */ | 
					
						
							|  |  |  | 	deserialize(data, context) { | 
					
						
							|  |  |  | 		let currentDataPos = 0; | 
					
						
							|  |  |  | 		const read = () => { | 
					
						
							|  |  |  | 			if (currentDataPos >= data.length) | 
					
						
							|  |  |  | 				throw new Error("Unexpected end of stream"); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 			return data[currentDataPos++]; | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		if (read() !== CURRENT_VERSION) | 
					
						
							| 
									
										
										
										
											2020-03-13 00:51:26 +08:00
										 |  |  | 			throw new Error("Version mismatch, serializer changed"); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		let currentPos = 0; | 
					
						
							| 
									
										
										
										
											2020-12-12 20:33:29 +08:00
										 |  |  | 		let referenceable = []; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		const addReferenceable = item => { | 
					
						
							| 
									
										
										
										
											2019-08-13 22:48:28 +08:00
										 |  |  | 			referenceable.push(item); | 
					
						
							|  |  |  | 			currentPos++; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		}; | 
					
						
							|  |  |  | 		let currentPosTypeLookup = 0; | 
					
						
							| 
									
										
										
										
											2020-12-12 20:33:29 +08:00
										 |  |  | 		let objectTypeLookup = []; | 
					
						
							| 
									
										
										
										
											2021-03-25 20:24:52 +08:00
										 |  |  | 		let result = []; | 
					
						
							|  |  |  | 		let ctx = { | 
					
						
							| 
									
										
										
										
											2019-06-19 19:16:05 +08:00
										 |  |  | 			read() { | 
					
						
							|  |  |  | 				return decodeValue(); | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2021-04-14 14:55:43 +08:00
										 |  |  | 			setCircularReference(ref) { | 
					
						
							|  |  |  | 				addReferenceable(ref); | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2019-06-19 19:16:05 +08:00
										 |  |  | 			...context | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		this.extendContext(ctx); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		const decodeValue = () => { | 
					
						
							|  |  |  | 			const item = read(); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 			if (item === ESCAPE) { | 
					
						
							|  |  |  | 				const nextItem = read(); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				if (nextItem === ESCAPE_ESCAPE_VALUE) { | 
					
						
							|  |  |  | 					return ESCAPE; | 
					
						
							|  |  |  | 				} else if (nextItem === ESCAPE_UNDEFINED) { | 
					
						
							|  |  |  | 					return undefined; | 
					
						
							|  |  |  | 				} else if (nextItem === ESCAPE_END_OBJECT) { | 
					
						
							| 
									
										
										
										
											2018-10-10 15:55:06 +08:00
										 |  |  | 					throw new Error( | 
					
						
							|  |  |  | 						`Unexpected end of object at position ${currentDataPos - 1}` | 
					
						
							|  |  |  | 					); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2018-10-10 15:55:06 +08:00
										 |  |  | 					const request = nextItem; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 					let serializer; | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 15:55:06 +08:00
										 |  |  | 					if (typeof request === "number") { | 
					
						
							| 
									
										
										
										
											2021-04-14 14:55:43 +08:00
										 |  |  | 						if (request < 0) { | 
					
						
							|  |  |  | 							// relative reference
 | 
					
						
							|  |  |  | 							return referenceable[currentPos + request]; | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-11-13 19:18:04 +08:00
										 |  |  | 						serializer = objectTypeLookup[currentPosTypeLookup - request]; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 					} else { | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | 						if (typeof request !== "string") { | 
					
						
							|  |  |  | 							throw new Error( | 
					
						
							|  |  |  | 								`Unexpected type (${typeof request}) of request ` + | 
					
						
							|  |  |  | 									`at position ${currentDataPos - 1}` | 
					
						
							|  |  |  | 							); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2018-10-10 15:55:06 +08:00
										 |  |  | 						const name = read(); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-15 02:50:10 +08:00
										 |  |  | 						serializer = ObjectMiddleware._getDeserializerForWithoutError( | 
					
						
							|  |  |  | 							request, | 
					
						
							|  |  |  | 							name | 
					
						
							|  |  |  | 						); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						if (serializer === undefined) { | 
					
						
							|  |  |  | 							if (request && !loadedRequests.has(request)) { | 
					
						
							|  |  |  | 								let loaded = false; | 
					
						
							|  |  |  | 								for (const [regExp, loader] of loaders) { | 
					
						
							|  |  |  | 									if (regExp.test(request)) { | 
					
						
							|  |  |  | 										if (loader(request)) { | 
					
						
							|  |  |  | 											loaded = true; | 
					
						
							|  |  |  | 											break; | 
					
						
							|  |  |  | 										} | 
					
						
							| 
									
										
										
										
											2018-12-20 16:05:32 +08:00
										 |  |  | 									} | 
					
						
							|  |  |  | 								} | 
					
						
							| 
									
										
										
										
											2021-02-15 02:50:10 +08:00
										 |  |  | 								if (!loaded) { | 
					
						
							|  |  |  | 									require(request); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 								loadedRequests.add(request); | 
					
						
							| 
									
										
										
										
											2018-12-20 16:05:32 +08:00
										 |  |  | 							} | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-15 02:50:10 +08:00
										 |  |  | 							serializer = ObjectMiddleware.getDeserializerFor(request, name); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-13 19:18:04 +08:00
										 |  |  | 						objectTypeLookup.push(serializer); | 
					
						
							|  |  |  | 						currentPosTypeLookup++; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-11-01 19:19:27 +08:00
										 |  |  | 					try { | 
					
						
							|  |  |  | 						const item = serializer.deserialize(ctx); | 
					
						
							|  |  |  | 						const end1 = read(); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-01 19:19:27 +08:00
										 |  |  | 						if (end1 !== ESCAPE) { | 
					
						
							|  |  |  | 							throw new Error("Expected end of object"); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-01 19:19:27 +08:00
										 |  |  | 						const end2 = read(); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-01 19:19:27 +08:00
										 |  |  | 						if (end2 !== ESCAPE_END_OBJECT) { | 
					
						
							|  |  |  | 							throw new Error("Expected end of object"); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-01 19:19:27 +08:00
										 |  |  | 						addReferenceable(item); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						return item; | 
					
						
							|  |  |  | 					} catch (err) { | 
					
						
							|  |  |  | 						// 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
 | 
					
						
							|  |  |  | 						let serializerEntry; | 
					
						
							|  |  |  | 						for (const entry of serializers) { | 
					
						
							|  |  |  | 							if (entry[1].serializer === serializer) { | 
					
						
							|  |  |  | 								serializerEntry = entry; | 
					
						
							|  |  |  | 								break; | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						const name = !serializerEntry | 
					
						
							|  |  |  | 							? "unknown" | 
					
						
							|  |  |  | 							: !serializerEntry[1].request | 
					
						
							| 
									
										
										
										
											2024-01-14 09:41:34 +08:00
										 |  |  | 								? serializerEntry[0].name | 
					
						
							|  |  |  | 								: serializerEntry[1].name | 
					
						
							|  |  |  | 									? `${serializerEntry[1].request} ${serializerEntry[1].name}` | 
					
						
							|  |  |  | 									: serializerEntry[1].request; | 
					
						
							| 
									
										
										
										
											2019-11-01 19:19:27 +08:00
										 |  |  | 						err.message += `\n(during deserialization of ${name})`; | 
					
						
							|  |  |  | 						throw err; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} else if (typeof item === "string") { | 
					
						
							| 
									
										
										
										
											2020-12-14 06:31:51 +08:00
										 |  |  | 				if (item.length > 1) { | 
					
						
							| 
									
										
										
										
											2018-10-10 16:24:44 +08:00
										 |  |  | 					addReferenceable(item); | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				return item; | 
					
						
							|  |  |  | 			} else if (Buffer.isBuffer(item)) { | 
					
						
							|  |  |  | 				addReferenceable(item); | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				return item; | 
					
						
							|  |  |  | 			} else if (typeof item === "function") { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 				return SerializerMiddleware.deserializeLazy( | 
					
						
							|  |  |  | 					item, | 
					
						
							|  |  |  | 					data => this.deserialize(data, context)[0] | 
					
						
							|  |  |  | 				); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				return item; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2018-10-20 00:24:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-25 20:24:52 +08:00
										 |  |  | 		try { | 
					
						
							|  |  |  | 			while (currentDataPos < data.length) { | 
					
						
							|  |  |  | 				result.push(decodeValue()); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return result; | 
					
						
							|  |  |  | 		} finally { | 
					
						
							|  |  |  | 			// Get rid of these references to avoid leaking memory
 | 
					
						
							|  |  |  | 			// This happens because the optimized code v8 generates
 | 
					
						
							|  |  |  | 			// is optimized for our "ctx.read" method so it will reference
 | 
					
						
							|  |  |  | 			// it from e. g. Dependency.prototype.deserialize -(IC)-> ctx.read
 | 
					
						
							|  |  |  | 			result = referenceable = data = objectTypeLookup = ctx = undefined; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = ObjectMiddleware; | 
					
						
							| 
									
										
										
										
											2019-01-19 18:47:19 +08:00
										 |  |  | module.exports.NOT_SERIALIZABLE = NOT_SERIALIZABLE; |