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

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 (269) 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/AppendFile.d.ts.map +1 -1
  8. package/dist/src/components/AppendFile.js +14 -3
  9. package/dist/src/components/AppendFile.js.map +1 -1
  10. package/dist/src/components/Block.js +1 -1
  11. package/dist/src/components/Block.js.map +1 -1
  12. package/dist/src/components/Declaration.d.ts.map +1 -1
  13. package/dist/src/components/Declaration.js +2 -1
  14. package/dist/src/components/Declaration.js.map +1 -1
  15. package/dist/src/components/Scope.d.ts.map +1 -1
  16. package/dist/src/components/Scope.js +4 -1
  17. package/dist/src/components/Scope.js.map +1 -1
  18. package/dist/src/components/TemplateFile.d.ts.map +1 -1
  19. package/dist/src/components/TemplateFile.js +18 -3
  20. package/dist/src/components/TemplateFile.js.map +1 -1
  21. package/dist/src/content-slot.d.ts.map +1 -1
  22. package/dist/src/content-slot.js +6 -5
  23. package/dist/src/content-slot.js.map +1 -1
  24. package/dist/src/context.d.ts.map +1 -1
  25. package/dist/src/context.js +8 -1
  26. package/dist/src/context.js.map +1 -1
  27. package/dist/src/debug/cli.d.ts +6 -0
  28. package/dist/src/debug/cli.d.ts.map +1 -0
  29. package/dist/src/{debug.js → debug/cli.js} +78 -84
  30. package/dist/src/debug/cli.js.map +1 -0
  31. package/dist/src/debug/diagnostics.test.d.ts +2 -0
  32. package/dist/src/debug/diagnostics.test.d.ts.map +1 -0
  33. package/dist/src/debug/diagnostics.test.js +45 -0
  34. package/dist/src/debug/diagnostics.test.js.map +1 -0
  35. package/dist/src/debug/effects.d.ts +69 -0
  36. package/dist/src/debug/effects.d.ts.map +1 -0
  37. package/dist/src/debug/effects.js +228 -0
  38. package/dist/src/debug/effects.js.map +1 -0
  39. package/dist/src/debug/effects.test.d.ts +2 -0
  40. package/dist/src/debug/effects.test.d.ts.map +1 -0
  41. package/dist/src/debug/effects.test.js +86 -0
  42. package/dist/src/debug/effects.test.js.map +1 -0
  43. package/dist/src/debug/files.d.ts +14 -0
  44. package/dist/src/debug/files.d.ts.map +1 -0
  45. package/dist/src/debug/files.js +40 -0
  46. package/dist/src/debug/files.js.map +1 -0
  47. package/dist/src/debug/files.test.d.ts +2 -0
  48. package/dist/src/debug/files.test.d.ts.map +1 -0
  49. package/dist/src/debug/files.test.js +89 -0
  50. package/dist/src/debug/files.test.js.map +1 -0
  51. package/dist/src/debug/index.d.ts +60 -0
  52. package/dist/src/debug/index.d.ts.map +1 -0
  53. package/dist/src/debug/index.js +68 -0
  54. package/dist/src/debug/index.js.map +1 -0
  55. package/dist/src/debug/render.d.ts +57 -0
  56. package/dist/src/debug/render.d.ts.map +1 -0
  57. package/dist/src/debug/render.js +519 -0
  58. package/dist/src/debug/render.js.map +1 -0
  59. package/dist/src/debug/render.test.d.ts +2 -0
  60. package/dist/src/debug/render.test.d.ts.map +1 -0
  61. package/dist/src/debug/render.test.js +328 -0
  62. package/dist/src/debug/render.test.js.map +1 -0
  63. package/dist/src/debug/serialize.d.ts +9 -0
  64. package/dist/src/debug/serialize.d.ts.map +1 -0
  65. package/dist/src/debug/serialize.js +70 -0
  66. package/dist/src/debug/serialize.js.map +1 -0
  67. package/dist/src/debug/symbols.d.ts +9 -0
  68. package/dist/src/debug/symbols.d.ts.map +1 -0
  69. package/dist/src/debug/symbols.js +164 -0
  70. package/dist/src/debug/symbols.js.map +1 -0
  71. package/dist/src/debug/symbols.test.d.ts +2 -0
  72. package/dist/src/debug/symbols.test.d.ts.map +1 -0
  73. package/dist/src/debug/symbols.test.js +104 -0
  74. package/dist/src/debug/symbols.test.js.map +1 -0
  75. package/dist/src/debug/trace.d.ts +342 -0
  76. package/dist/src/debug/trace.d.ts.map +1 -0
  77. package/dist/src/debug/trace.js +443 -0
  78. package/dist/src/debug/trace.js.map +1 -0
  79. package/dist/src/devtools/devtools-protocol.d.ts +232 -0
  80. package/dist/src/devtools/devtools-protocol.d.ts.map +1 -0
  81. package/dist/src/devtools/devtools-protocol.js +2 -0
  82. package/dist/src/devtools/devtools-protocol.js.map +1 -0
  83. package/dist/src/devtools/devtools-server.browser.d.ts +28 -0
  84. package/dist/src/devtools/devtools-server.browser.d.ts.map +1 -0
  85. package/dist/src/devtools/devtools-server.browser.js +36 -0
  86. package/dist/src/devtools/devtools-server.browser.js.map +1 -0
  87. package/dist/src/devtools/devtools-server.d.ts +72 -0
  88. package/dist/src/devtools/devtools-server.d.ts.map +1 -0
  89. package/dist/src/devtools/devtools-server.js +256 -0
  90. package/dist/src/devtools/devtools-server.js.map +1 -0
  91. package/dist/src/devtools/devtools-transport.d.ts +23 -0
  92. package/dist/src/devtools/devtools-transport.d.ts.map +1 -0
  93. package/dist/src/devtools/devtools-transport.js +114 -0
  94. package/dist/src/devtools/devtools-transport.js.map +1 -0
  95. package/dist/src/devtools-entry.browser.d.ts +4 -0
  96. package/dist/src/devtools-entry.browser.d.ts.map +1 -0
  97. package/dist/src/devtools-entry.browser.js +2 -0
  98. package/dist/src/devtools-entry.browser.js.map +1 -0
  99. package/dist/src/devtools-entry.d.ts +4 -0
  100. package/dist/src/devtools-entry.d.ts.map +1 -0
  101. package/dist/src/devtools-entry.js +2 -0
  102. package/dist/src/devtools-entry.js.map +1 -0
  103. package/dist/src/diagnostics.d.ts +34 -0
  104. package/dist/src/diagnostics.d.ts.map +1 -0
  105. package/dist/src/diagnostics.js +89 -0
  106. package/dist/src/diagnostics.js.map +1 -0
  107. package/dist/src/index.d.ts +3 -2
  108. package/dist/src/index.d.ts.map +1 -1
  109. package/dist/src/index.js +3 -2
  110. package/dist/src/index.js.map +1 -1
  111. package/dist/src/print-hook.d.ts +14 -0
  112. package/dist/src/print-hook.d.ts.map +1 -0
  113. package/dist/src/print-hook.js +10 -0
  114. package/dist/src/print-hook.js.map +1 -0
  115. package/dist/src/reactive-union-set.d.ts.map +1 -1
  116. package/dist/src/reactive-union-set.js +15 -0
  117. package/dist/src/reactive-union-set.js.map +1 -1
  118. package/dist/src/reactivity.d.ts +17 -3
  119. package/dist/src/reactivity.d.ts.map +1 -1
  120. package/dist/src/reactivity.js +162 -14
  121. package/dist/src/reactivity.js.map +1 -1
  122. package/dist/src/render-stack.d.ts +29 -0
  123. package/dist/src/render-stack.d.ts.map +1 -0
  124. package/dist/src/render-stack.js +247 -0
  125. package/dist/src/render-stack.js.map +1 -0
  126. package/dist/src/render.d.ts +9 -19
  127. package/dist/src/render.d.ts.map +1 -1
  128. package/dist/src/render.js +363 -153
  129. package/dist/src/render.js.map +1 -1
  130. package/dist/src/resource.d.ts.map +1 -1
  131. package/dist/src/resource.js +5 -0
  132. package/dist/src/resource.js.map +1 -1
  133. package/dist/src/runtime/component.d.ts +7 -1
  134. package/dist/src/runtime/component.d.ts.map +1 -1
  135. package/dist/src/runtime/component.js +4 -1
  136. package/dist/src/runtime/component.js.map +1 -1
  137. package/dist/src/scheduler.d.ts +3 -0
  138. package/dist/src/scheduler.d.ts.map +1 -1
  139. package/dist/src/scheduler.js +45 -2
  140. package/dist/src/scheduler.js.map +1 -1
  141. package/dist/src/symbols/basic-symbol.d.ts.map +1 -1
  142. package/dist/src/symbols/basic-symbol.js +6 -1
  143. package/dist/src/symbols/basic-symbol.js.map +1 -1
  144. package/dist/src/symbols/decl.d.ts.map +1 -1
  145. package/dist/src/symbols/decl.js +5 -1
  146. package/dist/src/symbols/decl.js.map +1 -1
  147. package/dist/src/symbols/output-scope.d.ts +2 -1
  148. package/dist/src/symbols/output-scope.d.ts.map +1 -1
  149. package/dist/src/symbols/output-scope.js +13 -8
  150. package/dist/src/symbols/output-scope.js.map +1 -1
  151. package/dist/src/symbols/output-symbol.d.ts +1 -0
  152. package/dist/src/symbols/output-symbol.d.ts.map +1 -1
  153. package/dist/src/symbols/output-symbol.js +23 -6
  154. package/dist/src/symbols/output-symbol.js.map +1 -1
  155. package/dist/src/symbols/symbol-flow.d.ts.map +1 -1
  156. package/dist/src/symbols/symbol-flow.js +22 -6
  157. package/dist/src/symbols/symbol-flow.js.map +1 -1
  158. package/dist/src/symbols/symbol-slot.d.ts.map +1 -1
  159. package/dist/src/symbols/symbol-slot.js +15 -0
  160. package/dist/src/symbols/symbol-slot.js.map +1 -1
  161. package/dist/src/symbols/symbol-slot.test.d.ts +2 -0
  162. package/dist/src/symbols/symbol-slot.test.d.ts.map +1 -0
  163. package/dist/src/symbols/symbol-slot.test.js +35 -0
  164. package/dist/src/symbols/symbol-slot.test.js.map +1 -0
  165. package/dist/src/symbols/symbol-table.d.ts.map +1 -1
  166. package/dist/src/symbols/symbol-table.js +6 -5
  167. package/dist/src/symbols/symbol-table.js.map +1 -1
  168. package/dist/src/trace.d.ts +2 -0
  169. package/dist/src/trace.d.ts.map +1 -0
  170. package/dist/src/trace.js +2 -0
  171. package/dist/src/trace.js.map +1 -0
  172. package/dist/src/tracer.d.ts +2 -228
  173. package/dist/src/tracer.d.ts.map +1 -1
  174. package/dist/src/tracer.js +5 -298
  175. package/dist/src/tracer.js.map +1 -1
  176. package/dist/src/utils.d.ts.map +1 -1
  177. package/dist/src/utils.js +5 -0
  178. package/dist/src/utils.js.map +1 -1
  179. package/dist/test/components/append-file.test.d.ts.map +1 -1
  180. package/dist/test/components/append-file.test.js +18 -10
  181. package/dist/test/components/append-file.test.js.map +1 -1
  182. package/dist/test/components/template-file.test.d.ts.map +1 -1
  183. package/dist/test/components/template-file.test.js +6 -4
  184. package/dist/test/components/template-file.test.js.map +1 -1
  185. package/dist/test/rendering/basic.test.js +3 -0
  186. package/dist/test/rendering/basic.test.js.map +1 -1
  187. package/dist/test/rendering/print-render-stack.test.d.ts +2 -0
  188. package/dist/test/rendering/print-render-stack.test.d.ts.map +1 -0
  189. package/dist/test/rendering/print-render-stack.test.js +207 -0
  190. package/dist/test/rendering/print-render-stack.test.js.map +1 -0
  191. package/dist/testing/create-test-wrapper.d.ts +1 -1
  192. package/dist/testing/create-test-wrapper.d.ts.map +1 -1
  193. package/dist/testing/create-test-wrapper.js +1 -1
  194. package/dist/testing/create-test-wrapper.js.map +1 -1
  195. package/dist/testing/devtools-utils.d.ts +26 -0
  196. package/dist/testing/devtools-utils.d.ts.map +1 -0
  197. package/dist/testing/devtools-utils.js +140 -0
  198. package/dist/testing/devtools-utils.js.map +1 -0
  199. package/dist/testing/extend-expect.d.ts.map +1 -1
  200. package/dist/testing/extend-expect.js +63 -1
  201. package/dist/testing/extend-expect.js.map +1 -1
  202. package/dist/testing/render.d.ts +2 -2
  203. package/dist/testing/render.d.ts.map +1 -1
  204. package/dist/testing/render.js +2 -2
  205. package/dist/testing/render.js.map +1 -1
  206. package/dist/tsconfig.tsbuildinfo +1 -1
  207. package/package.json +21 -7
  208. package/scripts/copy-devtools-ui.mjs +26 -0
  209. package/src/binder.ts +71 -16
  210. package/src/components/AppendFile.tsx +14 -9
  211. package/src/components/Block.tsx +1 -1
  212. package/src/components/Declaration.tsx +2 -1
  213. package/src/components/Scope.tsx +4 -1
  214. package/src/components/TemplateFile.tsx +18 -9
  215. package/src/content-slot.tsx +6 -6
  216. package/src/context.ts +15 -4
  217. package/src/{debug.ts → debug/cli.ts} +112 -127
  218. package/src/debug/diagnostics.test.tsx +55 -0
  219. package/src/debug/effects.test.tsx +96 -0
  220. package/src/debug/effects.ts +313 -0
  221. package/src/debug/files.test.tsx +96 -0
  222. package/src/debug/files.ts +40 -0
  223. package/src/debug/index.ts +126 -0
  224. package/src/debug/render.test.tsx +379 -0
  225. package/src/debug/render.ts +639 -0
  226. package/src/debug/serialize.ts +85 -0
  227. package/src/debug/symbols.test.tsx +106 -0
  228. package/src/debug/symbols.ts +230 -0
  229. package/src/debug/trace.ts +312 -0
  230. package/src/devtools/devtools-protocol.ts +312 -0
  231. package/src/devtools/devtools-server.browser.ts +71 -0
  232. package/src/devtools/devtools-server.ts +290 -0
  233. package/src/devtools/devtools-transport.ts +154 -0
  234. package/src/devtools-entry.browser.ts +52 -0
  235. package/src/devtools-entry.ts +54 -0
  236. package/src/diagnostics.ts +141 -0
  237. package/src/index.ts +2 -6
  238. package/src/print-hook.ts +22 -0
  239. package/src/reactive-union-set.ts +71 -41
  240. package/src/reactivity.ts +206 -23
  241. package/src/render-stack.ts +289 -0
  242. package/src/render.ts +464 -212
  243. package/src/resource.ts +28 -19
  244. package/src/runtime/component.ts +11 -0
  245. package/src/scheduler.ts +55 -3
  246. package/src/symbols/basic-symbol.ts +6 -1
  247. package/src/symbols/decl.ts +5 -1
  248. package/src/symbols/output-scope.ts +21 -12
  249. package/src/symbols/output-symbol.ts +33 -12
  250. package/src/symbols/symbol-flow.ts +68 -37
  251. package/src/symbols/symbol-slot.test.tsx +41 -0
  252. package/src/symbols/symbol-slot.tsx +47 -20
  253. package/src/symbols/symbol-table.ts +6 -10
  254. package/src/trace.ts +1 -0
  255. package/src/tracer.ts +13 -242
  256. package/src/utils.tsx +22 -13
  257. package/temp/api.json +1811 -277
  258. package/test/components/append-file.test.tsx +36 -29
  259. package/test/components/template-file.test.tsx +11 -11
  260. package/test/rendering/basic.test.tsx +4 -0
  261. package/test/rendering/print-render-stack.test.tsx +244 -0
  262. package/testing/create-test-wrapper.tsx +1 -1
  263. package/testing/devtools-utils.ts +203 -0
  264. package/testing/extend-expect.ts +89 -0
  265. package/testing/render.ts +2 -2
  266. package/testing/vitest.d.ts +9 -0
  267. package/dist/src/debug.d.ts +0 -15
  268. package/dist/src/debug.d.ts.map +0 -1
  269. 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,12 @@
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
8
  shallowReactive,
11
- shallowRef,
12
9
  toRaw,
13
- toRef,
14
- toRefs,
15
10
  track,
16
11
  trigger,
17
12
  watch,
@@ -28,6 +23,7 @@ export * from "./components/index.js";
28
23
  export * from "./content-slot.js";
29
24
  export * from "./context.js";
30
25
  export * from "./context/index.js";
26
+ export * from "./diagnostics.js";
31
27
  export * from "./library-symbol-reference.js";
32
28
  export * from "./name-policy.js";
33
29
  export * from "./props-combinators.js";
@@ -45,4 +41,4 @@ export * from "./symbols/symbol-flow.js";
45
41
  export * from "./tap.js";
46
42
  export * from "./utils.js";
47
43
  export * from "./write-output.js";
48
- import "./debug.js";
44
+ 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)[];
@@ -130,27 +130,36 @@ export class ReactiveUnionSet<T> extends Set<T> {
130
130
  */
131
131
  const prevValues = new Map<T, T>();
132
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);
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
+ }
139
141
  }
140
- }
141
142
 
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);
143
+ for (const value of subset) {
144
+ if (!prevValues.has(value)) {
145
+ if (onAdd) {
146
+ const added = untrack(() => onAdd(value));
147
+ prevValues.set(value, added);
148
+ } else {
149
+ this.add(value);
150
+ prevValues.set(value, value);
151
+ }
150
152
  }
151
153
  }
152
- }
153
- });
154
+ },
155
+ undefined,
156
+ {
157
+ debug: {
158
+ name: "reactiveUnionSet:subsetSync",
159
+ type: "collection",
160
+ },
161
+ },
162
+ );
154
163
  }
155
164
 
156
165
  createDerivedSet<U>(mapper: (value: T) => U | U[]) {
@@ -184,18 +193,27 @@ export class ReactiveUnionSet<T> extends Set<T> {
184
193
 
185
194
  this._indexes.push({
186
195
  add: (value: T) => {
187
- effect((prev: U[] | U | undefined) => {
188
- for (const id of [prev].flat()) {
189
- unref(id as any);
190
- }
196
+ effect(
197
+ (prev: U[] | U | undefined) => {
198
+ for (const id of [prev].flat()) {
199
+ unref(id as any);
200
+ }
191
201
 
192
- const mappedValue = mapper(value);
193
- for (const id of [mappedValue].flat()) {
194
- ref(id as any);
195
- }
202
+ const mappedValue = mapper(value);
203
+ for (const id of [mappedValue].flat()) {
204
+ ref(id as any);
205
+ }
196
206
 
197
- return mappedValue;
198
- });
207
+ return mappedValue;
208
+ },
209
+ undefined,
210
+ {
211
+ debug: {
212
+ name: `reactiveUnionSet:derived:${mapper.name || "mapper"}`,
213
+ type: "collection",
214
+ },
215
+ },
216
+ );
199
217
  },
200
218
  delete: (value: T) => {
201
219
  const mappedValue = mapper(value);
@@ -212,24 +230,36 @@ export class ReactiveUnionSet<T> extends Set<T> {
212
230
  const index = shallowReactive(new Map<U, T>());
213
231
  this._indexes.push({
214
232
  add: (value: T) => {
215
- effect((oldValue) => {
216
- if (oldValue) {
217
- for (const id of [oldValue].flat()) {
218
- index.delete(id as U);
233
+ type MappedValue = U extends readonly (infer InnerArr)[] ? InnerArr : U;
234
+ effect<MappedValue[]>(
235
+ (oldValue) => {
236
+ if (oldValue) {
237
+ for (const id of [oldValue].flat()) {
238
+ index.delete(id as U);
239
+ }
219
240
  }
220
- }
221
241
 
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)));
242
+ // we filter to avoid overwriting existing values (first wins)
243
+ const mappedValues = [mapper(value)]
244
+ .flat()
245
+ .filter((v) =>
246
+ untrack(() => !index.has(v as U)),
247
+ ) as MappedValue[];
226
248
 
227
- for (const id of mappedValues) {
228
- index.set(id as U, value as any);
229
- }
249
+ for (const id of mappedValues) {
250
+ index.set(id as U, value as any);
251
+ }
230
252
 
231
- return mappedValues;
232
- });
253
+ return mappedValues;
254
+ },
255
+ undefined as MappedValue[] | undefined,
256
+ {
257
+ debug: {
258
+ name: `reactiveUnionSet:index:${mapper.name || "mapper"}`,
259
+ type: "collection",
260
+ },
261
+ },
262
+ );
233
263
  },
234
264
  delete: (value: T) => {
235
265
  const mappedValue = mapper(value);