@smithers-orchestrator/devtools 0.16.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/LICENSE +21 -0
- package/package.json +31 -0
- package/src/DevToolsDelta.ts +8 -0
- package/src/DevToolsDeltaOp.ts +8 -0
- package/src/DevToolsEngineEvent.ts +98 -0
- package/src/DevToolsEventBus.ts +9 -0
- package/src/DevToolsEventHandler.ts +6 -0
- package/src/DevToolsNode.ts +23 -0
- package/src/DevToolsRunStore.js +232 -0
- package/src/DevToolsRunStoreOptions.ts +6 -0
- package/src/DevToolsSnapshot.ts +48 -0
- package/src/DevToolsSnapshotV1.ts +9 -0
- package/src/InvalidDeltaError.js +11 -0
- package/src/RunExecutionState.ts +18 -0
- package/src/SMITHERS_NODE_ICONS.js +21 -0
- package/src/SNAPSHOT_SERIALIZER_DEFAULT_MAX_DEPTH.js +2 -0
- package/src/SmithersDevToolsCore.js +146 -0
- package/src/SmithersDevToolsOptions.ts +11 -0
- package/src/SmithersNodeType.ts +17 -0
- package/src/SnapshotSerializerOptions.ts +7 -0
- package/src/SnapshotSerializerWarning.ts +9 -0
- package/src/TaskExecutionState.ts +21 -0
- package/src/applyDelta.js +115 -0
- package/src/buildSnapshot.js +15 -0
- package/src/collectTasks.js +15 -0
- package/src/countNodes.js +16 -0
- package/src/diffSnapshots.js +211 -0
- package/src/findNodeById.js +17 -0
- package/src/index.d.ts +550 -0
- package/src/index.js +30 -0
- package/src/printTree.js +35 -0
- package/src/snapshotSerializer.js +143 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/** @typedef {import("./DevToolsNode.ts").DevToolsNode} DevToolsNode */
|
|
2
|
+
/** @typedef {import("./SnapshotSerializerOptions.ts").SnapshotSerializerOptions} SnapshotSerializerOptions */
|
|
3
|
+
/** @typedef {import("./SnapshotSerializerWarning.ts").SnapshotSerializerWarning} SnapshotSerializerWarning */
|
|
4
|
+
|
|
5
|
+
import { SNAPSHOT_SERIALIZER_DEFAULT_MAX_DEPTH } from "./SNAPSHOT_SERIALIZER_DEFAULT_MAX_DEPTH.js";
|
|
6
|
+
|
|
7
|
+
const SNAPSHOT_SERIALIZER_DEFAULT_MAX_ENTRIES = 100_000;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {unknown} value
|
|
11
|
+
* @returns {value is Record<string, unknown>}
|
|
12
|
+
*/
|
|
13
|
+
function isPlainObject(value) {
|
|
14
|
+
if (!value || typeof value !== "object") {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const proto = Object.getPrototypeOf(value);
|
|
18
|
+
return proto === Object.prototype || proto === null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {SnapshotSerializerWarning["code"]} code
|
|
23
|
+
* @param {string} path
|
|
24
|
+
* @param {SnapshotSerializerOptions["onWarning"]} onWarning
|
|
25
|
+
* @param {string} [detail]
|
|
26
|
+
*/
|
|
27
|
+
function warn(code, path, onWarning, detail) {
|
|
28
|
+
if (!onWarning) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
onWarning({
|
|
32
|
+
code,
|
|
33
|
+
path,
|
|
34
|
+
...(detail ? { detail } : {}),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {unknown} value
|
|
40
|
+
* @param {{
|
|
41
|
+
* maxDepth: number;
|
|
42
|
+
* maxEntries: number;
|
|
43
|
+
* onWarning?: (warning: SnapshotSerializerWarning) => void;
|
|
44
|
+
* seen: WeakSet<object>;
|
|
45
|
+
* traversed: number;
|
|
46
|
+
* }} state
|
|
47
|
+
* @param {number} depth
|
|
48
|
+
* @param {string} path
|
|
49
|
+
* @returns {unknown}
|
|
50
|
+
*/
|
|
51
|
+
function serializeInternal(value, state, depth, path) {
|
|
52
|
+
if (depth > state.maxDepth) {
|
|
53
|
+
warn("MaxDepthExceeded", path, state.onWarning);
|
|
54
|
+
return "[MaxDepth]";
|
|
55
|
+
}
|
|
56
|
+
if (value === null || value === undefined) {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
const valueType = typeof value;
|
|
60
|
+
if (valueType === "string" || valueType === "number" || valueType === "boolean") {
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
if (valueType === "bigint") {
|
|
64
|
+
return `[BigInt: ${value.toString()}]`;
|
|
65
|
+
}
|
|
66
|
+
if (valueType === "function") {
|
|
67
|
+
return "[Function]";
|
|
68
|
+
}
|
|
69
|
+
if (valueType === "symbol") {
|
|
70
|
+
return value.description
|
|
71
|
+
? `[Symbol: ${value.description}]`
|
|
72
|
+
: "[Symbol]";
|
|
73
|
+
}
|
|
74
|
+
if (value instanceof Date) {
|
|
75
|
+
return Number.isNaN(value.getTime())
|
|
76
|
+
? "[Date: Invalid]"
|
|
77
|
+
: `[Date: ${value.toISOString()}]`;
|
|
78
|
+
}
|
|
79
|
+
if (valueType !== "object") {
|
|
80
|
+
return String(value);
|
|
81
|
+
}
|
|
82
|
+
if (state.traversed >= state.maxEntries) {
|
|
83
|
+
warn("MaxEntriesExceeded", path, state.onWarning);
|
|
84
|
+
return "[MaxEntries]";
|
|
85
|
+
}
|
|
86
|
+
state.traversed += 1;
|
|
87
|
+
const objectValue = /** @type {object} */ (value);
|
|
88
|
+
if (state.seen.has(objectValue)) {
|
|
89
|
+
warn("CircularReference", path, state.onWarning);
|
|
90
|
+
return "[Circular]";
|
|
91
|
+
}
|
|
92
|
+
state.seen.add(objectValue);
|
|
93
|
+
try {
|
|
94
|
+
if (Array.isArray(value)) {
|
|
95
|
+
return value.map((entry, index) => serializeInternal(entry, state, depth + 1, `${path}[${index}]`));
|
|
96
|
+
}
|
|
97
|
+
if (!isPlainObject(value)) {
|
|
98
|
+
const ctorName = value.constructor?.name;
|
|
99
|
+
if (ctorName && ctorName !== "Object") {
|
|
100
|
+
warn("UnsupportedType", path, state.onWarning, ctorName);
|
|
101
|
+
return `[${ctorName}]`;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** @type {Record<string, unknown>} */
|
|
105
|
+
const out = {};
|
|
106
|
+
for (const key of Object.keys(/** @type {Record<string, unknown>} */ (value)).sort()) {
|
|
107
|
+
try {
|
|
108
|
+
out[key] = serializeInternal(/** @type {Record<string, unknown>} */ (value)[key], state, depth + 1, `${path}.${key}`);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
warn("UnsupportedType", `${path}.${key}`, state.onWarning, "ThrownDuringRead");
|
|
112
|
+
out[key] = "[Unserializable]";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
state.seen.delete(objectValue);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Serialize arbitrary values into a stable JSON-safe shape for devtools snapshots.
|
|
124
|
+
*
|
|
125
|
+
* @param {unknown} value
|
|
126
|
+
* @param {SnapshotSerializerOptions} [options]
|
|
127
|
+
* @returns {unknown}
|
|
128
|
+
*/
|
|
129
|
+
export function snapshotSerialize(value, options = {}) {
|
|
130
|
+
const maxDepth = Number.isFinite(options.maxDepth)
|
|
131
|
+
? Math.max(0, Math.floor(options.maxDepth))
|
|
132
|
+
: SNAPSHOT_SERIALIZER_DEFAULT_MAX_DEPTH;
|
|
133
|
+
const maxEntries = Number.isFinite(options.maxEntries)
|
|
134
|
+
? Math.max(1, Math.floor(options.maxEntries))
|
|
135
|
+
: SNAPSHOT_SERIALIZER_DEFAULT_MAX_ENTRIES;
|
|
136
|
+
return serializeInternal(value, {
|
|
137
|
+
maxDepth,
|
|
138
|
+
maxEntries,
|
|
139
|
+
onWarning: options.onWarning,
|
|
140
|
+
seen: new WeakSet(),
|
|
141
|
+
traversed: 0,
|
|
142
|
+
}, 0, "$");
|
|
143
|
+
}
|