@alloy-js/core 0.23.0-dev.10 → 0.23.0-dev.11

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 (131) hide show
  1. package/dist/devtools/index.html +29 -17
  2. package/dist/src/binder.d.ts.map +1 -1
  3. package/dist/src/binder.js +5 -0
  4. package/dist/src/binder.js.map +1 -1
  5. package/dist/src/components/For.d.ts.map +1 -1
  6. package/dist/src/components/For.js +1 -1
  7. package/dist/src/components/For.js.map +1 -1
  8. package/dist/src/components/List.d.ts.map +1 -1
  9. package/dist/src/components/List.js +1 -1
  10. package/dist/src/components/List.js.map +1 -1
  11. package/dist/src/components/Switch.d.ts.map +1 -1
  12. package/dist/src/components/Switch.js +1 -1
  13. package/dist/src/components/Switch.js.map +1 -1
  14. package/dist/src/debug/diagnostics.test.js +3 -2
  15. package/dist/src/debug/diagnostics.test.js.map +1 -1
  16. package/dist/src/debug/effects.d.ts +12 -4
  17. package/dist/src/debug/effects.d.ts.map +1 -1
  18. package/dist/src/debug/effects.js +182 -52
  19. package/dist/src/debug/effects.js.map +1 -1
  20. package/dist/src/debug/effects.test.js +213 -41
  21. package/dist/src/debug/effects.test.js.map +1 -1
  22. package/dist/src/debug/files.d.ts.map +1 -1
  23. package/dist/src/debug/files.js +7 -18
  24. package/dist/src/debug/files.js.map +1 -1
  25. package/dist/src/debug/files.test.js +13 -36
  26. package/dist/src/debug/files.test.js.map +1 -1
  27. package/dist/src/debug/index.d.ts +4 -2
  28. package/dist/src/debug/index.d.ts.map +1 -1
  29. package/dist/src/debug/index.js +4 -2
  30. package/dist/src/debug/index.js.map +1 -1
  31. package/dist/src/debug/message-format.test.d.ts +2 -0
  32. package/dist/src/debug/message-format.test.d.ts.map +1 -0
  33. package/dist/src/debug/message-format.test.js +700 -0
  34. package/dist/src/debug/message-format.test.js.map +1 -0
  35. package/dist/src/debug/render-tree-orphans.test.d.ts +2 -0
  36. package/dist/src/debug/render-tree-orphans.test.d.ts.map +1 -0
  37. package/dist/src/debug/render-tree-orphans.test.js +297 -0
  38. package/dist/src/debug/render-tree-orphans.test.js.map +1 -0
  39. package/dist/src/debug/render.d.ts.map +1 -1
  40. package/dist/src/debug/render.js +83 -130
  41. package/dist/src/debug/render.js.map +1 -1
  42. package/dist/src/debug/render.test.js +91 -128
  43. package/dist/src/debug/render.test.js.map +1 -1
  44. package/dist/src/debug/symbols.d.ts +6 -5
  45. package/dist/src/debug/symbols.d.ts.map +1 -1
  46. package/dist/src/debug/symbols.js +46 -23
  47. package/dist/src/debug/symbols.js.map +1 -1
  48. package/dist/src/debug/symbols.test.js +15 -26
  49. package/dist/src/debug/symbols.test.js.map +1 -1
  50. package/dist/src/debug/trace-writer.d.ts +55 -0
  51. package/dist/src/debug/trace-writer.d.ts.map +1 -0
  52. package/dist/src/debug/trace-writer.js +658 -0
  53. package/dist/src/debug/trace-writer.js.map +1 -0
  54. package/dist/src/debug/trace.d.ts +10 -10
  55. package/dist/src/debug/trace.d.ts.map +1 -1
  56. package/dist/src/debug/trace.js +23 -20
  57. package/dist/src/debug/trace.js.map +1 -1
  58. package/dist/src/devtools/devtools-protocol.d.ts +318 -161
  59. package/dist/src/devtools/devtools-protocol.d.ts.map +1 -1
  60. package/dist/src/devtools/devtools-server.browser.d.ts +0 -5
  61. package/dist/src/devtools/devtools-server.browser.d.ts.map +1 -1
  62. package/dist/src/devtools/devtools-server.browser.js +0 -3
  63. package/dist/src/devtools/devtools-server.browser.js.map +1 -1
  64. package/dist/src/devtools/devtools-server.d.ts +0 -6
  65. package/dist/src/devtools/devtools-server.d.ts.map +1 -1
  66. package/dist/src/devtools/devtools-server.js +212 -24
  67. package/dist/src/devtools/devtools-server.js.map +1 -1
  68. package/dist/src/devtools/devtools-transport.d.ts +2 -2
  69. package/dist/src/devtools/devtools-transport.d.ts.map +1 -1
  70. package/dist/src/devtools/devtools-transport.js +2 -2
  71. package/dist/src/devtools/devtools-transport.js.map +1 -1
  72. package/dist/src/devtools-entry.browser.d.ts +1 -1
  73. package/dist/src/devtools-entry.browser.d.ts.map +1 -1
  74. package/dist/src/devtools-entry.browser.js.map +1 -1
  75. package/dist/src/devtools-entry.d.ts +1 -1
  76. package/dist/src/devtools-entry.d.ts.map +1 -1
  77. package/dist/src/devtools-entry.js.map +1 -1
  78. package/dist/src/diagnostics.d.ts.map +1 -1
  79. package/dist/src/diagnostics.js +5 -5
  80. package/dist/src/diagnostics.js.map +1 -1
  81. package/dist/src/reactivity.d.ts +13 -2
  82. package/dist/src/reactivity.d.ts.map +1 -1
  83. package/dist/src/reactivity.js +96 -13
  84. package/dist/src/reactivity.js.map +1 -1
  85. package/dist/src/render.d.ts.map +1 -1
  86. package/dist/src/render.js +84 -30
  87. package/dist/src/render.js.map +1 -1
  88. package/dist/src/scheduler.d.ts +5 -0
  89. package/dist/src/scheduler.d.ts.map +1 -1
  90. package/dist/src/scheduler.js +94 -23
  91. package/dist/src/scheduler.js.map +1 -1
  92. package/dist/src/utils.d.ts.map +1 -1
  93. package/dist/src/utils.js +11 -5
  94. package/dist/src/utils.js.map +1 -1
  95. package/dist/testing/devtools-utils.d.ts +12 -3
  96. package/dist/testing/devtools-utils.d.ts.map +1 -1
  97. package/dist/testing/devtools-utils.js +26 -4
  98. package/dist/testing/devtools-utils.js.map +1 -1
  99. package/dist/tsconfig.tsbuildinfo +1 -1
  100. package/package.json +1 -1
  101. package/src/binder.ts +47 -38
  102. package/src/components/For.tsx +14 -10
  103. package/src/components/List.tsx +7 -4
  104. package/src/components/Switch.tsx +11 -7
  105. package/src/debug/diagnostics.test.tsx +3 -2
  106. package/src/debug/effects.test.tsx +248 -36
  107. package/src/debug/effects.ts +276 -62
  108. package/src/debug/files.test.tsx +15 -35
  109. package/src/debug/files.ts +11 -11
  110. package/src/debug/index.ts +4 -0
  111. package/src/debug/message-format.test.tsx +759 -0
  112. package/src/debug/render-tree-orphans.test.tsx +344 -0
  113. package/src/debug/render.test.tsx +96 -118
  114. package/src/debug/render.ts +183 -124
  115. package/src/debug/symbols.test.tsx +19 -20
  116. package/src/debug/symbols.ts +106 -23
  117. package/src/debug/trace-writer.ts +969 -0
  118. package/src/debug/trace.ts +25 -28
  119. package/src/devtools/devtools-protocol.ts +361 -176
  120. package/src/devtools/devtools-server.browser.ts +0 -9
  121. package/src/devtools/devtools-server.ts +210 -32
  122. package/src/devtools/devtools-transport.ts +4 -4
  123. package/src/devtools-entry.browser.ts +11 -15
  124. package/src/devtools-entry.ts +9 -15
  125. package/src/diagnostics.ts +14 -5
  126. package/src/reactivity.ts +113 -17
  127. package/src/render.ts +104 -30
  128. package/src/scheduler.ts +145 -26
  129. package/src/utils.tsx +7 -4
  130. package/temp/api.json +142 -20
  131. package/testing/devtools-utils.ts +46 -4
@@ -13,11 +13,27 @@ import {
13
13
  type PrintHook,
14
14
  type RenderedTextTree,
15
15
  } from "../print-hook.js";
16
- import { untrack } from "../reactivity.js";
16
+ import { getContext, untrack } from "../reactivity.js";
17
17
  import type { ComponentCreator } from "../runtime/component.js";
18
18
  import { flushJobsAsync } from "../scheduler.js";
19
19
  import { sanitizeRecord } from "./serialize.js";
20
- import { emitDevtoolsMessage } from "./trace.js";
20
+ import {
21
+ deleteDirectory,
22
+ deleteOutputFile,
23
+ insertDirectory,
24
+ insertOutputFile,
25
+ insertRenderError,
26
+ insertRenderNode,
27
+ isTraceEnabled,
28
+ notifyFlushComplete,
29
+ notifyRenderComplete,
30
+ notifyRenderReset,
31
+ deleteRenderNode as traceDeleteRenderNode,
32
+ updateRenderNodeProps as traceUpdateRenderNodeProps,
33
+ updateEffectComponentByContext,
34
+ updateRenderNodeContext,
35
+ } from "./trace-writer.js";
36
+ import { isDebugEnabled, logDevtoolsMessage } from "./trace.js";
21
37
 
22
38
  /** The kind discriminant for render tree nodes. */
23
39
  export type RenderTreeNodeKind = RenderTreeNode["kind"];
@@ -63,6 +79,7 @@ type TrackedNode = RenderedTextTree | PrintHook;
63
79
  let nodeIds = new WeakMap<TrackedNode, number>();
64
80
  let idToNode = new Map<number, TrackedNode>();
65
81
  let entryIds = new WeakMap<RenderedTextTree, number[]>();
82
+ let nodeKinds = new WeakMap<TrackedNode, { kind: string; name?: string }>();
66
83
  let fileNodes = new Map<number, { path: string; filetype: string }>();
67
84
  let directoryNodes = new Map<number, { path: string }>();
68
85
  let nodeProps = new Map<number, string | undefined>();
@@ -94,11 +111,7 @@ function serializeRenderTreeProps(input: Record<string, unknown> | undefined) {
94
111
 
95
112
  function emitNodeRemoved(parentId: number | null, id: number) {
96
113
  clearRenderTreeChildrenForId(id);
97
- emitDevtoolsMessage({
98
- type: "render:nodeRemoved",
99
- parentId,
100
- id,
101
- });
114
+ traceDeleteRenderNode(id);
102
115
 
103
116
  rerenderActions.delete(id);
104
117
  nodeProps.delete(id);
@@ -106,19 +119,13 @@ function emitNodeRemoved(parentId: number | null, id: number) {
106
119
 
107
120
  const fileInfo = fileNodes.get(id);
108
121
  if (fileInfo) {
109
- emitDevtoolsMessage({
110
- type: "files:fileRemoved",
111
- path: fileInfo.path,
112
- });
122
+ deleteOutputFile(fileInfo.path);
113
123
  fileNodes.delete(id);
114
124
  }
115
125
 
116
126
  const dirInfo = directoryNodes.get(id);
117
127
  if (dirInfo) {
118
- emitDevtoolsMessage({
119
- type: "files:directoryRemoved",
120
- path: dirInfo.path,
121
- });
128
+ deleteDirectory(dirInfo.path);
122
129
  directoryNodes.delete(id);
123
130
  }
124
131
  }
@@ -134,7 +141,13 @@ function getEntryList(parent: RenderedTextTree) {
134
141
 
135
142
  function getOrCreateNodeId(node: TrackedNode) {
136
143
  const existing = nodeIds.get(node);
137
- if (existing) return existing;
144
+ if (existing) {
145
+ // Restore reverse mapping — emitNodeRemoved deletes idToNode but nodeIds
146
+ // (WeakMap) survives. Without this, clearRenderTreeChildrenForId can't
147
+ // find the node on the next cleanup, leaving orphaned children in the DB.
148
+ idToNode.set(existing, node);
149
+ return existing;
150
+ }
138
151
  const id = nextId++;
139
152
  nodeIds.set(node, id);
140
153
  idToNode.set(id, node);
@@ -142,7 +155,7 @@ function getOrCreateNodeId(node: TrackedNode) {
142
155
  }
143
156
 
144
157
  export function getRenderNodeId(node: RenderedTextTree | PrintHook) {
145
- if (!isDevtoolsEnabled()) return undefined;
158
+ if (!isDebugEnabled()) return undefined;
146
159
  return getOrCreateNodeId(node);
147
160
  }
148
161
 
@@ -152,33 +165,38 @@ function setEntryId(parent: RenderedTextTree, index: number, id: number) {
152
165
  }
153
166
 
154
167
  export function initialize(root: RenderedTextTree) {
155
- if (!isDevtoolsEnabled()) return;
168
+ if (!isDebugEnabled()) return;
156
169
  ensureDevtoolsHandler();
157
170
  nodeIds = new WeakMap();
158
171
  idToNode = new Map();
159
172
  entryIds = new WeakMap();
173
+ nodeKinds = new WeakMap();
160
174
  fileNodes = new Map();
161
175
  directoryNodes = new Map();
162
176
  nodeProps = new Map();
163
177
  rerenderActions = new Map();
164
178
  nextId = 1;
165
- emitDevtoolsMessage({ type: "render:reset" });
179
+ notifyRenderReset();
166
180
  const rootId = getOrCreateNodeId(root);
167
- emitDevtoolsMessage({
168
- type: "render:nodeAdded",
169
- parentId: null,
170
- node: {
171
- id: rootId,
172
- kind: "root",
173
- },
174
- });
181
+ insertRenderNode(
182
+ rootId,
183
+ null,
184
+ "root",
185
+ undefined,
186
+ undefined,
187
+ undefined,
188
+ undefined,
189
+ undefined,
190
+ null,
191
+ undefined,
192
+ );
175
193
  }
176
194
 
177
195
  export function registerRenderNodeActions(
178
196
  node: RenderedTextTree | PrintHook,
179
197
  actions: RenderNodeActions,
180
198
  ) {
181
- if (!isDevtoolsEnabled()) return;
199
+ if (!isDebugEnabled()) return;
182
200
  const id = getOrCreateNodeId(node);
183
201
  rerenderActions.set(id, actions);
184
202
  }
@@ -186,7 +204,7 @@ export function registerRenderNodeActions(
186
204
  export function unregisterRenderNodeActions(
187
205
  node: RenderedTextTree | PrintHook,
188
206
  ) {
189
- if (!isDevtoolsEnabled()) return;
207
+ if (!isDebugEnabled()) return;
190
208
  const id = nodeIds.get(node);
191
209
  if (id !== undefined) {
192
210
  rerenderActions.delete(id);
@@ -223,18 +241,21 @@ export function recordTextNode(
223
241
  index: number,
224
242
  value: string,
225
243
  ) {
226
- if (!isDevtoolsEnabled()) return;
244
+ if (!isDebugEnabled()) return;
227
245
  const id = nextId++;
228
246
  setEntryId(parent, index, id);
229
- emitDevtoolsMessage({
230
- type: "render:nodeAdded",
231
- parentId: getOrCreateNodeId(parent),
232
- node: {
233
- id,
234
- kind: "text",
235
- value,
236
- },
237
- });
247
+ insertRenderNode(
248
+ id,
249
+ getOrCreateNodeId(parent),
250
+ "text",
251
+ undefined,
252
+ undefined,
253
+ undefined,
254
+ undefined,
255
+ undefined,
256
+ null,
257
+ value,
258
+ );
238
259
  }
239
260
 
240
261
  function recordNodeAdded(
@@ -243,20 +264,26 @@ function recordNodeAdded(
243
264
  node: RenderedTextTree | PrintHook,
244
265
  info: RenderTreeNodeInfo,
245
266
  ) {
246
- if (!isDevtoolsEnabled()) return;
267
+ if (!isDebugEnabled()) return;
247
268
  const id = getOrCreateNodeId(node);
248
269
  if (info.propsSerialized !== undefined) {
249
270
  nodeProps.set(id, info.propsSerialized);
250
271
  }
272
+ // Remember the kind so cached re-adds preserve it
273
+ nodeKinds.set(node, { kind: info.kind, name: info.name });
251
274
  setEntryId(parent, index, id);
252
- emitDevtoolsMessage({
253
- type: "render:nodeAdded",
254
- parentId: getOrCreateNodeId(parent),
255
- node: {
256
- id,
257
- ...info,
258
- },
259
- });
275
+ insertRenderNode(
276
+ id,
277
+ getOrCreateNodeId(parent),
278
+ info.kind,
279
+ info.name,
280
+ info.propsSerialized,
281
+ info.source?.fileName,
282
+ info.source?.lineNumber,
283
+ info.source?.columnNumber,
284
+ null,
285
+ undefined,
286
+ );
260
287
  }
261
288
 
262
289
  function recordSubtreeAdded(
@@ -264,25 +291,32 @@ function recordSubtreeAdded(
264
291
  subtree: RenderedTextTree,
265
292
  info: RenderTreeNodeInfo = { kind: "fragment" },
266
293
  ) {
267
- if (!isDevtoolsEnabled()) return;
294
+ if (!isDebugEnabled()) return;
268
295
  const parentId = getOrCreateNodeId(parentNode);
269
296
  // Check if this node was previously rendered (cached) by seeing if it already has an ID
270
297
  const existingId = nodeIds.get(subtree);
271
298
  const isCached = existingId !== undefined;
272
299
  const id = isCached ? existingId : getOrCreateNodeId(subtree);
300
+ // Remember the kind so cached re-adds preserve it
301
+ nodeKinds.set(subtree, { kind: info.kind, name: info.name });
273
302
  // Track in entryIds so clearRenderTreeChildren can find and remove it
274
303
  if (Array.isArray(parentNode)) {
275
304
  const list = getEntryList(parentNode);
276
305
  list.push(id);
277
306
  }
278
- emitDevtoolsMessage({
279
- type: "render:nodeAdded",
307
+ insertRenderNode(
308
+ id,
280
309
  parentId,
281
- node: {
282
- id,
283
- ...info,
284
- },
285
- });
310
+ info.kind,
311
+ info.name,
312
+ info.propsSerialized,
313
+ info.source?.fileName,
314
+ info.source?.lineNumber,
315
+ info.source?.columnNumber,
316
+ null,
317
+ undefined,
318
+ );
319
+
286
320
  // For cached nodes, we need to recursively re-add all their children since
287
321
  // clearRenderTreeChildren removed them when the parent re-rendered
288
322
  if (isCached) {
@@ -297,9 +331,15 @@ function recordSubtreeAdded(
297
331
  * to be explicitly re-added.
298
332
  */
299
333
  function recordCachedSubtreeChildrenRecursively(node: RenderedTextTree) {
300
- if (!isDevtoolsEnabled()) return;
334
+ if (!isDebugEnabled()) return;
301
335
  const parentId = getOrCreateNodeId(node);
302
336
 
337
+ // Clear any previously-recorded children from the DB before re-adding.
338
+ // The in-memory tree may have changed since the last cached re-add
339
+ // (e.g., a memo inside the cached subtree re-ran), so old DB children
340
+ // that are no longer in the tree would become orphans.
341
+ clearRenderTreeChildren(node);
342
+
303
343
  // Rebuild the entryIds for this node
304
344
  const list = getEntryList(node);
305
345
  list.length = 0;
@@ -312,55 +352,70 @@ function recordCachedSubtreeChildrenRecursively(node: RenderedTextTree) {
312
352
  const id = nextId++;
313
353
  list.push(id);
314
354
  idToNode.set(id, child as unknown as RenderedTextTree);
315
- emitDevtoolsMessage({
316
- type: "render:nodeAdded",
355
+ insertRenderNode(
356
+ id,
317
357
  parentId,
318
- node: {
319
- id,
320
- kind: "text",
321
- value: child,
322
- },
323
- });
358
+ "text",
359
+ undefined,
360
+ undefined,
361
+ undefined,
362
+ undefined,
363
+ undefined,
364
+ null,
365
+ child,
366
+ );
324
367
  }
325
368
  } else if (Array.isArray(child)) {
326
- // Nested RenderedTextTree - record and recurse
369
+ // Nested RenderedTextTree - record and recurse, preserving original kind
327
370
  const id = getOrCreateNodeId(child);
328
371
  list.push(id);
329
- emitDevtoolsMessage({
330
- type: "render:nodeAdded",
372
+ const savedKind = nodeKinds.get(child);
373
+ insertRenderNode(
374
+ id,
331
375
  parentId,
332
- node: {
333
- id,
334
- kind: "fragment",
335
- },
336
- });
376
+ savedKind?.kind ?? "fragment",
377
+ savedKind?.name,
378
+ undefined,
379
+ undefined,
380
+ undefined,
381
+ undefined,
382
+ null,
383
+ undefined,
384
+ );
337
385
  recordCachedSubtreeChildrenRecursively(child);
338
386
  } else if (isPrintHook(child)) {
339
387
  // PrintHook - record and recurse into subtree
340
388
  const id = getOrCreateNodeId(child);
341
389
  list.push(id);
342
- emitDevtoolsMessage({
343
- type: "render:nodeAdded",
390
+ insertRenderNode(
391
+ id,
344
392
  parentId,
345
- node: {
346
- id,
347
- kind: "printHook",
348
- name: (child as { name?: string }).name ?? "hook",
349
- },
350
- });
393
+ "printHook",
394
+ (child as { name?: string }).name ?? "hook",
395
+ undefined,
396
+ undefined,
397
+ undefined,
398
+ undefined,
399
+ null,
400
+ undefined,
401
+ );
351
402
  if (child.subtree) {
352
403
  const subtreeId = getOrCreateNodeId(child.subtree);
353
404
  const hookList = getEntryList(child as unknown as RenderedTextTree);
354
405
  hookList.length = 0;
355
406
  hookList.push(subtreeId);
356
- emitDevtoolsMessage({
357
- type: "render:nodeAdded",
358
- parentId: id,
359
- node: {
360
- id: subtreeId,
361
- kind: "fragment",
362
- },
363
- });
407
+ insertRenderNode(
408
+ subtreeId,
409
+ id,
410
+ "fragment",
411
+ undefined,
412
+ undefined,
413
+ undefined,
414
+ undefined,
415
+ undefined,
416
+ null,
417
+ undefined,
418
+ );
364
419
  recordCachedSubtreeChildrenRecursively(child.subtree);
365
420
  }
366
421
  }
@@ -371,20 +426,16 @@ function recordNodePropsUpdated(
371
426
  node: RenderedTextTree | PrintHook,
372
427
  propsSerialized: string | undefined,
373
428
  ) {
374
- if (!isDevtoolsEnabled()) return;
429
+ if (!isDebugEnabled()) return;
375
430
  const id = getOrCreateNodeId(node);
376
431
  const previous = nodeProps.get(id);
377
432
  if (previous === propsSerialized) return;
378
433
  nodeProps.set(id, propsSerialized);
379
- emitDevtoolsMessage({
380
- type: "render:nodeUpdated",
381
- id,
382
- propsSerialized,
383
- });
434
+ traceUpdateRenderNodeProps(id, propsSerialized);
384
435
  }
385
436
 
386
437
  function clearRenderTreeChildren(parent: RenderedTextTree) {
387
- if (!isDevtoolsEnabled()) return;
438
+ if (!isDebugEnabled()) return;
388
439
  const list = entryIds.get(parent);
389
440
  if (!list || list.length === 0) return;
390
441
  const parentId = getOrCreateNodeId(parent);
@@ -403,9 +454,15 @@ function clearRenderTreeChildrenForId(id: number) {
403
454
  clearRenderTreeChildren(node);
404
455
  return;
405
456
  }
457
+ // For PrintHook nodes: clear the subtree's children, then remove
458
+ // the subtree node itself (which is a child of this hook).
406
459
  const subtree = (node as { subtree?: RenderedTextTree }).subtree;
407
460
  if (subtree && Array.isArray(subtree)) {
408
461
  clearRenderTreeChildren(subtree);
462
+ const subtreeId = nodeIds.get(subtree);
463
+ if (subtreeId !== undefined) {
464
+ emitNodeRemoved(id, subtreeId);
465
+ }
409
466
  }
410
467
  }
411
468
 
@@ -414,14 +471,11 @@ function clearRenderTreeChildrenForId(id: number) {
414
471
  // ─────────────────────────────────────────────────────────────────────────────
415
472
 
416
473
  function recordDirectoryNode(node: RenderedTextTree, path: string) {
417
- if (!isDevtoolsEnabled()) return;
474
+ if (!isDebugEnabled()) return;
418
475
  const id = getOrCreateNodeId(node);
419
476
  if (directoryNodes.has(id)) return;
420
477
  directoryNodes.set(id, { path });
421
- emitDevtoolsMessage({
422
- type: "files:directoryAdded",
423
- path,
424
- });
478
+ insertDirectory(path);
425
479
  }
426
480
 
427
481
  function recordFileNode(
@@ -429,36 +483,25 @@ function recordFileNode(
429
483
  path: string,
430
484
  filetype: string,
431
485
  ) {
432
- if (!isDevtoolsEnabled()) return;
486
+ if (!isDebugEnabled()) return;
433
487
  const id = getOrCreateNodeId(node);
434
488
  if (fileNodes.has(id)) return;
435
489
  fileNodes.set(id, { path, filetype });
436
- emitDevtoolsMessage({
437
- type: "files:fileAdded",
438
- path,
439
- filetype,
440
- renderNodeId: id,
441
- });
490
+ insertOutputFile(path, filetype, id);
442
491
  }
443
492
 
444
493
  function removeFileEntriesForNode(node: RenderedTextTree | PrintHook) {
445
- if (!isDevtoolsEnabled()) return;
494
+ if (!isDebugEnabled()) return;
446
495
  const id = nodeIds.get(node);
447
496
  if (id === undefined) return;
448
497
  const fileInfo = fileNodes.get(id);
449
498
  if (fileInfo) {
450
- emitDevtoolsMessage({
451
- type: "files:fileRemoved",
452
- path: fileInfo.path,
453
- });
499
+ deleteOutputFile(fileInfo.path);
454
500
  fileNodes.delete(id);
455
501
  }
456
502
  const dirInfo = directoryNodes.get(id);
457
503
  if (dirInfo) {
458
- emitDevtoolsMessage({
459
- type: "files:directoryRemoved",
460
- path: dirInfo.path,
461
- });
504
+ deleteDirectory(dirInfo.path);
462
505
  directoryNodes.delete(id);
463
506
  }
464
507
  }
@@ -482,7 +525,7 @@ export function beginComponent(
482
525
  actions,
483
526
  } = options;
484
527
 
485
- if (!isDevtoolsEnabled()) {
528
+ if (!isDebugEnabled()) {
486
529
  return {
487
530
  recordDirectory() {},
488
531
  recordFile() {},
@@ -511,6 +554,14 @@ export function beginComponent(
511
554
  source,
512
555
  });
513
556
  }
557
+
558
+ if (isTraceEnabled()) {
559
+ const ctx = getContext();
560
+ if (ctx) {
561
+ updateRenderNodeContext(getOrCreateNodeId(node), ctx.id);
562
+ updateEffectComponentByContext(ctx.id, componentName);
563
+ }
564
+ }
514
565
  recordNodePropsUpdated(node, propsSerialized);
515
566
  registerRenderNodeActions(node, actions);
516
567
 
@@ -610,7 +661,7 @@ export function error(
610
661
  error: RenderErrorInfo,
611
662
  componentStack: RenderErrorStackEntry[],
612
663
  ) {
613
- if (!isDevtoolsEnabled()) return;
664
+ if (!isDebugEnabled()) return;
614
665
  const serializedStack = untrack(() =>
615
666
  componentStack.map((entry) => ({
616
667
  name: entry.name,
@@ -619,21 +670,29 @@ export function error(
619
670
  source: entry.source,
620
671
  })),
621
672
  );
622
- const message = {
673
+ logDevtoolsMessage({
623
674
  type: "render:error" as const,
624
675
  id: nextErrorId++,
625
676
  name: error.name,
626
677
  message: error.message,
627
678
  stack: error.stack,
628
679
  componentStack: serializedStack,
629
- };
630
- emitDevtoolsMessage(message);
680
+ });
681
+
682
+ insertRenderError(
683
+ error.name,
684
+ error.message,
685
+ error.stack,
686
+ JSON.stringify(serializedStack),
687
+ );
631
688
  }
632
689
 
633
690
  export function complete() {
634
- emitDevtoolsMessage({ type: "render:complete" });
691
+ logDevtoolsMessage({ type: "render:complete" });
692
+ notifyRenderComplete();
635
693
  }
636
694
 
637
695
  export function flushJobsComplete() {
638
- emitDevtoolsMessage({ type: "flushJobs:complete" });
696
+ logDevtoolsMessage({ type: "flushJobs:complete" });
697
+ notifyFlushComplete();
639
698
  }
@@ -34,7 +34,7 @@ afterEach(async () => {
34
34
  });
35
35
 
36
36
  it("emits tracking info", async () => {
37
- const collector = createMessageCollector(socket!);
37
+ const collector = await createMessageCollector(socket!);
38
38
  const binder = createOutputBinder();
39
39
 
40
40
  const scope = createScope(BasicScope, "root", undefined, { binder });
@@ -44,14 +44,14 @@ it("emits tracking info", async () => {
44
44
  });
45
45
  symbol.name = "hi";
46
46
  await flushJobsAsync();
47
- const items = (await collector.waitForFlush()).filter((m) =>
48
- m.type.startsWith("effect:track"),
47
+ const items = (await collector.waitForFlush()).filter(
48
+ (m) => m.type.startsWith("edge:track") && !("triggerIds" in m),
49
49
  );
50
50
  expect(items.length).toBeGreaterThan(0);
51
51
  });
52
52
 
53
53
  it("emits symbol and scope add/update/remove messages", async () => {
54
- const collector = createMessageCollector(socket!);
54
+ const collector = await createMessageCollector(socket!);
55
55
  const binder = createOutputBinder();
56
56
 
57
57
  const scope = createScope(BasicScope, "root", undefined, { binder });
@@ -70,37 +70,36 @@ it("emits symbol and scope add/update/remove messages", async () => {
70
70
 
71
71
  const messages = await collector.waitForFlush();
72
72
  const symbolsMessages = messages.filter((m) => {
73
- return m.type.startsWith("scope:") || m.type.startsWith("symbol:");
73
+ return (
74
+ (m.type.startsWith("scope:") || m.type.startsWith("symbol:")) &&
75
+ !("triggerIds" in m)
76
+ );
74
77
  });
75
78
  collector.stop();
76
79
 
77
80
  expect(symbolsMessages[0]).toMatchObject({
78
- type: "scope:create",
79
- scope: expect.objectContaining({ name: "root" }),
81
+ type: "scope:added",
82
+ name: "root",
80
83
  });
81
84
 
82
85
  expect(symbolsMessages[1]).toMatchObject({
83
- type: "symbol:addToScope",
86
+ type: "symbol:added",
87
+ name: "Foo",
84
88
  });
85
-
86
89
  expect(symbolsMessages[2]).toMatchObject({
87
- type: "symbol:create",
88
- symbol: expect.objectContaining({ name: "Foo" }),
90
+ type: "scope:updated",
91
+ name: "root-updated",
89
92
  });
90
93
  expect(symbolsMessages[3]).toMatchObject({
91
- type: "scope:update",
92
- scope: expect.objectContaining({ name: "root-updated" }),
94
+ type: "symbol:updated",
95
+ name: "FooUpdated",
93
96
  });
94
97
  expect(symbolsMessages[4]).toMatchObject({
95
- type: "symbol:update",
96
- symbol: expect.objectContaining({ name: "FooUpdated" }),
97
- });
98
- expect(symbolsMessages[5]).toMatchObject({
99
- type: "symbol:delete",
98
+ type: "symbol:removed",
100
99
  id: expect.any(Number),
101
100
  });
102
- expect(symbolsMessages[6]).toMatchObject({
103
- type: "scope:delete",
101
+ expect(symbolsMessages[5]).toMatchObject({
102
+ type: "scope:removed",
104
103
  id: expect.any(Number),
105
104
  });
106
105
  });