ushman-equiv 0.4.0
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/AGENTS.md +81 -0
- package/LICENSE.md +21 -0
- package/README.md +201 -0
- package/bin/ushman-equiv +19 -0
- package/dist/analysis-context.d.ts +102 -0
- package/dist/analysis-context.d.ts.map +1 -0
- package/dist/analysis-context.js +708 -0
- package/dist/ast-guards.d.ts +24 -0
- package/dist/ast-guards.d.ts.map +1 -0
- package/dist/ast-guards.js +83 -0
- package/dist/candidate-boot.d.ts +30 -0
- package/dist/candidate-boot.d.ts.map +1 -0
- package/dist/candidate-boot.js +262 -0
- package/dist/canonicalize.d.ts +19 -0
- package/dist/canonicalize.d.ts.map +1 -0
- package/dist/canonicalize.js +525 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +312 -0
- package/dist/equiv-execution-context.d.ts +25 -0
- package/dist/equiv-execution-context.d.ts.map +1 -0
- package/dist/equiv-execution-context.js +82 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/run.d.ts +8 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +129 -0
- package/dist/shared.d.ts +9 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +47 -0
- package/dist/tier-i-import-graph.d.ts +7 -0
- package/dist/tier-i-import-graph.d.ts.map +1 -0
- package/dist/tier-i-import-graph.js +34 -0
- package/dist/tier-l-child-runtime.d.ts +2 -0
- package/dist/tier-l-child-runtime.d.ts.map +1 -0
- package/dist/tier-l-child-runtime.js +62 -0
- package/dist/tier-l-module-load.d.ts +6 -0
- package/dist/tier-l-module-load.d.ts.map +1 -0
- package/dist/tier-l-module-load.js +139 -0
- package/dist/tier-l-stub-source.d.ts +11 -0
- package/dist/tier-l-stub-source.d.ts.map +1 -0
- package/dist/tier-l-stub-source.js +246 -0
- package/dist/tier-r-replay.d.ts +6 -0
- package/dist/tier-r-replay.d.ts.map +1 -0
- package/dist/tier-r-replay.js +382 -0
- package/dist/tier-s-symbol-diff.d.ts +19 -0
- package/dist/tier-s-symbol-diff.d.ts.map +1 -0
- package/dist/tier-s-symbol-diff.js +156 -0
- package/dist/types.d.ts +91 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/workspace.d.ts +63 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +459 -0
- package/package.json +64 -0
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
// Matches the characterize serializer contract: default trace payloads are capped
|
|
2
|
+
// at 50 MB and floats are rounded to 4 decimals for stable replay comparisons.
|
|
3
|
+
import { compareStrings } from "./shared.js";
|
|
4
|
+
const DEFAULT_MAX_BYTES = 50 * 1024 * 1024;
|
|
5
|
+
const DEFAULT_PRECISION = 4;
|
|
6
|
+
const EXEMPT_FIELD_MARKER = '__ushman_exempt_field__';
|
|
7
|
+
const CANONICALIZE_MISS = Symbol('canonicalize-miss');
|
|
8
|
+
const asObject = (value) => value;
|
|
9
|
+
const roundNumber = (value, precision) => {
|
|
10
|
+
const scale = 10 ** precision;
|
|
11
|
+
return Math.round(value * scale) / scale;
|
|
12
|
+
};
|
|
13
|
+
const assertWithinSizeLimit = (bytes, maxBytes) => {
|
|
14
|
+
if (bytes > maxBytes) {
|
|
15
|
+
throw new RangeError(`Canonicalized value exceeds maxBytes (${bytes} > ${maxBytes}).`);
|
|
16
|
+
}
|
|
17
|
+
return bytes;
|
|
18
|
+
};
|
|
19
|
+
const addSizedBytes = (current, delta, maxBytes) => assertWithinSizeLimit(current + delta, maxBytes);
|
|
20
|
+
const jsonScalarByteLength = (value) => Buffer.byteLength(JSON.stringify(value));
|
|
21
|
+
const sizedScalar = (value, state) => ({
|
|
22
|
+
bytes: assertWithinSizeLimit(jsonScalarByteLength(value), state.maxBytes),
|
|
23
|
+
value,
|
|
24
|
+
});
|
|
25
|
+
const sizedObject = (state, entries) => {
|
|
26
|
+
let bytes = assertWithinSizeLimit(2, state.maxBytes);
|
|
27
|
+
const value = {};
|
|
28
|
+
for (const [index, [key, entry]] of entries.entries()) {
|
|
29
|
+
const propertyBytes = jsonScalarByteLength(key) + 1 + entry.bytes + (index > 0 ? 1 : 0);
|
|
30
|
+
bytes = addSizedBytes(bytes, propertyBytes, state.maxBytes);
|
|
31
|
+
value[key] = entry.value;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
bytes,
|
|
35
|
+
value,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
const sizedArray = (state, items) => {
|
|
39
|
+
let bytes = assertWithinSizeLimit(2, state.maxBytes);
|
|
40
|
+
const value = [];
|
|
41
|
+
for (const [index, item] of items.entries()) {
|
|
42
|
+
bytes = addSizedBytes(bytes, item.bytes + (index > 0 ? 1 : 0), state.maxBytes);
|
|
43
|
+
value.push(item.value);
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
bytes,
|
|
47
|
+
value,
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
const createSizedArrayBuilder = (state) => {
|
|
51
|
+
let bytes = assertWithinSizeLimit(2, state.maxBytes);
|
|
52
|
+
const value = [];
|
|
53
|
+
return {
|
|
54
|
+
add(entry) {
|
|
55
|
+
bytes = addSizedBytes(bytes, entry.bytes + (value.length > 0 ? 1 : 0), state.maxBytes);
|
|
56
|
+
value.push(entry.value);
|
|
57
|
+
},
|
|
58
|
+
done() {
|
|
59
|
+
return {
|
|
60
|
+
bytes,
|
|
61
|
+
value,
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
const roundFiniteNumber = (value, state) => sizedScalar(roundNumber(value, state.precision), state);
|
|
67
|
+
const canonicalizeNumber = (value, state) => {
|
|
68
|
+
if (Number.isNaN(value)) {
|
|
69
|
+
return sizedObject(state, [
|
|
70
|
+
['__t', sizedScalar('Number', state)],
|
|
71
|
+
['value', sizedScalar('NaN', state)],
|
|
72
|
+
]);
|
|
73
|
+
}
|
|
74
|
+
if (value === Number.POSITIVE_INFINITY) {
|
|
75
|
+
return sizedObject(state, [
|
|
76
|
+
['__t', sizedScalar('Number', state)],
|
|
77
|
+
['value', sizedScalar('Infinity', state)],
|
|
78
|
+
]);
|
|
79
|
+
}
|
|
80
|
+
if (value === Number.NEGATIVE_INFINITY) {
|
|
81
|
+
return sizedObject(state, [
|
|
82
|
+
['__t', sizedScalar('Number', state)],
|
|
83
|
+
['value', sizedScalar('-Infinity', state)],
|
|
84
|
+
]);
|
|
85
|
+
}
|
|
86
|
+
if (Object.is(value, -0)) {
|
|
87
|
+
return sizedObject(state, [
|
|
88
|
+
['__t', sizedScalar('Number', state)],
|
|
89
|
+
['value', sizedScalar('-0', state)],
|
|
90
|
+
]);
|
|
91
|
+
}
|
|
92
|
+
return roundFiniteNumber(value, state);
|
|
93
|
+
};
|
|
94
|
+
const isPlainObject = (value) => {
|
|
95
|
+
if (!value || typeof value !== 'object') {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const prototype = Object.getPrototypeOf(value);
|
|
99
|
+
return prototype === Object.prototype || prototype === null;
|
|
100
|
+
};
|
|
101
|
+
const encodeBytes = (bytes) => Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString('base64');
|
|
102
|
+
const trackReference = (value, state) => {
|
|
103
|
+
const id = state.nextId;
|
|
104
|
+
state.seen.set(value, { id });
|
|
105
|
+
state.nextId += 1;
|
|
106
|
+
return id;
|
|
107
|
+
};
|
|
108
|
+
const canonicalizePrimitive = (value, state) => {
|
|
109
|
+
if (value === undefined) {
|
|
110
|
+
return sizedObject(state, [['__t', sizedScalar('Undefined', state)]]);
|
|
111
|
+
}
|
|
112
|
+
if (typeof value === 'number') {
|
|
113
|
+
return canonicalizeNumber(value, state);
|
|
114
|
+
}
|
|
115
|
+
if (typeof value === 'string' || typeof value === 'boolean') {
|
|
116
|
+
return sizedScalar(value, state);
|
|
117
|
+
}
|
|
118
|
+
if (typeof value === 'bigint') {
|
|
119
|
+
return sizedObject(state, [
|
|
120
|
+
['__t', sizedScalar('BigInt', state)],
|
|
121
|
+
['value', sizedScalar(value.toString(), state)],
|
|
122
|
+
]);
|
|
123
|
+
}
|
|
124
|
+
if (typeof value === 'symbol') {
|
|
125
|
+
return sizedObject(state, [
|
|
126
|
+
['__t', sizedScalar('Symbol', state)],
|
|
127
|
+
['value', sizedScalar(String(value), state)],
|
|
128
|
+
]);
|
|
129
|
+
}
|
|
130
|
+
if (typeof value === 'function') {
|
|
131
|
+
return sizedObject(state, [
|
|
132
|
+
['__t', sizedScalar('Function', state)],
|
|
133
|
+
['name', sizedScalar(value.name || 'anonymous', state)],
|
|
134
|
+
]);
|
|
135
|
+
}
|
|
136
|
+
return CANONICALIZE_MISS;
|
|
137
|
+
};
|
|
138
|
+
const canonicalizeTypedArray = (value, state) => {
|
|
139
|
+
const base = sizedObject(state, [
|
|
140
|
+
['__t', sizedScalar(value.constructor.name, state)],
|
|
141
|
+
[
|
|
142
|
+
'base64',
|
|
143
|
+
sizedScalar(encodeBytes(new Uint8Array(value.buffer, value.byteOffset, value.byteLength)), state),
|
|
144
|
+
],
|
|
145
|
+
['byteLength', sizedScalar(value.byteLength, state)],
|
|
146
|
+
]);
|
|
147
|
+
if (!('length' in value) || typeof value.length !== 'number') {
|
|
148
|
+
return {
|
|
149
|
+
bytes: base.bytes,
|
|
150
|
+
value: {
|
|
151
|
+
...asObject(base.value),
|
|
152
|
+
length: undefined,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
return sizedObject(state, [
|
|
157
|
+
['__t', sizedScalar(value.constructor.name, state)],
|
|
158
|
+
[
|
|
159
|
+
'base64',
|
|
160
|
+
sizedScalar(encodeBytes(new Uint8Array(value.buffer, value.byteOffset, value.byteLength)), state),
|
|
161
|
+
],
|
|
162
|
+
['byteLength', sizedScalar(value.byteLength, state)],
|
|
163
|
+
['length', sizedScalar(value.length, state)],
|
|
164
|
+
]);
|
|
165
|
+
};
|
|
166
|
+
const canonicalizeArrayItems = (value, state, pathParts) => {
|
|
167
|
+
const items = createSizedArrayBuilder(state);
|
|
168
|
+
for (const [index, entry] of value.entries()) {
|
|
169
|
+
items.add(visitValue(entry, state, [...pathParts, String(index)]));
|
|
170
|
+
}
|
|
171
|
+
return items.done();
|
|
172
|
+
};
|
|
173
|
+
const canonicalizeMapEntries = (value, state, pathParts) => {
|
|
174
|
+
const entries = createSizedArrayBuilder(state);
|
|
175
|
+
let index = 0;
|
|
176
|
+
for (const [key, entry] of value.entries()) {
|
|
177
|
+
entries.add(sizedArray(state, [
|
|
178
|
+
visitValue(key, state, [...pathParts, `${index}`, '0']),
|
|
179
|
+
visitValue(entry, state, [...pathParts, `${index}`, '1']),
|
|
180
|
+
]));
|
|
181
|
+
index += 1;
|
|
182
|
+
}
|
|
183
|
+
return entries.done();
|
|
184
|
+
};
|
|
185
|
+
const canonicalizeSetValues = (value, state, pathParts) => {
|
|
186
|
+
const entries = createSizedArrayBuilder(state);
|
|
187
|
+
let index = 0;
|
|
188
|
+
for (const entry of value.values()) {
|
|
189
|
+
entries.add(visitValue(entry, state, [...pathParts, String(index)]));
|
|
190
|
+
index += 1;
|
|
191
|
+
}
|
|
192
|
+
return entries.done();
|
|
193
|
+
};
|
|
194
|
+
const canonicalizeObjectProps = (value, state, pathParts) => {
|
|
195
|
+
let bytes = assertWithinSizeLimit(2, state.maxBytes);
|
|
196
|
+
const props = {};
|
|
197
|
+
let propertyCount = 0;
|
|
198
|
+
for (const key of Object.keys(asObject(value))
|
|
199
|
+
// Characterize strips generic object UUIDs so replay fixtures stay stable
|
|
200
|
+
// across runs where transient object identities change.
|
|
201
|
+
.filter((entry) => entry !== 'uuid')
|
|
202
|
+
.sort(compareStrings)) {
|
|
203
|
+
const entry = visitValue(asObject(value)[key], state, [...pathParts, key]);
|
|
204
|
+
const propertyBytes = jsonScalarByteLength(key) + 1 + entry.bytes + (propertyCount > 0 ? 1 : 0);
|
|
205
|
+
bytes = addSizedBytes(bytes, propertyBytes, state.maxBytes);
|
|
206
|
+
props[key] = entry.value;
|
|
207
|
+
propertyCount += 1;
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
bytes,
|
|
211
|
+
value: props,
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
const canonicalizeKnownObject = (value, state) => {
|
|
215
|
+
if (value instanceof Error) {
|
|
216
|
+
return sizedObject(state, [
|
|
217
|
+
['__t', sizedScalar('Error', state)],
|
|
218
|
+
['message', sizedScalar(value.message, state)],
|
|
219
|
+
['name', sizedScalar(value.name, state)],
|
|
220
|
+
]);
|
|
221
|
+
}
|
|
222
|
+
if (value instanceof Date) {
|
|
223
|
+
return sizedObject(state, [
|
|
224
|
+
['__t', sizedScalar('Date', state)],
|
|
225
|
+
['iso', sizedScalar(value.toISOString(), state)],
|
|
226
|
+
]);
|
|
227
|
+
}
|
|
228
|
+
if (value instanceof URL) {
|
|
229
|
+
return sizedObject(state, [
|
|
230
|
+
['__t', sizedScalar('URL', state)],
|
|
231
|
+
['href', sizedScalar(value.href, state)],
|
|
232
|
+
]);
|
|
233
|
+
}
|
|
234
|
+
if (value instanceof ArrayBuffer) {
|
|
235
|
+
return sizedObject(state, [
|
|
236
|
+
['__t', sizedScalar('ArrayBuffer', state)],
|
|
237
|
+
['base64', sizedScalar(encodeBytes(new Uint8Array(value)), state)],
|
|
238
|
+
['byteLength', sizedScalar(value.byteLength, state)],
|
|
239
|
+
]);
|
|
240
|
+
}
|
|
241
|
+
if (ArrayBuffer.isView(value)) {
|
|
242
|
+
return canonicalizeTypedArray(value, state);
|
|
243
|
+
}
|
|
244
|
+
return null;
|
|
245
|
+
};
|
|
246
|
+
const visitValue = (value, state, pathParts) => {
|
|
247
|
+
if (value === null) {
|
|
248
|
+
return sizedScalar(null, state);
|
|
249
|
+
}
|
|
250
|
+
const exemptKey = pathParts.join('.');
|
|
251
|
+
if (exemptKey && state.exemptFields.has(exemptKey)) {
|
|
252
|
+
return sizedScalar(EXEMPT_FIELD_MARKER, state);
|
|
253
|
+
}
|
|
254
|
+
const primitive = canonicalizePrimitive(value, state);
|
|
255
|
+
if (primitive !== CANONICALIZE_MISS) {
|
|
256
|
+
return primitive;
|
|
257
|
+
}
|
|
258
|
+
if (!(value instanceof Object)) {
|
|
259
|
+
return sizedScalar(String(value), state);
|
|
260
|
+
}
|
|
261
|
+
const seenReference = state.seen.get(value);
|
|
262
|
+
if (seenReference) {
|
|
263
|
+
return sizedObject(state, [['$ref', sizedScalar(seenReference.id, state)]]);
|
|
264
|
+
}
|
|
265
|
+
const knownObject = canonicalizeKnownObject(value, state);
|
|
266
|
+
if (knownObject !== null) {
|
|
267
|
+
return knownObject;
|
|
268
|
+
}
|
|
269
|
+
if (Array.isArray(value)) {
|
|
270
|
+
const id = trackReference(value, state);
|
|
271
|
+
return sizedObject(state, [
|
|
272
|
+
['__id', sizedScalar(id, state)],
|
|
273
|
+
['__t', sizedScalar('Array', state)],
|
|
274
|
+
['items', canonicalizeArrayItems(value, state, pathParts)],
|
|
275
|
+
]);
|
|
276
|
+
}
|
|
277
|
+
if (value instanceof Map) {
|
|
278
|
+
const id = trackReference(value, state);
|
|
279
|
+
return sizedObject(state, [
|
|
280
|
+
['__id', sizedScalar(id, state)],
|
|
281
|
+
['__t', sizedScalar('Map', state)],
|
|
282
|
+
['entries', canonicalizeMapEntries(value, state, pathParts)],
|
|
283
|
+
]);
|
|
284
|
+
}
|
|
285
|
+
if (value instanceof Set) {
|
|
286
|
+
const id = trackReference(value, state);
|
|
287
|
+
return sizedObject(state, [
|
|
288
|
+
['__id', sizedScalar(id, state)],
|
|
289
|
+
['__t', sizedScalar('Set', state)],
|
|
290
|
+
['values', canonicalizeSetValues(value, state, pathParts)],
|
|
291
|
+
]);
|
|
292
|
+
}
|
|
293
|
+
const id = trackReference(value, state);
|
|
294
|
+
const type = isPlainObject(value) ? 'Object' : 'Instance';
|
|
295
|
+
const entries = [
|
|
296
|
+
['__id', sizedScalar(id, state)],
|
|
297
|
+
['__t', sizedScalar(type, state)],
|
|
298
|
+
['props', canonicalizeObjectProps(value, state, pathParts)],
|
|
299
|
+
];
|
|
300
|
+
if (type === 'Instance' && typeof value.constructor?.name === 'string') {
|
|
301
|
+
entries.push(['ctor', sizedScalar(value.constructor.name, state)]);
|
|
302
|
+
}
|
|
303
|
+
return sizedObject(state, entries);
|
|
304
|
+
};
|
|
305
|
+
/**
|
|
306
|
+
* Converts replay values into the stable characterize-compatible wire shape.
|
|
307
|
+
* `exemptFields` uses dot-notation paths and `maxBytes` aborts once the
|
|
308
|
+
* canonicalized payload would exceed the configured UTF-8 byte budget.
|
|
309
|
+
*/
|
|
310
|
+
export const canonicalize = (value, options = {}) => {
|
|
311
|
+
const state = {
|
|
312
|
+
exemptFields: new Set(options.exemptFields ?? []),
|
|
313
|
+
maxBytes: options.maxBytes ?? DEFAULT_MAX_BYTES,
|
|
314
|
+
nextId: 1,
|
|
315
|
+
precision: options.precision ?? DEFAULT_PRECISION,
|
|
316
|
+
seen: new WeakMap(),
|
|
317
|
+
};
|
|
318
|
+
return visitValue(value, state, []).value;
|
|
319
|
+
};
|
|
320
|
+
const decodeBytes = (base64) => {
|
|
321
|
+
const buffer = Buffer.from(base64, 'base64');
|
|
322
|
+
return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
323
|
+
};
|
|
324
|
+
const createHydrationState = () => ({
|
|
325
|
+
refs: new Map(),
|
|
326
|
+
});
|
|
327
|
+
const HYDRATION_MISS = Symbol('hydration-miss');
|
|
328
|
+
const hydrateTaggedNumber = (value) => {
|
|
329
|
+
if (value === 'NaN') {
|
|
330
|
+
return Number.NaN;
|
|
331
|
+
}
|
|
332
|
+
if (value === 'Infinity') {
|
|
333
|
+
return Number.POSITIVE_INFINITY;
|
|
334
|
+
}
|
|
335
|
+
if (value === '-Infinity') {
|
|
336
|
+
return Number.NEGATIVE_INFINITY;
|
|
337
|
+
}
|
|
338
|
+
if (value === '-0') {
|
|
339
|
+
return -0;
|
|
340
|
+
}
|
|
341
|
+
return Number(value);
|
|
342
|
+
};
|
|
343
|
+
const hydrateArrayBuffer = (base64) => {
|
|
344
|
+
const bytes = decodeBytes(base64);
|
|
345
|
+
const copy = new Uint8Array(bytes.byteLength);
|
|
346
|
+
copy.set(bytes);
|
|
347
|
+
return copy.buffer;
|
|
348
|
+
};
|
|
349
|
+
const hydrateTypedArray = (tag, base64) => {
|
|
350
|
+
const bytes = decodeBytes(base64);
|
|
351
|
+
const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
352
|
+
switch (tag) {
|
|
353
|
+
case 'DataView':
|
|
354
|
+
return new DataView(buffer);
|
|
355
|
+
case 'Float32Array':
|
|
356
|
+
return new Float32Array(buffer);
|
|
357
|
+
case 'Float64Array':
|
|
358
|
+
return new Float64Array(buffer);
|
|
359
|
+
case 'Int16Array':
|
|
360
|
+
return new Int16Array(buffer);
|
|
361
|
+
case 'Int32Array':
|
|
362
|
+
return new Int32Array(buffer);
|
|
363
|
+
case 'Int8Array':
|
|
364
|
+
return new Int8Array(buffer);
|
|
365
|
+
case 'Uint16Array':
|
|
366
|
+
return new Uint16Array(buffer);
|
|
367
|
+
case 'Uint32Array':
|
|
368
|
+
return new Uint32Array(buffer);
|
|
369
|
+
case 'Uint8Array':
|
|
370
|
+
return new Uint8Array(buffer);
|
|
371
|
+
case 'Uint8ClampedArray':
|
|
372
|
+
return new Uint8ClampedArray(buffer);
|
|
373
|
+
default:
|
|
374
|
+
return new Uint8Array(buffer);
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
const hydrateScalarTaggedValue = (entry) => {
|
|
378
|
+
const hydrateSymbol = (value) => {
|
|
379
|
+
if (!value.startsWith('Symbol(') || !value.endsWith(')')) {
|
|
380
|
+
return Symbol(value);
|
|
381
|
+
}
|
|
382
|
+
const description = value.slice('Symbol('.length, -1);
|
|
383
|
+
return description.length > 0 ? Symbol(description) : Symbol();
|
|
384
|
+
};
|
|
385
|
+
const hydrateFunction = (name) => {
|
|
386
|
+
const placeholder = () => undefined;
|
|
387
|
+
Object.defineProperty(placeholder, 'name', {
|
|
388
|
+
configurable: true,
|
|
389
|
+
value: name,
|
|
390
|
+
});
|
|
391
|
+
return placeholder;
|
|
392
|
+
};
|
|
393
|
+
switch (entry.__t) {
|
|
394
|
+
case 'Undefined':
|
|
395
|
+
return undefined;
|
|
396
|
+
case 'Number':
|
|
397
|
+
return typeof entry.value === 'string' ? hydrateTaggedNumber(entry.value) : HYDRATION_MISS;
|
|
398
|
+
case 'BigInt':
|
|
399
|
+
return typeof entry.value === 'string' ? BigInt(entry.value) : HYDRATION_MISS;
|
|
400
|
+
case 'Symbol':
|
|
401
|
+
return typeof entry.value === 'string' ? hydrateSymbol(entry.value) : HYDRATION_MISS;
|
|
402
|
+
case 'Function':
|
|
403
|
+
return typeof entry.name === 'string' ? hydrateFunction(entry.name) : HYDRATION_MISS;
|
|
404
|
+
case 'Date':
|
|
405
|
+
return typeof entry.iso === 'string' ? new Date(entry.iso) : HYDRATION_MISS;
|
|
406
|
+
case 'URL':
|
|
407
|
+
return typeof entry.href === 'string' ? new URL(entry.href) : HYDRATION_MISS;
|
|
408
|
+
case 'Error':
|
|
409
|
+
return new Error(typeof entry.message === 'string' ? entry.message : '');
|
|
410
|
+
default:
|
|
411
|
+
return HYDRATION_MISS;
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
const hydrateBinaryTaggedValue = (entry) => {
|
|
415
|
+
if (entry.__t === 'ArrayBuffer' && typeof entry.base64 === 'string') {
|
|
416
|
+
return hydrateArrayBuffer(entry.base64);
|
|
417
|
+
}
|
|
418
|
+
if (entry.__t === 'DataView' && typeof entry.base64 === 'string') {
|
|
419
|
+
return hydrateTypedArray('DataView', entry.base64);
|
|
420
|
+
}
|
|
421
|
+
if (typeof entry.__t === 'string' && typeof entry.base64 === 'string' && entry.__t.endsWith('Array')) {
|
|
422
|
+
return hydrateTypedArray(entry.__t, entry.base64);
|
|
423
|
+
}
|
|
424
|
+
return HYDRATION_MISS;
|
|
425
|
+
};
|
|
426
|
+
const hydrateSimpleTaggedValue = (entry) => {
|
|
427
|
+
const scalarValue = hydrateScalarTaggedValue(entry);
|
|
428
|
+
if (scalarValue !== HYDRATION_MISS) {
|
|
429
|
+
return scalarValue;
|
|
430
|
+
}
|
|
431
|
+
return hydrateBinaryTaggedValue(entry);
|
|
432
|
+
};
|
|
433
|
+
const hydrateTrackedArray = (entry, state) => {
|
|
434
|
+
if (entry.__t !== 'Array' || !Array.isArray(entry.items)) {
|
|
435
|
+
return HYDRATION_MISS;
|
|
436
|
+
}
|
|
437
|
+
const hydratedArray = [];
|
|
438
|
+
if (typeof entry.__id === 'number') {
|
|
439
|
+
state.refs.set(entry.__id, hydratedArray);
|
|
440
|
+
}
|
|
441
|
+
hydratedArray.push(...entry.items.map((item) => hydrateCanonicalized(item, state)));
|
|
442
|
+
return hydratedArray;
|
|
443
|
+
};
|
|
444
|
+
const hydrateTrackedMap = (entry, state) => {
|
|
445
|
+
if (entry.__t !== 'Map' || !Array.isArray(entry.entries)) {
|
|
446
|
+
return HYDRATION_MISS;
|
|
447
|
+
}
|
|
448
|
+
const hydratedMap = new Map();
|
|
449
|
+
if (typeof entry.__id === 'number') {
|
|
450
|
+
state.refs.set(entry.__id, hydratedMap);
|
|
451
|
+
}
|
|
452
|
+
for (const item of entry.entries) {
|
|
453
|
+
if (Array.isArray(item) && item.length === 2) {
|
|
454
|
+
hydratedMap.set(hydrateCanonicalized(item[0], state), hydrateCanonicalized(item[1], state));
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return hydratedMap;
|
|
458
|
+
};
|
|
459
|
+
const hydrateTrackedSet = (entry, state) => {
|
|
460
|
+
if (entry.__t !== 'Set' || !Array.isArray(entry.values)) {
|
|
461
|
+
return HYDRATION_MISS;
|
|
462
|
+
}
|
|
463
|
+
const hydratedSet = new Set();
|
|
464
|
+
if (typeof entry.__id === 'number') {
|
|
465
|
+
state.refs.set(entry.__id, hydratedSet);
|
|
466
|
+
}
|
|
467
|
+
for (const item of entry.values) {
|
|
468
|
+
hydratedSet.add(hydrateCanonicalized(item, state));
|
|
469
|
+
}
|
|
470
|
+
return hydratedSet;
|
|
471
|
+
};
|
|
472
|
+
const hydrateTrackedObject = (entry, state) => {
|
|
473
|
+
if ((entry.__t !== 'Object' && entry.__t !== 'Instance') || !entry.props || typeof entry.props !== 'object') {
|
|
474
|
+
return HYDRATION_MISS;
|
|
475
|
+
}
|
|
476
|
+
const target = entry.__t === 'Instance' && typeof entry.ctor === 'string'
|
|
477
|
+
? Object.create({ constructor: { name: entry.ctor } })
|
|
478
|
+
: {};
|
|
479
|
+
if (typeof entry.__id === 'number') {
|
|
480
|
+
state.refs.set(entry.__id, target);
|
|
481
|
+
}
|
|
482
|
+
for (const [key, item] of Object.entries(asObject(entry.props))) {
|
|
483
|
+
target[key] = hydrateCanonicalized(item, state);
|
|
484
|
+
}
|
|
485
|
+
return target;
|
|
486
|
+
};
|
|
487
|
+
const hydrateTrackedValue = (entry, state) => {
|
|
488
|
+
const trackedArray = hydrateTrackedArray(entry, state);
|
|
489
|
+
if (trackedArray !== HYDRATION_MISS) {
|
|
490
|
+
return trackedArray;
|
|
491
|
+
}
|
|
492
|
+
const trackedMap = hydrateTrackedMap(entry, state);
|
|
493
|
+
if (trackedMap !== HYDRATION_MISS) {
|
|
494
|
+
return trackedMap;
|
|
495
|
+
}
|
|
496
|
+
const trackedSet = hydrateTrackedSet(entry, state);
|
|
497
|
+
if (trackedSet !== HYDRATION_MISS) {
|
|
498
|
+
return trackedSet;
|
|
499
|
+
}
|
|
500
|
+
return hydrateTrackedObject(entry, state);
|
|
501
|
+
};
|
|
502
|
+
/**
|
|
503
|
+
* Hydrates a canonicalized replay payload back into runtime values for replay.
|
|
504
|
+
*/
|
|
505
|
+
export const hydrateCanonicalized = (value, state = createHydrationState()) => {
|
|
506
|
+
if (Array.isArray(value)) {
|
|
507
|
+
return value.map((entry) => hydrateCanonicalized(entry, state));
|
|
508
|
+
}
|
|
509
|
+
if (!value || typeof value !== 'object') {
|
|
510
|
+
return value;
|
|
511
|
+
}
|
|
512
|
+
const entry = asObject(value);
|
|
513
|
+
if (typeof entry.$ref === 'number') {
|
|
514
|
+
return state.refs.get(entry.$ref);
|
|
515
|
+
}
|
|
516
|
+
const simpleHydratedValue = hydrateSimpleTaggedValue(entry);
|
|
517
|
+
if (simpleHydratedValue !== HYDRATION_MISS) {
|
|
518
|
+
return simpleHydratedValue;
|
|
519
|
+
}
|
|
520
|
+
const trackedHydratedValue = hydrateTrackedValue(entry, state);
|
|
521
|
+
if (trackedHydratedValue !== HYDRATION_MISS) {
|
|
522
|
+
return trackedHydratedValue;
|
|
523
|
+
}
|
|
524
|
+
return Object.fromEntries(Object.entries(entry).map(([key, item]) => [key, hydrateCanonicalized(item, state)]));
|
|
525
|
+
};
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AA6BA,eAAO,MAAM,gBAAgB,GAAU,QAAQ,MAAM,OAAO,CAAC,MAAM,CAAC,KAAG,OAAO,CAAC,MAAM,CAuBpF,CAAC;AAkRF,eAAO,MAAM,IAAI,GAAU,OAAM,SAAS,MAAM,EAA0B,KAAG,OAAO,CAAC,MAAM,CAc1F,CAAC"}
|