@spearwolf/shadow-objects 0.34.0 → 0.35.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/CHANGELOG.md +30 -0
- package/README.md +11 -2
- package/bundle.js +4 -2
- package/package.json +9 -1
- package/src/WorkerTimeoutError.d.ts +4 -3
- package/src/WorkerTimeoutError.d.ts.map +1 -1
- package/src/WorkerTimeoutError.js.map +2 -2
- package/src/constants.d.ts +8 -4
- package/src/constants.d.ts.map +1 -1
- package/src/constants.js +6 -0
- package/src/constants.js.map +2 -2
- package/src/elements/ShaePropElement.d.ts +15 -6
- package/src/elements/ShaePropElement.d.ts.map +1 -1
- package/src/elements/ShaePropElement.js +31 -7
- package/src/elements/ShaePropElement.js.map +2 -2
- package/src/elements/ShaeWorkerElement.d.ts +10 -0
- package/src/elements/ShaeWorkerElement.d.ts.map +1 -1
- package/src/elements/ShaeWorkerElement.js +69 -2
- package/src/elements/ShaeWorkerElement.js.map +2 -2
- package/src/elements/constants.d.ts +3 -0
- package/src/elements/constants.d.ts.map +1 -1
- package/src/elements/constants.js +6 -0
- package/src/elements/constants.js.map +2 -2
- package/src/in-the-dark/Entity.d.ts +30 -0
- package/src/in-the-dark/Entity.d.ts.map +1 -1
- package/src/in-the-dark/Entity.js +38 -0
- package/src/in-the-dark/Entity.js.map +2 -2
- package/src/in-the-dark/Kernel.d.ts +24 -1
- package/src/in-the-dark/Kernel.d.ts.map +1 -1
- package/src/in-the-dark/Kernel.js +56 -1
- package/src/in-the-dark/Kernel.js.map +2 -2
- package/src/in-the-dark/Registry.d.ts +7 -1
- package/src/in-the-dark/Registry.d.ts.map +1 -1
- package/src/in-the-dark/Registry.js +29 -0
- package/src/in-the-dark/Registry.js.map +2 -2
- package/src/in-the-dark/ShadowObjectCreationScope.d.ts +8 -1
- package/src/in-the-dark/ShadowObjectCreationScope.d.ts.map +1 -1
- package/src/in-the-dark/ShadowObjectCreationScope.js +23 -16
- package/src/in-the-dark/ShadowObjectCreationScope.js.map +2 -2
- package/src/in-the-dark/SignalsPath.d.ts +2 -0
- package/src/in-the-dark/SignalsPath.d.ts.map +1 -1
- package/src/in-the-dark/SignalsPath.js +4 -0
- package/src/in-the-dark/SignalsPath.js.map +2 -2
- package/src/in-the-dark/displayName.d.ts +4 -0
- package/src/in-the-dark/displayName.d.ts.map +1 -0
- package/src/in-the-dark/displayName.js +5 -0
- package/src/in-the-dark/displayName.js.map +7 -0
- package/src/index.d.ts +2 -0
- package/src/index.d.ts.map +1 -1
- package/src/index.js +4 -0
- package/src/index.js.map +2 -2
- package/src/inspect/createKernelSnapshot.d.ts +17 -0
- package/src/inspect/createKernelSnapshot.d.ts.map +1 -0
- package/src/inspect/createKernelSnapshot.js +247 -0
- package/src/inspect/createKernelSnapshot.js.map +7 -0
- package/src/inspect/createViewSnapshot.d.ts +10 -0
- package/src/inspect/createViewSnapshot.d.ts.map +1 -0
- package/src/inspect/createViewSnapshot.js +143 -0
- package/src/inspect/createViewSnapshot.js.map +7 -0
- package/src/inspect/normalizeInspectRequest.d.ts +32 -0
- package/src/inspect/normalizeInspectRequest.d.ts.map +1 -0
- package/src/inspect/normalizeInspectRequest.js +55 -0
- package/src/inspect/normalizeInspectRequest.js.map +7 -0
- package/src/inspect/serializeValue.d.ts +13 -0
- package/src/inspect/serializeValue.d.ts.map +1 -0
- package/src/inspect/serializeValue.js +149 -0
- package/src/inspect/serializeValue.js.map +7 -0
- package/src/inspect/types.d.ts +260 -0
- package/src/inspect/types.d.ts.map +1 -0
- package/src/inspect/types.js +1 -0
- package/src/inspect/types.js.map +7 -0
- package/src/model-context/ModelContextLike.d.ts +59 -0
- package/src/model-context/ModelContextLike.d.ts.map +1 -0
- package/src/model-context/ModelContextLike.js +15 -0
- package/src/model-context/ModelContextLike.js.map +7 -0
- package/src/model-context/exposeShadowEnvsToModelContext.d.ts +48 -0
- package/src/model-context/exposeShadowEnvsToModelContext.d.ts.map +1 -0
- package/src/model-context/exposeShadowEnvsToModelContext.js +39 -0
- package/src/model-context/exposeShadowEnvsToModelContext.js.map +7 -0
- package/src/model-context/redactProps.d.ts +14 -0
- package/src/model-context/redactProps.d.ts.map +1 -0
- package/src/model-context/redactProps.js +22 -0
- package/src/model-context/redactProps.js.map +7 -0
- package/src/model-context/sharedExposure.d.ts +32 -0
- package/src/model-context/sharedExposure.d.ts.map +1 -0
- package/src/model-context/sharedExposure.js +108 -0
- package/src/model-context/sharedExposure.js.map +7 -0
- package/src/model-context/toolSupport.d.ts +72 -0
- package/src/model-context/toolSupport.d.ts.map +1 -0
- package/src/model-context/toolSupport.js +145 -0
- package/src/model-context/toolSupport.js.map +7 -0
- package/src/model-context/tools/findEntities.d.ts +15 -0
- package/src/model-context/tools/findEntities.d.ts.map +1 -0
- package/src/model-context/tools/findEntities.js +71 -0
- package/src/model-context/tools/findEntities.js.map +7 -0
- package/src/model-context/tools/getEntity.d.ts +16 -0
- package/src/model-context/tools/getEntity.d.ts.map +1 -0
- package/src/model-context/tools/getEntity.js +65 -0
- package/src/model-context/tools/getEntity.js.map +7 -0
- package/src/model-context/tools/getEntityTree.d.ts +4 -0
- package/src/model-context/tools/getEntityTree.d.ts.map +1 -0
- package/src/model-context/tools/getEntityTree.js +58 -0
- package/src/model-context/tools/getEntityTree.js.map +7 -0
- package/src/model-context/tools/getRegistry.d.ts +14 -0
- package/src/model-context/tools/getRegistry.d.ts.map +1 -0
- package/src/model-context/tools/getRegistry.js +39 -0
- package/src/model-context/tools/getRegistry.js.map +7 -0
- package/src/model-context/tools/index.d.ts +9 -0
- package/src/model-context/tools/index.d.ts.map +1 -0
- package/src/model-context/tools/index.js +16 -0
- package/src/model-context/tools/index.js.map +7 -0
- package/src/model-context/tools/listEnvs.d.ts +33 -0
- package/src/model-context/tools/listEnvs.d.ts.map +1 -0
- package/src/model-context/tools/listEnvs.js +37 -0
- package/src/model-context/tools/listEnvs.js.map +7 -0
- package/src/model-context.d.ts +13 -0
- package/src/model-context.d.ts.map +1 -0
- package/src/model-context.js +15 -0
- package/src/model-context.js.map +7 -0
- package/src/shadow-objects.d.ts +2 -0
- package/src/shadow-objects.d.ts.map +1 -1
- package/src/shadow-objects.js +4 -0
- package/src/shadow-objects.js.map +2 -2
- package/src/testing/TestEntity.d.ts +79 -0
- package/src/testing/TestEntity.d.ts.map +1 -0
- package/src/testing/TestEntity.js +135 -0
- package/src/testing/TestEntity.js.map +7 -0
- package/src/testing/createTestKernel.d.ts +11 -0
- package/src/testing/createTestKernel.d.ts.map +1 -0
- package/src/testing/createTestKernel.js +132 -0
- package/src/testing/createTestKernel.js.map +7 -0
- package/src/testing/mountShadowObject.d.ts +21 -0
- package/src/testing/mountShadowObject.d.ts.map +1 -0
- package/src/testing/mountShadowObject.js +72 -0
- package/src/testing/mountShadowObject.js.map +7 -0
- package/src/testing/recordKernelErrors.d.ts +21 -0
- package/src/testing/recordKernelErrors.d.ts.map +1 -0
- package/src/testing/recordKernelErrors.js +39 -0
- package/src/testing/recordKernelErrors.js.map +7 -0
- package/src/testing/settle.d.ts +15 -0
- package/src/testing/settle.d.ts.map +1 -0
- package/src/testing/settle.js +19 -0
- package/src/testing/settle.js.map +7 -0
- package/src/testing/types.d.ts +119 -0
- package/src/testing/types.d.ts.map +1 -0
- package/src/testing/types.js +1 -0
- package/src/testing/types.js.map +7 -0
- package/src/testing.d.ts +14 -0
- package/src/testing.d.ts.map +1 -0
- package/src/testing.js +11 -0
- package/src/testing.js.map +7 -0
- package/src/types.d.ts +150 -10
- package/src/types.d.ts.map +1 -1
- package/src/utils/attr-utils.d.ts +5 -0
- package/src/utils/attr-utils.d.ts.map +1 -1
- package/src/utils/attr-utils.js +2 -0
- package/src/utils/attr-utils.js.map +2 -2
- package/src/view/IShadowObjectEnvProxy.d.ts +9 -0
- package/src/view/IShadowObjectEnvProxy.d.ts.map +1 -1
- package/src/view/LocalShadowObjectEnv.d.ts +7 -0
- package/src/view/LocalShadowObjectEnv.d.ts.map +1 -1
- package/src/view/LocalShadowObjectEnv.js +15 -1
- package/src/view/LocalShadowObjectEnv.js.map +2 -2
- package/src/view/RemoteWorkerEnv.d.ts +22 -3
- package/src/view/RemoteWorkerEnv.d.ts.map +1 -1
- package/src/view/RemoteWorkerEnv.js +50 -1
- package/src/view/RemoteWorkerEnv.js.map +2 -2
- package/src/view/ShadowEnv.d.ts +30 -0
- package/src/view/ShadowEnv.d.ts.map +1 -1
- package/src/view/ShadowEnv.js +101 -0
- package/src/view/ShadowEnv.js.map +3 -3
- package/src/worker/MessageRouter.d.ts.map +1 -1
- package/src/worker/MessageRouter.js +23 -0
- package/src/worker/MessageRouter.js.map +2 -2
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { beQuiet } from "@spearwolf/signalize";
|
|
2
|
+
import { Registry } from "../in-the-dark/Registry.js";
|
|
3
|
+
import {
|
|
4
|
+
NodeBudget,
|
|
5
|
+
normalizeInspectRequest
|
|
6
|
+
} from "./normalizeInspectRequest.js";
|
|
7
|
+
import { serializeValue } from "./serializeValue.js";
|
|
8
|
+
const toContextName = (name) => typeof name === "symbol" ? { symbol: name.description ?? "" } : name;
|
|
9
|
+
const routesOnValue = (val) => val != null && val !== false && val !== "";
|
|
10
|
+
const holdsValue = (val) => val != null;
|
|
11
|
+
const isWorkerRealm = () => typeof globalThis.WorkerGlobalScope !== "undefined";
|
|
12
|
+
function createKernelSnapshot(kernel, request) {
|
|
13
|
+
return beQuiet(() => new KernelSnapshotBuilder(kernel, normalizeInspectRequest(request)).build());
|
|
14
|
+
}
|
|
15
|
+
class KernelSnapshotBuilder {
|
|
16
|
+
#kernel;
|
|
17
|
+
#req;
|
|
18
|
+
#budget;
|
|
19
|
+
#visited = /* @__PURE__ */ new Set();
|
|
20
|
+
#truncation = [];
|
|
21
|
+
#descriptions = /* @__PURE__ */ new Map();
|
|
22
|
+
#budgetNoted = false;
|
|
23
|
+
constructor(kernel, req) {
|
|
24
|
+
this.#kernel = kernel;
|
|
25
|
+
this.#req = req;
|
|
26
|
+
this.#budget = new NodeBudget(req.maxNodes);
|
|
27
|
+
}
|
|
28
|
+
build() {
|
|
29
|
+
const all = this.#kernel.traverseLevelOrderBFS();
|
|
30
|
+
const { filter } = this.#req;
|
|
31
|
+
const nodes = filter === void 0 ? this.#walk(all) : [];
|
|
32
|
+
const snapshot = {
|
|
33
|
+
takenAt: Date.now(),
|
|
34
|
+
thread: isWorkerRealm() ? "worker" : "main",
|
|
35
|
+
counts: {
|
|
36
|
+
entities: all.length,
|
|
37
|
+
roots: all.filter((e) => !e.hasParent).length,
|
|
38
|
+
shadowObjects: all.reduce((sum, e) => sum + this.#describe(e.uuid).length, 0)
|
|
39
|
+
},
|
|
40
|
+
roots: nodes,
|
|
41
|
+
globalContexts: this.#globalContexts(all)
|
|
42
|
+
};
|
|
43
|
+
if (this.#req.include.has("registry")) {
|
|
44
|
+
snapshot.registry = { ...this.#kernel.registry.describe(), isDefault: Registry.isDefault(this.#kernel.registry) };
|
|
45
|
+
}
|
|
46
|
+
if (this.#truncation.length > 0) snapshot.truncation = this.#truncation;
|
|
47
|
+
if (filter !== void 0) snapshot.search = this.#search(all, filter);
|
|
48
|
+
return snapshot;
|
|
49
|
+
}
|
|
50
|
+
#walk(all) {
|
|
51
|
+
const roots = this.#roots(all);
|
|
52
|
+
const named = this.#req.rootUuids !== void 0;
|
|
53
|
+
const nodes = [];
|
|
54
|
+
for (const root of roots) {
|
|
55
|
+
if (this.#visited.has(root.uuid)) continue;
|
|
56
|
+
if (!this.#budget.take()) {
|
|
57
|
+
this.#noteBudget(void 0);
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
const node = this.#node(root, 0);
|
|
61
|
+
if (named) node.ancestors = this.#ancestors(root);
|
|
62
|
+
nodes.push(node);
|
|
63
|
+
}
|
|
64
|
+
return nodes;
|
|
65
|
+
}
|
|
66
|
+
/** The chain above an entity, top down, without the entity itself. */
|
|
67
|
+
#ancestors(entity) {
|
|
68
|
+
const chain = [];
|
|
69
|
+
for (let ancestor = entity.parent; ancestor !== void 0; ancestor = ancestor.parent) {
|
|
70
|
+
chain.unshift({ uuid: ancestor.uuid, token: this.#kernel.tokenOf(ancestor.uuid) ?? "" });
|
|
71
|
+
}
|
|
72
|
+
return chain;
|
|
73
|
+
}
|
|
74
|
+
#search(all, filter) {
|
|
75
|
+
const matches = [];
|
|
76
|
+
let total = 0;
|
|
77
|
+
for (const entity of all) {
|
|
78
|
+
if (!this.#matches(entity, filter)) continue;
|
|
79
|
+
total++;
|
|
80
|
+
if (matches.length < filter.limit) {
|
|
81
|
+
const token = this.#kernel.tokenOf(entity.uuid) ?? "";
|
|
82
|
+
matches.push({ uuid: entity.uuid, token, path: [...this.#ancestors(entity).map((a) => a.token), token] });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { matches, total };
|
|
86
|
+
}
|
|
87
|
+
#matches(entity, filter) {
|
|
88
|
+
const { token, propName, shadowObject, contextName } = filter;
|
|
89
|
+
if (token !== void 0 && this.#kernel.tokenOf(entity.uuid) !== token) return false;
|
|
90
|
+
if (propName !== void 0 && !entity.propKeys().includes(propName)) return false;
|
|
91
|
+
if (shadowObject !== void 0 && !this.#describe(entity.uuid).some((d) => d.displayName === shadowObject)) return false;
|
|
92
|
+
if (contextName !== void 0 && !entity.contextNames().includes(contextName)) return false;
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
#roots(all) {
|
|
96
|
+
const { rootUuids } = this.#req;
|
|
97
|
+
if (rootUuids === void 0) return all.filter((e) => !e.hasParent);
|
|
98
|
+
const roots = [];
|
|
99
|
+
for (const uuid of rootUuids) {
|
|
100
|
+
const entity = this.#kernel.findEntity(uuid);
|
|
101
|
+
if (entity === void 0) {
|
|
102
|
+
this.#truncation.push({ reason: "unknown-root", uuid, message: `the kernel holds no entity "${uuid}"` });
|
|
103
|
+
} else {
|
|
104
|
+
roots.push(entity);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return roots;
|
|
108
|
+
}
|
|
109
|
+
#noteBudget(uuid) {
|
|
110
|
+
if (this.#budgetNoted) return;
|
|
111
|
+
this.#budgetNoted = true;
|
|
112
|
+
const note = {
|
|
113
|
+
reason: "max-nodes",
|
|
114
|
+
message: `maxNodes ${this.#req.maxNodes} reached; ask again with rootUuids below the last node written`
|
|
115
|
+
};
|
|
116
|
+
if (uuid !== void 0) note.uuid = uuid;
|
|
117
|
+
this.#truncation.push(note);
|
|
118
|
+
}
|
|
119
|
+
#describe(uuid) {
|
|
120
|
+
let descriptions = this.#descriptions.get(uuid);
|
|
121
|
+
if (descriptions === void 0) {
|
|
122
|
+
descriptions = this.#kernel.describeShadowObjects(uuid);
|
|
123
|
+
this.#descriptions.set(uuid, descriptions);
|
|
124
|
+
}
|
|
125
|
+
return descriptions;
|
|
126
|
+
}
|
|
127
|
+
#serialize(val) {
|
|
128
|
+
return serializeValue(val, this.#req.limits);
|
|
129
|
+
}
|
|
130
|
+
#node(entity, depth) {
|
|
131
|
+
const { uuid } = entity;
|
|
132
|
+
this.#visited.add(uuid);
|
|
133
|
+
const node = {
|
|
134
|
+
uuid,
|
|
135
|
+
token: this.#kernel.tokenOf(uuid) ?? "",
|
|
136
|
+
order: entity.order,
|
|
137
|
+
autoDestructionOnParentRemoval: entity.autoDestructionOnParentRemoval,
|
|
138
|
+
childCount: entity.children.length
|
|
139
|
+
};
|
|
140
|
+
if (entity.parentUuid !== void 0) node.parentUuid = entity.parentUuid;
|
|
141
|
+
const { include } = this.#req;
|
|
142
|
+
if (include.has("props")) node.props = this.#props(entity);
|
|
143
|
+
if (include.has("shadowObjects")) node.shadowObjects = this.#describe(uuid).map(toShadowObjectSnapshot);
|
|
144
|
+
if (include.has("contexts")) node.contexts = this.#contexts(entity);
|
|
145
|
+
if (depth < this.#req.maxDepth) {
|
|
146
|
+
this.#children(entity, node, depth);
|
|
147
|
+
} else if (entity.children.length > 0) {
|
|
148
|
+
this.#truncation.push({
|
|
149
|
+
reason: "max-depth",
|
|
150
|
+
uuid,
|
|
151
|
+
message: `the children of "${uuid}" were not walked: maxDepth ${this.#req.maxDepth} reached; ask again with rootUuids: ["${uuid}"]`
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return node;
|
|
155
|
+
}
|
|
156
|
+
#children(entity, node, depth) {
|
|
157
|
+
const children = [];
|
|
158
|
+
const omitted = [];
|
|
159
|
+
for (const child of entity.children) {
|
|
160
|
+
if (this.#kernel.findEntity(child.uuid) === void 0) {
|
|
161
|
+
omitted.push({ uuid: child.uuid, reason: "not-in-kernel" });
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (this.#visited.has(child.uuid)) {
|
|
165
|
+
omitted.push({ uuid: child.uuid, reason: "already-in-graph" });
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (!this.#budget.take()) {
|
|
169
|
+
this.#noteBudget(entity.uuid);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
children.push(this.#node(child, depth + 1));
|
|
173
|
+
}
|
|
174
|
+
node.children = children;
|
|
175
|
+
if (omitted.length > 0) node.omittedChildren = omitted;
|
|
176
|
+
}
|
|
177
|
+
#props(entity) {
|
|
178
|
+
return entity.propEntries().map(([name, val]) => ({ name, value: this.#serialize(val), routes: routesOnValue(val) }));
|
|
179
|
+
}
|
|
180
|
+
#contexts(entity) {
|
|
181
|
+
const descriptions = this.#describe(entity.uuid);
|
|
182
|
+
return entity.contextNames().map((name) => {
|
|
183
|
+
const ctx = entity.describeContext(name);
|
|
184
|
+
const snapshot = {
|
|
185
|
+
name: toContextName(name),
|
|
186
|
+
effective: this.#serialize(ctx.effective),
|
|
187
|
+
providedBy: descriptions.filter((d) => d.providesContexts.includes(name)).map((d) => d.displayName),
|
|
188
|
+
source: this.#sourceOf(entity, name, ctx.provided)
|
|
189
|
+
};
|
|
190
|
+
if (ctx.provided !== void 0) snapshot.provided = this.#serialize(ctx.provided);
|
|
191
|
+
if (ctx.inherited !== void 0) snapshot.inherited = this.#serialize(ctx.inherited);
|
|
192
|
+
return snapshot;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
#sourceOf(entity, name, provided) {
|
|
196
|
+
if (holdsValue(provided)) return { kind: "self" };
|
|
197
|
+
for (let ancestor = entity.parent; ancestor !== void 0; ancestor = ancestor.parent) {
|
|
198
|
+
if (holdsValue(ancestor.describeContext(name)?.provided)) return { kind: "ancestor", uuid: ancestor.uuid };
|
|
199
|
+
}
|
|
200
|
+
if (holdsValue(this.#kernel.describeRootContext(name)?.value)) return { kind: "global" };
|
|
201
|
+
return { kind: "none" };
|
|
202
|
+
}
|
|
203
|
+
#globalContexts(all) {
|
|
204
|
+
const contributors = /* @__PURE__ */ new Map();
|
|
205
|
+
for (const entity of all) {
|
|
206
|
+
for (const name of entity.globalContextNames()) {
|
|
207
|
+
let byEntity = contributors.get(name);
|
|
208
|
+
if (byEntity === void 0) contributors.set(name, byEntity = /* @__PURE__ */ new Map());
|
|
209
|
+
byEntity.set(entity.describeGlobalContext(name).signal, entity);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const snapshots = [];
|
|
213
|
+
for (const name of this.#kernel.rootContextNames()) {
|
|
214
|
+
const chain = this.#kernel.describeRootContext(name);
|
|
215
|
+
if (chain.signals.length === 0) continue;
|
|
216
|
+
const byEntity = contributors.get(name);
|
|
217
|
+
const providers = [];
|
|
218
|
+
for (const signal of chain.signals) {
|
|
219
|
+
const entity = byEntity?.get(signal);
|
|
220
|
+
if (entity === void 0) continue;
|
|
221
|
+
providers.push({
|
|
222
|
+
uuid: entity.uuid,
|
|
223
|
+
providedBy: this.#describe(entity.uuid).filter((d) => d.providesGlobalContexts.includes(name)).map((d) => d.displayName),
|
|
224
|
+
value: this.#serialize(entity.describeGlobalContext(name).value)
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
snapshots.push({ name: toContextName(name), value: this.#serialize(chain.value), providers });
|
|
228
|
+
}
|
|
229
|
+
return snapshots;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const toShadowObjectSnapshot = (d) => ({
|
|
233
|
+
displayName: d.displayName,
|
|
234
|
+
definedUnder: d.definedUnder,
|
|
235
|
+
usesProperties: d.usesProperties,
|
|
236
|
+
usesContexts: d.usesContexts.map(toContextName),
|
|
237
|
+
usesParentContexts: d.usesParentContexts.map(toContextName),
|
|
238
|
+
providesContexts: d.providesContexts.map(toContextName),
|
|
239
|
+
providesGlobalContexts: d.providesGlobalContexts.map(toContextName),
|
|
240
|
+
hooks: d.hooks
|
|
241
|
+
});
|
|
242
|
+
export {
|
|
243
|
+
createKernelSnapshot,
|
|
244
|
+
routesOnValue,
|
|
245
|
+
toContextName
|
|
246
|
+
};
|
|
247
|
+
//# sourceMappingURL=createKernelSnapshot.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/inspect/createKernelSnapshot.ts"],
|
|
4
|
+
"sourcesContent": ["import {beQuiet, type SignalLike} from '@spearwolf/signalize';\nimport type {Entity} from '../in-the-dark/Entity.js';\nimport type {Kernel} from '../in-the-dark/Kernel.js';\nimport {Registry} from '../in-the-dark/Registry.js';\nimport type {ShadowObjectDescription} from '../types.js';\nimport {\n NodeBudget,\n type NormalizedEntityFilter,\n type NormalizedInspectRequest,\n normalizeInspectRequest,\n} from './normalizeInspectRequest.js';\nimport {serializeValue} from './serializeValue.js';\nimport type {\n ContextName,\n EntityContextSnapshot,\n EntityContextSource,\n EntityMatch,\n EntityNodeSnapshot,\n EntitySearchSnapshot,\n GlobalContextSnapshot,\n InspectRequest,\n KernelSnapshot,\n PropSnapshot,\n SerializedValue,\n ShadowObjectSnapshot,\n TruncationNote,\n} from './types.js';\n\n/** A string name as it is; a symbol as its description, marked as such. */\nexport const toContextName = (name: string | symbol): ContextName =>\n typeof name === 'symbol' ? {symbol: name.description ?? ''} : name;\n\n/** The rule `Entity.truthyProps()` routes by. */\nexport const routesOnValue = (val: unknown): boolean => val != null && val !== false && val !== '';\n\n/** The rule `SignalsPath` resolves a chain by: a provider holding `null` is passed over. */\nconst holdsValue = (val: unknown): boolean => val != null;\n\nconst isWorkerRealm = (): boolean => typeof (globalThis as {WorkerGlobalScope?: unknown}).WorkerGlobalScope !== 'undefined';\n\n/**\n * A plain-data picture of the Entity Tree behind a Kernel, cut by the limits of the request: every\n * visited Entity with its properties, Shadow Objects and Entity Contexts, the global context chains,\n * and the Registry. Synchronous, read-only, and quiet -- no signal read here subscribes anything.\n *\n * The walk follows the rules of `getEntityGraph()`: every Entity appears once, a child the Kernel no\n * longer holds or the walk has already placed is named under `omittedChildren`. What this adds is\n * the depth and node budget, and the `truncation` notes that say where a budget cut the walk.\n */\nexport function createKernelSnapshot(kernel: Kernel, request?: InspectRequest): KernelSnapshot {\n return beQuiet(() => new KernelSnapshotBuilder(kernel, normalizeInspectRequest(request)).build());\n}\n\nclass KernelSnapshotBuilder {\n readonly #kernel: Kernel;\n readonly #req: NormalizedInspectRequest;\n readonly #budget: NodeBudget;\n readonly #visited = new Set<string>();\n readonly #truncation: TruncationNote[] = [];\n readonly #descriptions = new Map<string, ShadowObjectDescription[]>();\n #budgetNoted = false;\n\n constructor(kernel: Kernel, req: NormalizedInspectRequest) {\n this.#kernel = kernel;\n this.#req = req;\n this.#budget = new NodeBudget(req.maxNodes);\n }\n\n build(): KernelSnapshot {\n const all = this.#kernel.traverseLevelOrderBFS();\n const {filter} = this.#req;\n\n // a search reads every entity and ships none of the tree: the matches are the answer\n const nodes = filter === undefined ? this.#walk(all) : [];\n\n const snapshot: KernelSnapshot = {\n takenAt: Date.now(),\n thread: isWorkerRealm() ? 'worker' : 'main',\n counts: {\n entities: all.length,\n roots: all.filter((e) => !e.hasParent).length,\n shadowObjects: all.reduce((sum, e) => sum + this.#describe(e.uuid).length, 0),\n },\n roots: nodes,\n globalContexts: this.#globalContexts(all),\n };\n\n if (this.#req.include.has('registry')) {\n snapshot.registry = {...this.#kernel.registry.describe(), isDefault: Registry.isDefault(this.#kernel.registry)};\n }\n if (this.#truncation.length > 0) snapshot.truncation = this.#truncation;\n if (filter !== undefined) snapshot.search = this.#search(all, filter);\n\n return snapshot;\n }\n\n #walk(all: Entity[]): EntityNodeSnapshot[] {\n const roots = this.#roots(all);\n const named = this.#req.rootUuids !== undefined;\n\n const nodes: EntityNodeSnapshot[] = [];\n for (const root of roots) {\n // a root reached through an earlier root's subtree (a back-edge into the root set) drops\n // from the top level without a note, same as `getEntityGraph()`'s top-level `visited` check\n if (this.#visited.has(root.uuid)) continue;\n if (!this.#budget.take()) {\n this.#noteBudget(undefined);\n break;\n }\n const node = this.#node(root, 0);\n // only a caller that named the root wants to know what is above it\n if (named) node.ancestors = this.#ancestors(root);\n nodes.push(node);\n }\n return nodes;\n }\n\n /** The chain above an entity, top down, without the entity itself. */\n #ancestors(entity: Entity): {uuid: string; token: string}[] {\n const chain: {uuid: string; token: string}[] = [];\n // the parent chain is a chain: Entity.assertAttachableTo() refuses a parent that is a descendant\n for (let ancestor = entity.parent; ancestor !== undefined; ancestor = ancestor.parent) {\n chain.unshift({uuid: ancestor.uuid, token: this.#kernel.tokenOf(ancestor.uuid) ?? ''});\n }\n return chain;\n }\n\n #search(all: Entity[], filter: NormalizedEntityFilter): EntitySearchSnapshot {\n const matches: EntityMatch[] = [];\n let total = 0;\n for (const entity of all) {\n if (!this.#matches(entity, filter)) continue;\n total++;\n if (matches.length < filter.limit) {\n const token = this.#kernel.tokenOf(entity.uuid) ?? '';\n matches.push({uuid: entity.uuid, token, path: [...this.#ancestors(entity).map((a) => a.token), token]});\n }\n }\n return {matches, total};\n }\n\n #matches(entity: Entity, filter: NormalizedEntityFilter): boolean {\n const {token, propName, shadowObject, contextName} = filter;\n if (token !== undefined && this.#kernel.tokenOf(entity.uuid) !== token) return false;\n if (propName !== undefined && !entity.propKeys().includes(propName)) return false;\n if (shadowObject !== undefined && !this.#describe(entity.uuid).some((d) => d.displayName === shadowObject)) return false;\n if (contextName !== undefined && !entity.contextNames().includes(contextName)) return false;\n return true;\n }\n\n #roots(all: Entity[]): Entity[] {\n const {rootUuids} = this.#req;\n if (rootUuids === undefined) return all.filter((e) => !e.hasParent);\n\n const roots: Entity[] = [];\n for (const uuid of rootUuids) {\n const entity = this.#kernel.findEntity(uuid);\n if (entity === undefined) {\n this.#truncation.push({reason: 'unknown-root', uuid, message: `the kernel holds no entity \"${uuid}\"`});\n } else {\n roots.push(entity);\n }\n }\n return roots;\n }\n\n #noteBudget(uuid: string | undefined): void {\n if (this.#budgetNoted) return;\n this.#budgetNoted = true;\n const note: TruncationNote = {\n reason: 'max-nodes',\n message: `maxNodes ${this.#req.maxNodes} reached; ask again with rootUuids below the last node written`,\n };\n if (uuid !== undefined) note.uuid = uuid;\n this.#truncation.push(note);\n }\n\n #describe(uuid: string): ShadowObjectDescription[] {\n let descriptions = this.#descriptions.get(uuid);\n if (descriptions === undefined) {\n descriptions = this.#kernel.describeShadowObjects(uuid);\n this.#descriptions.set(uuid, descriptions);\n }\n return descriptions;\n }\n\n #serialize(val: unknown): SerializedValue {\n return serializeValue(val, this.#req.limits);\n }\n\n #node(entity: Entity, depth: number): EntityNodeSnapshot {\n const {uuid} = entity;\n this.#visited.add(uuid);\n\n const node: EntityNodeSnapshot = {\n uuid,\n token: this.#kernel.tokenOf(uuid) ?? '',\n order: entity.order,\n autoDestructionOnParentRemoval: entity.autoDestructionOnParentRemoval,\n childCount: entity.children.length,\n };\n if (entity.parentUuid !== undefined) node.parentUuid = entity.parentUuid;\n\n const {include} = this.#req;\n if (include.has('props')) node.props = this.#props(entity);\n if (include.has('shadowObjects')) node.shadowObjects = this.#describe(uuid).map(toShadowObjectSnapshot);\n if (include.has('contexts')) node.contexts = this.#contexts(entity);\n\n if (depth < this.#req.maxDepth) {\n this.#children(entity, node, depth);\n } else if (entity.children.length > 0) {\n this.#truncation.push({\n reason: 'max-depth',\n uuid,\n message: `the children of \"${uuid}\" were not walked: maxDepth ${this.#req.maxDepth} reached; ask again with rootUuids: [\"${uuid}\"]`,\n });\n }\n\n return node;\n }\n\n #children(entity: Entity, node: EntityNodeSnapshot, depth: number): void {\n const children: EntityNodeSnapshot[] = [];\n const omitted: NonNullable<EntityNodeSnapshot['omittedChildren']> = [];\n\n for (const child of entity.children) {\n // the kernel lookup first, so a uuid the kernel does not hold is named at every parent that\n // lists it -- the same order `getEntityGraph()` keeps\n if (this.#kernel.findEntity(child.uuid) === undefined) {\n omitted.push({uuid: child.uuid, reason: 'not-in-kernel'});\n continue;\n }\n if (this.#visited.has(child.uuid)) {\n omitted.push({uuid: child.uuid, reason: 'already-in-graph'});\n continue;\n }\n if (!this.#budget.take()) {\n this.#noteBudget(entity.uuid);\n break;\n }\n children.push(this.#node(child, depth + 1));\n }\n\n node.children = children;\n if (omitted.length > 0) node.omittedChildren = omitted;\n }\n\n #props(entity: Entity): PropSnapshot[] {\n return entity.propEntries().map(([name, val]) => ({name, value: this.#serialize(val), routes: routesOnValue(val)}));\n }\n\n #contexts(entity: Entity): EntityContextSnapshot[] {\n const descriptions = this.#describe(entity.uuid);\n\n return entity.contextNames().map((name) => {\n // the entity holds the name, `contextNames()` just said so\n const ctx = entity.describeContext(name)!;\n const snapshot: EntityContextSnapshot = {\n name: toContextName(name),\n effective: this.#serialize(ctx.effective),\n providedBy: descriptions.filter((d) => d.providesContexts.includes(name)).map((d) => d.displayName),\n source: this.#sourceOf(entity, name, ctx.provided),\n };\n if (ctx.provided !== undefined) snapshot.provided = this.#serialize(ctx.provided);\n if (ctx.inherited !== undefined) snapshot.inherited = this.#serialize(ctx.inherited);\n return snapshot;\n });\n }\n\n #sourceOf(entity: Entity, name: string | symbol, provided: unknown): EntityContextSource {\n if (holdsValue(provided)) return {kind: 'self'};\n // the parent chain is a chain: Entity.assertAttachableTo() refuses a parent that is a descendant\n for (let ancestor = entity.parent; ancestor !== undefined; ancestor = ancestor.parent) {\n if (holdsValue(ancestor.describeContext(name)?.provided)) return {kind: 'ancestor', uuid: ancestor.uuid};\n }\n if (holdsValue(this.#kernel.describeRootContext(name)?.value)) return {kind: 'global'};\n return {kind: 'none'};\n }\n\n #globalContexts(all: Entity[]): GlobalContextSnapshot[] {\n // signal -> contributing entity, per name; the chain hands out the same signal objects\n const contributors = new Map<string | symbol, Map<SignalLike<any>, Entity>>();\n for (const entity of all) {\n for (const name of entity.globalContextNames()) {\n let byEntity = contributors.get(name);\n if (byEntity === undefined) contributors.set(name, (byEntity = new Map()));\n byEntity.set(entity.describeGlobalContext(name)!.signal, entity);\n }\n }\n\n const snapshots: GlobalContextSnapshot[] = [];\n for (const name of this.#kernel.rootContextNames()) {\n const chain = this.#kernel.describeRootContext(name)!;\n // a chain without members is the lookup stub a root entity's `useContext()` leaves behind\n if (chain.signals.length === 0) continue;\n\n const byEntity = contributors.get(name);\n const providers: GlobalContextSnapshot['providers'] = [];\n for (const signal of chain.signals) {\n const entity = byEntity?.get(signal);\n if (entity === undefined) continue;\n providers.push({\n uuid: entity.uuid,\n providedBy: this.#describe(entity.uuid)\n .filter((d) => d.providesGlobalContexts.includes(name))\n .map((d) => d.displayName),\n value: this.#serialize(entity.describeGlobalContext(name)!.value),\n });\n }\n snapshots.push({name: toContextName(name), value: this.#serialize(chain.value), providers});\n }\n return snapshots;\n }\n}\n\nconst toShadowObjectSnapshot = (d: ShadowObjectDescription): ShadowObjectSnapshot => ({\n displayName: d.displayName,\n definedUnder: d.definedUnder,\n usesProperties: d.usesProperties,\n usesContexts: d.usesContexts.map(toContextName),\n usesParentContexts: d.usesParentContexts.map(toContextName),\n providesContexts: d.providesContexts.map(toContextName),\n providesGlobalContexts: d.providesGlobalContexts.map(toContextName),\n hooks: d.hooks,\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ComponentContext } from '../view/ComponentContext.js';
|
|
2
|
+
import type { InspectRequest, ViewSnapshot } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* A plain-data picture of the component tree a `ComponentContext` holds, cut by the limits of the
|
|
5
|
+
* request. Structure comes from the live components; properties come from the Component Memory,
|
|
6
|
+
* which holds the last committed state -- a component created and not yet synced has no `props`.
|
|
7
|
+
* Reads only; the pending changes are never built.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createViewSnapshot(context: ComponentContext, request?: InspectRequest): ViewSnapshot;
|
|
10
|
+
//# sourceMappingURL=createViewSnapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createViewSnapshot.d.ts","sourceRoot":"","sources":["../../../src/inspect/createViewSnapshot.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,6BAA6B,CAAC;AAKlE,OAAO,KAAK,EAAC,cAAc,EAAuD,YAAY,EAAC,MAAM,YAAY,CAAC;AAkClH;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,YAAY,CAEpG"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { SHAE_ENT } from "../elements/constants.js";
|
|
2
|
+
import { routesOnValue } from "./createKernelSnapshot.js";
|
|
3
|
+
import { NodeBudget, normalizeInspectRequest } from "./normalizeInspectRequest.js";
|
|
4
|
+
import { serializeValue } from "./serializeValue.js";
|
|
5
|
+
const SIMPLE_ID = /^[A-Za-z_][\w-]*$/;
|
|
6
|
+
const selectorPath = (el) => {
|
|
7
|
+
const parts = [];
|
|
8
|
+
for (let node = el; node !== null; node = node.parentElement) {
|
|
9
|
+
if (node.id && SIMPLE_ID.test(node.id)) {
|
|
10
|
+
parts.unshift(`#${node.id}`);
|
|
11
|
+
break;
|
|
12
|
+
}
|
|
13
|
+
const parent = node.parentElement;
|
|
14
|
+
if (parent === null) {
|
|
15
|
+
parts.unshift(node.localName);
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
const sameTag = Array.from(parent.children).filter((sibling) => sibling.localName === node.localName);
|
|
19
|
+
parts.unshift(`${node.localName}:nth-of-type(${sameTag.indexOf(node) + 1})`);
|
|
20
|
+
}
|
|
21
|
+
return parts.join(" > ");
|
|
22
|
+
};
|
|
23
|
+
const indexEntElements = () => {
|
|
24
|
+
const index = /* @__PURE__ */ new Map();
|
|
25
|
+
if (typeof document === "undefined") return index;
|
|
26
|
+
for (const el of Array.from(document.querySelectorAll(SHAE_ENT))) {
|
|
27
|
+
const uuid = el.uuid;
|
|
28
|
+
if (typeof uuid === "string" && !index.has(uuid)) index.set(uuid, selectorPath(el));
|
|
29
|
+
}
|
|
30
|
+
return index;
|
|
31
|
+
};
|
|
32
|
+
function createViewSnapshot(context, request) {
|
|
33
|
+
return new ViewSnapshotBuilder(context, normalizeInspectRequest(request)).build();
|
|
34
|
+
}
|
|
35
|
+
class ViewSnapshotBuilder {
|
|
36
|
+
#ctx;
|
|
37
|
+
#req;
|
|
38
|
+
#budget;
|
|
39
|
+
#visited = /* @__PURE__ */ new Set();
|
|
40
|
+
#truncation = [];
|
|
41
|
+
#elements;
|
|
42
|
+
#budgetNoted = false;
|
|
43
|
+
constructor(ctx, req) {
|
|
44
|
+
this.#ctx = ctx;
|
|
45
|
+
this.#req = req;
|
|
46
|
+
this.#budget = new NodeBudget(req.maxNodes);
|
|
47
|
+
this.#elements = indexEntElements();
|
|
48
|
+
}
|
|
49
|
+
build() {
|
|
50
|
+
const all = this.#ctx.traverseLevelOrderBFS();
|
|
51
|
+
const roots = this.#roots(all);
|
|
52
|
+
const nodes = [];
|
|
53
|
+
for (const root of roots) {
|
|
54
|
+
if (this.#visited.has(root.uuid)) continue;
|
|
55
|
+
if (!this.#budget.take()) {
|
|
56
|
+
this.#noteBudget(root.parent?.uuid);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
nodes.push(this.#node(root, 0));
|
|
60
|
+
}
|
|
61
|
+
const snapshot = {
|
|
62
|
+
takenAt: Date.now(),
|
|
63
|
+
counts: { components: all.length, roots: all.filter((c) => c.parent === void 0).length },
|
|
64
|
+
roots: nodes
|
|
65
|
+
};
|
|
66
|
+
if (this.#truncation.length > 0) snapshot.truncation = this.#truncation;
|
|
67
|
+
return snapshot;
|
|
68
|
+
}
|
|
69
|
+
#roots(all) {
|
|
70
|
+
const { rootUuids } = this.#req;
|
|
71
|
+
if (rootUuids === void 0) return all.filter((c) => c.parent === void 0);
|
|
72
|
+
const byUuid = new Map(all.map((c) => [c.uuid, c]));
|
|
73
|
+
const roots = [];
|
|
74
|
+
for (const uuid of rootUuids) {
|
|
75
|
+
const component = byUuid.get(uuid);
|
|
76
|
+
if (component === void 0) {
|
|
77
|
+
this.#truncation.push({ reason: "unknown-root", uuid, message: `the component context holds no component "${uuid}"` });
|
|
78
|
+
} else {
|
|
79
|
+
roots.push(component);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return roots;
|
|
83
|
+
}
|
|
84
|
+
#noteBudget(uuid) {
|
|
85
|
+
if (this.#budgetNoted) return;
|
|
86
|
+
this.#budgetNoted = true;
|
|
87
|
+
const note = {
|
|
88
|
+
reason: "max-nodes",
|
|
89
|
+
message: `maxNodes ${this.#req.maxNodes} reached; ask again with rootUuids below the last node written`
|
|
90
|
+
};
|
|
91
|
+
if (uuid !== void 0) note.uuid = uuid;
|
|
92
|
+
this.#truncation.push(note);
|
|
93
|
+
}
|
|
94
|
+
#node(component, depth) {
|
|
95
|
+
const { uuid } = component;
|
|
96
|
+
this.#visited.add(uuid);
|
|
97
|
+
const children = this.#ctx.getChildren(component);
|
|
98
|
+
const node = { uuid, token: component.token, order: component.order, childCount: children.length };
|
|
99
|
+
if (component.parent !== void 0) node.parentUuid = component.parent.uuid;
|
|
100
|
+
const element = this.#elements.get(uuid);
|
|
101
|
+
if (element !== void 0) node.element = element;
|
|
102
|
+
if (this.#req.include.has("props")) {
|
|
103
|
+
const props = this.#props(uuid);
|
|
104
|
+
if (props !== void 0) node.props = props;
|
|
105
|
+
}
|
|
106
|
+
if (depth < this.#req.maxDepth) {
|
|
107
|
+
const walked = [];
|
|
108
|
+
const omitted = [];
|
|
109
|
+
for (const child of children) {
|
|
110
|
+
if (this.#visited.has(child.uuid)) {
|
|
111
|
+
omitted.push({ uuid: child.uuid, reason: "already-in-graph" });
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (!this.#budget.take()) {
|
|
115
|
+
this.#noteBudget(uuid);
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
walked.push(this.#node(child, depth + 1));
|
|
119
|
+
}
|
|
120
|
+
node.children = walked;
|
|
121
|
+
if (omitted.length > 0) node.omittedChildren = omitted;
|
|
122
|
+
} else if (children.length > 0) {
|
|
123
|
+
this.#truncation.push({
|
|
124
|
+
reason: "max-depth",
|
|
125
|
+
uuid,
|
|
126
|
+
message: `the children of "${uuid}" were not walked: maxDepth ${this.#req.maxDepth} reached; ask again with rootUuids: ["${uuid}"]`
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return node;
|
|
130
|
+
}
|
|
131
|
+
#props(uuid) {
|
|
132
|
+
const properties = this.#ctx.getComponentState(uuid)?.properties;
|
|
133
|
+
if (properties === void 0) return void 0;
|
|
134
|
+
return properties.map((entry) => {
|
|
135
|
+
const val = entry.length === 2 ? entry[1] : void 0;
|
|
136
|
+
return { name: entry[0], value: serializeValue(val, this.#req.limits), routes: routesOnValue(val) };
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
export {
|
|
141
|
+
createViewSnapshot
|
|
142
|
+
};
|
|
143
|
+
//# sourceMappingURL=createViewSnapshot.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/inspect/createViewSnapshot.ts"],
|
|
4
|
+
"sourcesContent": ["import {SHAE_ENT} from '../elements/constants.js';\nimport type {ComponentContext} from '../view/ComponentContext.js';\nimport type {ViewComponent} from '../view/ViewComponent.js';\nimport {routesOnValue} from './createKernelSnapshot.js';\nimport {NodeBudget, type NormalizedInspectRequest, normalizeInspectRequest} from './normalizeInspectRequest.js';\nimport {serializeValue} from './serializeValue.js';\nimport type {InspectRequest, PropSnapshot, TruncationNote, ViewComponentSnapshot, ViewSnapshot} from './types.js';\n\nconst SIMPLE_ID = /^[A-Za-z_][\\w-]*$/;\n\n/** A selector path from the nearest ancestor with a simple id (or the document root) down to `el`. */\nconst selectorPath = (el: Element): string => {\n const parts: string[] = [];\n for (let node: Element | null = el; node !== null; node = node.parentElement) {\n if (node.id && SIMPLE_ID.test(node.id)) {\n parts.unshift(`#${node.id}`);\n break;\n }\n const parent = node.parentElement;\n if (parent === null) {\n parts.unshift(node.localName);\n break;\n }\n const sameTag = Array.from(parent.children).filter((sibling) => sibling.localName === node!.localName);\n parts.unshift(`${node.localName}:nth-of-type(${sameTag.indexOf(node) + 1})`);\n }\n return parts.join(' > ');\n};\n\n/** uuid -> selector path of the `<shae-ent>` carrying it. Empty without a document, and for elements inside shadow roots (the query pierces none, open or closed). */\nconst indexEntElements = (): Map<string, string> => {\n const index = new Map<string, string>();\n if (typeof document === 'undefined') return index;\n for (const el of Array.from(document.querySelectorAll(SHAE_ENT))) {\n const uuid = (el as {uuid?: unknown}).uuid;\n if (typeof uuid === 'string' && !index.has(uuid)) index.set(uuid, selectorPath(el));\n }\n return index;\n};\n\n/**\n * A plain-data picture of the component tree a `ComponentContext` holds, cut by the limits of the\n * request. Structure comes from the live components; properties come from the Component Memory,\n * which holds the last committed state -- a component created and not yet synced has no `props`.\n * Reads only; the pending changes are never built.\n */\nexport function createViewSnapshot(context: ComponentContext, request?: InspectRequest): ViewSnapshot {\n return new ViewSnapshotBuilder(context, normalizeInspectRequest(request)).build();\n}\n\nclass ViewSnapshotBuilder {\n readonly #ctx: ComponentContext;\n readonly #req: NormalizedInspectRequest;\n readonly #budget: NodeBudget;\n readonly #visited = new Set<string>();\n readonly #truncation: TruncationNote[] = [];\n readonly #elements: Map<string, string>;\n #budgetNoted = false;\n\n constructor(ctx: ComponentContext, req: NormalizedInspectRequest) {\n this.#ctx = ctx;\n this.#req = req;\n this.#budget = new NodeBudget(req.maxNodes);\n this.#elements = indexEntElements();\n }\n\n build(): ViewSnapshot {\n const all = this.#ctx.traverseLevelOrderBFS();\n const roots = this.#roots(all);\n\n const nodes: ViewComponentSnapshot[] = [];\n for (const root of roots) {\n // a root reached through an earlier root's subtree drops from the top level without a note,\n // the way the Kernel builder drops one\n if (this.#visited.has(root.uuid)) continue;\n if (!this.#budget.take()) {\n this.#noteBudget(root.parent?.uuid);\n break;\n }\n nodes.push(this.#node(root, 0));\n }\n\n const snapshot: ViewSnapshot = {\n takenAt: Date.now(),\n counts: {components: all.length, roots: all.filter((c) => c.parent === undefined).length},\n roots: nodes,\n };\n if (this.#truncation.length > 0) snapshot.truncation = this.#truncation;\n return snapshot;\n }\n\n #roots(all: ViewComponent[]): ViewComponent[] {\n const {rootUuids} = this.#req;\n if (rootUuids === undefined) return all.filter((c) => c.parent === undefined);\n\n const byUuid = new Map(all.map((c) => [c.uuid, c]));\n const roots: ViewComponent[] = [];\n for (const uuid of rootUuids) {\n const component = byUuid.get(uuid);\n if (component === undefined) {\n this.#truncation.push({reason: 'unknown-root', uuid, message: `the component context holds no component \"${uuid}\"`});\n } else {\n roots.push(component);\n }\n }\n return roots;\n }\n\n #noteBudget(uuid: string | undefined): void {\n if (this.#budgetNoted) return;\n this.#budgetNoted = true;\n const note: TruncationNote = {\n reason: 'max-nodes',\n message: `maxNodes ${this.#req.maxNodes} reached; ask again with rootUuids below the last node written`,\n };\n if (uuid !== undefined) note.uuid = uuid;\n this.#truncation.push(note);\n }\n\n #node(component: ViewComponent, depth: number): ViewComponentSnapshot {\n const {uuid} = component;\n this.#visited.add(uuid);\n const children = this.#ctx.getChildren(component);\n\n // the setter coerces a missing token to VoidToken, so the getter never actually reads back undefined --\n // TS 7's inference of the split accessor widens the read type regardless\n const node: ViewComponentSnapshot = {uuid, token: component.token!, order: component.order, childCount: children.length};\n if (component.parent !== undefined) node.parentUuid = component.parent.uuid;\n\n const element = this.#elements.get(uuid);\n if (element !== undefined) node.element = element;\n\n if (this.#req.include.has('props')) {\n const props = this.#props(uuid);\n if (props !== undefined) node.props = props;\n }\n\n if (depth < this.#req.maxDepth) {\n const walked: ViewComponentSnapshot[] = [];\n const omitted: NonNullable<ViewComponentSnapshot['omittedChildren']> = [];\n for (const child of children) {\n // `addToChildren()` writes a children list without taking the child out of the one it\n // already stands in, so a component can be reached twice and a list can point at an ancestor\n if (this.#visited.has(child.uuid)) {\n omitted.push({uuid: child.uuid, reason: 'already-in-graph'});\n continue;\n }\n if (!this.#budget.take()) {\n this.#noteBudget(uuid);\n break;\n }\n walked.push(this.#node(child, depth + 1));\n }\n node.children = walked;\n if (omitted.length > 0) node.omittedChildren = omitted;\n } else if (children.length > 0) {\n this.#truncation.push({\n reason: 'max-depth',\n uuid,\n message: `the children of \"${uuid}\" were not walked: maxDepth ${this.#req.maxDepth} reached; ask again with rootUuids: [\"${uuid}\"]`,\n });\n }\n\n return node;\n }\n\n #props(uuid: string): PropSnapshot[] | undefined {\n const properties = this.#ctx.getComponentState(uuid)?.properties;\n if (properties === undefined) return undefined;\n return properties.map((entry) => {\n const val = entry.length === 2 ? entry[1] : undefined;\n return {name: entry[0], value: serializeValue(val, this.#req.limits), routes: routesOnValue(val)};\n });\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { InspectInclude, InspectRequest, SerializeLimits } from './types.js';
|
|
2
|
+
export declare const InspectDefaults: Readonly<{
|
|
3
|
+
maxDepth: 4;
|
|
4
|
+
maxDepthCap: 64;
|
|
5
|
+
maxNodes: 250;
|
|
6
|
+
maxMatches: 50;
|
|
7
|
+
}>;
|
|
8
|
+
export interface NormalizedInspectRequest {
|
|
9
|
+
include: Set<InspectInclude>;
|
|
10
|
+
rootUuids: string[] | undefined;
|
|
11
|
+
maxDepth: number;
|
|
12
|
+
maxNodes: number;
|
|
13
|
+
limits: SerializeLimits;
|
|
14
|
+
filter: NormalizedEntityFilter | undefined;
|
|
15
|
+
}
|
|
16
|
+
/** A filter with every criterion present, `undefined` where the caller gave none, and the limit filled in. */
|
|
17
|
+
export interface NormalizedEntityFilter {
|
|
18
|
+
token: string | undefined;
|
|
19
|
+
propName: string | undefined;
|
|
20
|
+
shadowObject: string | undefined;
|
|
21
|
+
contextName: string | undefined;
|
|
22
|
+
limit: number;
|
|
23
|
+
}
|
|
24
|
+
/** Fills the defaults of spec §8.1 in and clamps what the caller gave. */
|
|
25
|
+
export declare const normalizeInspectRequest: (request?: InspectRequest) => NormalizedInspectRequest;
|
|
26
|
+
/** Counts the nodes a walk may still emit. `take()` answers `false` once the budget is spent. */
|
|
27
|
+
export declare class NodeBudget {
|
|
28
|
+
#private;
|
|
29
|
+
constructor(max: number);
|
|
30
|
+
take(): boolean;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=normalizeInspectRequest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizeInspectRequest.d.ts","sourceRoot":"","sources":["../../../src/inspect/normalizeInspectRequest.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAe,cAAc,EAAE,cAAc,EAAE,eAAe,EAAC,MAAM,YAAY,CAAC;AAE9F,eAAO,MAAM,eAAe;;;;;EAK1B,CAAC;AAIH,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,EAAE,sBAAsB,GAAG,SAAS,CAAC;CAC5C;AAED,8GAA8G;AAC9G,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;CACf;AA+BD,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,aAAc,cAAc,KAAG,wBAOjE,CAAC;AAEH,iGAAiG;AACjG,qBAAa,UAAU;;IAGrB,YAAY,GAAG,EAAE,MAAM,EAEtB;IAED,IAAI,IAAI,OAAO,CAId;CACF"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { resolveSerializeLimits } from "./serializeValue.js";
|
|
2
|
+
const InspectDefaults = Object.freeze({
|
|
3
|
+
maxDepth: 4,
|
|
4
|
+
maxDepthCap: 64,
|
|
5
|
+
maxNodes: 250,
|
|
6
|
+
maxMatches: 50
|
|
7
|
+
});
|
|
8
|
+
const AllIncludes = ["props", "shadowObjects", "contexts", "registry"];
|
|
9
|
+
const clampDepth = (depth) => {
|
|
10
|
+
if (depth === void 0) return InspectDefaults.maxDepth;
|
|
11
|
+
if (!Number.isFinite(depth)) return InspectDefaults.maxDepthCap;
|
|
12
|
+
return Math.min(InspectDefaults.maxDepthCap, Math.max(0, Math.floor(depth)));
|
|
13
|
+
};
|
|
14
|
+
const clampNodes = (nodes) => {
|
|
15
|
+
if (nodes === void 0) return InspectDefaults.maxNodes;
|
|
16
|
+
if (!Number.isFinite(nodes)) return Number.MAX_SAFE_INTEGER;
|
|
17
|
+
return Math.max(1, Math.floor(nodes));
|
|
18
|
+
};
|
|
19
|
+
const clampMatches = (limit) => {
|
|
20
|
+
if (limit === void 0) return InspectDefaults.maxMatches;
|
|
21
|
+
if (!Number.isFinite(limit)) return Number.MAX_SAFE_INTEGER;
|
|
22
|
+
return Math.max(1, Math.floor(limit));
|
|
23
|
+
};
|
|
24
|
+
const normalizeFilter = (filter) => filter === void 0 ? void 0 : {
|
|
25
|
+
token: filter.token,
|
|
26
|
+
propName: filter.propName,
|
|
27
|
+
shadowObject: filter.shadowObject,
|
|
28
|
+
contextName: filter.contextName,
|
|
29
|
+
limit: clampMatches(filter.limit)
|
|
30
|
+
};
|
|
31
|
+
const normalizeInspectRequest = (request) => ({
|
|
32
|
+
include: new Set(request?.include ?? AllIncludes),
|
|
33
|
+
rootUuids: request?.rootUuids,
|
|
34
|
+
maxDepth: clampDepth(request?.maxDepth),
|
|
35
|
+
maxNodes: clampNodes(request?.maxNodes),
|
|
36
|
+
limits: resolveSerializeLimits(request?.values),
|
|
37
|
+
filter: normalizeFilter(request?.filter)
|
|
38
|
+
});
|
|
39
|
+
class NodeBudget {
|
|
40
|
+
#left;
|
|
41
|
+
constructor(max) {
|
|
42
|
+
this.#left = max;
|
|
43
|
+
}
|
|
44
|
+
take() {
|
|
45
|
+
if (this.#left <= 0) return false;
|
|
46
|
+
this.#left--;
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
InspectDefaults,
|
|
52
|
+
NodeBudget,
|
|
53
|
+
normalizeInspectRequest
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=normalizeInspectRequest.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/inspect/normalizeInspectRequest.ts"],
|
|
4
|
+
"sourcesContent": ["import {resolveSerializeLimits} from './serializeValue.js';\nimport type {EntityFilter, InspectInclude, InspectRequest, SerializeLimits} from './types.js';\n\nexport const InspectDefaults = Object.freeze({\n maxDepth: 4,\n maxDepthCap: 64,\n maxNodes: 250,\n maxMatches: 50,\n});\n\nconst AllIncludes: readonly InspectInclude[] = ['props', 'shadowObjects', 'contexts', 'registry'];\n\nexport interface NormalizedInspectRequest {\n include: Set<InspectInclude>;\n rootUuids: string[] | undefined;\n maxDepth: number;\n maxNodes: number;\n limits: SerializeLimits;\n filter: NormalizedEntityFilter | undefined;\n}\n\n/** A filter with every criterion present, `undefined` where the caller gave none, and the limit filled in. */\nexport interface NormalizedEntityFilter {\n token: string | undefined;\n propName: string | undefined;\n shadowObject: string | undefined;\n contextName: string | undefined;\n limit: number;\n}\n\nconst clampDepth = (depth: number | undefined): number => {\n if (depth === undefined) return InspectDefaults.maxDepth;\n if (!Number.isFinite(depth)) return InspectDefaults.maxDepthCap;\n return Math.min(InspectDefaults.maxDepthCap, Math.max(0, Math.floor(depth)));\n};\n\nconst clampNodes = (nodes: number | undefined): number => {\n if (nodes === undefined) return InspectDefaults.maxNodes;\n if (!Number.isFinite(nodes)) return Number.MAX_SAFE_INTEGER;\n return Math.max(1, Math.floor(nodes));\n};\n\nconst clampMatches = (limit: number | undefined): number => {\n if (limit === undefined) return InspectDefaults.maxMatches;\n if (!Number.isFinite(limit)) return Number.MAX_SAFE_INTEGER;\n return Math.max(1, Math.floor(limit));\n};\n\nconst normalizeFilter = (filter: EntityFilter | undefined): NormalizedEntityFilter | undefined =>\n filter === undefined\n ? undefined\n : {\n token: filter.token,\n propName: filter.propName,\n shadowObject: filter.shadowObject,\n contextName: filter.contextName,\n limit: clampMatches(filter.limit),\n };\n\n/** Fills the defaults of spec \u00A78.1 in and clamps what the caller gave. */\nexport const normalizeInspectRequest = (request?: InspectRequest): NormalizedInspectRequest => ({\n include: new Set(request?.include ?? AllIncludes),\n rootUuids: request?.rootUuids,\n maxDepth: clampDepth(request?.maxDepth),\n maxNodes: clampNodes(request?.maxNodes),\n limits: resolveSerializeLimits(request?.values),\n filter: normalizeFilter(request?.filter),\n});\n\n/** Counts the nodes a walk may still emit. `take()` answers `false` once the budget is spent. */\nexport class NodeBudget {\n #left: number;\n\n constructor(max: number) {\n this.#left = max;\n }\n\n take(): boolean {\n if (this.#left <= 0) return false;\n this.#left--;\n return true;\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAQ,8BAA6B;AAG9B,MAAM,kBAAkB,OAAO,OAAO;AAAA,EAC3C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,UAAU;AAAA,EACV,YAAY;AACd,CAAC;AAED,MAAM,cAAyC,CAAC,SAAS,iBAAiB,YAAY,UAAU;AAoBhG,MAAM,aAAa,CAAC,UAAsC;AACxD,MAAI,UAAU,OAAW,QAAO,gBAAgB;AAChD,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO,gBAAgB;AACpD,SAAO,KAAK,IAAI,gBAAgB,aAAa,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC,CAAC;AAC7E;AAEA,MAAM,aAAa,CAAC,UAAsC;AACxD,MAAI,UAAU,OAAW,QAAO,gBAAgB;AAChD,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO,OAAO;AAC3C,SAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC;AACtC;AAEA,MAAM,eAAe,CAAC,UAAsC;AAC1D,MAAI,UAAU,OAAW,QAAO,gBAAgB;AAChD,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO,OAAO;AAC3C,SAAO,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC;AACtC;AAEA,MAAM,kBAAkB,CAAC,WACvB,WAAW,SACP,SACA;AAAA,EACE,OAAO,OAAO;AAAA,EACd,UAAU,OAAO;AAAA,EACjB,cAAc,OAAO;AAAA,EACrB,aAAa,OAAO;AAAA,EACpB,OAAO,aAAa,OAAO,KAAK;AAClC;AAGC,MAAM,0BAA0B,CAAC,aAAwD;AAAA,EAC9F,SAAS,IAAI,IAAI,SAAS,WAAW,WAAW;AAAA,EAChD,WAAW,SAAS;AAAA,EACpB,UAAU,WAAW,SAAS,QAAQ;AAAA,EACtC,UAAU,WAAW,SAAS,QAAQ;AAAA,EACtC,QAAQ,uBAAuB,SAAS,MAAM;AAAA,EAC9C,QAAQ,gBAAgB,SAAS,MAAM;AACzC;AAGO,MAAM,WAAW;AAAA,EACtB;AAAA,EAEA,YAAY,KAAa;AACvB,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,OAAgB;AACd,QAAI,KAAK,SAAS,EAAG,QAAO;AAC5B,SAAK;AACL,WAAO;AAAA,EACT;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SerializedValue, SerializeLimits } from './types.js';
|
|
2
|
+
export declare const SerializeDefaults: SerializeLimits;
|
|
3
|
+
export declare const resolveSerializeLimits: (limits?: Partial<SerializeLimits>) => SerializeLimits;
|
|
4
|
+
/**
|
|
5
|
+
* Turns any value into plain, JSON-safe data under a set of limits. Never throws, for any value: a
|
|
6
|
+
* getter that throws while it is read, a revoked proxy, a trap that refuses -- whatever inspecting a
|
|
7
|
+
* value costs, the throw becomes an error entry in place of the value.
|
|
8
|
+
*
|
|
9
|
+
* The rules and their order are the ones spec §7 lists. Cycles are detected along the current
|
|
10
|
+
* path, so a sub-object shared between two keys appears twice rather than as `$circular`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function serializeValue(value: unknown, limits?: Partial<SerializeLimits>): SerializedValue;
|
|
13
|
+
//# sourceMappingURL=serializeValue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializeValue.d.ts","sourceRoot":"","sources":["../../../src/inspect/serializeValue.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,eAAe,EAAE,eAAe,EAAC,MAAM,YAAY,CAAC;AAEjE,eAAO,MAAM,iBAAiB,EAAE,eAK9B,CAAC;AAMH,eAAO,MAAM,sBAAsB,YAAa,OAAO,CAAC,eAAe,CAAC,KAAG,eAKzE,CAAC;AAwBH;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe,CAEjG"}
|