socket-function 0.8.40 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +50 -50
- package/SocketFunction.ts +280 -276
- package/SocketFunctionTypes.ts +90 -90
- package/hot/HotReloadController.ts +105 -70
- package/mobx/UrlParam.ts +39 -39
- package/mobx/observer.tsx +49 -49
- package/mobx/promiseToObservable.tsx +41 -41
- package/package.json +30 -28
- package/require/CSSShim.ts +19 -19
- package/require/RequireController.ts +252 -252
- package/require/buffer.js +2368 -2368
- package/require/compileFlags.ts +44 -44
- package/require/require.html +13 -13
- package/require/require.js +462 -454
- package/spec.txt +115 -115
- package/src/CallFactory.ts +389 -383
- package/src/JSONLACKS/JSONLACKS.generated.js +17 -17
- package/src/JSONLACKS/JSONLACKS.pegjs +247 -247
- package/src/JSONLACKS/JSONLACKS.ts +429 -375
- package/src/args.ts +21 -21
- package/src/batching.ts +170 -126
- package/src/caching.ts +318 -314
- package/src/callHTTPHandler.ts +203 -203
- package/src/callManager.ts +134 -134
- package/src/certStore.ts +29 -25
- package/src/fixLargeNetworkCalls.ts +8 -8
- package/src/formatting/colors.ts +78 -78
- package/src/formatting/format.ts +160 -156
- package/src/formatting/logColors.ts +17 -17
- package/src/misc.ts +302 -150
- package/src/nodeCache.ts +92 -92
- package/src/nodeProxy.ts +54 -54
- package/src/profiling/getOwnTime.ts +142 -142
- package/src/profiling/measure.ts +273 -244
- package/src/profiling/stats.ts +212 -212
- package/src/profiling/tcpLagProxy.ts +63 -63
- package/src/storagePath.ts +10 -10
- package/src/tlsParsing.ts +96 -96
- package/src/types.ts +8 -8
- package/src/webSocketServer.ts +250 -237
- package/test/client.css +2 -2
- package/test/client.ts +46 -46
- package/test/server.ts +43 -43
- package/test/shared.ts +52 -58
- package/tsconfig.json +26 -26
|
@@ -1,376 +1,430 @@
|
|
|
1
|
-
|
|
2
|
-
import { enableMeasurements, measureBlock, measureCode, measureCodeSync, measureFnc } from "../profiling/measure";
|
|
3
|
-
// enableMeasurements();
|
|
4
|
-
|
|
5
|
-
import debugbreak from "debugbreak";
|
|
6
|
-
import fs from "fs";
|
|
7
|
-
|
|
8
|
-
import parser from "./JSONLACKS.generated.js";
|
|
9
|
-
import { recursiveFreeze } from "../misc";
|
|
10
|
-
import { canHaveChildren } from "../types";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
// -
|
|
18
|
-
// -
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
public static
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
let
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
let
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
async function
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
}
|
|
1
|
+
|
|
2
|
+
import { enableMeasurements, measureBlock, measureCode, measureCodeSync, measureFnc } from "../profiling/measure";
|
|
3
|
+
// enableMeasurements();
|
|
4
|
+
|
|
5
|
+
import debugbreak from "debugbreak";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
|
|
8
|
+
import parser from "./JSONLACKS.generated.js";
|
|
9
|
+
import { recursiveFreeze } from "../misc";
|
|
10
|
+
import { canHaveChildren } from "../types";
|
|
11
|
+
import { delay } from "../batching";
|
|
12
|
+
|
|
13
|
+
const SERIALIZE_OBJECT_BATCH_COUNT = 1000;
|
|
14
|
+
const PARSE_BYTE_CHUNK_SIZE = 1024 * 1024 * 10;
|
|
15
|
+
|
|
16
|
+
// Supports json and also:
|
|
17
|
+
// - Non quoted field names "{ x: 1 }"?
|
|
18
|
+
// - Trailing commas
|
|
19
|
+
// NOTE: Comma only syntax is not supported, ex, "[,,]", which is an array of length 2 in javascript
|
|
20
|
+
// - Comments on input, but not on output
|
|
21
|
+
// - References
|
|
22
|
+
// - Buffers (just Buffers, not typed arrays)
|
|
23
|
+
// The stringify function always creates valid json, as the syntax for references and buffers
|
|
24
|
+
// will just be special property names and values.
|
|
25
|
+
// NOTE: We don't support Date serialization. Never store Dates, store "number".
|
|
26
|
+
|
|
27
|
+
export interface JSONLACKS_ParseConfig {
|
|
28
|
+
// Defaults to true. Enables parsing of:
|
|
29
|
+
// - Trailing commas
|
|
30
|
+
// - Non-quoted field names (ex, "{ x: 1 }")
|
|
31
|
+
// - Comments (strips them, but doesn't throw)
|
|
32
|
+
extended?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface JSONLACKS_StringifyConfig {
|
|
35
|
+
// If specified, we are allowed to mutate the provided object. Speeds up serialization.
|
|
36
|
+
allowObjectMutation?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface HydrateState {
|
|
40
|
+
references: Map<string, unknown>,
|
|
41
|
+
visited: Set<unknown>,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export class JSONLACKS {
|
|
45
|
+
public static readonly LACKS_KEY = "__JSONLACKS__98cfb4a05fa34d828661cae15b8779ce__";
|
|
46
|
+
|
|
47
|
+
/** If set to true parses non-quoted field names, comments, trailing commas, etc */
|
|
48
|
+
public static EXTENDED_PARSER = true;
|
|
49
|
+
public static IGNORE_MISSING_REFERENCES = false;
|
|
50
|
+
|
|
51
|
+
@measureFnc
|
|
52
|
+
public static stringify(obj: unknown, config?: JSONLACKS_StringifyConfig): string {
|
|
53
|
+
let serialized = JSONLACKS.escapeSpecialObjects(obj, config);
|
|
54
|
+
return measureBlock(function JSONstringify() { return JSON.stringify(serialized); });
|
|
55
|
+
}
|
|
56
|
+
/** Is useful when serializing an array to a file with one object per line */
|
|
57
|
+
@measureFnc
|
|
58
|
+
public static async stringifyFile(obj: unknown[], config?: JSONLACKS_StringifyConfig): Promise<Buffer> {
|
|
59
|
+
let serialized = JSONLACKS.escapeSpecialObjects(obj, config) as unknown[];
|
|
60
|
+
return await measureBlock(async function JSONstringifyAndJoin() {
|
|
61
|
+
let buffers: Buffer[] = [];
|
|
62
|
+
for (let i = 0; i < serialized.length; i += SERIALIZE_OBJECT_BATCH_COUNT) {
|
|
63
|
+
let str = serialized.slice(i, i + SERIALIZE_OBJECT_BATCH_COUNT).map(x => JSON.stringify(x) + "\n").join("");
|
|
64
|
+
buffers.push(Buffer.from(str));
|
|
65
|
+
await delay("immediate");
|
|
66
|
+
}
|
|
67
|
+
// Break up into chunks, as string => Buffer i
|
|
68
|
+
return Buffer.concat(buffers);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
// TIMING: Seems to be about 40X slower than JSON.parse unless extended is set to false,
|
|
72
|
+
// then it is about 2X slower (although it depends on the size and complexity of the objects!)
|
|
73
|
+
@measureFnc
|
|
74
|
+
public static parse<T>(text: string, config?: JSONLACKS_ParseConfig, hydrateState?: HydrateState): T {
|
|
75
|
+
let obj: unknown;
|
|
76
|
+
|
|
77
|
+
let extendedParsing = config?.extended ?? JSONLACKS.EXTENDED_PARSER;
|
|
78
|
+
|
|
79
|
+
if (extendedParsing) {
|
|
80
|
+
obj = measureBlock(function JSONextendedParse() { return parser.parse(text); });
|
|
81
|
+
} else {
|
|
82
|
+
obj = measureBlock(function JSONparse() { return JSON.parse(text); });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return JSONLACKS.hydrateSpecialObjects(obj, hydrateState) as T;
|
|
86
|
+
}
|
|
87
|
+
@measureFnc
|
|
88
|
+
public static async parseLines<T>(buffer: Buffer, config?: JSONLACKS_ParseConfig): Promise<T[]> {
|
|
89
|
+
let output: T[] = [];
|
|
90
|
+
let pos = 0;
|
|
91
|
+
let hydrateState: HydrateState = {
|
|
92
|
+
references: new Map(),
|
|
93
|
+
visited: new Set(),
|
|
94
|
+
};
|
|
95
|
+
function parseChunk() {
|
|
96
|
+
let start = pos;
|
|
97
|
+
let lastNewLine = 0;
|
|
98
|
+
while (pos < buffer.length && (!lastNewLine || (pos - start) < PARSE_BYTE_CHUNK_SIZE)) {
|
|
99
|
+
let byte = buffer[pos];
|
|
100
|
+
if (byte === 10) {
|
|
101
|
+
lastNewLine = pos;
|
|
102
|
+
}
|
|
103
|
+
pos++;
|
|
104
|
+
}
|
|
105
|
+
if (pos === buffer.length) {
|
|
106
|
+
lastNewLine = pos;
|
|
107
|
+
}
|
|
108
|
+
pos = lastNewLine + 1;
|
|
109
|
+
|
|
110
|
+
let text = buffer.slice(start, lastNewLine).toString("utf8");
|
|
111
|
+
let lines = text
|
|
112
|
+
.replaceAll("\r", "")
|
|
113
|
+
.split("\n")
|
|
114
|
+
.filter(x => x && !x.startsWith("//"))
|
|
115
|
+
;
|
|
116
|
+
let linesJSON = "[";
|
|
117
|
+
for (let i = 0; i < lines.length; i++) {
|
|
118
|
+
if (i !== 0) linesJSON += ",";
|
|
119
|
+
linesJSON += lines[i];
|
|
120
|
+
}
|
|
121
|
+
linesJSON += "]";
|
|
122
|
+
let parts = JSONLACKS.parse(linesJSON, config, hydrateState) as T[];
|
|
123
|
+
for (let part of parts) {
|
|
124
|
+
output.push(part);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
while (pos < buffer.length) {
|
|
128
|
+
parseChunk();
|
|
129
|
+
// Wait, to allow other thread to do work. We wait a long time... because we parse 10MB at once,
|
|
130
|
+
// so... this gives us 2s of delay per 1GB of parsing, which should only be a fraction of our parse time
|
|
131
|
+
if (pos < buffer.length) {
|
|
132
|
+
await delay(20);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return output;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@measureFnc
|
|
139
|
+
private static escapeSpecialObjects(obj: unknown, config?: JSONLACKS_StringifyConfig): unknown {
|
|
140
|
+
// I think iterating twice for references is the fastest way to do it?
|
|
141
|
+
let objects = new Set<unknown>();
|
|
142
|
+
// NOTE: Use unique values for references, to allow concatenating files without having to
|
|
143
|
+
// deal with escaping references.
|
|
144
|
+
let refPrefix = Date.now() + Math.random() + "";
|
|
145
|
+
let nextRefId = 0;
|
|
146
|
+
function getNextRefKey() {
|
|
147
|
+
return refPrefix + "_" + nextRefId++;
|
|
148
|
+
}
|
|
149
|
+
let refObjects = new Map<unknown, string>();
|
|
150
|
+
findReferences(obj);
|
|
151
|
+
function findReferences(obj: unknown) {
|
|
152
|
+
if (!canHaveChildren(obj)) return;
|
|
153
|
+
if (refObjects.has(obj)) return;
|
|
154
|
+
if (objects.has(obj)) {
|
|
155
|
+
let refKey = getNextRefKey();
|
|
156
|
+
refObjects.set(obj, refKey);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
objects.add(obj);
|
|
160
|
+
for (let key in obj) {
|
|
161
|
+
findReferences(obj[key]);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let refsSeen = new Set<unknown>();
|
|
166
|
+
return iterate(obj);
|
|
167
|
+
function iterate(obj: unknown, refHandled?: boolean): unknown {
|
|
168
|
+
if (!canHaveChildren(obj)) return obj;
|
|
169
|
+
if (!refHandled) {
|
|
170
|
+
let refKey = refObjects.get(obj);
|
|
171
|
+
if (refKey) {
|
|
172
|
+
if (refsSeen.has(obj)) {
|
|
173
|
+
return {
|
|
174
|
+
[JSONLACKS.LACKS_KEY]: "ref",
|
|
175
|
+
id: refKey,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
refsSeen.add(obj);
|
|
179
|
+
return {
|
|
180
|
+
[JSONLACKS.LACKS_KEY]: "define",
|
|
181
|
+
id: refKey,
|
|
182
|
+
value: iterate(obj, true),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (JSONLACKS.LACKS_KEY in obj) {
|
|
188
|
+
let restOfObj = { ...obj };
|
|
189
|
+
let escapedValue = restOfObj[JSONLACKS.LACKS_KEY];
|
|
190
|
+
delete restOfObj[JSONLACKS.LACKS_KEY];
|
|
191
|
+
return {
|
|
192
|
+
[JSONLACKS.LACKS_KEY]: "removedSpecialKey",
|
|
193
|
+
escapedValue: iterate(escapedValue),
|
|
194
|
+
restOfObj: iterate(restOfObj),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (obj instanceof Buffer) {
|
|
199
|
+
return {
|
|
200
|
+
[JSONLACKS.LACKS_KEY]: "Buffer",
|
|
201
|
+
data: obj.toString("base64"),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let cloned = config?.allowObjectMutation;
|
|
206
|
+
function cloneObj() {
|
|
207
|
+
if (cloned) return;
|
|
208
|
+
cloned = true;
|
|
209
|
+
if (Array.isArray(obj)) {
|
|
210
|
+
obj = [...obj];
|
|
211
|
+
} else {
|
|
212
|
+
obj = { ...obj as any };
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
for (let key in obj) {
|
|
216
|
+
let originalValue = obj[key];
|
|
217
|
+
let value = iterate(originalValue);
|
|
218
|
+
if (value !== originalValue) {
|
|
219
|
+
cloneObj();
|
|
220
|
+
obj[key] = value;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return obj;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@measureFnc
|
|
228
|
+
private static hydrateSpecialObjects(obj: unknown, hydrateState?: HydrateState): unknown {
|
|
229
|
+
let references = hydrateState?.references || new Map<string, unknown>();
|
|
230
|
+
let visited = hydrateState?.visited || new Set<unknown>();
|
|
231
|
+
return iterate(obj);
|
|
232
|
+
function iterate(obj: unknown) {
|
|
233
|
+
if (!canHaveChildren(obj)) return obj;
|
|
234
|
+
if (visited.has(obj)) return obj;
|
|
235
|
+
visited.add(obj);
|
|
236
|
+
let type = obj[JSONLACKS.LACKS_KEY];
|
|
237
|
+
if (!type) {
|
|
238
|
+
for (let key in obj) {
|
|
239
|
+
let originalValue = obj[key];
|
|
240
|
+
let value = iterate(originalValue);
|
|
241
|
+
if (value !== originalValue) {
|
|
242
|
+
obj[key] = value;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return obj;
|
|
246
|
+
}
|
|
247
|
+
if (type === "removedSpecialKey") {
|
|
248
|
+
let restOfObj = iterate(obj.restOfObj) as any;
|
|
249
|
+
let escapedValue = iterate(obj.escapedValue) as any;
|
|
250
|
+
return { ...restOfObj, [JSONLACKS.LACKS_KEY]: escapedValue };
|
|
251
|
+
}
|
|
252
|
+
if (type === "Buffer") {
|
|
253
|
+
return Buffer.from(obj.data as string, "base64");
|
|
254
|
+
}
|
|
255
|
+
if (type === "define") {
|
|
256
|
+
references.set(obj.id as string, iterate(obj.value));
|
|
257
|
+
return obj.value;
|
|
258
|
+
}
|
|
259
|
+
if (type === "ref") {
|
|
260
|
+
let id = obj.id as string;
|
|
261
|
+
if (!JSONLACKS.IGNORE_MISSING_REFERENCES && !references.has(id)) {
|
|
262
|
+
debugbreak(2);
|
|
263
|
+
debugger;
|
|
264
|
+
throw new Error(`Reference to undefined id "${id}"`);
|
|
265
|
+
}
|
|
266
|
+
return references.get(id);
|
|
267
|
+
}
|
|
268
|
+
throw new Error(`Unknown lacks type "${type}"`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async function benchmark() {
|
|
274
|
+
const loops = 1000 * 100;
|
|
275
|
+
let inputs: string[] = [];
|
|
276
|
+
for (let i = 0; i < loops; i++) {
|
|
277
|
+
inputs.push(JSON.stringify({
|
|
278
|
+
i,
|
|
279
|
+
hello_there: i,
|
|
280
|
+
example: 5,
|
|
281
|
+
list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
|
282
|
+
}) + " ");
|
|
283
|
+
}
|
|
284
|
+
JSON.parse(inputs[0]);
|
|
285
|
+
parser.parse(inputs[0]);
|
|
286
|
+
|
|
287
|
+
measureCodeSync(function measure() {
|
|
288
|
+
measureBlock(function JSON_PARSE() {
|
|
289
|
+
for (let i = 0; i < loops; i++) {
|
|
290
|
+
JSON.parse(inputs[i]);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
measureBlock(function JSON_PARSE_FREEZE() {
|
|
294
|
+
for (let i = 0; i < loops; i++) {
|
|
295
|
+
let obj = JSON.parse(inputs[i]);
|
|
296
|
+
recursiveFreeze(obj);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
measureBlock(function EXTENDED_PARSE() {
|
|
301
|
+
for (let i = 0; i < loops; i++) {
|
|
302
|
+
parser.parse(inputs[i]);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
measureBlock(function JSONLACKS_PARSE() {
|
|
307
|
+
for (let i = 0; i < loops; i++) {
|
|
308
|
+
JSONLACKS.parse(inputs[i]);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
measureBlock(function JSONLACKS_PARSE_SIMPLE() {
|
|
312
|
+
for (let i = 0; i < loops; i++) {
|
|
313
|
+
JSONLACKS.parse(inputs[i], { extended: false });
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
}, {
|
|
318
|
+
thresholdInTable: 0
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
}
|
|
322
|
+
//benchmark().catch(console.error).finally(() => process.exit());
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
async function testJSONLACKS() {
|
|
326
|
+
{
|
|
327
|
+
let obj = {};
|
|
328
|
+
let str = JSONLACKS.stringify({
|
|
329
|
+
a: obj,
|
|
330
|
+
b: obj,
|
|
331
|
+
});
|
|
332
|
+
let parsed = JSONLACKS.parse(str) as any;
|
|
333
|
+
if (parsed.a !== parsed.b) {
|
|
334
|
+
throw new Error("Failed to maintain references");
|
|
335
|
+
}
|
|
336
|
+
if (typeof parsed.a !== "object") {
|
|
337
|
+
throw new Error("Object become corrupted");
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Test LACKS_KEY is escaped correctly
|
|
342
|
+
{
|
|
343
|
+
let obj = { [JSONLACKS.LACKS_KEY]: "hello" };
|
|
344
|
+
let str = JSONLACKS.stringify(obj);
|
|
345
|
+
let parsed = JSONLACKS.parse(str) as any;
|
|
346
|
+
if (JSON.stringify(obj) !== JSON.stringify(parsed)) {
|
|
347
|
+
throw new Error("Failed to escape LACKS_KEY");
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// Test buffers are preserved
|
|
351
|
+
{
|
|
352
|
+
let obj = { a: Buffer.from("hello") };
|
|
353
|
+
let str = JSONLACKS.stringify(obj);
|
|
354
|
+
let parsed = JSONLACKS.parse(str) as any;
|
|
355
|
+
if (obj.a.toString() !== parsed.a.toString()) {
|
|
356
|
+
throw new Error(`Failed to preserve buffers`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Test references 2
|
|
361
|
+
{
|
|
362
|
+
let base = {};
|
|
363
|
+
let arr = [base, base];
|
|
364
|
+
let obj = { x: arr, y: arr };
|
|
365
|
+
let result = JSONLACKS.parse(JSONLACKS.stringify(obj), { extended: false }) as typeof obj;
|
|
366
|
+
if (JSON.stringify(result) !== JSON.stringify(obj)) {
|
|
367
|
+
throw new Error(`Corrupted values, expected ${JSON.stringify(obj)}, got ${JSON.stringify(result)}`);
|
|
368
|
+
}
|
|
369
|
+
if (result.x !== result.y) {
|
|
370
|
+
throw new Error(`Failed to maintain references`);
|
|
371
|
+
}
|
|
372
|
+
if (result.x[0] !== result.x[1]) {
|
|
373
|
+
throw new Error(`Failed to maintain references`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
//testJSONLACKS().catch(console.error).finally(() => process.exit());
|
|
378
|
+
|
|
379
|
+
async function generateAndVerifyParser() {
|
|
380
|
+
const pegjs = await import("pegjs");
|
|
381
|
+
|
|
382
|
+
var grammar = fs.readFileSync(__dirname + "/JSONLACKS.pegjs", "utf8");
|
|
383
|
+
var parserSource = pegjs.generate(grammar, { output: "source", format: "commonjs" });
|
|
384
|
+
fs.writeFileSync(__dirname + "/JSONLACKS.generated.js", parserSource);
|
|
385
|
+
|
|
386
|
+
var module = { exports: {} };
|
|
387
|
+
eval(parserSource);
|
|
388
|
+
const parser = module.exports as any;
|
|
389
|
+
function verify(text: string) {
|
|
390
|
+
var parsed = parser.parse(text);
|
|
391
|
+
var result = JSON.stringify(parsed);
|
|
392
|
+
let realResult = JSON.stringify(eval("(" + text + ")"));
|
|
393
|
+
if (result !== realResult) {
|
|
394
|
+
throw new Error(`Failed to parse: ${text} should be ${realResult}, was ${result}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
verify(`
|
|
399
|
+
{
|
|
400
|
+
"hello_there": 1,
|
|
401
|
+
}
|
|
402
|
+
`);
|
|
403
|
+
verify(`
|
|
404
|
+
{
|
|
405
|
+
hello_there: 1,
|
|
406
|
+
}
|
|
407
|
+
`);
|
|
408
|
+
verify(`
|
|
409
|
+
{
|
|
410
|
+
hello_there: 1,
|
|
411
|
+
more: 1,
|
|
412
|
+
}
|
|
413
|
+
`);
|
|
414
|
+
verify(`{ list: [1,], }`);
|
|
415
|
+
verify(`{ list: [1,], list2: [1,], }`);
|
|
416
|
+
|
|
417
|
+
verify(`{ /* test */ list: [1,], list2: [1,], }`);
|
|
418
|
+
verify(`{ /* test */ /* test 2 */ list: [1,], list2: [1,], }`);
|
|
419
|
+
verify(`
|
|
420
|
+
{
|
|
421
|
+
/* test */
|
|
422
|
+
/* test 2 */
|
|
423
|
+
list: [1,],
|
|
424
|
+
// Single line comment
|
|
425
|
+
list2: [1,],
|
|
426
|
+
// list3: [3],
|
|
427
|
+
}
|
|
428
|
+
`);
|
|
429
|
+
}
|
|
376
430
|
//generateAndVerifyParser().catch(console.error).finally(() => process.exit());
|