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

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 (316) hide show
  1. package/CHANGELOG.md +0 -22
  2. package/dist/devtools/index.html +68 -0
  3. package/dist/src/binder.d.ts +2 -0
  4. package/dist/src/binder.d.ts.map +1 -1
  5. package/dist/src/binder.js +55 -12
  6. package/dist/src/binder.js.map +1 -1
  7. package/dist/src/components/AccessExpression.d.ts +78 -0
  8. package/dist/src/components/AccessExpression.d.ts.map +1 -0
  9. package/dist/src/components/AccessExpression.js +218 -0
  10. package/dist/src/components/AccessExpression.js.map +1 -0
  11. package/dist/src/components/AccessExpression.test.d.ts +2 -0
  12. package/dist/src/components/AccessExpression.test.d.ts.map +1 -0
  13. package/dist/src/components/AccessExpression.test.js +137 -0
  14. package/dist/src/components/AccessExpression.test.js.map +1 -0
  15. package/dist/src/components/AppendFile.d.ts.map +1 -1
  16. package/dist/src/components/AppendFile.js +14 -3
  17. package/dist/src/components/AppendFile.js.map +1 -1
  18. package/dist/src/components/Block.js +1 -1
  19. package/dist/src/components/Block.js.map +1 -1
  20. package/dist/src/components/Declaration.d.ts.map +1 -1
  21. package/dist/src/components/Declaration.js +2 -1
  22. package/dist/src/components/Declaration.js.map +1 -1
  23. package/dist/src/components/Prose.js +2 -2
  24. package/dist/src/components/Prose.js.map +1 -1
  25. package/dist/src/components/Scope.d.ts.map +1 -1
  26. package/dist/src/components/Scope.js +6 -1
  27. package/dist/src/components/Scope.js.map +1 -1
  28. package/dist/src/components/SourceDirectory.d.ts.map +1 -1
  29. package/dist/src/components/SourceDirectory.js +1 -2
  30. package/dist/src/components/SourceDirectory.js.map +1 -1
  31. package/dist/src/components/TemplateFile.d.ts.map +1 -1
  32. package/dist/src/components/TemplateFile.js +18 -3
  33. package/dist/src/components/TemplateFile.js.map +1 -1
  34. package/dist/src/components/index.d.ts +1 -0
  35. package/dist/src/components/index.d.ts.map +1 -1
  36. package/dist/src/components/index.js +1 -0
  37. package/dist/src/components/index.js.map +1 -1
  38. package/dist/src/content-slot.d.ts.map +1 -1
  39. package/dist/src/content-slot.js +7 -6
  40. package/dist/src/content-slot.js.map +1 -1
  41. package/dist/src/context.d.ts.map +1 -1
  42. package/dist/src/context.js +10 -3
  43. package/dist/src/context.js.map +1 -1
  44. package/dist/src/debug/cli.d.ts +6 -0
  45. package/dist/src/debug/cli.d.ts.map +1 -0
  46. package/dist/src/{debug.js → debug/cli.js} +78 -84
  47. package/dist/src/debug/cli.js.map +1 -0
  48. package/dist/src/debug/diagnostics.test.d.ts +2 -0
  49. package/dist/src/debug/diagnostics.test.d.ts.map +1 -0
  50. package/dist/src/debug/diagnostics.test.js +45 -0
  51. package/dist/src/debug/diagnostics.test.js.map +1 -0
  52. package/dist/src/debug/effects.d.ts +73 -0
  53. package/dist/src/debug/effects.d.ts.map +1 -0
  54. package/dist/src/debug/effects.js +228 -0
  55. package/dist/src/debug/effects.js.map +1 -0
  56. package/dist/src/debug/effects.test.d.ts +2 -0
  57. package/dist/src/debug/effects.test.d.ts.map +1 -0
  58. package/dist/src/debug/effects.test.js +84 -0
  59. package/dist/src/debug/effects.test.js.map +1 -0
  60. package/dist/src/debug/files.d.ts +14 -0
  61. package/dist/src/debug/files.d.ts.map +1 -0
  62. package/dist/src/debug/files.js +40 -0
  63. package/dist/src/debug/files.js.map +1 -0
  64. package/dist/src/debug/files.test.d.ts +2 -0
  65. package/dist/src/debug/files.test.d.ts.map +1 -0
  66. package/dist/src/debug/files.test.js +89 -0
  67. package/dist/src/debug/files.test.js.map +1 -0
  68. package/dist/src/debug/index.d.ts +61 -0
  69. package/dist/src/debug/index.d.ts.map +1 -0
  70. package/dist/src/debug/index.js +69 -0
  71. package/dist/src/debug/index.js.map +1 -0
  72. package/dist/src/debug/render.d.ts +57 -0
  73. package/dist/src/debug/render.d.ts.map +1 -0
  74. package/dist/src/debug/render.js +519 -0
  75. package/dist/src/debug/render.js.map +1 -0
  76. package/dist/src/debug/render.test.d.ts +2 -0
  77. package/dist/src/debug/render.test.d.ts.map +1 -0
  78. package/dist/src/debug/render.test.js +328 -0
  79. package/dist/src/debug/render.test.js.map +1 -0
  80. package/dist/src/debug/serialize.d.ts +9 -0
  81. package/dist/src/debug/serialize.d.ts.map +1 -0
  82. package/dist/src/debug/serialize.js +70 -0
  83. package/dist/src/debug/serialize.js.map +1 -0
  84. package/dist/src/debug/symbols.d.ts +15 -0
  85. package/dist/src/debug/symbols.d.ts.map +1 -0
  86. package/dist/src/debug/symbols.js +173 -0
  87. package/dist/src/debug/symbols.js.map +1 -0
  88. package/dist/src/debug/symbols.test.d.ts +2 -0
  89. package/dist/src/debug/symbols.test.d.ts.map +1 -0
  90. package/dist/src/debug/symbols.test.js +104 -0
  91. package/dist/src/debug/symbols.test.js.map +1 -0
  92. package/dist/src/debug/trace.d.ts +342 -0
  93. package/dist/src/debug/trace.d.ts.map +1 -0
  94. package/dist/src/debug/trace.js +443 -0
  95. package/dist/src/debug/trace.js.map +1 -0
  96. package/dist/src/devtools/devtools-protocol.d.ts +232 -0
  97. package/dist/src/devtools/devtools-protocol.d.ts.map +1 -0
  98. package/dist/src/devtools/devtools-protocol.js +2 -0
  99. package/dist/src/devtools/devtools-protocol.js.map +1 -0
  100. package/dist/src/devtools/devtools-server.browser.d.ts +28 -0
  101. package/dist/src/devtools/devtools-server.browser.d.ts.map +1 -0
  102. package/dist/src/devtools/devtools-server.browser.js +36 -0
  103. package/dist/src/devtools/devtools-server.browser.js.map +1 -0
  104. package/dist/src/devtools/devtools-server.d.ts +72 -0
  105. package/dist/src/devtools/devtools-server.d.ts.map +1 -0
  106. package/dist/src/devtools/devtools-server.js +256 -0
  107. package/dist/src/devtools/devtools-server.js.map +1 -0
  108. package/dist/src/devtools/devtools-transport.d.ts +23 -0
  109. package/dist/src/devtools/devtools-transport.d.ts.map +1 -0
  110. package/dist/src/devtools/devtools-transport.js +114 -0
  111. package/dist/src/devtools/devtools-transport.js.map +1 -0
  112. package/dist/src/devtools-entry.browser.d.ts +4 -0
  113. package/dist/src/devtools-entry.browser.d.ts.map +1 -0
  114. package/dist/src/devtools-entry.browser.js +2 -0
  115. package/dist/src/devtools-entry.browser.js.map +1 -0
  116. package/dist/src/devtools-entry.d.ts +4 -0
  117. package/dist/src/devtools-entry.d.ts.map +1 -0
  118. package/dist/src/devtools-entry.js +2 -0
  119. package/dist/src/devtools-entry.js.map +1 -0
  120. package/dist/src/diagnostics.d.ts +34 -0
  121. package/dist/src/diagnostics.d.ts.map +1 -0
  122. package/dist/src/diagnostics.js +89 -0
  123. package/dist/src/diagnostics.js.map +1 -0
  124. package/dist/src/index.d.ts +3 -2
  125. package/dist/src/index.d.ts.map +1 -1
  126. package/dist/src/index.js +3 -2
  127. package/dist/src/index.js.map +1 -1
  128. package/dist/src/print-hook.d.ts +14 -0
  129. package/dist/src/print-hook.d.ts.map +1 -0
  130. package/dist/src/print-hook.js +10 -0
  131. package/dist/src/print-hook.js.map +1 -0
  132. package/dist/src/reactive-union-set.d.ts.map +1 -1
  133. package/dist/src/reactive-union-set.js +28 -3
  134. package/dist/src/reactive-union-set.js.map +1 -1
  135. package/dist/src/reactivity.d.ts +50 -8
  136. package/dist/src/reactivity.d.ts.map +1 -1
  137. package/dist/src/reactivity.js +225 -39
  138. package/dist/src/reactivity.js.map +1 -1
  139. package/dist/src/render-stack.d.ts +30 -0
  140. package/dist/src/render-stack.d.ts.map +1 -0
  141. package/dist/src/render-stack.js +251 -0
  142. package/dist/src/render-stack.js.map +1 -0
  143. package/dist/src/render.d.ts +9 -19
  144. package/dist/src/render.d.ts.map +1 -1
  145. package/dist/src/render.js +371 -159
  146. package/dist/src/render.js.map +1 -1
  147. package/dist/src/resource.d.ts.map +1 -1
  148. package/dist/src/resource.js +5 -0
  149. package/dist/src/resource.js.map +1 -1
  150. package/dist/src/runtime/component.d.ts +7 -1
  151. package/dist/src/runtime/component.d.ts.map +1 -1
  152. package/dist/src/runtime/component.js +4 -1
  153. package/dist/src/runtime/component.js.map +1 -1
  154. package/dist/src/scheduler.d.ts +8 -0
  155. package/dist/src/scheduler.d.ts.map +1 -1
  156. package/dist/src/scheduler.js +69 -3
  157. package/dist/src/scheduler.js.map +1 -1
  158. package/dist/src/symbols/basic-symbol.d.ts.map +1 -1
  159. package/dist/src/symbols/basic-symbol.js +6 -1
  160. package/dist/src/symbols/basic-symbol.js.map +1 -1
  161. package/dist/src/symbols/decl.d.ts.map +1 -1
  162. package/dist/src/symbols/decl.js +5 -1
  163. package/dist/src/symbols/decl.js.map +1 -1
  164. package/dist/src/symbols/output-scope.d.ts +2 -1
  165. package/dist/src/symbols/output-scope.d.ts.map +1 -1
  166. package/dist/src/symbols/output-scope.js +13 -8
  167. package/dist/src/symbols/output-scope.js.map +1 -1
  168. package/dist/src/symbols/output-symbol.d.ts +1 -0
  169. package/dist/src/symbols/output-symbol.d.ts.map +1 -1
  170. package/dist/src/symbols/output-symbol.js +25 -8
  171. package/dist/src/symbols/output-symbol.js.map +1 -1
  172. package/dist/src/symbols/symbol-flow.d.ts.map +1 -1
  173. package/dist/src/symbols/symbol-flow.js +24 -8
  174. package/dist/src/symbols/symbol-flow.js.map +1 -1
  175. package/dist/src/symbols/symbol-slot.d.ts.map +1 -1
  176. package/dist/src/symbols/symbol-slot.js +15 -0
  177. package/dist/src/symbols/symbol-slot.js.map +1 -1
  178. package/dist/src/symbols/symbol-slot.test.d.ts +2 -0
  179. package/dist/src/symbols/symbol-slot.test.d.ts.map +1 -0
  180. package/dist/src/symbols/symbol-slot.test.js +35 -0
  181. package/dist/src/symbols/symbol-slot.test.js.map +1 -0
  182. package/dist/src/symbols/symbol-table.d.ts.map +1 -1
  183. package/dist/src/symbols/symbol-table.js +6 -5
  184. package/dist/src/symbols/symbol-table.js.map +1 -1
  185. package/dist/src/trace.d.ts +2 -0
  186. package/dist/src/trace.d.ts.map +1 -0
  187. package/dist/src/trace.js +2 -0
  188. package/dist/src/trace.js.map +1 -0
  189. package/dist/src/tracer.d.ts +2 -228
  190. package/dist/src/tracer.d.ts.map +1 -1
  191. package/dist/src/tracer.js +5 -298
  192. package/dist/src/tracer.js.map +1 -1
  193. package/dist/src/utils.d.ts.map +1 -1
  194. package/dist/src/utils.js +7 -5
  195. package/dist/src/utils.js.map +1 -1
  196. package/dist/test/components/append-file.test.d.ts.map +1 -1
  197. package/dist/test/components/append-file.test.js +18 -10
  198. package/dist/test/components/append-file.test.js.map +1 -1
  199. package/dist/test/components/template-file.test.d.ts.map +1 -1
  200. package/dist/test/components/template-file.test.js +6 -4
  201. package/dist/test/components/template-file.test.js.map +1 -1
  202. package/dist/test/lazy-isempty.test.d.ts +2 -0
  203. package/dist/test/lazy-isempty.test.d.ts.map +1 -0
  204. package/dist/test/lazy-isempty.test.js +89 -0
  205. package/dist/test/lazy-isempty.test.js.map +1 -0
  206. package/dist/test/reactive-union-set-disposers.test.d.ts +2 -0
  207. package/dist/test/reactive-union-set-disposers.test.d.ts.map +1 -0
  208. package/dist/test/reactive-union-set-disposers.test.js +98 -0
  209. package/dist/test/reactive-union-set-disposers.test.js.map +1 -0
  210. package/dist/test/reactivity/shallow-reactive.test.d.ts +2 -0
  211. package/dist/test/reactivity/shallow-reactive.test.d.ts.map +1 -0
  212. package/dist/test/reactivity/shallow-reactive.test.js +52 -0
  213. package/dist/test/reactivity/shallow-reactive.test.js.map +1 -0
  214. package/dist/test/rendering/basic.test.js +3 -0
  215. package/dist/test/rendering/basic.test.js.map +1 -1
  216. package/dist/test/rendering/print-render-stack.test.d.ts +2 -0
  217. package/dist/test/rendering/print-render-stack.test.d.ts.map +1 -0
  218. package/dist/test/rendering/print-render-stack.test.js +207 -0
  219. package/dist/test/rendering/print-render-stack.test.js.map +1 -0
  220. package/dist/test/scheduler-extended.test.d.ts +2 -0
  221. package/dist/test/scheduler-extended.test.d.ts.map +1 -0
  222. package/dist/test/scheduler-extended.test.js +96 -0
  223. package/dist/test/scheduler-extended.test.js.map +1 -0
  224. package/dist/test/scheduler.test.d.ts +2 -0
  225. package/dist/test/scheduler.test.d.ts.map +1 -0
  226. package/dist/test/scheduler.test.js +46 -0
  227. package/dist/test/scheduler.test.js.map +1 -0
  228. package/dist/testing/create-test-wrapper.d.ts +1 -1
  229. package/dist/testing/create-test-wrapper.d.ts.map +1 -1
  230. package/dist/testing/create-test-wrapper.js +1 -1
  231. package/dist/testing/create-test-wrapper.js.map +1 -1
  232. package/dist/testing/devtools-utils.d.ts +26 -0
  233. package/dist/testing/devtools-utils.d.ts.map +1 -0
  234. package/dist/testing/devtools-utils.js +140 -0
  235. package/dist/testing/devtools-utils.js.map +1 -0
  236. package/dist/testing/extend-expect.d.ts.map +1 -1
  237. package/dist/testing/extend-expect.js +63 -1
  238. package/dist/testing/extend-expect.js.map +1 -1
  239. package/dist/testing/render.d.ts +2 -2
  240. package/dist/testing/render.d.ts.map +1 -1
  241. package/dist/testing/render.js +2 -2
  242. package/dist/testing/render.js.map +1 -1
  243. package/dist/tsconfig.tsbuildinfo +1 -1
  244. package/package.json +21 -7
  245. package/scripts/copy-devtools-ui.mjs +26 -0
  246. package/src/binder.ts +71 -16
  247. package/src/components/AccessExpression.test.tsx +132 -0
  248. package/src/components/AccessExpression.tsx +344 -0
  249. package/src/components/AppendFile.tsx +14 -9
  250. package/src/components/Block.tsx +1 -1
  251. package/src/components/Declaration.tsx +2 -1
  252. package/src/components/Prose.tsx +1 -1
  253. package/src/components/Scope.tsx +6 -1
  254. package/src/components/SourceDirectory.tsx +1 -2
  255. package/src/components/TemplateFile.tsx +18 -9
  256. package/src/components/index.tsx +1 -0
  257. package/src/content-slot.tsx +7 -7
  258. package/src/context.ts +17 -6
  259. package/src/{debug.ts → debug/cli.ts} +112 -127
  260. package/src/debug/diagnostics.test.tsx +55 -0
  261. package/src/debug/effects.test.tsx +89 -0
  262. package/src/debug/effects.ts +317 -0
  263. package/src/debug/files.test.tsx +96 -0
  264. package/src/debug/files.ts +40 -0
  265. package/src/debug/index.ts +128 -0
  266. package/src/debug/render.test.tsx +379 -0
  267. package/src/debug/render.ts +639 -0
  268. package/src/debug/serialize.ts +85 -0
  269. package/src/debug/symbols.test.tsx +106 -0
  270. package/src/debug/symbols.ts +239 -0
  271. package/src/debug/trace.ts +312 -0
  272. package/src/devtools/devtools-protocol.ts +312 -0
  273. package/src/devtools/devtools-server.browser.ts +71 -0
  274. package/src/devtools/devtools-server.ts +290 -0
  275. package/src/devtools/devtools-transport.ts +154 -0
  276. package/src/devtools-entry.browser.ts +52 -0
  277. package/src/devtools-entry.ts +54 -0
  278. package/src/diagnostics.ts +141 -0
  279. package/src/index.ts +2 -7
  280. package/src/print-hook.ts +22 -0
  281. package/src/reactive-union-set.ts +85 -44
  282. package/src/reactivity.ts +301 -59
  283. package/src/render-stack.ts +294 -0
  284. package/src/render.ts +470 -216
  285. package/src/resource.ts +28 -19
  286. package/src/runtime/component.ts +11 -0
  287. package/src/scheduler.ts +80 -4
  288. package/src/symbols/basic-symbol.ts +6 -1
  289. package/src/symbols/decl.ts +5 -1
  290. package/src/symbols/output-scope.ts +21 -13
  291. package/src/symbols/output-symbol.ts +34 -14
  292. package/src/symbols/symbol-flow.ts +76 -39
  293. package/src/symbols/symbol-slot.test.tsx +41 -0
  294. package/src/symbols/symbol-slot.tsx +47 -20
  295. package/src/symbols/symbol-table.ts +6 -10
  296. package/src/trace.ts +1 -0
  297. package/src/tracer.ts +13 -242
  298. package/src/utils.tsx +24 -17
  299. package/temp/api.json +4187 -1603
  300. package/test/components/append-file.test.tsx +36 -29
  301. package/test/components/template-file.test.tsx +11 -11
  302. package/test/lazy-isempty.test.tsx +106 -0
  303. package/test/reactive-union-set-disposers.test.tsx +112 -0
  304. package/test/reactivity/shallow-reactive.test.tsx +56 -0
  305. package/test/rendering/basic.test.tsx +4 -0
  306. package/test/rendering/print-render-stack.test.tsx +244 -0
  307. package/test/scheduler-extended.test.tsx +122 -0
  308. package/test/scheduler.test.tsx +50 -0
  309. package/testing/create-test-wrapper.tsx +1 -1
  310. package/testing/devtools-utils.ts +203 -0
  311. package/testing/extend-expect.ts +89 -0
  312. package/testing/render.ts +2 -2
  313. package/testing/vitest.d.ts +9 -0
  314. package/dist/src/debug.d.ts +0 -15
  315. package/dist/src/debug.d.ts.map +0 -1
  316. package/dist/src/debug.js.map +0 -1
@@ -0,0 +1,317 @@
1
+ import { isReactive, isRef } from "@vue/reactivity";
2
+ import {
3
+ emitDevtoolsMessage,
4
+ isDevtoolsEnabled,
5
+ TracePhase,
6
+ traceType,
7
+ } from "./trace.js";
8
+
9
+ // ─────────────────────────────────────────────────────────────────────────────
10
+ // Effects debug
11
+ // ─────────────────────────────────────────────────────────────────────────────
12
+
13
+ export interface SourceLocation {
14
+ fileName?: string;
15
+ lineNumber?: number;
16
+ columnNumber?: number;
17
+ stack?: string;
18
+ }
19
+
20
+ export interface EffectDebugInfo {
21
+ id: number;
22
+ name?: string;
23
+ type?: string;
24
+ createdAt?: SourceLocation;
25
+ lastTriggeredByRefId?: number;
26
+ lastTriggeredAt?: SourceLocation;
27
+ }
28
+
29
+ export interface RefDebugInfo {
30
+ id: number;
31
+ kind?: string;
32
+ createdAt?: SourceLocation;
33
+ createdByEffectId?: number;
34
+ }
35
+
36
+ export interface EffectEdgeDebugInfo {
37
+ id: number;
38
+ type: "track" | "trigger" | "triggered-by";
39
+ effectId: number;
40
+ refId?: number;
41
+ targetId?: number;
42
+ targetKind?: "ref" | "target";
43
+ targetLabel?: string;
44
+ targetKey?: string | number;
45
+ location?: SourceLocation;
46
+ }
47
+
48
+ const effects = new Map<number, EffectDebugInfo>();
49
+ const refs = new Map<number, RefDebugInfo>();
50
+ let effectIdCounter = 1;
51
+ let edgeEventIdCounter = 1;
52
+ let nonRefTargetIdCounter = 1;
53
+ const nonRefTargetIds = new WeakMap<object, number>();
54
+ const primitiveTargetIds = new Map<unknown, number>();
55
+
56
+ const STACK_LINE = /\s*at\s+(?:.+?\s+\()?(.+?):(\d+):(\d+)\)?$/;
57
+ const STACK_SKIP = [
58
+ "node:internal",
59
+ "/node_modules/",
60
+ "\\node_modules\\",
61
+ "/@vue/",
62
+ "\\@vue\\",
63
+ "/@alloy-js/",
64
+ "\\@alloy-js\\",
65
+ "/packages/core/src/reactivity",
66
+ "/packages/core/src/devtools/effects-debug",
67
+ "/packages/core/dist/src/reactivity",
68
+ "\\packages\\core\\dist\\src\\reactivity",
69
+ "/packages/core/dist/src/devtools/effects-debug",
70
+ "\\packages\\core\\dist\\src\\devtools\\effects-debug",
71
+ "captureSourceLocation",
72
+ ];
73
+
74
+ const VUE_REACTIVITY_MARKERS = [
75
+ "@vue/reactivity",
76
+ "reactivity.esm",
77
+ "reactivity.cjs",
78
+ "reactivity.global",
79
+ "/@vue/",
80
+ "\\@vue\\",
81
+ ];
82
+
83
+ function isVueReactivityLine(line: string) {
84
+ return VUE_REACTIVITY_MARKERS.some((marker) => line.includes(marker));
85
+ }
86
+
87
+ function parseStackLine(
88
+ line: string,
89
+ stack?: string,
90
+ ): SourceLocation | undefined {
91
+ const match = STACK_LINE.exec(line);
92
+ if (!match) return undefined;
93
+ const [, fileName, lineNumber, columnNumber] = match;
94
+ return {
95
+ fileName,
96
+ lineNumber: Number(lineNumber),
97
+ columnNumber: Number(columnNumber),
98
+ stack,
99
+ };
100
+ }
101
+
102
+ export function captureSourceLocation(
103
+ skipReactives = true,
104
+ ): SourceLocation | undefined {
105
+ if (!isDevtoolsEnabled()) return undefined;
106
+ const stack = new Error().stack;
107
+ if (!stack) {
108
+ return { stack: "" };
109
+ }
110
+ const lines = stack.split("\n").slice(1);
111
+ for (const line of lines) {
112
+ if (STACK_SKIP.some((skip) => line.includes(skip))) continue;
113
+ const parsed = parseStackLine(line, stack);
114
+ if (parsed) return parsed;
115
+ }
116
+
117
+ if (skipReactives) {
118
+ for (const line of lines) {
119
+ if (STACK_SKIP.some((skip) => line.includes(skip))) continue;
120
+ if (isVueReactivityLine(line)) continue;
121
+ const parsed = parseStackLine(line, stack);
122
+ if (parsed) return parsed;
123
+ }
124
+ }
125
+
126
+ return { stack };
127
+ }
128
+
129
+ function getNonRefTargetId(target: unknown): number {
130
+ if (typeof target === "object" && target !== null) {
131
+ const existing = nonRefTargetIds.get(target);
132
+ if (existing) return existing;
133
+ const id = nonRefTargetIdCounter++;
134
+ nonRefTargetIds.set(target, id);
135
+ return id;
136
+ }
137
+ if (typeof target === "function") {
138
+ const existing = nonRefTargetIds.get(target as object);
139
+ if (existing) return existing;
140
+ const id = nonRefTargetIdCounter++;
141
+ nonRefTargetIds.set(target as object, id);
142
+ return id;
143
+ }
144
+ const existing = primitiveTargetIds.get(target);
145
+ if (existing) return existing;
146
+ const id = nonRefTargetIdCounter++;
147
+ primitiveTargetIds.set(target, id);
148
+ return id;
149
+ }
150
+
151
+ function formatNonRefTargetLabel(target: unknown): string {
152
+ if (Array.isArray(target)) return "[]";
153
+ try {
154
+ return String(target);
155
+ } catch {
156
+ return "[Unserializable]";
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Sanitize a Map/object key to ensure it's serializable.
162
+ */
163
+ function sanitizeTargetKey(key: unknown): string | number | undefined {
164
+ if (key === undefined) return undefined;
165
+ if (typeof key === "string" || typeof key === "number") return key;
166
+ if (typeof key === "symbol") return key.toString();
167
+ if (typeof key === "object" || typeof key === "function") return "Object";
168
+ return String(key);
169
+ }
170
+
171
+ function buildEffectTargetInfo(input: {
172
+ target: unknown;
173
+ refId?: number;
174
+ }): Pick<
175
+ EffectEdgeDebugInfo,
176
+ "refId" | "targetId" | "targetKind" | "targetLabel"
177
+ > {
178
+ if (input.refId !== undefined) {
179
+ return {
180
+ refId: input.refId,
181
+ targetId: input.refId,
182
+ targetKind: "ref",
183
+ };
184
+ }
185
+
186
+ const targetId = getNonRefTargetId(input.target);
187
+ return {
188
+ targetId,
189
+ targetKind: "target",
190
+ targetLabel: formatNonRefTargetLabel(input.target),
191
+ };
192
+ }
193
+
194
+ function emitEffect(message: { type: string; [key: string]: unknown }) {
195
+ emitDevtoolsMessage(message);
196
+ }
197
+
198
+ export function update(input: Partial<EffectDebugInfo> & { id: number }) {
199
+ if (!isDevtoolsEnabled()) return;
200
+ const existing = effects.get(input.id);
201
+ if (!existing) return;
202
+ const next: EffectDebugInfo = { ...existing, ...input };
203
+ effects.set(input.id, next);
204
+ emitEffect({
205
+ type: traceType(TracePhase.effect.effectUpdated),
206
+ effect: next,
207
+ });
208
+ }
209
+
210
+ export function register(input: {
211
+ name?: string;
212
+ type?: string;
213
+ createdAt?: SourceLocation;
214
+ contextId?: number;
215
+ ownerContextId?: number | null;
216
+ }): number {
217
+ if (!isDevtoolsEnabled()) return -1;
218
+ const id = effectIdCounter++;
219
+ const info: EffectDebugInfo = {
220
+ id,
221
+ name: input.name,
222
+ type: input.type,
223
+ createdAt: input.createdAt ?? captureSourceLocation(),
224
+ };
225
+ effects.set(id, info);
226
+ emitEffect({ type: traceType(TracePhase.effect.effectAdded), effect: info });
227
+ return id;
228
+ }
229
+
230
+ export function registerRef(input: {
231
+ id: number;
232
+ kind?: string;
233
+ createdAt?: SourceLocation;
234
+ createdByEffectId?: number;
235
+ isInfrastructure?: boolean;
236
+ }) {
237
+ if (!isDevtoolsEnabled()) return;
238
+ if (refs.has(input.id)) return;
239
+ const info: RefDebugInfo = {
240
+ id: input.id,
241
+ kind: input.kind,
242
+ createdAt: input.createdAt ?? captureSourceLocation(),
243
+ createdByEffectId: input.createdByEffectId,
244
+ };
245
+ refs.set(input.id, info);
246
+ emitEffect({ type: traceType(TracePhase.effect.refAdded), ref: info });
247
+ }
248
+
249
+ export function ensureRef(input: { id: number; kind?: string }) {
250
+ if (!isDevtoolsEnabled()) return;
251
+ if (refs.has(input.id)) return;
252
+ registerRef({ id: input.id, kind: input.kind });
253
+ }
254
+
255
+ export function track(input: {
256
+ effectId: number;
257
+ target: unknown;
258
+ refId?: number;
259
+ targetKey?: unknown;
260
+ location?: SourceLocation;
261
+ }) {
262
+ if (!isDevtoolsEnabled()) return;
263
+ const edge: EffectEdgeDebugInfo = {
264
+ id: edgeEventIdCounter++,
265
+ type: "track",
266
+ effectId: input.effectId,
267
+ ...buildEffectTargetInfo({ target: input.target, refId: input.refId }),
268
+ targetKey: sanitizeTargetKey(input.targetKey),
269
+ location: input.location ?? captureSourceLocation(),
270
+ };
271
+ emitEffect({ type: traceType(TracePhase.effect.track), edge });
272
+ }
273
+
274
+ export function trigger(input: {
275
+ effectId: number;
276
+ target: unknown;
277
+ refId?: number;
278
+ targetKey?: unknown;
279
+ location?: SourceLocation;
280
+ kind?: "trigger" | "triggered-by";
281
+ causedBy?: number;
282
+ }) {
283
+ if (!isDevtoolsEnabled()) return;
284
+ const edge: EffectEdgeDebugInfo = {
285
+ id: edgeEventIdCounter++,
286
+ type: input.kind ?? "triggered-by",
287
+ effectId: input.effectId,
288
+ ...buildEffectTargetInfo({ target: input.target, refId: input.refId }),
289
+ targetKey: sanitizeTargetKey(input.targetKey),
290
+ location: input.location ?? captureSourceLocation(),
291
+ };
292
+ emitEffect({ type: traceType(TracePhase.effect.trigger), edge });
293
+
294
+ update({
295
+ id: input.effectId,
296
+ ...(input.refId !== undefined ? { lastTriggeredByRefId: input.refId } : {}),
297
+ lastTriggeredAt: input.location ?? captureSourceLocation(),
298
+ });
299
+ }
300
+
301
+ export function reset() {
302
+ effects.clear();
303
+ refs.clear();
304
+ primitiveTargetIds.clear();
305
+ effectIdCounter = 1;
306
+ edgeEventIdCounter = 1;
307
+ nonRefTargetIdCounter = 1;
308
+ }
309
+
310
+ // Utilities used by other debug sections
311
+ export function isRefTarget(value: unknown) {
312
+ return isRef(value);
313
+ }
314
+
315
+ export function isReactiveTarget(value: unknown) {
316
+ return isReactive(value);
317
+ }
@@ -0,0 +1,96 @@
1
+ import { afterEach, beforeEach, expect, it } from "vitest";
2
+ import WebSocket from "ws";
3
+ import {
4
+ createMessageCollector,
5
+ type DevtoolsMessage,
6
+ } from "../../testing/devtools-utils.js";
7
+ import { Output } from "../components/Output.jsx";
8
+ import { Show } from "../components/Show.jsx";
9
+ import { SourceDirectory } from "../components/SourceDirectory.jsx";
10
+ import { SourceFile } from "../components/SourceFile.jsx";
11
+ import {
12
+ enableDevtools,
13
+ resetDevtoolsServerForTests,
14
+ } from "../devtools/devtools-server.js";
15
+ import { ref } from "../reactivity.js";
16
+ import { renderAsync } from "../render.js";
17
+ import { flushJobsAsync } from "../scheduler.js";
18
+
19
+ let socket: WebSocket | undefined;
20
+
21
+ beforeEach(async () => {
22
+ const server = await enableDevtools({ port: 0 });
23
+ socket = new WebSocket(`ws://127.0.0.1:${server.port}`);
24
+
25
+ await new Promise<void>((resolve, reject) => {
26
+ socket?.once("open", resolve);
27
+ socket?.once("error", reject);
28
+ });
29
+ });
30
+
31
+ afterEach(async () => {
32
+ if (socket) {
33
+ socket.close();
34
+ socket = undefined;
35
+ }
36
+
37
+ await resetDevtoolsServerForTests();
38
+ });
39
+
40
+ it("emits file and directory add/update/remove messages", async () => {
41
+ const show = ref(true);
42
+ const collector = createMessageCollector(socket!);
43
+
44
+ await renderAsync(
45
+ <Output>
46
+ <Show when={show.value}>
47
+ <SourceDirectory path="src">
48
+ <SourceFile path="index.ts" filetype="ts">
49
+ {"export const value = 1;"}
50
+ </SourceFile>
51
+ </SourceDirectory>
52
+ </Show>
53
+ </Output>,
54
+ );
55
+
56
+ const initialMessages = await collector.waitForRender();
57
+ const initialFiles = initialMessages.filter((m: DevtoolsMessage) =>
58
+ m.type.startsWith("files:"),
59
+ );
60
+ expect(initialFiles[0]).toMatchObject({
61
+ path: "./",
62
+ });
63
+ expect(initialFiles[1]).toMatchObject({
64
+ type: "files:directoryAdded",
65
+ path: "src",
66
+ });
67
+ expect(initialFiles[2]).toMatchObject({
68
+ type: "files:fileAdded",
69
+ path: "src/index.ts",
70
+ filetype: "ts",
71
+ });
72
+ expect(initialFiles[3]).toMatchObject({
73
+ type: "files:fileUpdated",
74
+ path: "src/index.ts",
75
+ filetype: "ts",
76
+ contents: expect.any(String),
77
+ });
78
+
79
+ show.value = false;
80
+ await flushJobsAsync();
81
+
82
+ const updateMessages = await collector.waitForFlush();
83
+ const updateFiles = updateMessages.filter((m: DevtoolsMessage) =>
84
+ m.type.startsWith("files:"),
85
+ );
86
+ collector.stop();
87
+
88
+ expect(updateFiles[0]).toMatchObject({
89
+ type: "files:fileRemoved",
90
+ path: "src/index.ts",
91
+ });
92
+ expect(updateFiles[1]).toMatchObject({
93
+ type: "files:directoryRemoved",
94
+ path: "src",
95
+ });
96
+ });
@@ -0,0 +1,40 @@
1
+ import { emitDevtoolsMessage, isDevtoolsEnabled } from "./trace.js";
2
+
3
+ export interface FileUpdateInfo {
4
+ path: string;
5
+ filetype: string;
6
+ contents: string;
7
+ }
8
+
9
+ const fileContentCache = new Map<string, string>();
10
+
11
+ /** Record a directory being added to the output. */
12
+ export function recordDirectory(path: string) {
13
+ if (!isDevtoolsEnabled()) return;
14
+ emitDevtoolsMessage({ type: "files:directoryAdded", path });
15
+ }
16
+
17
+ /** Record a file being added to the output. */
18
+ export function recordFile(path: string, filetype: string) {
19
+ if (!isDevtoolsEnabled()) return;
20
+ emitDevtoolsMessage({ type: "files:fileAdded", path, filetype });
21
+ }
22
+
23
+ /** Notify devtools that a file's contents have changed. De-duplicates by content. */
24
+ export function updated(info: FileUpdateInfo) {
25
+ const previous = fileContentCache.get(info.path);
26
+ if (previous === info.contents) return;
27
+ fileContentCache.set(info.path, info.contents);
28
+
29
+ emitDevtoolsMessage({
30
+ type: "files:fileUpdated",
31
+ path: info.path,
32
+ filetype: info.filetype,
33
+ contents: info.contents,
34
+ });
35
+ }
36
+
37
+ /** Clear all cached file state. Called when a new render begins. */
38
+ export function reset() {
39
+ fileContentCache.clear();
40
+ }
@@ -0,0 +1,128 @@
1
+ import {
2
+ assertDevtoolsConnectedForSyncRender,
3
+ initDevtoolsIfEnabled,
4
+ isDevtoolsEnabled,
5
+ } from "../devtools/devtools-server.js";
6
+ import {
7
+ debugContext,
8
+ debugRender,
9
+ debugStack,
10
+ debugTree,
11
+ debugWatch,
12
+ } from "./cli.js";
13
+ import {
14
+ ensureRef,
15
+ register,
16
+ registerRef,
17
+ reset,
18
+ track,
19
+ trigger,
20
+ update,
21
+ } from "./effects.js";
22
+ import {
23
+ recordDirectory,
24
+ recordFile,
25
+ reset as resetFiles,
26
+ updated,
27
+ } from "./files.js";
28
+ import {
29
+ appendCustomContext,
30
+ appendFragmentChild,
31
+ appendPrintHook,
32
+ appendTextNode,
33
+ beginComponent,
34
+ complete,
35
+ error,
36
+ flushJobsComplete,
37
+ initialize,
38
+ prepareMemoNode,
39
+ } from "./render.js";
40
+ import {
41
+ registerScope,
42
+ registerSymbol,
43
+ relocateScope,
44
+ reset as resetSymbols,
45
+ unregisterScope,
46
+ unregisterSymbol,
47
+ } from "./symbols.js";
48
+ import { trace, type TracePhaseInfo } from "./trace.js";
49
+
50
+ export { captureSourceLocation } from "./effects.js";
51
+ export type {
52
+ EffectDebugInfo,
53
+ EffectEdgeDebugInfo,
54
+ RefDebugInfo,
55
+ } from "./effects.js";
56
+ export { getRenderNodeId, type RenderTreeNodeInfo } from "./render.js";
57
+ export type {
58
+ BeginComponentOptions,
59
+ ComponentDebugSession,
60
+ RenderErrorInfo,
61
+ RenderErrorStackEntry,
62
+ } from "./render.js";
63
+ export {
64
+ isConsoleTraceEnabled,
65
+ isDevtoolsEnabled,
66
+ trace,
67
+ TracePhase,
68
+ type TracePhaseInfo,
69
+ } from "./trace.js";
70
+
71
+ /** The full debug runtime interface, derived from the `debug` object implementation. */
72
+ export type DebugRuntime = typeof debug;
73
+
74
+ export const debug = {
75
+ component: {
76
+ stack: debugStack,
77
+ tree: debugTree,
78
+ watch: debugWatch,
79
+ render: debugRender,
80
+ context: debugContext,
81
+ },
82
+ effect: {
83
+ register,
84
+ update,
85
+ registerRef,
86
+ ensureRef,
87
+ track,
88
+ trigger,
89
+ reset,
90
+ },
91
+ render: {
92
+ initialize,
93
+ appendTextNode,
94
+ appendCustomContext,
95
+ appendPrintHook,
96
+ appendFragmentChild,
97
+ beginComponent,
98
+ prepareMemoNode,
99
+ error,
100
+ complete,
101
+ flushJobsComplete,
102
+ },
103
+ files: {
104
+ recordDirectory,
105
+ recordFile,
106
+ updated,
107
+ reset: resetFiles,
108
+ },
109
+ symbols: {
110
+ registerScope,
111
+ relocateScope,
112
+ unregisterScope,
113
+ registerSymbol,
114
+ unregisterSymbol,
115
+ reset: resetSymbols,
116
+ },
117
+ trace(phase: TracePhaseInfo, cb: () => string, triggerIds?: Set<number>) {
118
+ trace(phase, cb, triggerIds ?? new Set());
119
+ },
120
+ async prepare(): Promise<void> {
121
+ await initDevtoolsIfEnabled();
122
+ },
123
+ assertReadyForSyncRender(): void {
124
+ if (isDevtoolsEnabled()) {
125
+ assertDevtoolsConnectedForSyncRender();
126
+ }
127
+ },
128
+ };