@alloy-js/core 0.23.0-dev.1 → 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 (310) 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} +79 -82
  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 +18 -1
  140. package/dist/src/render-stack.d.ts.map +1 -1
  141. package/dist/src/render-stack.js +61 -1
  142. package/dist/src/render-stack.js.map +1 -1
  143. package/dist/src/render.d.ts +8 -15
  144. package/dist/src/render.d.ts.map +1 -1
  145. package/dist/src/render.js +370 -109
  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/scheduler.d.ts +8 -0
  151. package/dist/src/scheduler.d.ts.map +1 -1
  152. package/dist/src/scheduler.js +69 -3
  153. package/dist/src/scheduler.js.map +1 -1
  154. package/dist/src/symbols/basic-symbol.d.ts.map +1 -1
  155. package/dist/src/symbols/basic-symbol.js +6 -1
  156. package/dist/src/symbols/basic-symbol.js.map +1 -1
  157. package/dist/src/symbols/decl.d.ts.map +1 -1
  158. package/dist/src/symbols/decl.js +5 -1
  159. package/dist/src/symbols/decl.js.map +1 -1
  160. package/dist/src/symbols/output-scope.d.ts +2 -1
  161. package/dist/src/symbols/output-scope.d.ts.map +1 -1
  162. package/dist/src/symbols/output-scope.js +13 -8
  163. package/dist/src/symbols/output-scope.js.map +1 -1
  164. package/dist/src/symbols/output-symbol.d.ts +1 -0
  165. package/dist/src/symbols/output-symbol.d.ts.map +1 -1
  166. package/dist/src/symbols/output-symbol.js +25 -8
  167. package/dist/src/symbols/output-symbol.js.map +1 -1
  168. package/dist/src/symbols/symbol-flow.d.ts.map +1 -1
  169. package/dist/src/symbols/symbol-flow.js +24 -8
  170. package/dist/src/symbols/symbol-flow.js.map +1 -1
  171. package/dist/src/symbols/symbol-slot.d.ts.map +1 -1
  172. package/dist/src/symbols/symbol-slot.js +15 -0
  173. package/dist/src/symbols/symbol-slot.js.map +1 -1
  174. package/dist/src/symbols/symbol-slot.test.d.ts +2 -0
  175. package/dist/src/symbols/symbol-slot.test.d.ts.map +1 -0
  176. package/dist/src/symbols/symbol-slot.test.js +35 -0
  177. package/dist/src/symbols/symbol-slot.test.js.map +1 -0
  178. package/dist/src/symbols/symbol-table.d.ts.map +1 -1
  179. package/dist/src/symbols/symbol-table.js +6 -5
  180. package/dist/src/symbols/symbol-table.js.map +1 -1
  181. package/dist/src/trace.d.ts +2 -0
  182. package/dist/src/trace.d.ts.map +1 -0
  183. package/dist/src/trace.js +2 -0
  184. package/dist/src/trace.js.map +1 -0
  185. package/dist/src/tracer.d.ts +2 -228
  186. package/dist/src/tracer.d.ts.map +1 -1
  187. package/dist/src/tracer.js +5 -298
  188. package/dist/src/tracer.js.map +1 -1
  189. package/dist/src/utils.d.ts.map +1 -1
  190. package/dist/src/utils.js +7 -5
  191. package/dist/src/utils.js.map +1 -1
  192. package/dist/test/components/append-file.test.d.ts.map +1 -1
  193. package/dist/test/components/append-file.test.js +18 -10
  194. package/dist/test/components/append-file.test.js.map +1 -1
  195. package/dist/test/components/template-file.test.d.ts.map +1 -1
  196. package/dist/test/components/template-file.test.js +6 -4
  197. package/dist/test/components/template-file.test.js.map +1 -1
  198. package/dist/test/lazy-isempty.test.d.ts +2 -0
  199. package/dist/test/lazy-isempty.test.d.ts.map +1 -0
  200. package/dist/test/lazy-isempty.test.js +89 -0
  201. package/dist/test/lazy-isempty.test.js.map +1 -0
  202. package/dist/test/reactive-union-set-disposers.test.d.ts +2 -0
  203. package/dist/test/reactive-union-set-disposers.test.d.ts.map +1 -0
  204. package/dist/test/reactive-union-set-disposers.test.js +98 -0
  205. package/dist/test/reactive-union-set-disposers.test.js.map +1 -0
  206. package/dist/test/reactivity/shallow-reactive.test.d.ts +2 -0
  207. package/dist/test/reactivity/shallow-reactive.test.d.ts.map +1 -0
  208. package/dist/test/reactivity/shallow-reactive.test.js +52 -0
  209. package/dist/test/reactivity/shallow-reactive.test.js.map +1 -0
  210. package/dist/test/rendering/basic.test.js +3 -0
  211. package/dist/test/rendering/basic.test.js.map +1 -1
  212. package/dist/test/rendering/print-render-stack.test.d.ts.map +1 -1
  213. package/dist/test/rendering/print-render-stack.test.js +91 -98
  214. package/dist/test/rendering/print-render-stack.test.js.map +1 -1
  215. package/dist/test/scheduler-extended.test.d.ts +2 -0
  216. package/dist/test/scheduler-extended.test.d.ts.map +1 -0
  217. package/dist/test/scheduler-extended.test.js +96 -0
  218. package/dist/test/scheduler-extended.test.js.map +1 -0
  219. package/dist/test/scheduler.test.d.ts +2 -0
  220. package/dist/test/scheduler.test.d.ts.map +1 -0
  221. package/dist/test/scheduler.test.js +46 -0
  222. package/dist/test/scheduler.test.js.map +1 -0
  223. package/dist/testing/create-test-wrapper.d.ts +1 -1
  224. package/dist/testing/create-test-wrapper.d.ts.map +1 -1
  225. package/dist/testing/create-test-wrapper.js +1 -1
  226. package/dist/testing/create-test-wrapper.js.map +1 -1
  227. package/dist/testing/devtools-utils.d.ts +26 -0
  228. package/dist/testing/devtools-utils.d.ts.map +1 -0
  229. package/dist/testing/devtools-utils.js +140 -0
  230. package/dist/testing/devtools-utils.js.map +1 -0
  231. package/dist/testing/extend-expect.d.ts.map +1 -1
  232. package/dist/testing/extend-expect.js +63 -1
  233. package/dist/testing/extend-expect.js.map +1 -1
  234. package/dist/testing/render.d.ts +2 -2
  235. package/dist/testing/render.d.ts.map +1 -1
  236. package/dist/testing/render.js +2 -2
  237. package/dist/testing/render.js.map +1 -1
  238. package/dist/tsconfig.tsbuildinfo +1 -1
  239. package/package.json +21 -7
  240. package/scripts/copy-devtools-ui.mjs +26 -0
  241. package/src/binder.ts +71 -16
  242. package/src/components/AccessExpression.test.tsx +132 -0
  243. package/src/components/AccessExpression.tsx +344 -0
  244. package/src/components/AppendFile.tsx +14 -9
  245. package/src/components/Block.tsx +1 -1
  246. package/src/components/Declaration.tsx +2 -1
  247. package/src/components/Prose.tsx +1 -1
  248. package/src/components/Scope.tsx +6 -1
  249. package/src/components/SourceDirectory.tsx +1 -2
  250. package/src/components/TemplateFile.tsx +18 -9
  251. package/src/components/index.tsx +1 -0
  252. package/src/content-slot.tsx +7 -7
  253. package/src/context.ts +17 -6
  254. package/src/{debug.ts → debug/cli.ts} +114 -125
  255. package/src/debug/diagnostics.test.tsx +55 -0
  256. package/src/debug/effects.test.tsx +89 -0
  257. package/src/debug/effects.ts +317 -0
  258. package/src/debug/files.test.tsx +96 -0
  259. package/src/debug/files.ts +40 -0
  260. package/src/debug/index.ts +128 -0
  261. package/src/debug/render.test.tsx +379 -0
  262. package/src/debug/render.ts +639 -0
  263. package/src/debug/serialize.ts +85 -0
  264. package/src/debug/symbols.test.tsx +106 -0
  265. package/src/debug/symbols.ts +239 -0
  266. package/src/debug/trace.ts +312 -0
  267. package/src/devtools/devtools-protocol.ts +312 -0
  268. package/src/devtools/devtools-server.browser.ts +71 -0
  269. package/src/devtools/devtools-server.ts +290 -0
  270. package/src/devtools/devtools-transport.ts +154 -0
  271. package/src/devtools-entry.browser.ts +52 -0
  272. package/src/devtools-entry.ts +54 -0
  273. package/src/diagnostics.ts +141 -0
  274. package/src/index.ts +2 -7
  275. package/src/print-hook.ts +22 -0
  276. package/src/reactive-union-set.ts +85 -44
  277. package/src/reactivity.ts +301 -59
  278. package/src/render-stack.ts +73 -1
  279. package/src/render.ts +470 -161
  280. package/src/resource.ts +28 -19
  281. package/src/scheduler.ts +80 -4
  282. package/src/symbols/basic-symbol.ts +6 -1
  283. package/src/symbols/decl.ts +5 -1
  284. package/src/symbols/output-scope.ts +21 -13
  285. package/src/symbols/output-symbol.ts +34 -14
  286. package/src/symbols/symbol-flow.ts +76 -39
  287. package/src/symbols/symbol-slot.test.tsx +41 -0
  288. package/src/symbols/symbol-slot.tsx +47 -20
  289. package/src/symbols/symbol-table.ts +6 -10
  290. package/src/trace.ts +1 -0
  291. package/src/tracer.ts +13 -242
  292. package/src/utils.tsx +24 -17
  293. package/temp/api.json +5658 -3095
  294. package/test/components/append-file.test.tsx +36 -29
  295. package/test/components/template-file.test.tsx +11 -11
  296. package/test/lazy-isempty.test.tsx +106 -0
  297. package/test/reactive-union-set-disposers.test.tsx +112 -0
  298. package/test/reactivity/shallow-reactive.test.tsx +56 -0
  299. package/test/rendering/basic.test.tsx +4 -0
  300. package/test/rendering/print-render-stack.test.tsx +52 -43
  301. package/test/scheduler-extended.test.tsx +122 -0
  302. package/test/scheduler.test.tsx +50 -0
  303. package/testing/create-test-wrapper.tsx +1 -1
  304. package/testing/devtools-utils.ts +203 -0
  305. package/testing/extend-expect.ts +89 -0
  306. package/testing/render.ts +2 -2
  307. package/testing/vitest.d.ts +9 -0
  308. package/dist/src/debug.d.ts +0 -14
  309. package/dist/src/debug.d.ts.map +0 -1
  310. package/dist/src/debug.js.map +0 -1
@@ -0,0 +1,154 @@
1
+ /**
2
+ * WebSocket transport for the Alloy devtools server.
3
+ *
4
+ * Handles HTTP serving of the devtools UI and WebSocket connection management.
5
+ * This module is concerned only with the transport layer — session lifecycle
6
+ * is managed by devtools-server.ts.
7
+ */
8
+ import { readFileSync } from "node:fs";
9
+ import {
10
+ createServer as createHttpServer,
11
+ type IncomingMessage,
12
+ type ServerResponse,
13
+ } from "node:http";
14
+
15
+ export interface DevtoolsTransportState {
16
+ port: number;
17
+ connected: boolean;
18
+ clients: Set<any>;
19
+ httpServer: ReturnType<typeof createHttpServer>;
20
+ ready: Promise<void>;
21
+ close(): Promise<void>;
22
+ }
23
+
24
+ let cachedAlloyVersion: string | null = null;
25
+
26
+ /** Read the @alloy-js/core package version, cached after first call. */
27
+ export function getAlloyVersion(): string {
28
+ if (cachedAlloyVersion) return cachedAlloyVersion;
29
+ try {
30
+ const pkgUrl = new URL("../../../package.json", import.meta.url);
31
+ const pkg = JSON.parse(readFileSync(pkgUrl, "utf-8")) as {
32
+ version?: string;
33
+ };
34
+ cachedAlloyVersion = pkg.version ?? "0.0.0";
35
+ } catch {
36
+ cachedAlloyVersion = "0.0.0";
37
+ }
38
+ return cachedAlloyVersion;
39
+ }
40
+
41
+ /** Attempt to load the devtools UI HTML from known candidate paths. */
42
+ function loadDevtoolsUiHtml(): string | null {
43
+ const candidates = [
44
+ new URL("../../dist/devtools/index.html", import.meta.url),
45
+ new URL("../../devtools/index.html", import.meta.url),
46
+ new URL("../../../devtools/dist/index.html", import.meta.url),
47
+ ];
48
+ for (const candidate of candidates) {
49
+ try {
50
+ return readFileSync(candidate, "utf-8");
51
+ } catch {
52
+ // try next
53
+ }
54
+ }
55
+ return null;
56
+ }
57
+
58
+ export interface CreateTransportOptions {
59
+ port: number;
60
+ onConnection(socket: any): void;
61
+ onMessage(message: unknown): void;
62
+ onDisconnect(): void;
63
+ }
64
+
65
+ /**
66
+ * Create the HTTP + WebSocket transport. Returns when the server is listening.
67
+ * Only accepts one concurrent WebSocket connection.
68
+ */
69
+ export async function createTransport(
70
+ options: CreateTransportOptions,
71
+ ): Promise<DevtoolsTransportState> {
72
+ const { WebSocketServer } = await import("ws");
73
+ const devtoolsUiHtml = loadDevtoolsUiHtml();
74
+
75
+ const httpServer = createHttpServer(
76
+ (req: IncomingMessage, res: ServerResponse) => {
77
+ const url = req.url ?? "/";
78
+ if (url !== "/" && url !== "/index.html") {
79
+ res.statusCode = 404;
80
+ res.end("Not Found");
81
+ return;
82
+ }
83
+ if (!devtoolsUiHtml) {
84
+ res.statusCode = 404;
85
+ res.end("Not Found");
86
+ return;
87
+ }
88
+ res.statusCode = 200;
89
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
90
+ res.end(devtoolsUiHtml);
91
+ },
92
+ );
93
+ const wss = new WebSocketServer({ server: httpServer });
94
+
95
+ await new Promise<void>((resolve, reject) => {
96
+ httpServer.once("listening", resolve);
97
+ httpServer.once("error", reject);
98
+ httpServer.listen(options.port);
99
+ });
100
+
101
+ const address = httpServer.address();
102
+ const actualPort =
103
+ typeof address === "object" && address !== null ?
104
+ address.port
105
+ : options.port;
106
+
107
+ let resolveReady: (() => void) | undefined;
108
+ const ready = new Promise<void>((resolve) => {
109
+ resolveReady = resolve;
110
+ });
111
+
112
+ const clients = new Set<any>();
113
+ const state: DevtoolsTransportState = {
114
+ port: actualPort,
115
+ connected: false,
116
+ clients,
117
+ httpServer,
118
+ ready,
119
+ close: async () => {
120
+ await new Promise<void>((resolve) => wss.close(() => resolve()));
121
+ await new Promise<void>((resolve) => httpServer.close(() => resolve()));
122
+ clients.clear();
123
+ state.connected = false;
124
+ },
125
+ };
126
+
127
+ wss.on("connection", (socket) => {
128
+ if (state.connected) {
129
+ socket.close(1000, "Another devtools client is already connected");
130
+ return;
131
+ }
132
+
133
+ clients.add(socket);
134
+ state.connected = true;
135
+ resolveReady?.();
136
+ options.onConnection(socket);
137
+
138
+ socket.on("message", (data) => {
139
+ try {
140
+ options.onMessage(JSON.parse(String(data)));
141
+ } catch {
142
+ // ignore malformed messages
143
+ }
144
+ });
145
+
146
+ socket.on("close", () => {
147
+ clients.delete(socket);
148
+ state.connected = clients.size > 0;
149
+ options.onDisconnect();
150
+ });
151
+ });
152
+
153
+ return state;
154
+ }
@@ -0,0 +1,52 @@
1
+ export type {
2
+ BatchMessage,
3
+ ClientToServerMessage,
4
+ DebuggerInfoMessage,
5
+ DiagnosticInfo,
6
+ DiagnosticsReportMessage,
7
+ DirectoryAddedMessage,
8
+ DirectoryRemovedMessage,
9
+ EffectAddedMessage,
10
+ EffectEdgeInfo,
11
+ EffectEdgeUpdatedMessage,
12
+ EffectInfo,
13
+ EffectTrackMessage,
14
+ EffectTriggerMessage,
15
+ EffectUpdatedMessage,
16
+ FileAddedMessage,
17
+ FileRemovedMessage,
18
+ FileUpdatedMessage,
19
+ FlushJobsCompleteMessage,
20
+ RefAddedMessage,
21
+ RefInfo,
22
+ RenderCompleteMessage,
23
+ RenderErrorMessage,
24
+ RenderErrorStackEntry,
25
+ RenderNodeAddedMessage,
26
+ RenderNodeRemovedMessage,
27
+ RenderNodeUpdatedMessage,
28
+ RenderResetMessage,
29
+ RenderTreeNode,
30
+ RerenderBreakRequestMessage,
31
+ RerenderRequestMessage,
32
+ ScopeCreateMessage,
33
+ ScopeDeleteMessage,
34
+ ScopeInfo,
35
+ ScopeUpdateMessage,
36
+ ServerToClientMessage,
37
+ SourceLocation,
38
+ SymbolCreateMessage,
39
+ SymbolDeleteMessage,
40
+ SymbolInfo,
41
+ SymbolUpdateMessage,
42
+ } from "./devtools/devtools-protocol.js";
43
+ export {
44
+ enableDevtools,
45
+ enableDevtoolsAndConnect,
46
+ resetDevtoolsServerForTests,
47
+ waitForDevtoolsConnection,
48
+ } from "./devtools/devtools-server.browser.js";
49
+ export type {
50
+ DevtoolsServerInfo,
51
+ EnableDevtoolsOptions,
52
+ } from "./devtools/devtools-server.browser.js";
@@ -0,0 +1,54 @@
1
+ export type {
2
+ // Individual server→client message types
3
+ BatchMessage,
4
+ ClientToServerMessage,
5
+ DebuggerInfoMessage,
6
+ DiagnosticInfo,
7
+ DiagnosticsReportMessage,
8
+ DirectoryAddedMessage,
9
+ DirectoryRemovedMessage,
10
+ EffectAddedMessage,
11
+ EffectEdgeInfo,
12
+ EffectEdgeUpdatedMessage,
13
+ EffectInfo,
14
+ EffectTrackMessage,
15
+ EffectTriggerMessage,
16
+ EffectUpdatedMessage,
17
+ FileAddedMessage,
18
+ FileRemovedMessage,
19
+ FileUpdatedMessage,
20
+ FlushJobsCompleteMessage,
21
+ RefAddedMessage,
22
+ RefInfo,
23
+ RenderCompleteMessage,
24
+ RenderErrorMessage,
25
+ RenderErrorStackEntry,
26
+ RenderNodeAddedMessage,
27
+ RenderNodeRemovedMessage,
28
+ RenderNodeUpdatedMessage,
29
+ RenderResetMessage,
30
+ RenderTreeNode,
31
+ // Individual client→server message types
32
+ RerenderBreakRequestMessage,
33
+ RerenderRequestMessage,
34
+ ScopeCreateMessage,
35
+ ScopeDeleteMessage,
36
+ ScopeInfo,
37
+ ScopeUpdateMessage,
38
+ ServerToClientMessage,
39
+ SourceLocation,
40
+ SymbolCreateMessage,
41
+ SymbolDeleteMessage,
42
+ SymbolInfo,
43
+ SymbolUpdateMessage,
44
+ } from "./devtools/devtools-protocol.js";
45
+ export {
46
+ enableDevtools,
47
+ enableDevtoolsAndConnect,
48
+ resetDevtoolsServerForTests,
49
+ waitForDevtoolsConnection,
50
+ } from "./devtools/devtools-server.js";
51
+ export type {
52
+ DevtoolsServerInfo,
53
+ EnableDevtoolsOptions,
54
+ } from "./devtools/devtools-server.js";
@@ -0,0 +1,141 @@
1
+ import { getRenderNodeId } from "./debug/index.js";
2
+ import { broadcastDevtoolsMessage } from "./devtools/devtools-server.js";
3
+ import { getContext } from "./reactivity.js";
4
+ import { getRenderStackSnapshot } from "./render-stack.js";
5
+ import type { SourceLocation } from "./runtime/component.js";
6
+
7
+ export interface DiagnosticStackEntry {
8
+ name: string;
9
+ renderNodeId?: number;
10
+ source?: SourceLocation;
11
+ }
12
+
13
+ export type DiagnosticSeverity = "info" | "warning" | "error";
14
+
15
+ export interface Diagnostic {
16
+ id: string;
17
+ message: string;
18
+ severity: DiagnosticSeverity;
19
+ source?: SourceLocation;
20
+ componentStack?: DiagnosticStackEntry[];
21
+ }
22
+
23
+ export interface DiagnosticInput {
24
+ message: string;
25
+ severity?: DiagnosticSeverity;
26
+ source?: SourceLocation;
27
+ componentStack?: DiagnosticStackEntry[];
28
+ }
29
+
30
+ export interface DiagnosticHandle {
31
+ dismiss(): void;
32
+ }
33
+
34
+ export class DiagnosticsCollector {
35
+ private entries = new Map<string, Diagnostic>();
36
+ private order: string[] = [];
37
+
38
+ emit(input: DiagnosticInput): DiagnosticHandle {
39
+ const componentStack =
40
+ input.componentStack ??
41
+ getRenderStackSnapshot().map((entry) => ({
42
+ name: entry.displayName,
43
+ renderNodeId:
44
+ entry.context?.meta?.renderNode ?
45
+ getRenderNodeId(entry.context.meta.renderNode)
46
+ : undefined,
47
+ source: entry.source,
48
+ }));
49
+ const source = input.source ?? componentStack.at(-1)?.source ?? undefined;
50
+ const id = `diag-${this.order.length + 1}-${Date.now()}`;
51
+ const diagnostic: Diagnostic = {
52
+ id,
53
+ message: input.message,
54
+ severity: input.severity ?? "warning",
55
+ source,
56
+ componentStack,
57
+ };
58
+ this.entries.set(id, diagnostic);
59
+ this.order.push(id);
60
+
61
+ // Broadcast updated diagnostics
62
+ this.broadcast();
63
+
64
+ return {
65
+ dismiss: () => {
66
+ this.entries.delete(id);
67
+ // Broadcast updated diagnostics after dismissal
68
+ this.broadcast();
69
+ },
70
+ };
71
+ }
72
+
73
+ getDiagnostics() {
74
+ return this.order
75
+ .map((id) => this.entries.get(id))
76
+ .filter((entry): entry is Diagnostic => Boolean(entry));
77
+ }
78
+
79
+ private broadcast() {
80
+ broadcastDevtoolsMessage({
81
+ type: "diagnostics:report",
82
+ diagnostics: this.getDiagnostics(),
83
+ });
84
+ }
85
+ }
86
+
87
+ const DIAGNOSTICS_META_KEY = "diagnostics";
88
+
89
+ export function attachDiagnosticsCollector(collector: DiagnosticsCollector) {
90
+ const context = getContext();
91
+ if (!context) return;
92
+ context.meta ??= {};
93
+ context.meta[DIAGNOSTICS_META_KEY] = collector;
94
+ }
95
+
96
+ function getDiagnosticsCollectorFromContext():
97
+ | DiagnosticsCollector
98
+ | undefined {
99
+ let context = getContext();
100
+ while (context) {
101
+ const collector = context.meta?.[DIAGNOSTICS_META_KEY] as
102
+ | DiagnosticsCollector
103
+ | undefined;
104
+ if (collector) return collector;
105
+ context = context.owner;
106
+ }
107
+ return undefined;
108
+ }
109
+
110
+ export function emitDiagnostic(input: DiagnosticInput): DiagnosticHandle {
111
+ const collector = getDiagnosticsCollectorFromContext();
112
+ if (!collector) {
113
+ return {
114
+ dismiss: () => undefined,
115
+ };
116
+ }
117
+ return collector.emit(input);
118
+ }
119
+
120
+ export function reportDiagnostics(collector: DiagnosticsCollector) {
121
+ const diagnostics = collector.getDiagnostics();
122
+ if (diagnostics.length === 0) return;
123
+
124
+ for (const diagnostic of diagnostics) {
125
+ const location =
126
+ diagnostic.source ?
127
+ ` (${diagnostic.source.fileName}:${diagnostic.source.lineNumber}:${diagnostic.source.columnNumber})`
128
+ : "";
129
+ const line = `${diagnostic.message}${location}`;
130
+ if (diagnostic.severity === "error") {
131
+ // eslint-disable-next-line no-console
132
+ console.error(line);
133
+ } else if (diagnostic.severity === "warning") {
134
+ // eslint-disable-next-line no-console
135
+ console.warn(line);
136
+ } else {
137
+ // eslint-disable-next-line no-console
138
+ console.log(line);
139
+ }
140
+ }
141
+ }
package/src/index.ts CHANGED
@@ -1,17 +1,11 @@
1
1
  export {
2
2
  TrackOpTypes,
3
3
  TriggerOpTypes,
4
- computed,
5
4
  isProxy,
6
5
  isReactive,
7
6
  isRef,
8
7
  reactive,
9
- ref,
10
- shallowReactive,
11
- shallowRef,
12
8
  toRaw,
13
- toRef,
14
- toRefs,
15
9
  track,
16
10
  trigger,
17
11
  watch,
@@ -28,6 +22,7 @@ export * from "./components/index.js";
28
22
  export * from "./content-slot.js";
29
23
  export * from "./context.js";
30
24
  export * from "./context/index.js";
25
+ export * from "./diagnostics.js";
31
26
  export * from "./library-symbol-reference.js";
32
27
  export * from "./name-policy.js";
33
28
  export * from "./props-combinators.js";
@@ -45,4 +40,4 @@ export * from "./symbols/symbol-flow.js";
45
40
  export * from "./tap.js";
46
41
  export * from "./utils.js";
47
42
  export * from "./write-output.js";
48
- import "./debug.js";
43
+ import "./debug/index.js";
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Print hook types and utilities.
3
+ * This is a separate file to avoid circular dependencies between render.ts and debug/render.ts.
4
+ */
5
+
6
+ export const printHookTag = Symbol();
7
+
8
+ export interface PrintHook {
9
+ [printHookTag]: true;
10
+ transform?(tree: RenderedTextTree): RenderedTextTree;
11
+ print?(
12
+ tree: RenderedTextTree,
13
+ print: (subtree: RenderedTextTree) => import("prettier").Doc,
14
+ ): import("prettier").Doc;
15
+ subtree: RenderedTextTree;
16
+ }
17
+
18
+ export function isPrintHook(type: unknown): type is PrintHook {
19
+ return typeof type === "object" && type !== null && printHookTag in type;
20
+ }
21
+
22
+ export type RenderedTextTree = (string | RenderedTextTree | PrintHook)[];
@@ -1,14 +1,13 @@
1
1
  import {
2
2
  ITERATE_KEY,
3
3
  ReactiveFlags,
4
- shallowReactive,
5
4
  shallowReadonly,
6
5
  track,
7
6
  TrackOpTypes,
8
7
  trigger,
9
8
  TriggerOpTypes,
10
9
  } from "@vue/reactivity";
11
- import { effect, untrack } from "./reactivity.js";
10
+ import { effect, root, shallowReactive, untrack } from "./reactivity.js";
12
11
 
13
12
  export interface ReactiveUnionSetOptions<T> {
14
13
  onAdd?: OnReactiveSetAddCallback<T>;
@@ -129,28 +128,49 @@ export class ReactiveUnionSet<T> extends Set<T> {
129
128
  * that were added to the parent set.
130
129
  */
131
130
  const prevValues = new Map<T, T>();
132
-
133
- effect(() => {
134
- for (const [prevSourceValue, prevTargetValue] of prevValues) {
135
- if (!subset.has(prevSourceValue)) {
136
- untrack(() => onDelete?.(prevSourceValue));
137
- prevValues.delete(prevSourceValue);
138
- this.delete(prevTargetValue);
131
+ const itemDisposers = new Map<T, () => void>();
132
+
133
+ effect(
134
+ () => {
135
+ for (const [prevSourceValue, prevTargetValue] of prevValues) {
136
+ if (!subset.has(prevSourceValue)) {
137
+ untrack(() => onDelete?.(prevSourceValue));
138
+ prevValues.delete(prevSourceValue);
139
+ this.delete(prevTargetValue);
140
+ const disposer = itemDisposers.get(prevSourceValue);
141
+ if (disposer) {
142
+ disposer();
143
+ itemDisposers.delete(prevSourceValue);
144
+ }
145
+ }
139
146
  }
140
- }
141
147
 
142
- for (const value of subset) {
143
- if (!prevValues.has(value)) {
144
- if (onAdd) {
145
- const added = untrack(() => onAdd(value));
146
- prevValues.set(value, added);
147
- } else {
148
- this.add(value);
149
- prevValues.set(value, value);
148
+ for (const value of subset) {
149
+ if (!prevValues.has(value)) {
150
+ if (onAdd) {
151
+ const added = untrack(() =>
152
+ root((disposer) => {
153
+ const result = onAdd(value);
154
+ itemDisposers.set(value, disposer);
155
+ return result;
156
+ }),
157
+ );
158
+ prevValues.set(value, added);
159
+ } else {
160
+ this.add(value);
161
+ prevValues.set(value, value);
162
+ }
150
163
  }
151
164
  }
152
- }
153
- });
165
+ },
166
+ undefined,
167
+ {
168
+ debug: {
169
+ name: "reactiveUnionSet:subsetSync",
170
+ type: "collection",
171
+ },
172
+ },
173
+ );
154
174
  }
155
175
 
156
176
  createDerivedSet<U>(mapper: (value: T) => U | U[]) {
@@ -184,18 +204,27 @@ export class ReactiveUnionSet<T> extends Set<T> {
184
204
 
185
205
  this._indexes.push({
186
206
  add: (value: T) => {
187
- effect((prev: U[] | U | undefined) => {
188
- for (const id of [prev].flat()) {
189
- unref(id as any);
190
- }
207
+ effect(
208
+ (prev: U[] | U | undefined) => {
209
+ for (const id of [prev].flat()) {
210
+ unref(id as any);
211
+ }
191
212
 
192
- const mappedValue = mapper(value);
193
- for (const id of [mappedValue].flat()) {
194
- ref(id as any);
195
- }
213
+ const mappedValue = mapper(value);
214
+ for (const id of [mappedValue].flat()) {
215
+ ref(id as any);
216
+ }
196
217
 
197
- return mappedValue;
198
- });
218
+ return mappedValue;
219
+ },
220
+ undefined,
221
+ {
222
+ debug: {
223
+ name: `reactiveUnionSet:derived:${mapper.name || "mapper"}`,
224
+ type: "collection",
225
+ },
226
+ },
227
+ );
199
228
  },
200
229
  delete: (value: T) => {
201
230
  const mappedValue = mapper(value);
@@ -212,24 +241,36 @@ export class ReactiveUnionSet<T> extends Set<T> {
212
241
  const index = shallowReactive(new Map<U, T>());
213
242
  this._indexes.push({
214
243
  add: (value: T) => {
215
- effect((oldValue) => {
216
- if (oldValue) {
217
- for (const id of [oldValue].flat()) {
218
- index.delete(id as U);
244
+ type MappedValue = U extends readonly (infer InnerArr)[] ? InnerArr : U;
245
+ effect<MappedValue[]>(
246
+ (oldValue) => {
247
+ if (oldValue) {
248
+ for (const id of [oldValue].flat()) {
249
+ index.delete(id as U);
250
+ }
219
251
  }
220
- }
221
252
 
222
- // we filter to avoid overwriting existing values (first wins)
223
- const mappedValues = [mapper(value)]
224
- .flat()
225
- .filter((v) => untrack(() => !index.has(v as U)));
253
+ // we filter to avoid overwriting existing values (first wins)
254
+ const mappedValues = [mapper(value)]
255
+ .flat()
256
+ .filter((v) =>
257
+ untrack(() => !index.has(v as U)),
258
+ ) as MappedValue[];
226
259
 
227
- for (const id of mappedValues) {
228
- index.set(id as U, value as any);
229
- }
260
+ for (const id of mappedValues) {
261
+ index.set(id as U, value as any);
262
+ }
230
263
 
231
- return mappedValues;
232
- });
264
+ return mappedValues;
265
+ },
266
+ undefined as MappedValue[] | undefined,
267
+ {
268
+ debug: {
269
+ name: `reactiveUnionSet:index:${mapper.name || "mapper"}`,
270
+ type: "collection",
271
+ },
272
+ },
273
+ );
233
274
  },
234
275
  delete: (value: T) => {
235
276
  const mappedValue = mapper(value);