UNPKG

7.08 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.registerSerializableClass = registerSerializableClass;
7exports.unregisterSerializableClass = unregisterSerializableClass;
8exports.prepareForSerialization = prepareForSerialization;
9exports.restoreDeserializedObject = restoreDeserializedObject;
10exports.serialize = serialize;
11exports.deserialize = deserialize;
12
13var _v = _interopRequireDefault(require("v8"));
14
15function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16
17function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
18
19function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
20
21function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
22
23const nameToCtor = new Map();
24const ctorToName = new Map();
25
26function registerSerializableClass(name, ctor) {
27 if (nameToCtor.has(name)) {
28 throw new Error('Name already registered with serializer');
29 }
30
31 if (ctorToName.has(ctor)) {
32 throw new Error('Class already registered with serializer');
33 }
34
35 nameToCtor.set(name, ctor);
36 ctorToName.set(ctor, name);
37}
38
39function unregisterSerializableClass(name, ctor) {
40 if (nameToCtor.get(name) === ctor) {
41 nameToCtor.delete(name);
42 }
43
44 if (ctorToName.get(ctor) === name) {
45 ctorToName.delete(ctor);
46 }
47}
48
49function shallowCopy(object) {
50 if (object && typeof object === 'object') {
51 if (Array.isArray(object)) {
52 return [...object];
53 }
54
55 if (object instanceof Map) {
56 return new Map(object);
57 }
58
59 if (object instanceof Set) {
60 return new Set(object);
61 }
62
63 return Object.create(Object.getPrototypeOf(object), Object.getOwnPropertyDescriptors(object));
64 }
65
66 return object;
67}
68
69function isBuffer(object) {
70 return object.buffer instanceof ArrayBuffer || object.buffer instanceof SharedArrayBuffer;
71}
72
73function mapObject(object, fn, preOrder = false) {
74 let cache = new Map();
75 let memo = new Map(); // Memoize the passed function to ensure it always returns the exact same
76 // output by reference for the same input. This is important to maintain
77 // reference integrity when deserializing rather than cloning.
78
79 let memoizedFn = val => {
80 let res = memo.get(val);
81
82 if (res == null) {
83 res = fn(val);
84 memo.set(val, res);
85 }
86
87 return res;
88 };
89
90 let walk = (object, shouldCopy = false) => {
91 // Check the cache first, both for performance and cycle detection.
92 if (cache.has(object)) {
93 return cache.get(object);
94 }
95
96 let result = object;
97 cache.set(object, result);
98
99 let processKey = (key, value) => {
100 let newValue = value;
101
102 if (preOrder && value && typeof value === 'object') {
103 newValue = memoizedFn(value);
104 } // Recursively walk the children
105
106
107 if (newValue && typeof newValue === 'object' && newValue.$$raw !== true) {
108 newValue = walk(newValue, newValue === value);
109 }
110
111 if (!preOrder && newValue && typeof newValue === 'object') {
112 newValue = memoizedFn(newValue);
113 }
114
115 if (newValue !== value) {
116 // Copy on write. We only need to do this when serializing, not deserializing.
117 if (object === result && preOrder && shouldCopy) {
118 result = shallowCopy(object);
119 cache.set(object, result);
120 } // Replace the key with the new value
121
122
123 if (result instanceof Map) {
124 result.set(key, newValue);
125 } else if (result instanceof Set) {
126 // TODO: do we care about iteration order??
127 result.delete(value);
128 result.add(newValue);
129 } else {
130 result[key] = newValue;
131 }
132 }
133 }; // Iterate in various ways depending on type.
134
135
136 if (Array.isArray(object)) {
137 for (let i = 0; i < object.length; i++) {
138 processKey(i, object[i]);
139 }
140 } else if (object instanceof Map || object instanceof Set) {
141 for (let [key, val] of object.entries()) {
142 processKey(key, val);
143 }
144 } else if (!isBuffer(object)) {
145 for (let key in object) {
146 processKey(key, object[key]);
147 }
148 }
149
150 return result;
151 };
152
153 let mapped = memoizedFn(object);
154
155 if (mapped && typeof mapped === 'object' && mapped.$$raw !== true) {
156 return walk(mapped, mapped === object);
157 }
158
159 return mapped;
160}
161
162function prepareForSerialization(object) {
163 return mapObject(object, value => {
164 // Add a $$type property with the name of this class, if any is registered.
165 if (value && typeof value === 'object' && typeof value.constructor === 'function') {
166 let type = ctorToName.get(value.constructor);
167
168 if (type != null) {
169 let serialized = value;
170 let raw = false;
171
172 if (value && typeof value.serialize === 'function') {
173 var _ref;
174
175 // If the object has a serialize method, call it
176 serialized = value.serialize();
177 raw = (_ref = serialized && serialized.$$raw) !== null && _ref !== void 0 ? _ref : true;
178
179 if (serialized) {
180 delete serialized.$$raw;
181 }
182 }
183
184 return {
185 $$type: type,
186 $$raw: raw,
187 value: _objectSpread({}, serialized)
188 };
189 }
190 }
191
192 return value;
193 }, true);
194}
195
196function restoreDeserializedObject(object) {
197 return mapObject(object, value => {
198 // If the value has a $$type property, use it to restore the object type
199 if (value && value.$$type) {
200 let ctor = nameToCtor.get(value.$$type);
201
202 if (ctor == null) {
203 throw new Error(`Expected constructor ${value.$$type} to be registered with serializer to deserialize`);
204 }
205
206 if (typeof ctor.deserialize === 'function') {
207 return ctor.deserialize(value.value);
208 }
209
210 value = value.value;
211 Object.setPrototypeOf(value, ctor.prototype);
212 }
213
214 return value;
215 });
216}
217
218function serialize(object) {
219 let mapped = prepareForSerialization(object); // $FlowFixMe - flow doesn't know about this method yet
220
221 return _v.default.serialize(mapped);
222}
223
224function deserialize(buffer) {
225 // $FlowFixMe - flow doesn't know about this method yet
226 let obj = _v.default.deserialize(buffer);
227
228 return restoreDeserializedObject(obj);
229}
\No newline at end of file