@shadow-js/devtools 0.0.0-stage → 1.0.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.
Files changed (112) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +21 -0
  3. package/README.md +576 -2
  4. package/dist/dev/agent.d.ts +239 -0
  5. package/dist/dev/agent.js +626 -0
  6. package/dist/dev/agent.js.map +1 -0
  7. package/dist/dev/backend.d.ts +53 -0
  8. package/dist/dev/backend.js +53 -0
  9. package/dist/dev/backend.js.map +1 -0
  10. package/dist/dev/hook.d.ts +83 -0
  11. package/dist/dev/hook.js +136 -0
  12. package/dist/dev/hook.js.map +1 -0
  13. package/dist/dev/index.d.ts +17 -0
  14. package/dist/dev/index.js +17 -0
  15. package/dist/dev/inspection.d.ts +226 -0
  16. package/dist/dev/inspection.js +263 -0
  17. package/dist/dev/inspection.js.map +1 -0
  18. package/dist/dev/inspector.d.ts +331 -0
  19. package/dist/dev/inspector.js +429 -0
  20. package/dist/dev/inspector.js.map +1 -0
  21. package/dist/dev/model.d.ts +418 -0
  22. package/dist/dev/model.js +559 -0
  23. package/dist/dev/model.js.map +1 -0
  24. package/dist/dev/mount.d.ts +207 -0
  25. package/dist/dev/mount.js +256 -0
  26. package/dist/dev/mount.js.map +1 -0
  27. package/dist/dev/overlay.d.ts +102 -0
  28. package/dist/dev/overlay.js +176 -0
  29. package/dist/dev/overlay.js.map +1 -0
  30. package/dist/dev/preview.d.ts +74 -0
  31. package/dist/dev/preview.js +122 -0
  32. package/dist/dev/preview.js.map +1 -0
  33. package/dist/dev/protocol.d.ts +288 -0
  34. package/dist/dev/protocol.js +109 -0
  35. package/dist/dev/protocol.js.map +1 -0
  36. package/dist/dev/ring.d.ts +50 -0
  37. package/dist/dev/ring.js +94 -0
  38. package/dist/dev/ring.js.map +1 -0
  39. package/dist/dev/transport.d.ts +347 -0
  40. package/dist/dev/transport.js +394 -0
  41. package/dist/dev/transport.js.map +1 -0
  42. package/dist/dev/ui/details.js +294 -0
  43. package/dist/dev/ui/details.js.map +1 -0
  44. package/dist/dev/ui/inspection-state.d.ts +43 -0
  45. package/dist/dev/ui/inspection-state.js +62 -0
  46. package/dist/dev/ui/inspection-state.js.map +1 -0
  47. package/dist/dev/ui/issues.js +85 -0
  48. package/dist/dev/ui/issues.js.map +1 -0
  49. package/dist/dev/ui/panel.d.ts +55 -0
  50. package/dist/dev/ui/panel.js +160 -0
  51. package/dist/dev/ui/panel.js.map +1 -0
  52. package/dist/dev/ui/rows.d.ts +44 -0
  53. package/dist/dev/ui/rows.js +132 -0
  54. package/dist/dev/ui/rows.js.map +1 -0
  55. package/dist/dev/ui/state-tab.js +88 -0
  56. package/dist/dev/ui/state-tab.js.map +1 -0
  57. package/dist/dev/ui/state.d.ts +192 -0
  58. package/dist/dev/ui/state.js +147 -0
  59. package/dist/dev/ui/state.js.map +1 -0
  60. package/dist/dev/ui/styles.d.ts +31 -0
  61. package/dist/dev/ui/styles.js +113 -0
  62. package/dist/dev/ui/styles.js.map +1 -0
  63. package/dist/dev/ui/timeline.js +135 -0
  64. package/dist/dev/ui/timeline.js.map +1 -0
  65. package/dist/dev/ui/tree.js +186 -0
  66. package/dist/dev/ui/tree.js.map +1 -0
  67. package/dist/prod/agent.js +2 -0
  68. package/dist/prod/agent.js.map +1 -0
  69. package/dist/prod/backend.js +2 -0
  70. package/dist/prod/backend.js.map +1 -0
  71. package/dist/prod/hook.js +2 -0
  72. package/dist/prod/hook.js.map +1 -0
  73. package/dist/prod/index.js +1 -0
  74. package/dist/prod/inspection.js +2 -0
  75. package/dist/prod/inspection.js.map +1 -0
  76. package/dist/prod/inspector.js +2 -0
  77. package/dist/prod/inspector.js.map +1 -0
  78. package/dist/prod/model.js +2 -0
  79. package/dist/prod/model.js.map +1 -0
  80. package/dist/prod/mount.js +2 -0
  81. package/dist/prod/mount.js.map +1 -0
  82. package/dist/prod/overlay.js +11 -0
  83. package/dist/prod/overlay.js.map +1 -0
  84. package/dist/prod/preview.js +2 -0
  85. package/dist/prod/preview.js.map +1 -0
  86. package/dist/prod/protocol.js +2 -0
  87. package/dist/prod/protocol.js.map +1 -0
  88. package/dist/prod/ring.js +2 -0
  89. package/dist/prod/ring.js.map +1 -0
  90. package/dist/prod/transport.js +2 -0
  91. package/dist/prod/transport.js.map +1 -0
  92. package/dist/prod/ui/details.js +2 -0
  93. package/dist/prod/ui/details.js.map +1 -0
  94. package/dist/prod/ui/inspection-state.js +2 -0
  95. package/dist/prod/ui/inspection-state.js.map +1 -0
  96. package/dist/prod/ui/issues.js +2 -0
  97. package/dist/prod/ui/issues.js.map +1 -0
  98. package/dist/prod/ui/panel.js +2 -0
  99. package/dist/prod/ui/panel.js.map +1 -0
  100. package/dist/prod/ui/rows.js +2 -0
  101. package/dist/prod/ui/rows.js.map +1 -0
  102. package/dist/prod/ui/state-tab.js +2 -0
  103. package/dist/prod/ui/state-tab.js.map +1 -0
  104. package/dist/prod/ui/state.js +2 -0
  105. package/dist/prod/ui/state.js.map +1 -0
  106. package/dist/prod/ui/styles.js +82 -0
  107. package/dist/prod/ui/styles.js.map +1 -0
  108. package/dist/prod/ui/timeline.js +2 -0
  109. package/dist/prod/ui/timeline.js.map +1 -0
  110. package/dist/prod/ui/tree.js +2 -0
  111. package/dist/prod/ui/tree.js.map +1 -0
  112. package/package.json +69 -4
@@ -0,0 +1,418 @@
1
+ import { WireEvent } from "./protocol.js";
2
+ import { RingBuffer } from "./ring.js";
3
+ //#region src/model.d.ts
4
+ /**
5
+ * The kinds of node the inspector tracks: an owner, a signal, a memo, an effect, a resource or a store.
6
+ *
7
+ * @since 1.0.0
8
+ */
9
+ export type NodeType = "owner" | "signal" | "memo" | "effect" | "resource" | "store";
10
+ /**
11
+ * Everything the model knows about one node. Live nodes are in {@link InspectorModel.nodes}; disposed
12
+ * ones are kept in {@link InspectorModel.graveyard} for a while.
13
+ *
14
+ * @since 1.0.0
15
+ */
16
+ export interface NodeRecord {
17
+ /** Id assigned by the agent. Never {@link NO_ID}. */
18
+ id: number;
19
+ /** What kind of node this is. */
20
+ type: NodeType;
21
+ /**
22
+ * How the node is labeled: `owner` or `root` for owners, `memo` or `effect` for computations,
23
+ * `signal`, `resource` or `store`, and `component` once a component was mounted on the owner.
24
+ */
25
+ label: string;
26
+ /** The name the code gave the node, or `undefined`. */
27
+ name: string | undefined;
28
+ /** Owner id, or {@link NO_ID} for roots and detached nodes. */
29
+ parent: number;
30
+ /** False once the node left the live set: disposed, cascaded from its owner, or garbage collected. */
31
+ alive: boolean;
32
+ /** Sequence number of the event that created the node. */
33
+ createdSeq: number;
34
+ /** Time of the creation event, in milliseconds on the agent's clock. */
35
+ createdAt: number;
36
+ /** Sequence number of the event that removed the node, or 0 while it is alive. */
37
+ disposedSeq: number;
38
+ /** Computations: created in the render phase (the runtime's render flag). False otherwise. */
39
+ render: boolean;
40
+ /** True once a component was mounted on this owner. */
41
+ isComponent: boolean;
42
+ /** Time the component was mounted (agent clock, in milliseconds), or 0 when it never was. */
43
+ mountedAt: number;
44
+ /** Computations: ids of the nodes read by the last run. Empty for other nodes. */
45
+ sources: number[];
46
+ /** Ids of the computations that currently read this node. `null` when none ever did. */
47
+ observers: Set<number> | null;
48
+ /** Number of runs recorded. 0 for nodes that never run. */
49
+ runs: number;
50
+ /** Sum of the durations of all runs, in milliseconds. */
51
+ totalDuration: number;
52
+ /** Duration of the last run, in milliseconds. */
53
+ lastDuration: number;
54
+ /** Start of the last run, in milliseconds as the runtime reported it. */
55
+ lastStart: number;
56
+ /** Sequence number of the last run, or 0 when it never ran. */
57
+ lastRunSeq: number;
58
+ /**
59
+ * Sequence number of the last change: a write for a signal, a run that changed the value for a
60
+ * computation, or a state report for a resource. 0 when there was none.
61
+ */
62
+ lastChangeSeq: number;
63
+ /** Signals: a short text of the current value. Empty for other nodes. */
64
+ preview: string;
65
+ /** Number of writes to a signal. 0 for other nodes. */
66
+ writes: number;
67
+ /** Sequence number of the last write, or 0 when never written. */
68
+ lastWriteSeq: number;
69
+ /**
70
+ * Id of the computation that performed the last write, or {@link NO_ID} for a write from outside
71
+ * any computation (an event handler, for example) and when the node was never written.
72
+ */
73
+ lastWriter: number;
74
+ /** Resources: the last reported state, for example `"pending"` or `"ready"`. Empty for other nodes. */
75
+ state: string;
76
+ }
77
+ /**
78
+ * Why a computation ran. A run has one cause per source that changed since the computation's previous
79
+ * run; see {@link RunRecord.causes}.
80
+ *
81
+ * @since 1.0.0
82
+ */
83
+ export interface Cause {
84
+ /**
85
+ * `write` when the source is a signal that was written, `run` when the source is a memo or a
86
+ * resource that changed, `initial` for the first run, and `unknown` when no recorded source changed.
87
+ */
88
+ kind: "write" | "run" | "initial" | "unknown";
89
+ /** The source that changed: a signal or memo ({@link NO_ID} for `initial` and `unknown`). */
90
+ id: number;
91
+ /**
92
+ * Sequence number of that change: the write or the run of the source, the creation for `initial`,
93
+ * or the last run for `unknown`.
94
+ */
95
+ seq: number;
96
+ }
97
+ /**
98
+ * One run of an effect or a memo, as recorded.
99
+ *
100
+ * @since 1.0.0
101
+ */
102
+ export interface RunRecord {
103
+ /** Sequence number of the run event. */
104
+ seq: number;
105
+ /** Time the event was received (agent clock, in milliseconds). */
106
+ t: number;
107
+ /** The computation that ran. */
108
+ id: number;
109
+ /** When the run started, in milliseconds as the runtime reported it. */
110
+ start: number;
111
+ /** How long the run took, in milliseconds. */
112
+ duration: number;
113
+ /** True when the computation's value changed. */
114
+ changed: boolean;
115
+ /** Sequence number of the flush it belongs to ({@link FlushRecord.seq}), or 0 when it ran outside a flush. */
116
+ flush: number;
117
+ /** Why it ran: see {@link Cause}. */
118
+ causes: Cause[];
119
+ }
120
+ /**
121
+ * One signal write, as recorded.
122
+ *
123
+ * @since 1.0.0
124
+ */
125
+ export interface WriteRecord {
126
+ /** Sequence number of the write event. */
127
+ seq: number;
128
+ /** Time the event was received (agent clock, in milliseconds). */
129
+ t: number;
130
+ /** The signal that was written. */
131
+ id: number;
132
+ /** A short text of the value written. */
133
+ preview: string;
134
+ /** Computation that wrote ({@link NO_ID}: outside code such as an event handler). */
135
+ writer: number;
136
+ /** Ids of the computations that read the signal when it was written. */
137
+ observers: number[];
138
+ }
139
+ /**
140
+ * One flush of the effect queues. A flush is recorded only when at least one of its runs is recorded.
141
+ *
142
+ * @since 1.0.0
143
+ */
144
+ export interface FlushRecord {
145
+ /** Sequence number of the flush-start event. */
146
+ seq: number;
147
+ /** The same value as {@link FlushRecord.seq}: the sequence number of the flush-start event. */
148
+ startSeq: number;
149
+ /** When the flush started (agent clock, in milliseconds). */
150
+ start: number;
151
+ /** When the flush ended (agent clock, in milliseconds). Equal to `start` while the flush is open. */
152
+ end: number;
153
+ /** Effects in the render queue when the flush started. */
154
+ render: number;
155
+ /** Effects in the user queue when the flush started. */
156
+ user: number;
157
+ /**
158
+ * Runs recorded during the flush. The model counts them itself, so runs the agent ignored (the
159
+ * tools' own) are not included. 0 while the flush is open.
160
+ */
161
+ runs: number;
162
+ /** True until the flush-end event arrives. */
163
+ open: boolean;
164
+ }
165
+ /**
166
+ * An error the runtime reported.
167
+ *
168
+ * @since 1.0.0
169
+ */
170
+ export interface ErrorRecord {
171
+ /** Sequence number of the error event. */
172
+ seq: number;
173
+ /** Time the event was received (agent clock, in milliseconds). */
174
+ t: number;
175
+ /** The node the error came from, or {@link NO_ID} when it had no known owner. */
176
+ id: number;
177
+ /** The thrown value as text (see `errorMessage`). */
178
+ message: string;
179
+ /** A diagnostic code the runtime attached, for example `WRITE_IN_MEMO`, or `undefined`. */
180
+ code: string | undefined;
181
+ }
182
+ /**
183
+ * A hydration mismatch the runtime reported.
184
+ *
185
+ * @since 1.0.0
186
+ */
187
+ export interface HydrationRecord {
188
+ /** Sequence number of the mismatch event. */
189
+ seq: number;
190
+ /** Time the event was received (agent clock, in milliseconds). */
191
+ t: number;
192
+ /** What the runtime reported, as text. */
193
+ message: string;
194
+ /** The expected side, as text: a string, or the HTML of an element. Cut to 200 characters. */
195
+ expected: string;
196
+ /** The actual side, as text: a string, or the HTML of an element. Cut to 200 characters. */
197
+ actual: string;
198
+ }
199
+ /**
200
+ * Capacities of the model's bounded buffers. When passed to the constructor each field is optional;
201
+ * the values left out are the defaults in {@link DEFAULT_LIMITS}.
202
+ *
203
+ * @since 1.0.0
204
+ */
205
+ export interface ModelLimits {
206
+ /**
207
+ * Run records kept for timelines and causal chains.
208
+ *
209
+ * @default 20000
210
+ */
211
+ runs: number;
212
+ /**
213
+ * Write records kept for causal chains.
214
+ *
215
+ * @default 10000
216
+ */
217
+ writes: number;
218
+ /**
219
+ * Flush records kept.
220
+ *
221
+ * @default 1000
222
+ */
223
+ flushes: number;
224
+ /**
225
+ * Error records kept, and hydration records kept (each buffer on its own).
226
+ *
227
+ * @default 200
228
+ */
229
+ errors: number;
230
+ /**
231
+ * Disposed nodes kept so that old runs can still be named.
232
+ *
233
+ * @default 4000
234
+ */
235
+ graveyard: number;
236
+ /**
237
+ * Live nodes tracked. A node created beyond this count is counted in {@link InspectorModel.overflow}
238
+ * and ignored; later events about it count as unknown.
239
+ *
240
+ * @default 100000
241
+ */
242
+ maxNodes: number;
243
+ }
244
+ /**
245
+ * The default capacities of the model's buffers, used for each limit that the caller leaves out.
246
+ *
247
+ * @example
248
+ * import { DEFAULT_LIMITS, foldEvents } from "@shadow-js/devtools";
249
+ * import type { WireEvent } from "@shadow-js/devtools";
250
+ *
251
+ * const events: WireEvent[] = [];
252
+ * const model = foldEvents(events, { ...DEFAULT_LIMITS, runs: 500 });
253
+ * @since 1.0.0
254
+ */
255
+ export declare const DEFAULT_LIMITS: Readonly<ModelLimits>;
256
+ /**
257
+ * The JSON-safe state of a model: what the agent's `state()` returns (for a remote panel) and what
258
+ * {@link InspectorModel.importState} accepts.
259
+ *
260
+ * @since 1.0.0
261
+ */
262
+ export interface ModelState {
263
+ /** Sequence number of the last applied event. */
264
+ lastSeq: number;
265
+ /** Nodes ignored because `maxNodes` was reached. */
266
+ overflow: number;
267
+ /** Events that referred to a node the model does not know. */
268
+ unknown: number;
269
+ /** Live nodes, in insertion order, with observers as arrays. */
270
+ nodes: Array<Omit<NodeRecord, "observers"> & {
271
+ observers: number[];
272
+ }>;
273
+ /** Disposed nodes, oldest first, with observers as arrays. */
274
+ graveyard: Array<Omit<NodeRecord, "observers"> & {
275
+ observers: number[];
276
+ }>;
277
+ /** Run records, oldest first. */
278
+ runs: RunRecord[];
279
+ /** Write records, oldest first. */
280
+ writes: WriteRecord[];
281
+ /** Flush records, oldest first. */
282
+ flushes: FlushRecord[];
283
+ /** Error records, oldest first. */
284
+ errors: ErrorRecord[];
285
+ /** Hydration records, oldest first. */
286
+ hydration: HydrationRecord[];
287
+ }
288
+ /**
289
+ * The inspector's incremental model of the reactive graph: a pure fold over {@link WireEvent}s. The
290
+ * same events always produce the same state, on the page or in a remote panel. `apply` is O(1) for
291
+ * most events and O(sources) for a run; memory is bounded by {@link ModelLimits}.
292
+ *
293
+ * A development tool: its events come from the dev hook, which exists only in development builds.
294
+ *
295
+ * @example
296
+ * import { InspectorModel, snapshotOf } from "@shadow-js/devtools";
297
+ * import type { WireEvent } from "@shadow-js/devtools";
298
+ *
299
+ * const events: WireEvent[] = [];
300
+ * const model = new InspectorModel();
301
+ * for (const event of events) model.apply(event);
302
+ * snapshotOf(model).counts.nodes;
303
+ * @since 1.0.0
304
+ */
305
+ export declare class InspectorModel {
306
+ /** The capacities in effect: the defaults with the constructor's overrides applied. */
307
+ readonly limits: Readonly<ModelLimits>;
308
+ /** Live nodes by id. */
309
+ readonly nodes: Map<number, NodeRecord>;
310
+ /**
311
+ * Recently disposed nodes, oldest first, kept so that history can still be named. Holds at most
312
+ * `limits.graveyard` nodes.
313
+ */
314
+ readonly graveyard: Map<number, NodeRecord>;
315
+ /** Run records, the newest kept (capacity `limits.runs`). */
316
+ readonly runs: RingBuffer<RunRecord>;
317
+ /** Write records, the newest kept (capacity `limits.writes`). */
318
+ readonly writes: RingBuffer<WriteRecord>;
319
+ /** Flush records, the newest kept (capacity `limits.flushes`). */
320
+ readonly flushes: RingBuffer<FlushRecord>;
321
+ /** Error records, the newest kept (capacity `limits.errors`). */
322
+ readonly errors: RingBuffer<ErrorRecord>;
323
+ /** Hydration records, the newest kept (capacity `limits.errors`). */
324
+ readonly hydration: RingBuffer<HydrationRecord>;
325
+ /** Sequence number of the last applied event. */
326
+ lastSeq: number;
327
+ /** Nodes ignored because `maxNodes` was reached. */
328
+ overflow: number;
329
+ /** Events that referred to a node the model does not know. */
330
+ unknown: number;
331
+ /**
332
+ * Changes whenever a component is mounted, unmounted or disposed. Cached component lookups from an
333
+ * older epoch are recomputed.
334
+ */
335
+ componentEpoch: number;
336
+ /** Entries in the component lookup cache. Never more than `limits.maxNodes` after a lookup. */
337
+ get componentCacheSize(): number;
338
+ /**
339
+ * Called once for every node that leaves the live set, after it moved to the graveyard: when it is
340
+ * disposed, or when its owner is disposed (cascade). The agent uses it to drop its own references.
341
+ */
342
+ onBury: ((record: NodeRecord) => void) | undefined;
343
+ private openFlush;
344
+ private flushRuns;
345
+ /** Signals, stores and resources by owner: they have no lifecycle event of their own. */
346
+ private readonly leaves;
347
+ private readonly componentCache;
348
+ /**
349
+ * @param limits Overrides for the buffer capacities. Values left out take the defaults in
350
+ * {@link DEFAULT_LIMITS}.
351
+ */
352
+ constructor(limits?: Partial<ModelLimits>);
353
+ /**
354
+ * The node with this id: alive, or recently disposed (in the graveyard).
355
+ *
356
+ * @param id The node id.
357
+ * @returns The record, or `undefined` when the model does not know the id.
358
+ */
359
+ node(id: number): NodeRecord | undefined;
360
+ private create;
361
+ /** Remembers that a leaf node (signal, store, resource) belongs to `owner`. */
362
+ private addLeaf;
363
+ private bury;
364
+ private setSources;
365
+ /**
366
+ * Applies one event. Events must arrive in `seq` order: an event whose `seq` is not greater than
367
+ * {@link InspectorModel.lastSeq} is ignored, so a remote panel can receive events it already holds.
368
+ * An event about a node the model does not know is counted in `unknown`. A node created beyond
369
+ * `maxNodes` is counted in `overflow` and ignored.
370
+ *
371
+ * @param event The event, with the sequence number and time the agent gave it.
372
+ */
373
+ apply(event: WireEvent): void;
374
+ /** Why a computation is about to run again: sources that changed since its last run. */
375
+ private causesOf;
376
+ /**
377
+ * The nearest component that owns `id`: the node itself when it is a component, otherwise the first
378
+ * component above it in the owner chain (the graveyard included). The answer is cached until the next
379
+ * component mount, unmount or disposal.
380
+ *
381
+ * @param id The node id.
382
+ * @returns The component's id, or {@link NO_ID} when no component owns the node.
383
+ */
384
+ componentOf(id: number): number;
385
+ /**
386
+ * A serializable copy of the whole state, safe to send as JSON. {@link InspectorModel.importState}
387
+ * rebuilds an equal model from it.
388
+ *
389
+ * @returns The state.
390
+ */
391
+ exportState(): ModelState;
392
+ /**
393
+ * Replaces the whole state with `state`. A remote panel does this with the agent's state, and the
394
+ * agent does it with an empty state to implement `clear`. The capacities of this model do not change:
395
+ * when `state` holds more records than a buffer can keep, the newest are kept.
396
+ *
397
+ * @param state The state to load.
398
+ */
399
+ importState(state: ModelState): void;
400
+ }
401
+ /**
402
+ * Builds a model from a finished event stream. A pure function: the same input always gives the same
403
+ * model. A development tool, like {@link InspectorModel}.
404
+ *
405
+ * @param events Events in `seq` order.
406
+ * @param limits Buffer capacities. Values left out take the defaults in {@link DEFAULT_LIMITS}.
407
+ * @returns The folded model.
408
+ * @example
409
+ * import { foldEvents } from "@shadow-js/devtools";
410
+ * import type { WireEvent } from "@shadow-js/devtools";
411
+ *
412
+ * const events: WireEvent[] = [];
413
+ * const model = foldEvents(events, { runs: 1000 });
414
+ * @since 1.0.0
415
+ */
416
+ export declare function foldEvents(events: Iterable<WireEvent>, limits?: Partial<ModelLimits>): InspectorModel;
417
+ //#endregion
418
+ //# sourceMappingURL=model.d.ts.map