Skip to content

Commit f7df43d

Browse files
michael-ciniawskysokra
authored andcommitted
feat(ErrorObjectSerializer): make {Error} serializable
1 parent c27b46a commit f7df43d

File tree

2 files changed

+83
-1
lines changed

2 files changed

+83
-1
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"use strict";
2+
3+
class ErrorObjectSerializer {
4+
serialize(obj, { write }) {
5+
write(obj.name);
6+
write(obj.message);
7+
write(obj.stack);
8+
}
9+
10+
deserialize({ read }) {
11+
const err = new Error();
12+
13+
err.name = read();
14+
err.message = read();
15+
err.stack = read();
16+
17+
return err;
18+
}
19+
}
20+
21+
module.exports = ErrorObjectSerializer;

lib/serialization/ObjectMiddleware.js

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
"use strict";
66

7+
const ErrorObjectSerializer = require("./ErrorObjectSerializer");
78
const MapObjectSerializer = require("./MapObjectSerializer");
89
const PlainObjectSerializer = require("./PlainObjectSerializer");
910
const RegExpObjectSerializer = require("./RegExpObjectSerializer");
@@ -57,6 +58,7 @@ const ESCAPE_UNDEFINED = false;
5758

5859
const CURRENT_VERSION = 1;
5960

61+
const errorObjectSerializer = new ErrorObjectSerializer();
6062
const plainObjectSerializer = new PlainObjectSerializer();
6163
const mapObjectSerializer = new MapObjectSerializer();
6264
const setObjectSerializer = new SetObjectSerializer();
@@ -74,26 +76,37 @@ serializers.set(Object, {
7476
name: null,
7577
serializer: plainObjectSerializer
7678
});
79+
7780
serializers.set(Array, {
7881
request: "",
7982
name: null,
8083
serializer: plainObjectSerializer
8184
});
85+
8286
serializers.set(Map, {
8387
request: "",
8488
name: 1,
8589
serializer: mapObjectSerializer
8690
});
91+
8792
serializers.set(Set, {
8893
request: "",
8994
name: 2,
9095
serializer: setObjectSerializer
9196
});
97+
9298
serializers.set(RegExp, {
9399
request: "",
94100
name: 3,
95101
serializer: regExpObjectSerializer
96102
});
103+
104+
serializers.set(Error, {
105+
request: "",
106+
name: 4,
107+
serializer: errorObjectSerializer
108+
});
109+
97110
for (const { request, name, serializer } of serializers.values()) {
98111
serializerInversed.set(`${request}/${name}`, serializer);
99112
}
@@ -108,23 +121,27 @@ class ObjectMiddleware extends SerializerMiddleware {
108121
*/
109122
static register(Constructor, request, name, serializer) {
110123
const key = request + "/" + name;
124+
111125
if (serializers.has(Constructor)) {
112126
throw new Error(
113127
`ObjectMiddleware.register: serializer for ${
114128
Constructor.name
115129
} is already registered`
116130
);
117131
}
132+
118133
if (serializerInversed.has(key)) {
119134
throw new Error(
120135
`ObjectMiddleware.register: serializer for ${key} is already registered`
121136
);
122137
}
138+
123139
serializers.set(Constructor, {
124140
request,
125141
name,
126142
serializer
127143
});
144+
128145
serializerInversed.set(key, serializer);
129146
}
130147

@@ -140,40 +157,49 @@ class ObjectMiddleware extends SerializerMiddleware {
140157
} is already registered`
141158
);
142159
}
160+
143161
serializers.set(Constructor, NOT_SERIALIZABLE);
144162
}
145163

146164
static getSerializerFor(object) {
147165
const c = object.constructor;
148166
const config = serializers.get(c);
167+
149168
if (!config) throw new Error(`No serializer registered for ${c.name}`);
150169
if (config === NOT_SERIALIZABLE) throw NOT_SERIALIZABLE;
170+
151171
return config;
152172
}
153173

154174
static getDeserializerFor(request, name) {
155175
const key = request + "/" + name;
156176
const serializer = serializerInversed.get(key);
177+
157178
if (serializer === undefined) {
158179
throw new Error(`No deserializer registered for ${key}`);
159180
}
181+
160182
return serializer;
161183
}
162184

163185
_handleFunctionSerialization(fn, context) {
164186
return () => {
165187
const r = fn();
188+
166189
if (r instanceof Promise)
167190
return r.then(data => this.serialize([data], context));
191+
168192
return this.serialize([r], context);
169193
};
170194
}
171195

172196
_handleFunctionDeserialization(fn, context) {
173197
return () => {
174198
const r = fn();
199+
175200
if (r instanceof Promise)
176201
return r.then(data => this.deserialize(data, context)[0]);
202+
177203
return this.deserialize(r, context)[0];
178204
};
179205
}
@@ -197,12 +223,16 @@ class ObjectMiddleware extends SerializerMiddleware {
197223
const process = item => {
198224
// check if we can emit a reference
199225
const ref = referenceable.get(item);
226+
200227
if (ref !== undefined) {
201228
result.push(ESCAPE, ref - currentPos);
229+
202230
return;
203231
}
232+
204233
if (Buffer.isBuffer(item)) {
205234
addReferenceable(item);
235+
206236
result.push(item);
207237
} else if (typeof item === "object" && item !== null) {
208238
if (cycleStack.has(item)) {
@@ -213,32 +243,40 @@ class ObjectMiddleware extends SerializerMiddleware {
213243
.join(" -> ")})`
214244
);
215245
}
246+
216247
const { request, name, serializer } = ObjectMiddleware.getSerializerFor(
217248
item
218249
);
219-
220250
const key = `${request}/${name}`;
221251
const lastIndex = objectTypeLookup.get(key);
252+
222253
if (lastIndex === undefined) {
223254
objectTypeLookup.set(key, currentPosTypeLookup++);
255+
224256
result.push(ESCAPE, request, name);
225257
} else {
226258
result.push(ESCAPE, currentPosTypeLookup - lastIndex);
227259
}
260+
228261
cycleStack.add(item);
262+
229263
serializer.serialize(item, {
230264
write(value) {
231265
process(value);
232266
}
233267
});
268+
234269
cycleStack.delete(item);
270+
235271
result.push(ESCAPE, ESCAPE_END_OBJECT);
272+
236273
addReferenceable(item);
237274
} else if (typeof item === "string") {
238275
if (item !== "") {
239276
// empty strings are shorter when not emitting a reference (this saves 1 byte per empty string)
240277
addReferenceable(item);
241278
}
279+
242280
result.push(item);
243281
} else if (item === ESCAPE) {
244282
result.push(ESCAPE, ESCAPE_ESCAPE_VALUE);
@@ -250,14 +288,17 @@ class ObjectMiddleware extends SerializerMiddleware {
250288
result.push(item);
251289
}
252290
};
291+
253292
try {
254293
for (const item of data) {
255294
process(item);
256295
}
257296
} catch (e) {
258297
if (e === NOT_SERIALIZABLE) return null;
298+
259299
throw e;
260300
}
301+
261302
return result;
262303
}
263304

@@ -271,10 +312,13 @@ class ObjectMiddleware extends SerializerMiddleware {
271312
const read = () => {
272313
if (currentDataPos >= data.length)
273314
throw new Error("Unexpected end of stream");
315+
274316
return data[currentDataPos++];
275317
};
318+
276319
if (read() !== CURRENT_VERSION)
277320
throw new Error("Version missmatch, serializer changed");
321+
278322
let currentPos = 0;
279323
const referenceable = new Map();
280324
const addReferenceable = item => {
@@ -285,8 +329,10 @@ class ObjectMiddleware extends SerializerMiddleware {
285329
const result = [];
286330
const decodeValue = () => {
287331
const item = read();
332+
288333
if (item === ESCAPE) {
289334
const nextItem = read();
335+
290336
if (nextItem === ESCAPE_ESCAPE_VALUE) {
291337
return ESCAPE;
292338
} else if (nextItem === ESCAPE_UNDEFINED) {
@@ -301,51 +347,66 @@ class ObjectMiddleware extends SerializerMiddleware {
301347
} else {
302348
const request = nextItem;
303349
let serializer;
350+
304351
if (typeof request === "number") {
305352
serializer = objectTypeLookup.get(currentPosTypeLookup - request);
306353
} else {
307354
const name = read();
355+
308356
if (request && !loadedRequests.has(request)) {
309357
require(request);
358+
310359
loadedRequests.add(request);
311360
}
361+
312362
serializer = ObjectMiddleware.getDeserializerFor(request, name);
363+
313364
objectTypeLookup.set(currentPosTypeLookup++, serializer);
314365
}
315366
const item = serializer.deserialize({
316367
read() {
317368
const item = decodeValue();
369+
318370
return item;
319371
}
320372
});
321373
const end1 = read();
374+
322375
if (end1 !== ESCAPE) {
323376
throw new Error("Expected end of object");
324377
}
378+
325379
const end2 = read();
380+
326381
if (end2 !== ESCAPE_END_OBJECT) {
327382
throw new Error("Expected end of object");
328383
}
384+
329385
addReferenceable(item);
386+
330387
return item;
331388
}
332389
} else if (typeof item === "string") {
333390
if (item !== "") {
334391
addReferenceable(item);
335392
}
393+
336394
return item;
337395
} else if (Buffer.isBuffer(item)) {
338396
addReferenceable(item);
397+
339398
return item;
340399
} else if (typeof item === "function") {
341400
return this._handleFunctionDeserialization(item, context);
342401
} else {
343402
return item;
344403
}
345404
};
405+
346406
while (currentDataPos < data.length) {
347407
result.push(decodeValue());
348408
}
409+
349410
return result;
350411
}
351412
}

0 commit comments

Comments
 (0)