octane 0.2.12 → 0.2.13

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 (243) hide show
  1. package/README.md +5 -0
  2. package/dist/behavior-root.d.ts +13 -3
  3. package/dist/behavior-root.js +28 -5
  4. package/dist/cjs/behavior-root.cjs +32 -6
  5. package/dist/cjs/class-names.cjs +49 -0
  6. package/dist/cjs/constants.cjs +9 -0
  7. package/dist/cjs/css.cjs +6 -27
  8. package/dist/cjs/data-encoding.cjs +160 -0
  9. package/dist/cjs/dom-binding-handoff.cjs +93 -0
  10. package/dist/cjs/dom-binding-protocol.cjs +115 -0
  11. package/dist/cjs/dom-binding-styles.cjs +292 -0
  12. package/dist/cjs/dom-bindings.cjs +700 -0
  13. package/dist/cjs/dom-focused-move.cjs +66 -0
  14. package/dist/cjs/dom-tables.cjs +4 -94
  15. package/dist/cjs/error-codes.client.generated.cjs +33 -0
  16. package/dist/cjs/error-codes.server.generated.cjs +19 -0
  17. package/dist/cjs/hydration/control-capture.cjs +244 -0
  18. package/dist/cjs/hydration/event-capture.cjs +125 -43
  19. package/dist/cjs/hydration/interaction-config.cjs +50 -2
  20. package/dist/cjs/hydration-markers.cjs +37 -0
  21. package/dist/cjs/independent-hydration-protocol.cjs +106 -0
  22. package/dist/cjs/index.cjs +6 -0
  23. package/dist/cjs/runtime.cjs +2791 -215
  24. package/dist/cjs/runtime.server.cjs +716 -76
  25. package/dist/cjs/server/early-signals.cjs +45 -0
  26. package/dist/cjs/server/index.cjs +44 -0
  27. package/dist/cjs/server/rpc-batch.cjs +149 -0
  28. package/dist/cjs/server/rpc-stream.cjs +146 -0
  29. package/dist/cjs/server/rpc.cjs +34 -15
  30. package/dist/cjs/server/signal-query-observation.cjs +158 -0
  31. package/dist/cjs/server/streamed-region.cjs +52 -0
  32. package/dist/cjs/server/streamed-signals.cjs +512 -0
  33. package/dist/cjs/server-call.cjs +103 -0
  34. package/dist/cjs/server-rpc-batch-client.cjs +141 -0
  35. package/dist/cjs/server-rpc-client.cjs +94 -27
  36. package/dist/cjs/server-rpc-protocol.cjs +141 -0
  37. package/dist/cjs/server-rpc-stream-client.cjs +118 -0
  38. package/dist/cjs/signals/actions.cjs +474 -0
  39. package/dist/cjs/signals/client.cjs +4 -1
  40. package/dist/cjs/signals/computations.cjs +460 -0
  41. package/dist/cjs/signals/control-binding.cjs +368 -0
  42. package/dist/cjs/signals/derived-facade.cjs +50 -0
  43. package/dist/cjs/signals/document-owner.cjs +71 -0
  44. package/dist/cjs/signals/early-values.cjs +155 -0
  45. package/dist/cjs/signals/encoding.cjs +6 -95
  46. package/dist/cjs/signals/engine.cjs +276 -12
  47. package/dist/cjs/signals/errors.cjs +18 -0
  48. package/dist/cjs/signals/facade.cjs +477 -0
  49. package/dist/cjs/signals/graph.cjs +163 -20
  50. package/dist/cjs/signals/handle-protocol.cjs +36 -0
  51. package/dist/cjs/signals/index.cjs +81 -2
  52. package/dist/cjs/signals/native-read-client.cjs +10 -0
  53. package/dist/cjs/signals/owner-context.cjs +112 -0
  54. package/dist/cjs/signals/query-attempt-observer.cjs +68 -0
  55. package/dist/cjs/signals/query-facade.cjs +72 -0
  56. package/dist/cjs/signals/read-protocol.cjs +32 -0
  57. package/dist/cjs/signals/requests.cjs +287 -14
  58. package/dist/cjs/signals/scalar-computations.cjs +76 -0
  59. package/dist/cjs/signals/server.cjs +4 -1
  60. package/dist/cjs/signals/transition-candidate.cjs +567 -0
  61. package/dist/cjs/signals/transition-state.cjs +106 -0
  62. package/dist/cjs/signals/types.cjs +50 -0
  63. package/dist/cjs/stream-protocol.cjs +3 -0
  64. package/dist/cjs/streamed-signals-protocol.cjs +159 -0
  65. package/dist/cjs/style-values.cjs +122 -0
  66. package/dist/cjs/version.cjs +1 -1
  67. package/dist/class-names.d.ts +5 -0
  68. package/dist/class-names.js +25 -0
  69. package/dist/compiler/bundler.js +34 -6
  70. package/dist/compiler/compile.js +2234 -559
  71. package/dist/compiler/dom-binding-program.js +935 -0
  72. package/dist/compiler/dom-binding-request.js +57 -0
  73. package/dist/compiler/dom-bindings.js +2245 -0
  74. package/dist/compiler/hook-deps.js +26 -0
  75. package/dist/compiler/hydrate-boundaries.js +358 -15
  76. package/dist/compiler/index.d.ts +38 -1
  77. package/dist/compiler/index.js +1 -0
  78. package/dist/compiler/native-attribute-reads.js +127 -0
  79. package/dist/compiler/native-read-diagnostics.js +48 -9
  80. package/dist/compiler/native-read-types.js +55 -32
  81. package/dist/compiler/server-context.js +118 -0
  82. package/dist/compiler/signal-attempt-reads.js +479 -0
  83. package/dist/compiler/signal-declarations.js +557 -0
  84. package/dist/compiler/signal-start-reads.js +413 -0
  85. package/dist/compiler/slot-hooks.js +57 -10
  86. package/dist/compiler/template-ir.js +200 -0
  87. package/dist/compiler/typescript.d.ts +2 -0
  88. package/dist/compiler/typescript.js +2 -0
  89. package/dist/compiler/vite.d.ts +3 -1
  90. package/dist/compiler/vite.js +122 -4
  91. package/dist/compiler/volar.d.ts +6 -1
  92. package/dist/compiler/volar.js +674 -143
  93. package/dist/constants.d.ts +1 -0
  94. package/dist/constants.js +10 -0
  95. package/dist/css.d.ts +2 -5
  96. package/dist/css.js +2 -23
  97. package/dist/data-encoding.d.ts +9 -0
  98. package/dist/data-encoding.js +134 -0
  99. package/dist/dom-binding-classes.d.ts +15 -0
  100. package/dist/dom-binding-classes.js +129 -0
  101. package/dist/dom-binding-controls.d.ts +2 -0
  102. package/dist/dom-binding-controls.js +6 -0
  103. package/dist/dom-binding-handoff.d.ts +55 -0
  104. package/dist/dom-binding-handoff.js +62 -0
  105. package/dist/dom-binding-program.d.ts +300 -0
  106. package/dist/dom-binding-program.js +1625 -0
  107. package/dist/dom-binding-projections.d.ts +15 -0
  108. package/dist/dom-binding-projections.js +187 -0
  109. package/dist/dom-binding-protocol.d.ts +13 -0
  110. package/dist/dom-binding-protocol.js +88 -0
  111. package/dist/dom-binding-signals.d.ts +21 -0
  112. package/dist/dom-binding-signals.js +109 -0
  113. package/dist/dom-binding-styles.d.ts +17 -0
  114. package/dist/dom-binding-styles.js +272 -0
  115. package/dist/dom-bindings.d.ts +89 -0
  116. package/dist/dom-bindings.js +670 -0
  117. package/dist/dom-focused-move.d.ts +6 -0
  118. package/dist/dom-focused-move.js +42 -0
  119. package/dist/dom-tables.d.ts +1 -3
  120. package/dist/dom-tables.js +1 -91
  121. package/dist/error-codes.client.generated.d.ts +9 -0
  122. package/dist/error-codes.client.generated.js +33 -0
  123. package/dist/error-codes.server.generated.d.ts +5 -0
  124. package/dist/error-codes.server.generated.js +19 -0
  125. package/dist/hydration/control-capture.d.ts +62 -0
  126. package/dist/hydration/control-capture.js +223 -0
  127. package/dist/hydration/document-lifecycle.d.ts +23 -0
  128. package/dist/hydration/document-lifecycle.js +80 -0
  129. package/dist/hydration/event-capture.d.ts +20 -1
  130. package/dist/hydration/event-capture.js +128 -42
  131. package/dist/hydration/independent-island.d.ts +45 -0
  132. package/dist/hydration/independent-island.js +233 -0
  133. package/dist/hydration/index.d.ts +8 -1
  134. package/dist/hydration/index.js +55 -1
  135. package/dist/hydration/interaction-config.d.ts +5 -0
  136. package/dist/hydration/interaction-config.js +44 -1
  137. package/dist/hydration/stream-delivery.d.ts +32 -0
  138. package/dist/hydration/stream-delivery.js +333 -0
  139. package/dist/hydration/stream-receiver.d.ts +26 -0
  140. package/dist/hydration/stream-receiver.js +204 -0
  141. package/dist/hydration/stream-result-receiver.d.ts +70 -0
  142. package/dist/hydration/stream-result-receiver.js +246 -0
  143. package/dist/hydration/streamed-signals.d.ts +44 -0
  144. package/dist/hydration/streamed-signals.js +186 -0
  145. package/dist/hydration/types.d.ts +6 -0
  146. package/dist/hydration-markers.d.ts +7 -0
  147. package/dist/hydration-markers.js +10 -0
  148. package/dist/independent-hydration-protocol.d.ts +31 -0
  149. package/dist/independent-hydration-protocol.js +80 -0
  150. package/dist/index.d.ts +2 -0
  151. package/dist/index.js +4 -0
  152. package/dist/internal/signal-read.d.ts +1 -0
  153. package/dist/internal/signal-read.js +4 -0
  154. package/dist/jsx-runtime.d.ts +45 -6
  155. package/dist/public-types.d.ts +15 -1
  156. package/dist/runtime.d.ts +259 -21
  157. package/dist/runtime.js +2812 -220
  158. package/dist/runtime.server.d.ts +86 -6
  159. package/dist/runtime.server.js +712 -76
  160. package/dist/server/early-signals.d.ts +14 -0
  161. package/dist/server/early-signals.js +34 -0
  162. package/dist/server/index.d.ts +10 -2
  163. package/dist/server/index.js +48 -1
  164. package/dist/server/rpc-batch.d.ts +17 -0
  165. package/dist/server/rpc-batch.js +129 -0
  166. package/dist/server/rpc-stream.d.ts +9 -0
  167. package/dist/server/rpc-stream.js +125 -0
  168. package/dist/server/rpc.d.ts +5 -1
  169. package/dist/server/rpc.js +37 -13
  170. package/dist/server/signal-query-observation.d.ts +3 -0
  171. package/dist/server/signal-query-observation.js +134 -0
  172. package/dist/server/streamed-region.d.ts +15 -0
  173. package/dist/server/streamed-region.js +30 -0
  174. package/dist/server/streamed-signals.d.ts +45 -0
  175. package/dist/server/streamed-signals.js +488 -0
  176. package/dist/server-call.d.ts +41 -0
  177. package/dist/server-call.js +75 -0
  178. package/dist/server-rpc-batch-client.d.ts +7 -0
  179. package/dist/server-rpc-batch-client.js +119 -0
  180. package/dist/server-rpc-client.d.ts +3 -1
  181. package/dist/server-rpc-client.js +99 -27
  182. package/dist/server-rpc-protocol.d.ts +47 -0
  183. package/dist/server-rpc-protocol.js +109 -0
  184. package/dist/server-rpc-stream-client.d.ts +5 -0
  185. package/dist/server-rpc-stream-client.js +98 -0
  186. package/dist/signals/actions.d.ts +12 -0
  187. package/dist/signals/actions.js +466 -0
  188. package/dist/signals/client.d.ts +2 -0
  189. package/dist/signals/client.js +1 -0
  190. package/dist/signals/computations.d.ts +37 -0
  191. package/dist/signals/computations.js +455 -0
  192. package/dist/signals/control-binding.d.ts +31 -0
  193. package/dist/signals/control-binding.js +353 -0
  194. package/dist/signals/derived-facade.d.ts +3 -0
  195. package/dist/signals/derived-facade.js +25 -0
  196. package/dist/signals/document-owner.d.ts +10 -0
  197. package/dist/signals/document-owner.js +43 -0
  198. package/dist/signals/early-values.d.ts +24 -0
  199. package/dist/signals/early-values.js +124 -0
  200. package/dist/signals/encoding.d.ts +1 -3
  201. package/dist/signals/encoding.js +3 -93
  202. package/dist/signals/engine.d.ts +53 -4
  203. package/dist/signals/engine.js +275 -16
  204. package/dist/signals/errors.d.ts +7 -0
  205. package/dist/signals/errors.js +16 -0
  206. package/dist/signals/facade.d.ts +74 -0
  207. package/dist/signals/facade.js +452 -0
  208. package/dist/signals/graph.d.ts +50 -5
  209. package/dist/signals/graph.js +170 -19
  210. package/dist/signals/handle-protocol.d.ts +4 -0
  211. package/dist/signals/handle-protocol.js +11 -0
  212. package/dist/signals/index.d.ts +12 -3
  213. package/dist/signals/index.js +71 -2
  214. package/dist/signals/native-read-client.d.ts +3 -0
  215. package/dist/signals/native-read-client.js +10 -0
  216. package/dist/signals/owner-context.d.ts +22 -0
  217. package/dist/signals/owner-context.js +77 -0
  218. package/dist/signals/query-attempt-observer.d.ts +49 -0
  219. package/dist/signals/query-attempt-observer.js +41 -0
  220. package/dist/signals/query-facade.d.ts +3 -0
  221. package/dist/signals/query-facade.js +49 -0
  222. package/dist/signals/read-protocol.d.ts +32 -3
  223. package/dist/signals/read-protocol.js +26 -0
  224. package/dist/signals/requests.d.ts +38 -5
  225. package/dist/signals/requests.js +298 -15
  226. package/dist/signals/scalar-computations.d.ts +2 -0
  227. package/dist/signals/scalar-computations.js +60 -0
  228. package/dist/signals/server.d.ts +2 -0
  229. package/dist/signals/server.js +1 -0
  230. package/dist/signals/transition-candidate.d.ts +71 -0
  231. package/dist/signals/transition-candidate.js +557 -0
  232. package/dist/signals/transition-state.d.ts +51 -0
  233. package/dist/signals/transition-state.js +70 -0
  234. package/dist/signals/types.d.ts +95 -5
  235. package/dist/signals/types.js +20 -0
  236. package/dist/stream-protocol.d.ts +0 -5
  237. package/dist/stream-protocol.js +3 -0
  238. package/dist/streamed-signals-protocol.d.ts +60 -0
  239. package/dist/streamed-signals-protocol.js +130 -0
  240. package/dist/style-values.d.ts +3 -0
  241. package/dist/style-values.js +96 -0
  242. package/dist/version.js +1 -1
  243. package/package.json +37 -1
@@ -0,0 +1,479 @@
1
+ import { builders as b } from '@tsrx/core';
2
+ import { createLexicalAnalysis } from './compile-universal.js';
3
+ import { inheritHookMemoOrigin } from './inline-hook-memo.js';
4
+
5
+ const SIGNAL_MODULES = new Set([
6
+ 'octane/signals',
7
+ 'octane/signals/client',
8
+ 'octane/signals/server',
9
+ ]);
10
+ const METADATA = new Set([
11
+ 'loc',
12
+ 'start',
13
+ 'end',
14
+ 'range',
15
+ 'metadata',
16
+ 'parent',
17
+ 'leadingComments',
18
+ 'trailingComments',
19
+ 'innerComments',
20
+ 'comments',
21
+ ]);
22
+
23
+ function propertyName(member) {
24
+ if (member?.computed === true) {
25
+ return member.property?.type === 'Literal' ? member.property.value : null;
26
+ }
27
+ return member?.property?.name ?? null;
28
+ }
29
+
30
+ function directGet(node) {
31
+ return (
32
+ node?.type === 'CallExpression' &&
33
+ node.arguments?.length === 0 &&
34
+ (node.callee?.type === 'MemberExpression' ||
35
+ node.callee?.type === 'OptionalMemberExpression') &&
36
+ propertyName(node.callee) === 'get'
37
+ );
38
+ }
39
+
40
+ function directSignal(node) {
41
+ while (
42
+ node &&
43
+ (node.type === 'TSAsExpression' ||
44
+ node.type === 'TSTypeAssertion' ||
45
+ node.type === 'TSNonNullExpression')
46
+ ) {
47
+ node = node.expression;
48
+ }
49
+ if (node?.type === 'Identifier') return node.name.endsWith('$');
50
+ if (node?.type === 'MemberExpression' || node?.type === 'OptionalMemberExpression') {
51
+ const name = propertyName(node);
52
+ return typeof name === 'string' && name.endsWith('$');
53
+ }
54
+ return false;
55
+ }
56
+
57
+ function collectNames(root) {
58
+ const names = new Set();
59
+ const seen = new WeakSet();
60
+ const visit = (node) => {
61
+ if (node === null || typeof node !== 'object' || seen.has(node)) return;
62
+ seen.add(node);
63
+ if (Array.isArray(node)) {
64
+ for (const child of node) visit(child);
65
+ return;
66
+ }
67
+ if (node.type === 'Identifier') names.add(node.name);
68
+ for (const key in node) if (!METADATA.has(key)) visit(node[key]);
69
+ };
70
+ visit(root);
71
+ return names;
72
+ }
73
+
74
+ function allocate(names, preferred) {
75
+ let name = preferred;
76
+ let suffix = 0;
77
+ while (names.has(name)) name = `${preferred}$${++suffix}`;
78
+ names.add(name);
79
+ return name;
80
+ }
81
+
82
+ function simpleHelper(node) {
83
+ const fn =
84
+ node?.type === 'FunctionDeclaration' ||
85
+ node?.type === 'FunctionExpression' ||
86
+ node?.type === 'ArrowFunctionExpression'
87
+ ? node
88
+ : null;
89
+ if (fn === null || fn.async || fn.generator) return null;
90
+ let expression = fn.body;
91
+ if (expression?.type === 'BlockStatement') {
92
+ if (expression.body?.length !== 1 || expression.body[0].type !== 'ReturnStatement') return null;
93
+ expression = expression.body[0].argument;
94
+ }
95
+ if (!directGet(expression)) return null;
96
+ const handle = expression.callee.object;
97
+ if (handle.type === 'Identifier') {
98
+ const parameter = (fn.params ?? []).findIndex(
99
+ (param) => param.type === 'Identifier' && param.name === handle.name,
100
+ );
101
+ if (parameter !== -1) return { parameter, handle: null, params: fn.params ?? [] };
102
+ }
103
+ return directSignal(handle) ? { parameter: -1, handle, params: fn.params ?? [] } : null;
104
+ }
105
+
106
+ function recordHelpers(statements, helpers) {
107
+ for (const statement of statements ?? []) {
108
+ if (statement.type === 'FunctionDeclaration' && statement.id?.name) {
109
+ helpers.set(statement.id.name, simpleHelper(statement));
110
+ }
111
+ if (statement.type === 'VariableDeclaration') {
112
+ for (const declaration of statement.declarations ?? []) {
113
+ if (declaration.id?.type === 'Identifier') {
114
+ helpers.set(declaration.id.name, simpleHelper(declaration.init));
115
+ }
116
+ }
117
+ }
118
+ }
119
+ return helpers;
120
+ }
121
+
122
+ function collectHelpers(ast) {
123
+ const helpers = new Map();
124
+ recordHelpers(ast.body, helpers);
125
+ return helpers;
126
+ }
127
+
128
+ function attemptReader(callback, names) {
129
+ const parameters = callback.params ?? [];
130
+ if (parameters.length === 0) {
131
+ const name = allocate(names, '_$signalContext');
132
+ return {
133
+ callback: { ...callback, params: [inheritHookMemoOrigin(b.id(name), callback)] },
134
+ reader: b.member(b.id(name), 'read'),
135
+ };
136
+ }
137
+ const first = parameters[0];
138
+ if (first.type === 'Identifier') {
139
+ return { callback, reader: b.member(b.id(first.name), 'read') };
140
+ }
141
+ if (first.type === 'ObjectPattern') {
142
+ for (const property of first.properties ?? []) {
143
+ if (property.type === 'RestElement') continue;
144
+ const key = property.key?.name ?? property.key?.value;
145
+ if (key !== 'read') continue;
146
+ const value = property.value;
147
+ if (value?.type === 'Identifier') return { callback, reader: b.id(value.name) };
148
+ }
149
+ const name = allocate(names, '_$signalRead');
150
+ const property = inheritHookMemoOrigin(
151
+ b.prop('init', b.id('read'), b.id(name), false, false),
152
+ first,
153
+ );
154
+ const properties = [...(first.properties ?? [])];
155
+ const rest = properties.findIndex((item) => item.type === 'RestElement');
156
+ if (rest === -1) properties.push(property);
157
+ else properties.splice(rest, 0, property);
158
+ return {
159
+ callback: {
160
+ ...callback,
161
+ params: [{ ...first, properties }, ...parameters.slice(1)],
162
+ },
163
+ reader: b.id(name),
164
+ };
165
+ }
166
+ return null;
167
+ }
168
+
169
+ function importedNames(ast) {
170
+ const names = new Set();
171
+ for (const statement of ast.body ?? []) {
172
+ if (statement.type !== 'ImportDeclaration' || statement.importKind === 'type') continue;
173
+ for (const specifier of statement.specifiers ?? []) {
174
+ if (specifier.importKind !== 'type' && specifier.local?.name) names.add(specifier.local.name);
175
+ }
176
+ }
177
+ return names;
178
+ }
179
+
180
+ function transformAttempt(callback, reader, helpers, imports) {
181
+ let changed = false;
182
+ const passesReader = (argument) => {
183
+ if (reader.type === 'Identifier') {
184
+ return argument?.type === 'Identifier' && argument.name === reader.name;
185
+ }
186
+ return (
187
+ argument?.type === 'MemberExpression' &&
188
+ argument.computed === reader.computed &&
189
+ argument.object?.type === 'Identifier' &&
190
+ reader.object?.type === 'Identifier' &&
191
+ argument.object.name === reader.object.name &&
192
+ propertyName(argument) === propertyName(reader)
193
+ );
194
+ };
195
+ const failOpaque = (call) => {
196
+ const callee = call.callee?.type === 'Identifier' ? call.callee.name : null;
197
+ if (
198
+ callee &&
199
+ (imports.has(callee) || (helpers.has(callee) && helpers.get(callee) === null)) &&
200
+ (call.arguments ?? []).some(directSignal) &&
201
+ !(call.arguments ?? []).some(passesReader)
202
+ ) {
203
+ throw new Error(
204
+ `Post-await signal read through opaque helper \`${callee}\` cannot be proven attempt-bound. ` +
205
+ 'Pass the derived context `read` function explicitly, or move the direct `.get()` into the compiled callback.',
206
+ );
207
+ }
208
+ };
209
+ const awaitedRead = (handle, origin) => {
210
+ changed = true;
211
+ return inheritHookMemoOrigin(b.await(b.call(reader, handle)), origin);
212
+ };
213
+
214
+ function expression(node, deferred) {
215
+ if (node === null || typeof node !== 'object') return { node, deferred };
216
+ if (deferred && directGet(node) && directSignal(node.callee.object)) {
217
+ return { node: awaitedRead(node.callee.object, node), deferred: true };
218
+ }
219
+ if (node.type === 'AwaitExpression') {
220
+ const argument = expression(node.argument, deferred);
221
+ return {
222
+ node: argument.node === node.argument ? node : { ...node, argument: argument.node },
223
+ deferred: true,
224
+ };
225
+ }
226
+ if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
227
+ return { node, deferred };
228
+ }
229
+ if (node.type === 'CallExpression' || node.type === 'OptionalCallExpression') {
230
+ if (deferred && node.callee?.type === 'Identifier') {
231
+ const helper = helpers.get(node.callee.name);
232
+ if (helper) {
233
+ const handle =
234
+ helper.parameter === -1 ? helper.handle : node.arguments?.[helper.parameter];
235
+ if (directSignal(handle)) {
236
+ // Substituting a helper must not discard argument evaluation or
237
+ // parameter initialization. Only these two signatures prove that.
238
+ if (
239
+ !node.optional &&
240
+ node.arguments.length === helper.params.length &&
241
+ ((helper.parameter === -1 && helper.params.length === 0) ||
242
+ (helper.parameter === 0 && helper.params.length === 1))
243
+ )
244
+ return { node: awaitedRead(handle, node), deferred: true };
245
+ throw new Error(
246
+ `Post-await signal helper \`${node.callee.name}\` cannot be proven safe to inline. ` +
247
+ 'Pass the derived context `read` function explicitly.',
248
+ );
249
+ }
250
+ }
251
+ failOpaque(node);
252
+ }
253
+ let phase = deferred;
254
+ const callee = expression(node.callee, phase);
255
+ phase = callee.deferred;
256
+ let argsChanged = false;
257
+ const args = (node.arguments ?? []).map((argument) => {
258
+ const mapped = expression(argument, phase);
259
+ phase = mapped.deferred;
260
+ if (mapped.node !== argument) argsChanged = true;
261
+ return mapped.node;
262
+ });
263
+ return {
264
+ node:
265
+ callee.node === node.callee && !argsChanged
266
+ ? node
267
+ : { ...node, callee: callee.node, arguments: args },
268
+ deferred: phase,
269
+ };
270
+ }
271
+ if (node.type === 'ConditionalExpression') {
272
+ const test = expression(node.test, deferred);
273
+ const consequent = expression(node.consequent, test.deferred);
274
+ const alternate = expression(node.alternate, test.deferred);
275
+ return {
276
+ node:
277
+ test.node === node.test &&
278
+ consequent.node === node.consequent &&
279
+ alternate.node === node.alternate
280
+ ? node
281
+ : {
282
+ ...node,
283
+ test: test.node,
284
+ consequent: consequent.node,
285
+ alternate: alternate.node,
286
+ },
287
+ deferred: consequent.deferred || alternate.deferred,
288
+ };
289
+ }
290
+ let phase = deferred;
291
+ let output = null;
292
+ for (const key in node) {
293
+ if (METADATA.has(key) || key === 'type') continue;
294
+ const value = node[key];
295
+ if (value === null || typeof value !== 'object') continue;
296
+ if (Array.isArray(value)) {
297
+ let array = null;
298
+ for (let index = 0; index < value.length; index++) {
299
+ const mapped = expression(value[index], phase);
300
+ phase = mapped.deferred;
301
+ if (array === null && mapped.node !== value[index]) array = value.slice(0, index);
302
+ if (array !== null) array.push(mapped.node);
303
+ }
304
+ if (array !== null) {
305
+ output ??= { ...node };
306
+ output[key] = array;
307
+ }
308
+ } else {
309
+ const mapped = expression(value, phase);
310
+ phase = mapped.deferred;
311
+ if (mapped.node !== value) {
312
+ output ??= { ...node };
313
+ output[key] = mapped.node;
314
+ }
315
+ }
316
+ }
317
+ return { node: output ?? node, deferred: phase };
318
+ }
319
+
320
+ function statementList(body, deferred) {
321
+ let phase = deferred;
322
+ let output = null;
323
+ for (let index = 0; index < body.length; index++) {
324
+ const current = body[index];
325
+ let mapped;
326
+ if (current.type === 'IfStatement') {
327
+ const test = expression(current.test, phase);
328
+ const consequent = statement(current.consequent, test.deferred);
329
+ const alternate = current.alternate
330
+ ? statement(current.alternate, test.deferred)
331
+ : { node: null, deferred: test.deferred };
332
+ mapped = {
333
+ node:
334
+ test.node === current.test &&
335
+ consequent.node === current.consequent &&
336
+ alternate.node === current.alternate
337
+ ? current
338
+ : {
339
+ ...current,
340
+ test: test.node,
341
+ consequent: consequent.node,
342
+ alternate: alternate.node,
343
+ },
344
+ deferred: consequent.deferred || alternate.deferred,
345
+ };
346
+ } else {
347
+ mapped = statement(current, phase);
348
+ }
349
+ phase = mapped.deferred;
350
+ if (output === null && mapped.node !== current) output = body.slice(0, index);
351
+ if (output !== null) output.push(mapped.node);
352
+ }
353
+ return { body: output ?? body, deferred: phase };
354
+ }
355
+
356
+ function statement(node, deferred) {
357
+ if (node?.type === 'BlockStatement') {
358
+ const mapped = statementList(node.body ?? [], deferred);
359
+ return {
360
+ node: mapped.body === node.body ? node : { ...node, body: mapped.body },
361
+ deferred: mapped.deferred,
362
+ };
363
+ }
364
+ if (node?.type === 'FunctionDeclaration') return { node, deferred };
365
+ const mapped = expression(node, deferred);
366
+ return mapped;
367
+ }
368
+
369
+ const body = callback.body;
370
+ if (body.type !== 'BlockStatement') {
371
+ const transformed = expression(body, false);
372
+ return changed ? { ...callback, body: transformed.node } : null;
373
+ }
374
+ const transformed = statementList(body.body ?? [], false);
375
+ return changed ? { ...callback, body: { ...body, body: transformed.body } } : null;
376
+ }
377
+
378
+ /** Lower post-await reads only inside trusted owner-facade derived callbacks. */
379
+ export function lowerSignalAttemptReads(ast) {
380
+ const lexical = createLexicalAnalysis(ast);
381
+ const derivedImports = new Map();
382
+ const signalNamespaces = new Map();
383
+ for (const statement of ast.body ?? []) {
384
+ if (
385
+ statement.type !== 'ImportDeclaration' ||
386
+ statement.importKind === 'type' ||
387
+ !SIGNAL_MODULES.has(statement.source?.value)
388
+ ) {
389
+ continue;
390
+ }
391
+ for (const specifier of statement.specifiers ?? []) {
392
+ if (specifier.type === 'ImportNamespaceSpecifier') {
393
+ signalNamespaces.set(specifier.local.name, statement.source.value);
394
+ continue;
395
+ }
396
+ if (specifier.type !== 'ImportSpecifier' || specifier.importKind === 'type') continue;
397
+ const imported = specifier.imported?.name ?? specifier.imported?.value;
398
+ if (imported === 'derived$') derivedImports.set(specifier.local.name, statement.source.value);
399
+ }
400
+ }
401
+ if (derivedImports.size === 0 && signalNamespaces.size === 0) return ast;
402
+ const names = collectNames(ast);
403
+ const helpers = collectHelpers(ast);
404
+ const imports = importedNames(ast);
405
+ let changed = false;
406
+
407
+ function visit(node) {
408
+ if (node === null || typeof node !== 'object') return node;
409
+ if (Array.isArray(node)) {
410
+ let output = null;
411
+ for (let index = 0; index < node.length; index++) {
412
+ const mapped = visit(node[index]);
413
+ if (output === null && mapped !== node[index]) output = node.slice(0, index);
414
+ if (output !== null) output.push(mapped);
415
+ }
416
+ return output ?? node;
417
+ }
418
+ if (node.type === 'CallExpression') {
419
+ const callee = node.callee;
420
+ const named = callee?.type === 'Identifier' ? callee.name : null;
421
+ const namespace =
422
+ (callee?.type === 'MemberExpression' || callee?.type === 'OptionalMemberExpression') &&
423
+ callee.object?.type === 'Identifier' &&
424
+ propertyName(callee) === 'derived$'
425
+ ? callee.object.name
426
+ : null;
427
+ const source = named === null ? signalNamespaces.get(namespace) : derivedImports.get(named);
428
+ const bindingName = named ?? namespace;
429
+ const scope = lexical.nodeScopes.get(callee) ?? lexical.rootScope;
430
+ const binding = bindingName === null ? undefined : lexical.resolveBinding(scope, bindingName);
431
+ const callback = node.arguments?.[0];
432
+ if (
433
+ source !== undefined &&
434
+ binding?.scope === lexical.rootScope &&
435
+ binding.importSource?.value === source &&
436
+ callback?.async === true &&
437
+ (callback.type === 'ArrowFunctionExpression' || callback.type === 'FunctionExpression')
438
+ ) {
439
+ const attempt = attemptReader(callback, names);
440
+ if (attempt !== null) {
441
+ const localHelpers = recordHelpers(
442
+ callback.body?.type === 'BlockStatement' ? callback.body.body : [],
443
+ new Map(helpers),
444
+ );
445
+ const transformed = transformAttempt(
446
+ attempt.callback,
447
+ attempt.reader,
448
+ localHelpers,
449
+ imports,
450
+ );
451
+ if (transformed !== null) {
452
+ changed = true;
453
+ return {
454
+ ...node,
455
+ arguments: node.arguments.map((argument, index) =>
456
+ index === 0 ? transformed : visit(argument),
457
+ ),
458
+ };
459
+ }
460
+ }
461
+ }
462
+ }
463
+ let output = null;
464
+ for (const key in node) {
465
+ if (METADATA.has(key)) continue;
466
+ const value = node[key];
467
+ if (value === null || typeof value !== 'object') continue;
468
+ const mapped = visit(value);
469
+ if (mapped !== value) {
470
+ output ??= { ...node };
471
+ output[key] = mapped;
472
+ }
473
+ }
474
+ return output ?? node;
475
+ }
476
+
477
+ const output = visit(ast);
478
+ return changed ? output : ast;
479
+ }