@alloy-js/core 0.23.0-dev.1 → 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 (337) hide show
  1. package/CHANGELOG.md +0 -22
  2. package/dist/devtools/index.html +80 -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 +60 -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/For.d.ts.map +1 -1
  24. package/dist/src/components/For.js +1 -1
  25. package/dist/src/components/For.js.map +1 -1
  26. package/dist/src/components/List.d.ts.map +1 -1
  27. package/dist/src/components/List.js +1 -1
  28. package/dist/src/components/List.js.map +1 -1
  29. package/dist/src/components/Prose.js +2 -2
  30. package/dist/src/components/Prose.js.map +1 -1
  31. package/dist/src/components/Scope.d.ts.map +1 -1
  32. package/dist/src/components/Scope.js +6 -1
  33. package/dist/src/components/Scope.js.map +1 -1
  34. package/dist/src/components/SourceDirectory.d.ts.map +1 -1
  35. package/dist/src/components/SourceDirectory.js +1 -2
  36. package/dist/src/components/SourceDirectory.js.map +1 -1
  37. package/dist/src/components/Switch.d.ts.map +1 -1
  38. package/dist/src/components/Switch.js +1 -1
  39. package/dist/src/components/Switch.js.map +1 -1
  40. package/dist/src/components/TemplateFile.d.ts.map +1 -1
  41. package/dist/src/components/TemplateFile.js +18 -3
  42. package/dist/src/components/TemplateFile.js.map +1 -1
  43. package/dist/src/components/index.d.ts +1 -0
  44. package/dist/src/components/index.d.ts.map +1 -1
  45. package/dist/src/components/index.js +1 -0
  46. package/dist/src/components/index.js.map +1 -1
  47. package/dist/src/content-slot.d.ts.map +1 -1
  48. package/dist/src/content-slot.js +7 -6
  49. package/dist/src/content-slot.js.map +1 -1
  50. package/dist/src/context.d.ts.map +1 -1
  51. package/dist/src/context.js +10 -3
  52. package/dist/src/context.js.map +1 -1
  53. package/dist/src/debug/cli.d.ts +6 -0
  54. package/dist/src/debug/cli.d.ts.map +1 -0
  55. package/dist/src/{debug.js → debug/cli.js} +79 -82
  56. package/dist/src/debug/cli.js.map +1 -0
  57. package/dist/src/debug/diagnostics.test.d.ts +2 -0
  58. package/dist/src/debug/diagnostics.test.d.ts.map +1 -0
  59. package/dist/src/debug/diagnostics.test.js +46 -0
  60. package/dist/src/debug/diagnostics.test.js.map +1 -0
  61. package/dist/src/debug/effects.d.ts +81 -0
  62. package/dist/src/debug/effects.d.ts.map +1 -0
  63. package/dist/src/debug/effects.js +358 -0
  64. package/dist/src/debug/effects.js.map +1 -0
  65. package/dist/src/debug/effects.test.d.ts +2 -0
  66. package/dist/src/debug/effects.test.d.ts.map +1 -0
  67. package/dist/src/debug/effects.test.js +256 -0
  68. package/dist/src/debug/effects.test.js.map +1 -0
  69. package/dist/src/debug/files.d.ts +14 -0
  70. package/dist/src/debug/files.d.ts.map +1 -0
  71. package/dist/src/debug/files.js +29 -0
  72. package/dist/src/debug/files.js.map +1 -0
  73. package/dist/src/debug/files.test.d.ts +2 -0
  74. package/dist/src/debug/files.test.d.ts.map +1 -0
  75. package/dist/src/debug/files.test.js +66 -0
  76. package/dist/src/debug/files.test.js.map +1 -0
  77. package/dist/src/debug/index.d.ts +63 -0
  78. package/dist/src/debug/index.d.ts.map +1 -0
  79. package/dist/src/debug/index.js +71 -0
  80. package/dist/src/debug/index.js.map +1 -0
  81. package/dist/src/debug/message-format.test.d.ts +2 -0
  82. package/dist/src/debug/message-format.test.d.ts.map +1 -0
  83. package/dist/src/debug/message-format.test.js +700 -0
  84. package/dist/src/debug/message-format.test.js.map +1 -0
  85. package/dist/src/debug/render-tree-orphans.test.d.ts +2 -0
  86. package/dist/src/debug/render-tree-orphans.test.d.ts.map +1 -0
  87. package/dist/src/debug/render-tree-orphans.test.js +297 -0
  88. package/dist/src/debug/render-tree-orphans.test.js.map +1 -0
  89. package/dist/src/debug/render.d.ts +57 -0
  90. package/dist/src/debug/render.d.ts.map +1 -0
  91. package/dist/src/debug/render.js +472 -0
  92. package/dist/src/debug/render.js.map +1 -0
  93. package/dist/src/debug/render.test.d.ts +2 -0
  94. package/dist/src/debug/render.test.d.ts.map +1 -0
  95. package/dist/src/debug/render.test.js +291 -0
  96. package/dist/src/debug/render.test.js.map +1 -0
  97. package/dist/src/debug/serialize.d.ts +9 -0
  98. package/dist/src/debug/serialize.d.ts.map +1 -0
  99. package/dist/src/debug/serialize.js +70 -0
  100. package/dist/src/debug/serialize.js.map +1 -0
  101. package/dist/src/debug/symbols.d.ts +16 -0
  102. package/dist/src/debug/symbols.d.ts.map +1 -0
  103. package/dist/src/debug/symbols.js +196 -0
  104. package/dist/src/debug/symbols.js.map +1 -0
  105. package/dist/src/debug/symbols.test.d.ts +2 -0
  106. package/dist/src/debug/symbols.test.d.ts.map +1 -0
  107. package/dist/src/debug/symbols.test.js +93 -0
  108. package/dist/src/debug/symbols.test.js.map +1 -0
  109. package/dist/src/debug/trace-writer.d.ts +55 -0
  110. package/dist/src/debug/trace-writer.d.ts.map +1 -0
  111. package/dist/src/debug/trace-writer.js +658 -0
  112. package/dist/src/debug/trace-writer.js.map +1 -0
  113. package/dist/src/debug/trace.d.ts +342 -0
  114. package/dist/src/debug/trace.d.ts.map +1 -0
  115. package/dist/src/debug/trace.js +446 -0
  116. package/dist/src/debug/trace.js.map +1 -0
  117. package/dist/src/devtools/devtools-protocol.d.ts +389 -0
  118. package/dist/src/devtools/devtools-protocol.d.ts.map +1 -0
  119. package/dist/src/devtools/devtools-protocol.js +2 -0
  120. package/dist/src/devtools/devtools-protocol.js.map +1 -0
  121. package/dist/src/devtools/devtools-server.browser.d.ts +23 -0
  122. package/dist/src/devtools/devtools-server.browser.d.ts.map +1 -0
  123. package/dist/src/devtools/devtools-server.browser.js +33 -0
  124. package/dist/src/devtools/devtools-server.browser.js.map +1 -0
  125. package/dist/src/devtools/devtools-server.d.ts +66 -0
  126. package/dist/src/devtools/devtools-server.d.ts.map +1 -0
  127. package/dist/src/devtools/devtools-server.js +444 -0
  128. package/dist/src/devtools/devtools-server.js.map +1 -0
  129. package/dist/src/devtools/devtools-transport.d.ts +23 -0
  130. package/dist/src/devtools/devtools-transport.d.ts.map +1 -0
  131. package/dist/src/devtools/devtools-transport.js +114 -0
  132. package/dist/src/devtools/devtools-transport.js.map +1 -0
  133. package/dist/src/devtools-entry.browser.d.ts +4 -0
  134. package/dist/src/devtools-entry.browser.d.ts.map +1 -0
  135. package/dist/src/devtools-entry.browser.js +2 -0
  136. package/dist/src/devtools-entry.browser.js.map +1 -0
  137. package/dist/src/devtools-entry.d.ts +4 -0
  138. package/dist/src/devtools-entry.d.ts.map +1 -0
  139. package/dist/src/devtools-entry.js +2 -0
  140. package/dist/src/devtools-entry.js.map +1 -0
  141. package/dist/src/diagnostics.d.ts +34 -0
  142. package/dist/src/diagnostics.d.ts.map +1 -0
  143. package/dist/src/diagnostics.js +89 -0
  144. package/dist/src/diagnostics.js.map +1 -0
  145. package/dist/src/index.d.ts +3 -2
  146. package/dist/src/index.d.ts.map +1 -1
  147. package/dist/src/index.js +3 -2
  148. package/dist/src/index.js.map +1 -1
  149. package/dist/src/print-hook.d.ts +14 -0
  150. package/dist/src/print-hook.d.ts.map +1 -0
  151. package/dist/src/print-hook.js +10 -0
  152. package/dist/src/print-hook.js.map +1 -0
  153. package/dist/src/reactive-union-set.d.ts.map +1 -1
  154. package/dist/src/reactive-union-set.js +28 -3
  155. package/dist/src/reactive-union-set.js.map +1 -1
  156. package/dist/src/reactivity.d.ts +60 -7
  157. package/dist/src/reactivity.d.ts.map +1 -1
  158. package/dist/src/reactivity.js +308 -39
  159. package/dist/src/reactivity.js.map +1 -1
  160. package/dist/src/render-stack.d.ts +18 -1
  161. package/dist/src/render-stack.d.ts.map +1 -1
  162. package/dist/src/render-stack.js +61 -1
  163. package/dist/src/render-stack.js.map +1 -1
  164. package/dist/src/render.d.ts +8 -15
  165. package/dist/src/render.d.ts.map +1 -1
  166. package/dist/src/render.js +424 -109
  167. package/dist/src/render.js.map +1 -1
  168. package/dist/src/resource.d.ts.map +1 -1
  169. package/dist/src/resource.js +5 -0
  170. package/dist/src/resource.js.map +1 -1
  171. package/dist/src/scheduler.d.ts +13 -0
  172. package/dist/src/scheduler.d.ts.map +1 -1
  173. package/dist/src/scheduler.js +150 -13
  174. package/dist/src/scheduler.js.map +1 -1
  175. package/dist/src/symbols/basic-symbol.d.ts.map +1 -1
  176. package/dist/src/symbols/basic-symbol.js +6 -1
  177. package/dist/src/symbols/basic-symbol.js.map +1 -1
  178. package/dist/src/symbols/decl.d.ts.map +1 -1
  179. package/dist/src/symbols/decl.js +5 -1
  180. package/dist/src/symbols/decl.js.map +1 -1
  181. package/dist/src/symbols/output-scope.d.ts +2 -1
  182. package/dist/src/symbols/output-scope.d.ts.map +1 -1
  183. package/dist/src/symbols/output-scope.js +13 -8
  184. package/dist/src/symbols/output-scope.js.map +1 -1
  185. package/dist/src/symbols/output-symbol.d.ts +1 -0
  186. package/dist/src/symbols/output-symbol.d.ts.map +1 -1
  187. package/dist/src/symbols/output-symbol.js +25 -8
  188. package/dist/src/symbols/output-symbol.js.map +1 -1
  189. package/dist/src/symbols/symbol-flow.d.ts.map +1 -1
  190. package/dist/src/symbols/symbol-flow.js +24 -8
  191. package/dist/src/symbols/symbol-flow.js.map +1 -1
  192. package/dist/src/symbols/symbol-slot.d.ts.map +1 -1
  193. package/dist/src/symbols/symbol-slot.js +15 -0
  194. package/dist/src/symbols/symbol-slot.js.map +1 -1
  195. package/dist/src/symbols/symbol-slot.test.d.ts +2 -0
  196. package/dist/src/symbols/symbol-slot.test.d.ts.map +1 -0
  197. package/dist/src/symbols/symbol-slot.test.js +35 -0
  198. package/dist/src/symbols/symbol-slot.test.js.map +1 -0
  199. package/dist/src/symbols/symbol-table.d.ts.map +1 -1
  200. package/dist/src/symbols/symbol-table.js +6 -5
  201. package/dist/src/symbols/symbol-table.js.map +1 -1
  202. package/dist/src/trace.d.ts +2 -0
  203. package/dist/src/trace.d.ts.map +1 -0
  204. package/dist/src/trace.js +2 -0
  205. package/dist/src/trace.js.map +1 -0
  206. package/dist/src/tracer.d.ts +2 -228
  207. package/dist/src/tracer.d.ts.map +1 -1
  208. package/dist/src/tracer.js +5 -298
  209. package/dist/src/tracer.js.map +1 -1
  210. package/dist/src/utils.d.ts.map +1 -1
  211. package/dist/src/utils.js +17 -9
  212. package/dist/src/utils.js.map +1 -1
  213. package/dist/test/components/append-file.test.d.ts.map +1 -1
  214. package/dist/test/components/append-file.test.js +18 -10
  215. package/dist/test/components/append-file.test.js.map +1 -1
  216. package/dist/test/components/template-file.test.d.ts.map +1 -1
  217. package/dist/test/components/template-file.test.js +6 -4
  218. package/dist/test/components/template-file.test.js.map +1 -1
  219. package/dist/test/lazy-isempty.test.d.ts +2 -0
  220. package/dist/test/lazy-isempty.test.d.ts.map +1 -0
  221. package/dist/test/lazy-isempty.test.js +89 -0
  222. package/dist/test/lazy-isempty.test.js.map +1 -0
  223. package/dist/test/reactive-union-set-disposers.test.d.ts +2 -0
  224. package/dist/test/reactive-union-set-disposers.test.d.ts.map +1 -0
  225. package/dist/test/reactive-union-set-disposers.test.js +98 -0
  226. package/dist/test/reactive-union-set-disposers.test.js.map +1 -0
  227. package/dist/test/reactivity/shallow-reactive.test.d.ts +2 -0
  228. package/dist/test/reactivity/shallow-reactive.test.d.ts.map +1 -0
  229. package/dist/test/reactivity/shallow-reactive.test.js +52 -0
  230. package/dist/test/reactivity/shallow-reactive.test.js.map +1 -0
  231. package/dist/test/rendering/basic.test.js +3 -0
  232. package/dist/test/rendering/basic.test.js.map +1 -1
  233. package/dist/test/rendering/print-render-stack.test.d.ts.map +1 -1
  234. package/dist/test/rendering/print-render-stack.test.js +91 -98
  235. package/dist/test/rendering/print-render-stack.test.js.map +1 -1
  236. package/dist/test/scheduler-extended.test.d.ts +2 -0
  237. package/dist/test/scheduler-extended.test.d.ts.map +1 -0
  238. package/dist/test/scheduler-extended.test.js +96 -0
  239. package/dist/test/scheduler-extended.test.js.map +1 -0
  240. package/dist/test/scheduler.test.d.ts +2 -0
  241. package/dist/test/scheduler.test.d.ts.map +1 -0
  242. package/dist/test/scheduler.test.js +46 -0
  243. package/dist/test/scheduler.test.js.map +1 -0
  244. package/dist/testing/create-test-wrapper.d.ts +1 -1
  245. package/dist/testing/create-test-wrapper.d.ts.map +1 -1
  246. package/dist/testing/create-test-wrapper.js +1 -1
  247. package/dist/testing/create-test-wrapper.js.map +1 -1
  248. package/dist/testing/devtools-utils.d.ts +35 -0
  249. package/dist/testing/devtools-utils.d.ts.map +1 -0
  250. package/dist/testing/devtools-utils.js +162 -0
  251. package/dist/testing/devtools-utils.js.map +1 -0
  252. package/dist/testing/extend-expect.d.ts.map +1 -1
  253. package/dist/testing/extend-expect.js +63 -1
  254. package/dist/testing/extend-expect.js.map +1 -1
  255. package/dist/testing/render.d.ts +2 -2
  256. package/dist/testing/render.d.ts.map +1 -1
  257. package/dist/testing/render.js +2 -2
  258. package/dist/testing/render.js.map +1 -1
  259. package/dist/tsconfig.tsbuildinfo +1 -1
  260. package/package.json +21 -7
  261. package/scripts/copy-devtools-ui.mjs +26 -0
  262. package/src/binder.ts +117 -53
  263. package/src/components/AccessExpression.test.tsx +132 -0
  264. package/src/components/AccessExpression.tsx +344 -0
  265. package/src/components/AppendFile.tsx +14 -9
  266. package/src/components/Block.tsx +1 -1
  267. package/src/components/Declaration.tsx +2 -1
  268. package/src/components/For.tsx +14 -10
  269. package/src/components/List.tsx +7 -4
  270. package/src/components/Prose.tsx +1 -1
  271. package/src/components/Scope.tsx +6 -1
  272. package/src/components/SourceDirectory.tsx +1 -2
  273. package/src/components/Switch.tsx +11 -7
  274. package/src/components/TemplateFile.tsx +18 -9
  275. package/src/components/index.tsx +1 -0
  276. package/src/content-slot.tsx +7 -7
  277. package/src/context.ts +17 -6
  278. package/src/{debug.ts → debug/cli.ts} +114 -125
  279. package/src/debug/diagnostics.test.tsx +56 -0
  280. package/src/debug/effects.test.tsx +301 -0
  281. package/src/debug/effects.ts +531 -0
  282. package/src/debug/files.test.tsx +76 -0
  283. package/src/debug/files.ts +40 -0
  284. package/src/debug/index.ts +132 -0
  285. package/src/debug/message-format.test.tsx +759 -0
  286. package/src/debug/render-tree-orphans.test.tsx +344 -0
  287. package/src/debug/render.test.tsx +357 -0
  288. package/src/debug/render.ts +698 -0
  289. package/src/debug/serialize.ts +85 -0
  290. package/src/debug/symbols.test.tsx +105 -0
  291. package/src/debug/symbols.ts +322 -0
  292. package/src/debug/trace-writer.ts +969 -0
  293. package/src/debug/trace.ts +309 -0
  294. package/src/devtools/devtools-protocol.ts +497 -0
  295. package/src/devtools/devtools-server.browser.ts +62 -0
  296. package/src/devtools/devtools-server.ts +468 -0
  297. package/src/devtools/devtools-transport.ts +154 -0
  298. package/src/devtools-entry.browser.ts +48 -0
  299. package/src/devtools-entry.ts +48 -0
  300. package/src/diagnostics.ts +150 -0
  301. package/src/index.ts +2 -7
  302. package/src/print-hook.ts +22 -0
  303. package/src/reactive-union-set.ts +85 -44
  304. package/src/reactivity.ts +396 -58
  305. package/src/render-stack.ts +73 -1
  306. package/src/render.ts +544 -161
  307. package/src/resource.ts +28 -19
  308. package/src/scheduler.ts +209 -14
  309. package/src/symbols/basic-symbol.ts +6 -1
  310. package/src/symbols/decl.ts +5 -1
  311. package/src/symbols/output-scope.ts +21 -13
  312. package/src/symbols/output-symbol.ts +34 -14
  313. package/src/symbols/symbol-flow.ts +76 -39
  314. package/src/symbols/symbol-slot.test.tsx +41 -0
  315. package/src/symbols/symbol-slot.tsx +47 -20
  316. package/src/symbols/symbol-table.ts +6 -10
  317. package/src/trace.ts +1 -0
  318. package/src/tracer.ts +13 -242
  319. package/src/utils.tsx +31 -21
  320. package/temp/api.json +5700 -3015
  321. package/test/components/append-file.test.tsx +36 -29
  322. package/test/components/template-file.test.tsx +11 -11
  323. package/test/lazy-isempty.test.tsx +106 -0
  324. package/test/reactive-union-set-disposers.test.tsx +112 -0
  325. package/test/reactivity/shallow-reactive.test.tsx +56 -0
  326. package/test/rendering/basic.test.tsx +4 -0
  327. package/test/rendering/print-render-stack.test.tsx +52 -43
  328. package/test/scheduler-extended.test.tsx +122 -0
  329. package/test/scheduler.test.tsx +50 -0
  330. package/testing/create-test-wrapper.tsx +1 -1
  331. package/testing/devtools-utils.ts +245 -0
  332. package/testing/extend-expect.ts +89 -0
  333. package/testing/render.ts +2 -2
  334. package/testing/vitest.d.ts +9 -0
  335. package/dist/src/debug.d.ts +0 -14
  336. package/dist/src/debug.d.ts.map +0 -1
  337. package/dist/src/debug.js.map +0 -1
@@ -0,0 +1,531 @@
1
+ import { isReactive, isRef } from "@vue/reactivity";
2
+ import {
3
+ formatReactivePropertyLabel,
4
+ getReactiveCreationLocation,
5
+ nextReactiveId,
6
+ } from "../reactivity.js";
7
+ import { insertEdge, insertEffect, insertRef } from "./trace-writer.js";
8
+ import {
9
+ isDebugEnabled,
10
+ isTraceEnabled,
11
+ logDevtoolsMessage,
12
+ TracePhase,
13
+ traceType,
14
+ } from "./trace.js";
15
+
16
+ // ─────────────────────────────────────────────────────────────────────────────
17
+ // Effects debug
18
+ // ─────────────────────────────────────────────────────────────────────────────
19
+
20
+ export interface SourceLocation {
21
+ fileName?: string;
22
+ lineNumber?: number;
23
+ columnNumber?: number;
24
+ stack?: string;
25
+ }
26
+
27
+ export interface EffectDebugInfo {
28
+ id: number;
29
+ name?: string;
30
+ type?: string;
31
+ createdAt?: SourceLocation;
32
+ contextId?: number;
33
+ ownerContextId?: number | null;
34
+ component?: string;
35
+ lastTriggeredByRefId?: number;
36
+ }
37
+
38
+ export interface RefDebugInfo {
39
+ id: number;
40
+ kind?: string;
41
+ label?: string;
42
+ createdAt?: SourceLocation;
43
+ createdByEffectId?: number;
44
+ isApproxLocation?: boolean;
45
+ }
46
+
47
+ export interface EffectEdgeDebugInfo {
48
+ id: number;
49
+ type: "track" | "trigger" | "triggered-by";
50
+ effectId: number;
51
+ refId?: number;
52
+ targetId?: number;
53
+ targetKind?: "ref" | "target";
54
+ targetLabel?: string;
55
+ targetKey?: string | number;
56
+ }
57
+
58
+ const effects = new Map<number, EffectDebugInfo>();
59
+ const refs = new Map<number, RefDebugInfo>();
60
+ let effectIdCounter = 1;
61
+ let edgeEventIdCounter = 1;
62
+ const nonRefTargetIds = new WeakMap<object, number>();
63
+ const primitiveTargetIds = new Map<unknown, number>();
64
+
65
+ // Alloy-internal paths to skip when capturing source locations.
66
+ // We skip core infrastructure (reactivity, render, debug, scheduler, etc.)
67
+ // but allow component and symbol frames through so the location points at
68
+ // the component/symbol that created the effect.
69
+ // These patterns match both src/ and dist/src/ paths.
70
+ const CORE_INTERNAL_PATHS = [
71
+ "/core/src/reactivity",
72
+ "/core/src/render",
73
+ "/core/src/scheduler",
74
+ "/core/src/debug/",
75
+ "/core/src/devtools/",
76
+ "/core/src/resource",
77
+ "/core/src/context",
78
+ "/core/src/tracer",
79
+ "/core/src/reactive-union-set",
80
+ "/core/src/utils",
81
+ "/core/dist/src/reactivity",
82
+ "/core/dist/src/render",
83
+ "/core/dist/src/scheduler",
84
+ "/core/dist/src/debug/",
85
+ "/core/dist/src/devtools/",
86
+ "/core/dist/src/resource",
87
+ "/core/dist/src/context",
88
+ "/core/dist/src/tracer",
89
+ "/core/dist/src/reactive-union-set",
90
+ "/core/dist/src/utils",
91
+ ];
92
+
93
+ const STACK_SKIP = [
94
+ "node:internal",
95
+ "/@vue/",
96
+ "\\@vue\\",
97
+ "captureSourceLocation",
98
+ ...CORE_INTERNAL_PATHS,
99
+ ...CORE_INTERNAL_PATHS.map((p) => p.replace(/\//g, "\\")),
100
+ ];
101
+
102
+ const VUE_REACTIVITY_MARKERS = [
103
+ "@vue/reactivity",
104
+ "reactivity.esm",
105
+ "reactivity.cjs",
106
+ "reactivity.global",
107
+ "/@vue/",
108
+ "\\@vue\\",
109
+ ];
110
+
111
+ // ─────────────────────────────────────────────────────────────────────────────
112
+ // Fast source location capture using V8 structured CallSite API
113
+ // ─────────────────────────────────────────────────────────────────────────────
114
+
115
+ // Lazily loaded findSourceMap from node:module
116
+ let findSourceMap:
117
+ | ((path: string) =>
118
+ | {
119
+ findEntry: (
120
+ line: number,
121
+ col: number,
122
+ ) =>
123
+ | {
124
+ originalSource: string;
125
+ originalLine: number;
126
+ originalColumn: number;
127
+ }
128
+ | undefined;
129
+ }
130
+ | undefined)
131
+ | undefined;
132
+ let findSourceMapLoaded = false;
133
+ let realpathSync: ((path: string) => string) | undefined;
134
+ // Cache realpath lookups to avoid repeated fs calls
135
+ const realpathCache = new Map<string, string>();
136
+
137
+ function loadFindSourceMap() {
138
+ if (findSourceMapLoaded) return;
139
+ findSourceMapLoaded = true;
140
+ // process.getBuiltinModule works in both ESM and CJS contexts
141
+ try {
142
+ const mod = process.getBuiltinModule?.("node:module") as
143
+ | typeof import("node:module")
144
+ | undefined;
145
+ if (mod && typeof mod.findSourceMap === "function") {
146
+ findSourceMap = mod.findSourceMap as typeof findSourceMap;
147
+ }
148
+ } catch {
149
+ // not available
150
+ }
151
+ try {
152
+ const fs = process.getBuiltinModule?.("node:fs") as
153
+ | typeof import("node:fs")
154
+ | undefined;
155
+ if (fs) {
156
+ realpathSync = fs.realpathSync;
157
+ }
158
+ } catch {
159
+ // not available
160
+ }
161
+ }
162
+
163
+ function getRealPath(fileName: string): string {
164
+ if (!realpathSync) return fileName;
165
+ let real = realpathCache.get(fileName);
166
+ if (real === undefined) {
167
+ try {
168
+ real = realpathSync(fileName);
169
+ } catch {
170
+ real = fileName;
171
+ }
172
+ realpathCache.set(fileName, real);
173
+ }
174
+ return real;
175
+ }
176
+
177
+ function isSkipFile(fileName: string): boolean {
178
+ for (const skip of STACK_SKIP) {
179
+ if (fileName.includes(skip)) return true;
180
+ }
181
+ return false;
182
+ }
183
+
184
+ function isVueReactivityFile(fileName: string): boolean {
185
+ for (const marker of VUE_REACTIVITY_MARKERS) {
186
+ if (fileName.includes(marker)) return true;
187
+ }
188
+ return false;
189
+ }
190
+
191
+ function resolveSourceMap(
192
+ fileName: string,
193
+ line: number,
194
+ col: number,
195
+ ): { fileName: string; line: number; col: number } {
196
+ if (!findSourceMap) return { fileName, line, col };
197
+ // pnpm uses symlinks; findSourceMap only matches the real path
198
+ const real = getRealPath(fileName);
199
+ const map = findSourceMap(real);
200
+ if (!map) return { fileName, line, col };
201
+ const entry = map.findEntry(line - 1, col - 1);
202
+ if (!entry) return { fileName, line, col };
203
+ return {
204
+ fileName: entry.originalSource,
205
+ line: entry.originalLine + 1,
206
+ col: entry.originalColumn + 1,
207
+ };
208
+ }
209
+
210
+ // V8 structured stack capture — avoids string formatting entirely
211
+ const structuredPrepare = (
212
+ _err: Error,
213
+ callSites: NodeJS.CallSite[],
214
+ ): NodeJS.CallSite[] => callSites;
215
+
216
+ function captureCallSites(): NodeJS.CallSite[] {
217
+ const orig = Error.prepareStackTrace;
218
+ Error.prepareStackTrace = structuredPrepare;
219
+ const obj: { stack?: NodeJS.CallSite[] } = {};
220
+ Error.captureStackTrace(obj, captureCallSites);
221
+ const sites = obj.stack ?? [];
222
+ Error.prepareStackTrace = orig;
223
+ return sites;
224
+ }
225
+
226
+ export function captureSourceLocation(
227
+ skipReactives = true,
228
+ ): SourceLocation | undefined {
229
+ if (!isDebugEnabled()) return undefined;
230
+ loadFindSourceMap();
231
+
232
+ const sites = captureCallSites();
233
+
234
+ // First pass: skip internal/framework frames
235
+ for (const site of sites) {
236
+ const fn = site.getFileName();
237
+ if (!fn) continue;
238
+ if (isSkipFile(fn)) continue;
239
+ if (skipReactives && isVueReactivityFile(fn)) continue;
240
+
241
+ const line = site.getLineNumber() ?? 0;
242
+ const col = site.getColumnNumber() ?? 0;
243
+ const resolved = resolveSourceMap(fn, line, col);
244
+ return {
245
+ fileName: resolved.fileName,
246
+ lineNumber: resolved.line,
247
+ columnNumber: resolved.col,
248
+ };
249
+ }
250
+
251
+ // Second pass without reactive filter
252
+ if (skipReactives) {
253
+ for (const site of sites) {
254
+ const fn = site.getFileName();
255
+ if (!fn) continue;
256
+ if (isSkipFile(fn)) continue;
257
+
258
+ const line = site.getLineNumber() ?? 0;
259
+ const col = site.getColumnNumber() ?? 0;
260
+ const resolved = resolveSourceMap(fn, line, col);
261
+ return {
262
+ fileName: resolved.fileName,
263
+ lineNumber: resolved.line,
264
+ columnNumber: resolved.col,
265
+ };
266
+ }
267
+ }
268
+
269
+ return undefined;
270
+ }
271
+
272
+ function getNonRefTargetId(target: unknown): number {
273
+ if (typeof target === "object" && target !== null) {
274
+ const existing = nonRefTargetIds.get(target);
275
+ if (existing) return existing;
276
+ const id = nextReactiveId();
277
+ nonRefTargetIds.set(target, id);
278
+ return id;
279
+ }
280
+ if (typeof target === "function") {
281
+ const existing = nonRefTargetIds.get(target as object);
282
+ if (existing) return existing;
283
+ const id = nextReactiveId();
284
+ nonRefTargetIds.set(target as object, id);
285
+ return id;
286
+ }
287
+ const existing = primitiveTargetIds.get(target);
288
+ if (existing) return existing;
289
+ const id = nextReactiveId();
290
+ primitiveTargetIds.set(target, id);
291
+ return id;
292
+ }
293
+
294
+ function formatNonRefTargetLabel(target: unknown): string {
295
+ if (Array.isArray(target)) return "[]";
296
+ try {
297
+ return String(target);
298
+ } catch {
299
+ return "[Unserializable]";
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Sanitize a Map/object key to ensure it's serializable.
305
+ */
306
+ function sanitizeTargetKey(key: unknown): string | number | undefined {
307
+ if (key === undefined) return undefined;
308
+ if (typeof key === "string" || typeof key === "number") return key;
309
+ if (typeof key === "symbol") return key.toString();
310
+ if (typeof key === "object" || typeof key === "function") return "Object";
311
+ return String(key);
312
+ }
313
+
314
+ function buildEffectTargetInfo(input: {
315
+ target: unknown;
316
+ refId?: number;
317
+ }): Pick<
318
+ EffectEdgeDebugInfo,
319
+ "refId" | "targetId" | "targetKind" | "targetLabel"
320
+ > {
321
+ if (input.refId !== undefined) {
322
+ return {
323
+ refId: input.refId,
324
+ targetId: input.refId,
325
+ targetKind: "ref",
326
+ };
327
+ }
328
+
329
+ const targetId = getNonRefTargetId(input.target);
330
+ return {
331
+ targetId,
332
+ targetKind: "target",
333
+ targetLabel: formatNonRefTargetLabel(input.target),
334
+ };
335
+ }
336
+
337
+ function emitEffect(message: { type: string; [key: string]: unknown }) {
338
+ logDevtoolsMessage(message);
339
+ }
340
+
341
+ export function update(input: Partial<EffectDebugInfo> & { id: number }) {
342
+ if (!isDebugEnabled()) return;
343
+ const existing = effects.get(input.id);
344
+ if (!existing) return;
345
+ const next: EffectDebugInfo = { ...existing, ...input };
346
+ effects.set(input.id, next);
347
+ emitEffect({
348
+ type: traceType(TracePhase.effect.effectUpdated),
349
+ effect: next,
350
+ });
351
+ }
352
+
353
+ export function register(input: {
354
+ name?: string;
355
+ type?: string;
356
+ createdAt?: SourceLocation;
357
+ contextId?: number;
358
+ ownerContextId?: number | null;
359
+ }): number {
360
+ if (!isDebugEnabled()) return -1;
361
+ const id = effectIdCounter++;
362
+ const info: EffectDebugInfo = {
363
+ id,
364
+ name: input.name,
365
+ type: input.type,
366
+ createdAt: input.createdAt ?? captureSourceLocation(),
367
+ contextId: input.contextId,
368
+ ownerContextId: input.ownerContextId ?? null,
369
+ };
370
+ effects.set(id, info);
371
+ emitEffect({ type: traceType(TracePhase.effect.effectAdded), effect: info });
372
+
373
+ if (isTraceEnabled()) {
374
+ insertEffect(
375
+ id,
376
+ input.name,
377
+ input.type,
378
+ input.contextId,
379
+ input.ownerContextId ?? null,
380
+ info.createdAt?.fileName,
381
+ info.createdAt?.lineNumber,
382
+ info.createdAt?.columnNumber,
383
+ );
384
+ }
385
+
386
+ return id;
387
+ }
388
+
389
+ export function registerRef(input: {
390
+ id: number;
391
+ kind?: string;
392
+ createdAt?: SourceLocation;
393
+ createdByEffectId?: number;
394
+ isInfrastructure?: boolean;
395
+ isApproxLocation?: boolean;
396
+ label?: string;
397
+ }) {
398
+ if (!isDebugEnabled()) return;
399
+ if (refs.has(input.id)) return;
400
+ const info: RefDebugInfo = {
401
+ id: input.id,
402
+ kind: input.kind,
403
+ createdAt: input.createdAt ?? captureSourceLocation(),
404
+ createdByEffectId: input.createdByEffectId,
405
+ label: input.label,
406
+ isApproxLocation: input.isApproxLocation,
407
+ };
408
+ refs.set(input.id, info);
409
+ emitEffect({ type: traceType(TracePhase.effect.refAdded), ref: info });
410
+
411
+ if (isTraceEnabled()) {
412
+ insertRef(
413
+ input.id,
414
+ input.kind,
415
+ input.createdByEffectId,
416
+ info.createdAt?.fileName,
417
+ info.createdAt?.lineNumber,
418
+ info.createdAt?.columnNumber,
419
+ input.label,
420
+ input.isApproxLocation,
421
+ );
422
+ }
423
+ }
424
+
425
+ export function ensureRef(input: { id: number; kind?: string }) {
426
+ if (!isDebugEnabled()) return;
427
+ if (refs.has(input.id)) return;
428
+ registerRef({ id: input.id, kind: input.kind });
429
+ }
430
+
431
+ export function ensureReactivePropertyRef(input: {
432
+ id: number;
433
+ target: object;
434
+ key: string | number;
435
+ }) {
436
+ if (!isDebugEnabled()) return;
437
+ if (refs.has(input.id)) return;
438
+ const label = formatReactivePropertyLabel(input.target, input.key);
439
+ const createdAt = getReactiveCreationLocation(input.target);
440
+ // If the reactive wasn't created via alloy's shallowReactive wrapper,
441
+ // createdAt is undefined and registerRef falls back to captureSourceLocation
442
+ // (the first track site). Flag this as approximate.
443
+ const isApproxLocation = !createdAt;
444
+ registerRef({
445
+ id: input.id,
446
+ kind: "reactive-property",
447
+ label,
448
+ createdAt,
449
+ isApproxLocation,
450
+ });
451
+ }
452
+
453
+ export function track(input: {
454
+ effectId: number;
455
+ target: unknown;
456
+ refId?: number;
457
+ targetKey?: unknown;
458
+ }) {
459
+ if (!isDebugEnabled()) return;
460
+ const edge: EffectEdgeDebugInfo = {
461
+ id: edgeEventIdCounter++,
462
+ type: "track",
463
+ effectId: input.effectId,
464
+ ...buildEffectTargetInfo({ target: input.target, refId: input.refId }),
465
+ targetKey: sanitizeTargetKey(input.targetKey),
466
+ };
467
+ emitEffect({ type: traceType(TracePhase.effect.track), edge });
468
+
469
+ if (isTraceEnabled()) {
470
+ insertEdge(
471
+ "track",
472
+ input.effectId,
473
+ edge.refId,
474
+ edge.targetId,
475
+ edge.targetKey,
476
+ undefined,
477
+ );
478
+ }
479
+ }
480
+
481
+ export function trigger(input: {
482
+ effectId: number;
483
+ target: unknown;
484
+ refId?: number;
485
+ targetKey?: unknown;
486
+ kind?: "trigger" | "triggered-by";
487
+ causedBy?: number;
488
+ }) {
489
+ if (!isDebugEnabled()) return;
490
+ const edge: EffectEdgeDebugInfo = {
491
+ id: edgeEventIdCounter++,
492
+ type: input.kind ?? "trigger",
493
+ effectId: input.effectId,
494
+ ...buildEffectTargetInfo({ target: input.target, refId: input.refId }),
495
+ targetKey: sanitizeTargetKey(input.targetKey),
496
+ };
497
+ emitEffect({ type: traceType(TracePhase.effect.trigger), edge });
498
+
499
+ if (isTraceEnabled()) {
500
+ insertEdge(
501
+ edge.type,
502
+ input.effectId,
503
+ edge.refId,
504
+ edge.targetId,
505
+ edge.targetKey,
506
+ input.causedBy,
507
+ );
508
+ }
509
+
510
+ update({
511
+ id: input.effectId,
512
+ ...(input.refId !== undefined ? { lastTriggeredByRefId: input.refId } : {}),
513
+ });
514
+ }
515
+
516
+ export function reset() {
517
+ effects.clear();
518
+ refs.clear();
519
+ primitiveTargetIds.clear();
520
+ effectIdCounter = 1;
521
+ edgeEventIdCounter = 1;
522
+ }
523
+
524
+ // Utilities used by other debug sections
525
+ export function isRefTarget(value: unknown) {
526
+ return isRef(value);
527
+ }
528
+
529
+ export function isReactiveTarget(value: unknown) {
530
+ return isReactive(value);
531
+ }
@@ -0,0 +1,76 @@
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 { SourceDirectory } from "../components/SourceDirectory.jsx";
9
+ import { SourceFile } from "../components/SourceFile.jsx";
10
+ import {
11
+ enableDevtools,
12
+ resetDevtoolsServerForTests,
13
+ } from "../devtools/devtools-server.js";
14
+ import { renderAsync } from "../render.js";
15
+
16
+ let socket: WebSocket | undefined;
17
+
18
+ beforeEach(async () => {
19
+ const server = await enableDevtools({ port: 0 });
20
+ socket = new WebSocket(`ws://127.0.0.1:${server.port}`);
21
+
22
+ await new Promise<void>((resolve, reject) => {
23
+ socket?.once("open", resolve);
24
+ socket?.once("error", reject);
25
+ });
26
+ });
27
+
28
+ afterEach(async () => {
29
+ if (socket) {
30
+ socket.close();
31
+ socket = undefined;
32
+ }
33
+
34
+ await resetDevtoolsServerForTests();
35
+ });
36
+
37
+ it("emits file and directory add/update/remove messages", async () => {
38
+ const collector = await createMessageCollector(socket!);
39
+
40
+ await renderAsync(
41
+ <Output>
42
+ <SourceDirectory path="src">
43
+ <SourceFile path="index.ts" filetype="ts">
44
+ {"export const value = 1;"}
45
+ </SourceFile>
46
+ </SourceDirectory>
47
+ </Output>,
48
+ );
49
+
50
+ const initialMessages = await collector.waitForRender();
51
+ const initialFiles = initialMessages.filter(
52
+ (m: DevtoolsMessage) =>
53
+ (m.type.startsWith("file:") || m.type.startsWith("directory:")) &&
54
+ !("triggerIds" in m),
55
+ );
56
+ expect(initialFiles[0]).toMatchObject({
57
+ type: "directory:added",
58
+ path: "./",
59
+ });
60
+ expect(initialFiles[1]).toMatchObject({
61
+ type: "directory:added",
62
+ path: "src",
63
+ });
64
+ expect(initialFiles[2]).toMatchObject({
65
+ type: "file:added",
66
+ path: "src/index.ts",
67
+ filetype: "ts",
68
+ });
69
+ expect(initialFiles[3]).toMatchObject({
70
+ type: "file:updated",
71
+ path: "src/index.ts",
72
+ content: expect.any(String),
73
+ });
74
+
75
+ collector.stop();
76
+ });
@@ -0,0 +1,40 @@
1
+ import {
2
+ insertDirectory,
3
+ insertOutputFile,
4
+ updateOutputFileContent,
5
+ } from "./trace-writer.js";
6
+ import { isDebugEnabled } from "./trace.js";
7
+
8
+ export interface FileUpdateInfo {
9
+ path: string;
10
+ filetype: string;
11
+ contents: string;
12
+ }
13
+
14
+ const fileContentCache = new Map<string, string>();
15
+
16
+ /** Record a directory being added to the output. */
17
+ export function recordDirectory(path: string) {
18
+ if (!isDebugEnabled()) return;
19
+ insertDirectory(path);
20
+ }
21
+
22
+ /** Record a file being added to the output. */
23
+ export function recordFile(path: string, filetype: string) {
24
+ if (!isDebugEnabled()) return;
25
+ insertOutputFile(path, filetype, undefined);
26
+ }
27
+
28
+ /** Notify devtools that a file's contents have changed. De-duplicates by content. */
29
+ export function updated(info: FileUpdateInfo) {
30
+ const previous = fileContentCache.get(info.path);
31
+ if (previous === info.contents) return;
32
+ fileContentCache.set(info.path, info.contents);
33
+
34
+ updateOutputFileContent(info.path, info.contents);
35
+ }
36
+
37
+ /** Clear all cached file state. Called when a new render begins. */
38
+ export function reset() {
39
+ fileContentCache.clear();
40
+ }