ushman-characterize 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 +110 -0
- package/CHANGELOG.md +41 -0
- package/LICENSE.md +21 -0
- package/README.md +193 -0
- package/bin/ushman-characterize +19 -0
- package/dist/babel-config.d.ts +7 -0
- package/dist/babel-config.d.ts.map +1 -0
- package/dist/babel-config.js +17 -0
- package/dist/capture-server.d.ts +31 -0
- package/dist/capture-server.d.ts.map +1 -0
- package/dist/capture-server.js +199 -0
- package/dist/capture.d.ts +97 -0
- package/dist/capture.d.ts.map +1 -0
- package/dist/capture.js +620 -0
- package/dist/cli/logger.d.ts +7 -0
- package/dist/cli/logger.d.ts.map +1 -0
- package/dist/cli/logger.js +14 -0
- package/dist/cli/parse-flags.d.ts +8 -0
- package/dist/cli/parse-flags.d.ts.map +1 -0
- package/dist/cli/parse-flags.js +60 -0
- package/dist/cli.d.ts +39 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +439 -0
- package/dist/constants.d.ts +20 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +19 -0
- package/dist/dedupe-contract.d.ts +26 -0
- package/dist/dedupe-contract.d.ts.map +1 -0
- package/dist/dedupe-contract.js +12 -0
- package/dist/default-export.d.ts +6 -0
- package/dist/default-export.d.ts.map +1 -0
- package/dist/default-export.js +52 -0
- package/dist/format-contract.d.ts +25 -0
- package/dist/format-contract.d.ts.map +1 -0
- package/dist/format-contract.js +96 -0
- package/dist/function-utils.d.ts +6 -0
- package/dist/function-utils.d.ts.map +1 -0
- package/dist/function-utils.js +22 -0
- package/dist/generate-replay.d.ts +18 -0
- package/dist/generate-replay.d.ts.map +1 -0
- package/dist/generate-replay.js +158 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/instrument.d.ts +39 -0
- package/dist/instrument.d.ts.map +1 -0
- package/dist/instrument.js +605 -0
- package/dist/ledger.d.ts +19 -0
- package/dist/ledger.d.ts.map +1 -0
- package/dist/ledger.js +50 -0
- package/dist/puppeteer-harness.d.ts +74 -0
- package/dist/puppeteer-harness.d.ts.map +1 -0
- package/dist/puppeteer-harness.js +248 -0
- package/dist/purity-classifier.d.ts +28 -0
- package/dist/purity-classifier.d.ts.map +1 -0
- package/dist/purity-classifier.js +363 -0
- package/dist/rebind.d.ts +26 -0
- package/dist/rebind.d.ts.map +1 -0
- package/dist/rebind.js +356 -0
- package/dist/replay-report.d.ts +18 -0
- package/dist/replay-report.d.ts.map +1 -0
- package/dist/replay-report.js +12 -0
- package/dist/scene.d.ts +24 -0
- package/dist/scene.d.ts.map +1 -0
- package/dist/scene.js +235 -0
- package/dist/schema-types.d.ts +40 -0
- package/dist/schema-types.d.ts.map +1 -0
- package/dist/schema-types.js +32 -0
- package/dist/seed-scaffolds.d.ts +31 -0
- package/dist/seed-scaffolds.d.ts.map +1 -0
- package/dist/seed-scaffolds.js +96 -0
- package/dist/shared.d.ts +36 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +390 -0
- package/dist/state-dag.d.ts +5 -0
- package/dist/state-dag.d.ts.map +1 -0
- package/dist/state-dag.js +27 -0
- package/dist/stub-pure.d.ts +57 -0
- package/dist/stub-pure.d.ts.map +1 -0
- package/dist/stub-pure.js +987 -0
- package/dist/time.d.ts +3 -0
- package/dist/time.d.ts.map +1 -0
- package/dist/time.js +10 -0
- package/dist/trace-format.d.ts +24 -0
- package/dist/trace-format.d.ts.map +1 -0
- package/dist/trace-format.js +213 -0
- package/dist/trace-serializer.d.ts +94 -0
- package/dist/trace-serializer.d.ts.map +1 -0
- package/dist/trace-serializer.js +607 -0
- package/dist/tracer-runtime.d.ts +25 -0
- package/dist/tracer-runtime.d.ts.map +1 -0
- package/dist/tracer-runtime.js +291 -0
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +0 -0
- package/dist/workspace-paths.d.ts +64 -0
- package/dist/workspace-paths.d.ts.map +1 -0
- package/dist/workspace-paths.js +288 -0
- package/package.json +86 -0
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
import { DEFAULT_TRACE_DEPTH_LIMIT, EXEMPT_FIELD_MARKER, FLOAT_PRECISION_SCALE } from "./constants.js";
|
|
2
|
+
const HASH_OFFSET_BASIS = 2_166_136_261;
|
|
3
|
+
const HASH_PRIME = 16_777_619;
|
|
4
|
+
const MAX_COLLECTION_ITEMS = 32;
|
|
5
|
+
const MAX_TYPED_ARRAY_BYTES = 1_024;
|
|
6
|
+
const MAX_OBJECT_PATH_SEGMENTS = 16;
|
|
7
|
+
const asSerializableObject = (value) => value;
|
|
8
|
+
export const roundTraceNumber = (value) => Math.round(value * FLOAT_PRECISION_SCALE) / FLOAT_PRECISION_SCALE;
|
|
9
|
+
export const canonicalizeNumericValue = (value) => {
|
|
10
|
+
if (Number.isNaN(value)) {
|
|
11
|
+
return { __t: 'Number', value: 'NaN' };
|
|
12
|
+
}
|
|
13
|
+
if (value === Number.POSITIVE_INFINITY) {
|
|
14
|
+
return { __t: 'Number', value: 'Infinity' };
|
|
15
|
+
}
|
|
16
|
+
if (value === Number.NEGATIVE_INFINITY) {
|
|
17
|
+
return { __t: 'Number', value: '-Infinity' };
|
|
18
|
+
}
|
|
19
|
+
if (Object.is(value, -0)) {
|
|
20
|
+
return { __t: 'Number', value: '-0' };
|
|
21
|
+
}
|
|
22
|
+
return roundTraceNumber(value);
|
|
23
|
+
};
|
|
24
|
+
export const isPlainObject = (value) => {
|
|
25
|
+
if (!value || typeof value !== 'object') {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const prototype = Object.getPrototypeOf(value);
|
|
29
|
+
return prototype === Object.prototype || prototype === null;
|
|
30
|
+
};
|
|
31
|
+
export const buildStableHash = (value) => {
|
|
32
|
+
let hash = HASH_OFFSET_BASIS;
|
|
33
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
34
|
+
hash ^= value.charCodeAt(index);
|
|
35
|
+
hash = Math.imul(hash, HASH_PRIME);
|
|
36
|
+
}
|
|
37
|
+
return (hash >>> 0).toString(16).padStart(8, '0');
|
|
38
|
+
};
|
|
39
|
+
const normalizePathSegment = (value) => {
|
|
40
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
};
|
|
45
|
+
const buildObjectPath = (value) => {
|
|
46
|
+
const segments = [];
|
|
47
|
+
let cursor = value;
|
|
48
|
+
while (cursor && segments.length < MAX_OBJECT_PATH_SEGMENTS) {
|
|
49
|
+
const label = normalizePathSegment(cursor.name) ??
|
|
50
|
+
normalizePathSegment(cursor.type) ??
|
|
51
|
+
normalizePathSegment(cursor.uuid) ??
|
|
52
|
+
cursor.constructor?.name;
|
|
53
|
+
if (label) {
|
|
54
|
+
segments.unshift(label);
|
|
55
|
+
}
|
|
56
|
+
cursor = isPlainObject(cursor.parent) ? cursor.parent : null;
|
|
57
|
+
}
|
|
58
|
+
return segments.join('/');
|
|
59
|
+
};
|
|
60
|
+
const sampleArrayLikeValues = (value) => {
|
|
61
|
+
if (ArrayBuffer.isView(value)) {
|
|
62
|
+
return Array.from(new Uint8Array(value.buffer, value.byteOffset, Math.min(value.byteLength, MAX_TYPED_ARRAY_BYTES))).map((item) => canonicalizeNumericValue(item));
|
|
63
|
+
}
|
|
64
|
+
if (Array.isArray(value)) {
|
|
65
|
+
return value
|
|
66
|
+
.slice(0, MAX_TYPED_ARRAY_BYTES)
|
|
67
|
+
.map((item) => (typeof item === 'number' ? canonicalizeNumericValue(item) : String(item)));
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
};
|
|
71
|
+
const describeGeometryAttribute = (value) => {
|
|
72
|
+
if (!isPlainObject(value)) {
|
|
73
|
+
return { type: 'unknown' };
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
arraySample: sampleArrayLikeValues(value.array),
|
|
77
|
+
arrayType: value.array?.constructor?.name ?? null,
|
|
78
|
+
count: typeof value.count === 'number' ? value.count : null,
|
|
79
|
+
itemSize: typeof value.itemSize === 'number' ? value.itemSize : null,
|
|
80
|
+
normalized: typeof value.normalized === 'boolean' ? value.normalized : null,
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
const hashGeometryAttributes = (value) => {
|
|
84
|
+
const attributes = isPlainObject(value.attributes) ? value.attributes : {};
|
|
85
|
+
const describedAttributes = Object.fromEntries(Object.entries(attributes)
|
|
86
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
87
|
+
.map(([key, attribute]) => [key, describeGeometryAttribute(attribute)]));
|
|
88
|
+
const indexDescription = describeGeometryAttribute(value.index);
|
|
89
|
+
return buildStableHash(JSON.stringify({
|
|
90
|
+
attributes: describedAttributes,
|
|
91
|
+
index: indexDescription,
|
|
92
|
+
}));
|
|
93
|
+
};
|
|
94
|
+
const resolveHexString = (value) => {
|
|
95
|
+
if (typeof value.getHexString !== 'function') {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
return String(value.getHexString());
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const summarizeVectorLike = (value) => {
|
|
106
|
+
const ctor = typeof value.constructor?.name === 'string' ? value.constructor.name : 'Object';
|
|
107
|
+
if ((ctor !== 'Vector2' && ctor !== 'Vector3' && ctor !== 'Vector4') ||
|
|
108
|
+
typeof value.x !== 'number' ||
|
|
109
|
+
typeof value.y !== 'number') {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
__t: ctor,
|
|
114
|
+
...(typeof value.z === 'number' ? { z: canonicalizeNumericValue(value.z) } : {}),
|
|
115
|
+
...(typeof value.w === 'number' ? { w: canonicalizeNumericValue(value.w) } : {}),
|
|
116
|
+
x: canonicalizeNumericValue(value.x),
|
|
117
|
+
y: canonicalizeNumericValue(value.y),
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
const summarizeEulerLike = (value) => {
|
|
121
|
+
if (value.constructor?.name !== 'Euler' ||
|
|
122
|
+
typeof value.x !== 'number' ||
|
|
123
|
+
typeof value.y !== 'number' ||
|
|
124
|
+
typeof value.z !== 'number') {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
__t: 'Euler',
|
|
129
|
+
order: typeof value.order === 'string' ? value.order : null,
|
|
130
|
+
x: canonicalizeNumericValue(value.x),
|
|
131
|
+
y: canonicalizeNumericValue(value.y),
|
|
132
|
+
z: canonicalizeNumericValue(value.z),
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
const summarizeQuaternionLike = (value) => {
|
|
136
|
+
if (value.constructor?.name !== 'Quaternion' ||
|
|
137
|
+
typeof value.x !== 'number' ||
|
|
138
|
+
typeof value.y !== 'number' ||
|
|
139
|
+
typeof value.z !== 'number' ||
|
|
140
|
+
typeof value.w !== 'number') {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
__t: 'Quaternion',
|
|
145
|
+
w: canonicalizeNumericValue(value.w),
|
|
146
|
+
x: canonicalizeNumericValue(value.x),
|
|
147
|
+
y: canonicalizeNumericValue(value.y),
|
|
148
|
+
z: canonicalizeNumericValue(value.z),
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
const summarizeMatrix4Like = (value) => {
|
|
152
|
+
if (value.constructor?.name !== 'Matrix4' || !Array.isArray(value.elements)) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
__t: 'Matrix4',
|
|
157
|
+
elements: value.elements.map((entry) => (typeof entry === 'number' ? canonicalizeNumericValue(entry) : entry)),
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
const summarizeColorLike = (value) => {
|
|
161
|
+
const hex = resolveHexString(value);
|
|
162
|
+
if (hex === null) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
__t: value.constructor?.name === 'Color' ? 'Color' : String(value.constructor?.name ?? 'Color'),
|
|
167
|
+
hex,
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
const summarizeObject3DLike = (value) => {
|
|
171
|
+
if (typeof value.type !== 'string' || !Array.isArray(value.children)) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
__t: 'Object3D',
|
|
176
|
+
children: value.children.length,
|
|
177
|
+
name: typeof value.name === 'string' ? value.name : '',
|
|
178
|
+
path: buildObjectPath(value),
|
|
179
|
+
type: value.type,
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
const summarizeBufferGeometryLike = (value) => {
|
|
183
|
+
if (typeof value.type !== 'string' || !('attributes' in value)) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
const attributes = isPlainObject(value.attributes) ? Object.keys(value.attributes).sort() : [];
|
|
187
|
+
return {
|
|
188
|
+
__t: 'BufferGeometry',
|
|
189
|
+
attributeKeys: attributes,
|
|
190
|
+
hash: hashGeometryAttributes(value),
|
|
191
|
+
hasIndex: Boolean(value.index),
|
|
192
|
+
type: value.type,
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
const summarizeMaterialLike = (value) => {
|
|
196
|
+
if (typeof value.type !== 'string' || !('opacity' in value || 'transparent' in value)) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
const color = isPlainObject(value.color) ? resolveHexString(value.color) : null;
|
|
200
|
+
return {
|
|
201
|
+
__t: 'Material',
|
|
202
|
+
color,
|
|
203
|
+
opacity: typeof value.opacity === 'number' ? canonicalizeNumericValue(value.opacity) : null,
|
|
204
|
+
transparent: typeof value.transparent === 'boolean' ? value.transparent : null,
|
|
205
|
+
type: value.type,
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
const summarizeTextureLike = (value) => {
|
|
209
|
+
if (!('image' in value) || !('name' in value)) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
const image = isPlainObject(value.image)
|
|
213
|
+
? {
|
|
214
|
+
height: typeof value.image.height === 'number' ? value.image.height : null,
|
|
215
|
+
src: typeof value.image.src === 'string' ? value.image.src : null,
|
|
216
|
+
width: typeof value.image.width === 'number' ? value.image.width : null,
|
|
217
|
+
}
|
|
218
|
+
: null;
|
|
219
|
+
return {
|
|
220
|
+
__t: 'Texture',
|
|
221
|
+
image,
|
|
222
|
+
name: typeof value.name === 'string' ? value.name : '',
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
const THREE_LIKE_CANONICALIZERS = [
|
|
226
|
+
summarizeVectorLike,
|
|
227
|
+
summarizeEulerLike,
|
|
228
|
+
summarizeQuaternionLike,
|
|
229
|
+
summarizeMatrix4Like,
|
|
230
|
+
summarizeColorLike,
|
|
231
|
+
summarizeObject3DLike,
|
|
232
|
+
summarizeBufferGeometryLike,
|
|
233
|
+
summarizeMaterialLike,
|
|
234
|
+
summarizeTextureLike,
|
|
235
|
+
];
|
|
236
|
+
export const canonicalizeThreeLikeValue = (value) => {
|
|
237
|
+
for (const canonicalize of THREE_LIKE_CANONICALIZERS) {
|
|
238
|
+
const summary = canonicalize(value);
|
|
239
|
+
if (summary !== null) {
|
|
240
|
+
return summary;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
};
|
|
245
|
+
const canonicalizePrimitiveValue = (value) => {
|
|
246
|
+
if (value === null) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
if (value === undefined) {
|
|
250
|
+
return { __t: 'Undefined' };
|
|
251
|
+
}
|
|
252
|
+
if (typeof value === 'number') {
|
|
253
|
+
return canonicalizeNumericValue(value);
|
|
254
|
+
}
|
|
255
|
+
if (typeof value === 'string' || typeof value === 'boolean') {
|
|
256
|
+
return value;
|
|
257
|
+
}
|
|
258
|
+
if (typeof value === 'bigint') {
|
|
259
|
+
return { __t: 'BigInt', value: value.toString() };
|
|
260
|
+
}
|
|
261
|
+
if (typeof value === 'symbol') {
|
|
262
|
+
return { __t: 'Symbol', value: String(value) };
|
|
263
|
+
}
|
|
264
|
+
if (typeof value === 'function') {
|
|
265
|
+
return { __t: 'Function', name: value.name || 'anonymous' };
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
};
|
|
269
|
+
const canonicalizeSimpleObject = (value) => {
|
|
270
|
+
if (value instanceof Error) {
|
|
271
|
+
return { __t: 'Error', message: value.message, name: value.name };
|
|
272
|
+
}
|
|
273
|
+
if (value instanceof Date) {
|
|
274
|
+
return { __t: 'Date', iso: value.toISOString() };
|
|
275
|
+
}
|
|
276
|
+
if (value instanceof URL) {
|
|
277
|
+
return { __t: 'URL', href: value.href };
|
|
278
|
+
}
|
|
279
|
+
if (value instanceof ArrayBuffer) {
|
|
280
|
+
return { __t: 'ArrayBuffer', byteLength: value.byteLength };
|
|
281
|
+
}
|
|
282
|
+
if (ArrayBuffer.isView(value)) {
|
|
283
|
+
return {
|
|
284
|
+
__t: value.constructor.name,
|
|
285
|
+
byteLength: value.byteLength,
|
|
286
|
+
values: sampleArrayLikeValues(value),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
return null;
|
|
290
|
+
};
|
|
291
|
+
const createTracker = (state, type) => {
|
|
292
|
+
const reference = {
|
|
293
|
+
__id: state.nextId,
|
|
294
|
+
__t: type,
|
|
295
|
+
};
|
|
296
|
+
const tracker = {
|
|
297
|
+
id: state.nextId,
|
|
298
|
+
reference,
|
|
299
|
+
};
|
|
300
|
+
state.nextId += 1;
|
|
301
|
+
return tracker;
|
|
302
|
+
};
|
|
303
|
+
const trackReference = (entry, state, type) => {
|
|
304
|
+
const tracker = createTracker(state, type);
|
|
305
|
+
state.seen.set(entry, tracker);
|
|
306
|
+
return tracker.reference;
|
|
307
|
+
};
|
|
308
|
+
const createTraceReference = ({ ctorName, value, }) => Array.isArray(value)
|
|
309
|
+
? 'Array'
|
|
310
|
+
: ctorName === 'Object'
|
|
311
|
+
? 'Object'
|
|
312
|
+
: ctorName === 'Map'
|
|
313
|
+
? 'Map'
|
|
314
|
+
: ctorName === 'Set'
|
|
315
|
+
? 'Set'
|
|
316
|
+
: 'Instance';
|
|
317
|
+
const canonicalizeTrackedCollection = ({ depth, state, value, }) => {
|
|
318
|
+
const seenTracker = state.seen.get(value);
|
|
319
|
+
if (seenTracker) {
|
|
320
|
+
return { __ref: seenTracker.id };
|
|
321
|
+
}
|
|
322
|
+
if (Array.isArray(value)) {
|
|
323
|
+
const reference = trackReference(value, state, 'Array');
|
|
324
|
+
reference.items = value.map((entry) => visitTraceValue(entry, depth + 1, state));
|
|
325
|
+
return reference;
|
|
326
|
+
}
|
|
327
|
+
if (value instanceof Map) {
|
|
328
|
+
const reference = trackReference(value, state, 'Map');
|
|
329
|
+
reference.entries = [...value.entries()]
|
|
330
|
+
.slice(0, MAX_COLLECTION_ITEMS)
|
|
331
|
+
.map(([key, entry]) => [visitTraceValue(key, depth + 1, state), visitTraceValue(entry, depth + 1, state)]);
|
|
332
|
+
return reference;
|
|
333
|
+
}
|
|
334
|
+
if (value instanceof Set) {
|
|
335
|
+
const reference = trackReference(value, state, 'Set');
|
|
336
|
+
reference.values = [...value.values()]
|
|
337
|
+
.slice(0, MAX_COLLECTION_ITEMS)
|
|
338
|
+
.map((entry) => visitTraceValue(entry, depth + 1, state));
|
|
339
|
+
return reference;
|
|
340
|
+
}
|
|
341
|
+
const ctorName = typeof value.constructor?.name === 'string' ? value.constructor.name : 'Object';
|
|
342
|
+
const reference = trackReference(value, state, createTraceReference({ ctorName, value }));
|
|
343
|
+
const props = Object.fromEntries(Object.keys(asSerializableObject(value))
|
|
344
|
+
.filter((key) => key !== 'uuid')
|
|
345
|
+
.sort((left, right) => left.localeCompare(right))
|
|
346
|
+
.map((key) => [key, visitTraceValue(asSerializableObject(value)[key], depth + 1, state)]));
|
|
347
|
+
reference.props = props;
|
|
348
|
+
if (ctorName !== 'Object') {
|
|
349
|
+
reference.ctor = ctorName;
|
|
350
|
+
}
|
|
351
|
+
return reference;
|
|
352
|
+
};
|
|
353
|
+
const visitTraceValue = (value, depth, state) => {
|
|
354
|
+
const primitive = canonicalizePrimitiveValue(value);
|
|
355
|
+
if (primitive !== null) {
|
|
356
|
+
return primitive;
|
|
357
|
+
}
|
|
358
|
+
if (depth > state.depthLimit) {
|
|
359
|
+
return { __t: 'DepthLimit' };
|
|
360
|
+
}
|
|
361
|
+
if (!(value instanceof Object)) {
|
|
362
|
+
return String(value);
|
|
363
|
+
}
|
|
364
|
+
const simple = canonicalizeSimpleObject(value);
|
|
365
|
+
if (simple !== null) {
|
|
366
|
+
return simple;
|
|
367
|
+
}
|
|
368
|
+
const threeLike = canonicalizeThreeLikeValue(asSerializableObject(value));
|
|
369
|
+
if (threeLike !== null) {
|
|
370
|
+
return threeLike;
|
|
371
|
+
}
|
|
372
|
+
const maybeElement = asSerializableObject(value);
|
|
373
|
+
if (typeof maybeElement.nodeType === 'number' && 'tagName' in maybeElement) {
|
|
374
|
+
return {
|
|
375
|
+
__t: 'Element',
|
|
376
|
+
className: typeof maybeElement.className === 'string' ? maybeElement.className : '',
|
|
377
|
+
id: typeof maybeElement.id === 'string' ? maybeElement.id : '',
|
|
378
|
+
tagName: typeof maybeElement.tagName === 'string' ? maybeElement.tagName : '',
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
return canonicalizeTrackedCollection({
|
|
382
|
+
depth,
|
|
383
|
+
state,
|
|
384
|
+
value,
|
|
385
|
+
});
|
|
386
|
+
};
|
|
387
|
+
export const canonicalizeTraceValue = (value, options = {}) => visitTraceValue(value, 0, {
|
|
388
|
+
depthLimit: options.depthLimit ?? DEFAULT_TRACE_DEPTH_LIMIT,
|
|
389
|
+
nextId: 1,
|
|
390
|
+
seen: new WeakMap(),
|
|
391
|
+
});
|
|
392
|
+
export const canonicalizeThrownValue = (value) => canonicalizeTraceValue(value);
|
|
393
|
+
const createHydrationState = () => ({
|
|
394
|
+
refs: new Map(),
|
|
395
|
+
});
|
|
396
|
+
const hydrateSequence = ({ state, value, }) => value.map((entry) => hydrateTraceValue(entry, state));
|
|
397
|
+
const hydrateEntries = (value, state) => Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, hydrateTraceValue(entry, state)]));
|
|
398
|
+
const hydrateReference = ({ state, value, }) => (typeof value.__ref === 'number' ? (state.refs.get(value.__ref) ?? undefined) : null);
|
|
399
|
+
const hydrateTrackedArray = ({ state, value, }) => {
|
|
400
|
+
if (value.__t !== 'Array' || !Array.isArray(value.items)) {
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
const array = [];
|
|
404
|
+
if (typeof value.__id === 'number') {
|
|
405
|
+
state.refs.set(value.__id, array);
|
|
406
|
+
}
|
|
407
|
+
array.push(...hydrateSequence({ state, value: value.items }));
|
|
408
|
+
return array;
|
|
409
|
+
};
|
|
410
|
+
const hydrateTrackedMap = ({ state, value, }) => {
|
|
411
|
+
if (value.__t !== 'Map' || !Array.isArray(value.entries)) {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
const map = new Map();
|
|
415
|
+
if (typeof value.__id === 'number') {
|
|
416
|
+
state.refs.set(value.__id, map);
|
|
417
|
+
}
|
|
418
|
+
for (const entry of value.entries) {
|
|
419
|
+
if (!Array.isArray(entry) || entry.length !== 2) {
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
map.set(hydrateTraceValue(entry[0], state), hydrateTraceValue(entry[1], state));
|
|
423
|
+
}
|
|
424
|
+
return map;
|
|
425
|
+
};
|
|
426
|
+
const hydrateTrackedSet = ({ state, value, }) => {
|
|
427
|
+
if (value.__t !== 'Set' || !Array.isArray(value.values)) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
const set = new Set();
|
|
431
|
+
if (typeof value.__id === 'number') {
|
|
432
|
+
state.refs.set(value.__id, set);
|
|
433
|
+
}
|
|
434
|
+
for (const entry of value.values) {
|
|
435
|
+
set.add(hydrateTraceValue(entry, state));
|
|
436
|
+
}
|
|
437
|
+
return set;
|
|
438
|
+
};
|
|
439
|
+
const hydrateTrackedObject = ({ state, value, }) => {
|
|
440
|
+
if ((value.__t !== 'Object' && value.__t !== 'Instance') || !isPlainObject(value.props)) {
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
const target = value.__t === 'Instance' && typeof value.ctor === 'string'
|
|
444
|
+
? Object.create({ constructor: { name: value.ctor } })
|
|
445
|
+
: {};
|
|
446
|
+
if (typeof value.__id === 'number') {
|
|
447
|
+
state.refs.set(value.__id, target);
|
|
448
|
+
}
|
|
449
|
+
Object.assign(target, hydrateEntries(value.props, state));
|
|
450
|
+
return target;
|
|
451
|
+
};
|
|
452
|
+
export const hydrateTraceValue = (value, state = createHydrationState()) => {
|
|
453
|
+
if (Array.isArray(value)) {
|
|
454
|
+
return hydrateSequence({ state, value });
|
|
455
|
+
}
|
|
456
|
+
if (!value || typeof value !== 'object') {
|
|
457
|
+
return value;
|
|
458
|
+
}
|
|
459
|
+
const serialized = asSerializableObject(value);
|
|
460
|
+
if (serialized.__t === 'Undefined') {
|
|
461
|
+
return undefined;
|
|
462
|
+
}
|
|
463
|
+
const ref = hydrateReference({ state, value: serialized });
|
|
464
|
+
if (ref !== null) {
|
|
465
|
+
return ref;
|
|
466
|
+
}
|
|
467
|
+
const array = hydrateTrackedArray({ state, value: serialized });
|
|
468
|
+
if (array !== null) {
|
|
469
|
+
return array;
|
|
470
|
+
}
|
|
471
|
+
const map = hydrateTrackedMap({ state, value: serialized });
|
|
472
|
+
if (map !== null) {
|
|
473
|
+
return map;
|
|
474
|
+
}
|
|
475
|
+
const set = hydrateTrackedSet({ state, value: serialized });
|
|
476
|
+
if (set !== null) {
|
|
477
|
+
return set;
|
|
478
|
+
}
|
|
479
|
+
const object = hydrateTrackedObject({ state, value: serialized });
|
|
480
|
+
if (object !== null) {
|
|
481
|
+
return object;
|
|
482
|
+
}
|
|
483
|
+
return hydrateEntries(serialized, state);
|
|
484
|
+
};
|
|
485
|
+
const compareObjects = ({ actual, expected, mode, }) => {
|
|
486
|
+
const expectedKeys = Object.keys(expected).sort();
|
|
487
|
+
const actualKeys = Object.keys(actual).sort();
|
|
488
|
+
if (mode === 'strict' && expectedKeys.join('|') !== actualKeys.join('|')) {
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
return expectedKeys.every((key) => {
|
|
492
|
+
if (!(key in actual) && mode === 'lenient') {
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
495
|
+
return compareTraceValues(expected[key], actual[key], mode);
|
|
496
|
+
});
|
|
497
|
+
};
|
|
498
|
+
export const compareTraceValues = (expected, actual, mode = 'strict') => {
|
|
499
|
+
if (Array.isArray(expected) && Array.isArray(actual)) {
|
|
500
|
+
return (expected.length === actual.length &&
|
|
501
|
+
expected.every((entry, index) => compareTraceValues(entry, actual[index], mode)));
|
|
502
|
+
}
|
|
503
|
+
if (expected && actual && typeof expected === 'object' && typeof actual === 'object') {
|
|
504
|
+
return compareObjects({
|
|
505
|
+
actual: asSerializableObject(actual),
|
|
506
|
+
expected: asSerializableObject(expected),
|
|
507
|
+
mode,
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
return JSON.stringify(expected) === JSON.stringify(actual);
|
|
511
|
+
};
|
|
512
|
+
const visitSceneValue = (value, exempt, pathParts) => {
|
|
513
|
+
const pathKey = pathParts.join('.');
|
|
514
|
+
if (exempt.has(pathKey)) {
|
|
515
|
+
return EXEMPT_FIELD_MARKER;
|
|
516
|
+
}
|
|
517
|
+
if (value === null || value === undefined) {
|
|
518
|
+
return value;
|
|
519
|
+
}
|
|
520
|
+
if (typeof value === 'number') {
|
|
521
|
+
return roundTraceNumber(value);
|
|
522
|
+
}
|
|
523
|
+
if (typeof value === 'string' || typeof value === 'boolean') {
|
|
524
|
+
return value;
|
|
525
|
+
}
|
|
526
|
+
if (Array.isArray(value)) {
|
|
527
|
+
return value.map((entry, index) => visitSceneValue(entry, exempt, [...pathParts, String(index)]));
|
|
528
|
+
}
|
|
529
|
+
if (typeof value === 'object') {
|
|
530
|
+
return Object.fromEntries(Object.entries(asSerializableObject(value))
|
|
531
|
+
.filter(([key]) => key !== 'uuid')
|
|
532
|
+
.map(([key, entry]) => [key, visitSceneValue(entry, exempt, [...pathParts, key])]));
|
|
533
|
+
}
|
|
534
|
+
return String(value);
|
|
535
|
+
};
|
|
536
|
+
export const canonicalizeSceneTree = (value, options = {}) => visitSceneValue(value, new Set(options.exemptFields ?? []), []);
|
|
537
|
+
const TRACE_RUNTIME_FUNCTIONS = [
|
|
538
|
+
asSerializableObject,
|
|
539
|
+
roundTraceNumber,
|
|
540
|
+
canonicalizeNumericValue,
|
|
541
|
+
isPlainObject,
|
|
542
|
+
buildStableHash,
|
|
543
|
+
normalizePathSegment,
|
|
544
|
+
sampleArrayLikeValues,
|
|
545
|
+
describeGeometryAttribute,
|
|
546
|
+
hashGeometryAttributes,
|
|
547
|
+
resolveHexString,
|
|
548
|
+
summarizeVectorLike,
|
|
549
|
+
summarizeEulerLike,
|
|
550
|
+
summarizeQuaternionLike,
|
|
551
|
+
summarizeMatrix4Like,
|
|
552
|
+
summarizeColorLike,
|
|
553
|
+
buildObjectPath,
|
|
554
|
+
summarizeObject3DLike,
|
|
555
|
+
summarizeBufferGeometryLike,
|
|
556
|
+
summarizeMaterialLike,
|
|
557
|
+
summarizeTextureLike,
|
|
558
|
+
canonicalizeThreeLikeValue,
|
|
559
|
+
canonicalizePrimitiveValue,
|
|
560
|
+
canonicalizeSimpleObject,
|
|
561
|
+
createTracker,
|
|
562
|
+
trackReference,
|
|
563
|
+
createTraceReference,
|
|
564
|
+
canonicalizeTrackedCollection,
|
|
565
|
+
visitTraceValue,
|
|
566
|
+
canonicalizeTraceValue,
|
|
567
|
+
createHydrationState,
|
|
568
|
+
hydrateSequence,
|
|
569
|
+
hydrateEntries,
|
|
570
|
+
hydrateReference,
|
|
571
|
+
hydrateTrackedArray,
|
|
572
|
+
hydrateTrackedMap,
|
|
573
|
+
hydrateTrackedSet,
|
|
574
|
+
hydrateTrackedObject,
|
|
575
|
+
hydrateTraceValue,
|
|
576
|
+
compareObjects,
|
|
577
|
+
compareTraceValues,
|
|
578
|
+
visitSceneValue,
|
|
579
|
+
canonicalizeSceneTree,
|
|
580
|
+
];
|
|
581
|
+
export const buildTraceRuntimeSupportSource = () => [
|
|
582
|
+
`const FLOAT_PRECISION_SCALE = ${FLOAT_PRECISION_SCALE};`,
|
|
583
|
+
`const DEFAULT_TRACE_DEPTH_LIMIT = ${DEFAULT_TRACE_DEPTH_LIMIT};`,
|
|
584
|
+
`const EXEMPT_FIELD_MARKER = ${JSON.stringify(EXEMPT_FIELD_MARKER)};`,
|
|
585
|
+
`const HASH_OFFSET_BASIS = ${HASH_OFFSET_BASIS};`,
|
|
586
|
+
`const HASH_PRIME = ${HASH_PRIME};`,
|
|
587
|
+
`const MAX_COLLECTION_ITEMS = ${MAX_COLLECTION_ITEMS};`,
|
|
588
|
+
`const MAX_TYPED_ARRAY_BYTES = ${MAX_TYPED_ARRAY_BYTES};`,
|
|
589
|
+
`const MAX_OBJECT_PATH_SEGMENTS = ${MAX_OBJECT_PATH_SEGMENTS};`,
|
|
590
|
+
'const THREE_LIKE_CANONICALIZERS = [];',
|
|
591
|
+
...TRACE_RUNTIME_FUNCTIONS.map((fn) => {
|
|
592
|
+
const source = fn.toString();
|
|
593
|
+
return source.startsWith('function ') ? source : `const ${fn.name} = ${source};`;
|
|
594
|
+
}),
|
|
595
|
+
`THREE_LIKE_CANONICALIZERS.push(
|
|
596
|
+
summarizeVectorLike,
|
|
597
|
+
summarizeEulerLike,
|
|
598
|
+
summarizeQuaternionLike,
|
|
599
|
+
summarizeMatrix4Like,
|
|
600
|
+
summarizeColorLike,
|
|
601
|
+
summarizeObject3DLike,
|
|
602
|
+
summarizeBufferGeometryLike,
|
|
603
|
+
summarizeMaterialLike,
|
|
604
|
+
summarizeTextureLike,
|
|
605
|
+
);`,
|
|
606
|
+
].join('\n\n');
|
|
607
|
+
export { EXEMPT_FIELD_MARKER };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type TraceDrainPayload = {
|
|
2
|
+
readonly records: readonly {
|
|
3
|
+
readonly args: unknown;
|
|
4
|
+
readonly bindingName: string;
|
|
5
|
+
readonly className: null | string;
|
|
6
|
+
readonly count: number;
|
|
7
|
+
readonly expected: unknown;
|
|
8
|
+
readonly functionName: string;
|
|
9
|
+
readonly memberKind: 'function' | 'method';
|
|
10
|
+
readonly methodName: null | string;
|
|
11
|
+
readonly sideEffects: readonly unknown[];
|
|
12
|
+
readonly thisArg: unknown;
|
|
13
|
+
readonly threw: unknown;
|
|
14
|
+
}[];
|
|
15
|
+
readonly stats: {
|
|
16
|
+
readonly droppedUniqueCalls: number;
|
|
17
|
+
readonly rawCalls: number;
|
|
18
|
+
readonly uniqueCalls: number;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export declare const buildTracerRuntimeSource: ({ captureSideEffects, perFunctionShapeCap, }: {
|
|
22
|
+
readonly captureSideEffects: boolean;
|
|
23
|
+
readonly perFunctionShapeCap?: number;
|
|
24
|
+
}) => string;
|
|
25
|
+
//# sourceMappingURL=tracer-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracer-runtime.d.ts","sourceRoot":"","sources":["../src/tracer-runtime.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,iBAAiB,GAAG;IAC5B,QAAQ,CAAC,OAAO,EAAE,SAAS;QACvB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;QACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,CAAC;QAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,QAAQ,CAAC;QAC3C,QAAQ,CAAC,UAAU,EAAE,IAAI,GAAG,MAAM,CAAC;QACnC,QAAQ,CAAC,WAAW,EAAE,SAAS,OAAO,EAAE,CAAC;QACzC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;KAC3B,EAAE,CAAC;IACJ,QAAQ,CAAC,KAAK,EAAE;QACZ,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;QACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;KAChC,CAAC;CACL,CAAC;AAuGF,eAAO,MAAM,wBAAwB,GAAI,8CAGtC;IACC,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CACzC,WA4LK,CAAC"}
|