octane 0.2.11 → 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 (264) 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/context-epoch.cjs +38 -0
  8. package/dist/cjs/css.cjs +6 -27
  9. package/dist/cjs/data-encoding.cjs +160 -0
  10. package/dist/cjs/dom-binding-handoff.cjs +93 -0
  11. package/dist/cjs/dom-binding-protocol.cjs +115 -0
  12. package/dist/cjs/dom-binding-styles.cjs +292 -0
  13. package/dist/cjs/dom-bindings.cjs +700 -0
  14. package/dist/cjs/dom-focused-move.cjs +66 -0
  15. package/dist/cjs/dom-stage.cjs +896 -0
  16. package/dist/cjs/dom-tables.cjs +4 -94
  17. package/dist/cjs/error-codes.client.generated.cjs +33 -0
  18. package/dist/cjs/error-codes.server.generated.cjs +24 -0
  19. package/dist/cjs/hydration/control-capture.cjs +244 -0
  20. package/dist/cjs/hydration/event-capture.cjs +125 -43
  21. package/dist/cjs/hydration/interaction-config.cjs +50 -2
  22. package/dist/cjs/hydration-markers.cjs +37 -0
  23. package/dist/cjs/independent-hydration-protocol.cjs +106 -0
  24. package/dist/cjs/index.cjs +9 -0
  25. package/dist/cjs/runtime.cjs +6397 -1683
  26. package/dist/cjs/runtime.server.cjs +1465 -213
  27. package/dist/cjs/server/early-signals.cjs +45 -0
  28. package/dist/cjs/server/index.cjs +47 -0
  29. package/dist/cjs/server/rpc-batch.cjs +149 -0
  30. package/dist/cjs/server/rpc-stream.cjs +146 -0
  31. package/dist/cjs/server/rpc.cjs +34 -15
  32. package/dist/cjs/server/signal-query-observation.cjs +158 -0
  33. package/dist/cjs/server/streamed-region.cjs +52 -0
  34. package/dist/cjs/server/streamed-signals.cjs +512 -0
  35. package/dist/cjs/server-call.cjs +103 -0
  36. package/dist/cjs/server-rpc-batch-client.cjs +141 -0
  37. package/dist/cjs/server-rpc-client.cjs +94 -27
  38. package/dist/cjs/server-rpc-protocol.cjs +141 -0
  39. package/dist/cjs/server-rpc-stream-client.cjs +118 -0
  40. package/dist/cjs/signals/actions.cjs +474 -0
  41. package/dist/cjs/signals/client.cjs +4 -1
  42. package/dist/cjs/signals/computations.cjs +460 -0
  43. package/dist/cjs/signals/control-binding.cjs +368 -0
  44. package/dist/cjs/signals/derived-facade.cjs +50 -0
  45. package/dist/cjs/signals/document-owner.cjs +71 -0
  46. package/dist/cjs/signals/early-values.cjs +155 -0
  47. package/dist/cjs/signals/encoding.cjs +6 -95
  48. package/dist/cjs/signals/engine.cjs +276 -12
  49. package/dist/cjs/signals/errors.cjs +18 -0
  50. package/dist/cjs/signals/facade.cjs +477 -0
  51. package/dist/cjs/signals/graph.cjs +166 -20
  52. package/dist/cjs/signals/handle-protocol.cjs +36 -0
  53. package/dist/cjs/signals/index.cjs +81 -2
  54. package/dist/cjs/signals/native-read-client.cjs +10 -0
  55. package/dist/cjs/signals/owner-context.cjs +112 -0
  56. package/dist/cjs/signals/query-attempt-observer.cjs +68 -0
  57. package/dist/cjs/signals/query-facade.cjs +72 -0
  58. package/dist/cjs/signals/read-protocol.cjs +54 -0
  59. package/dist/cjs/signals/requests.cjs +287 -14
  60. package/dist/cjs/signals/scalar-computations.cjs +76 -0
  61. package/dist/cjs/signals/server.cjs +4 -1
  62. package/dist/cjs/signals/transition-candidate.cjs +567 -0
  63. package/dist/cjs/signals/transition-state.cjs +106 -0
  64. package/dist/cjs/signals/types.cjs +50 -0
  65. package/dist/cjs/stream-protocol.cjs +3 -0
  66. package/dist/cjs/streamed-signals-protocol.cjs +159 -0
  67. package/dist/cjs/style-values.cjs +122 -0
  68. package/dist/cjs/trusted-html.cjs +30 -0
  69. package/dist/cjs/version.cjs +1 -1
  70. package/dist/class-names.d.ts +5 -0
  71. package/dist/class-names.js +25 -0
  72. package/dist/compiler/bundler.js +51 -22
  73. package/dist/compiler/compile.js +2534 -581
  74. package/dist/compiler/dom-binding-program.js +935 -0
  75. package/dist/compiler/dom-binding-request.js +57 -0
  76. package/dist/compiler/dom-bindings.js +2245 -0
  77. package/dist/compiler/hook-deps.js +759 -10
  78. package/dist/compiler/hydrate-boundaries.js +358 -15
  79. package/dist/compiler/index.d.ts +39 -2
  80. package/dist/compiler/index.js +1 -0
  81. package/dist/compiler/native-attribute-reads.js +127 -0
  82. package/dist/compiler/native-change-diagnostics.js +32 -38
  83. package/dist/compiler/native-read-diagnostics.js +69 -8
  84. package/dist/compiler/native-read-types.js +80 -34
  85. package/dist/compiler/parser.node.js +1 -1
  86. package/dist/compiler/renderer-boundaries.js +40 -0
  87. package/dist/compiler/server-context.js +118 -0
  88. package/dist/compiler/signal-attempt-reads.js +479 -0
  89. package/dist/compiler/signal-declarations.js +557 -0
  90. package/dist/compiler/signal-start-reads.js +413 -0
  91. package/dist/compiler/slot-hooks.d.ts +3 -1
  92. package/dist/compiler/slot-hooks.js +168 -34
  93. package/dist/compiler/strong-auto-memo.js +90 -0
  94. package/dist/compiler/strong-html.js +143 -0
  95. package/dist/compiler/strong-mode.js +764 -66
  96. package/dist/compiler/strong-template-policy.js +307 -0
  97. package/dist/compiler/template-ir.js +200 -0
  98. package/dist/compiler/typescript.d.ts +2 -0
  99. package/dist/compiler/typescript.js +2 -0
  100. package/dist/compiler/vite.d.ts +3 -1
  101. package/dist/compiler/vite.js +122 -4
  102. package/dist/compiler/volar.LICENSES.txt +1 -1
  103. package/dist/compiler/volar.d.ts +6 -1
  104. package/dist/compiler/volar.js +750 -1433
  105. package/dist/constants.d.ts +1 -0
  106. package/dist/constants.js +10 -0
  107. package/dist/context-epoch.d.ts +2 -0
  108. package/dist/context-epoch.js +13 -0
  109. package/dist/css.d.ts +2 -5
  110. package/dist/css.js +2 -23
  111. package/dist/data-encoding.d.ts +9 -0
  112. package/dist/data-encoding.js +134 -0
  113. package/dist/dom-binding-classes.d.ts +15 -0
  114. package/dist/dom-binding-classes.js +129 -0
  115. package/dist/dom-binding-controls.d.ts +2 -0
  116. package/dist/dom-binding-controls.js +6 -0
  117. package/dist/dom-binding-handoff.d.ts +55 -0
  118. package/dist/dom-binding-handoff.js +62 -0
  119. package/dist/dom-binding-program.d.ts +300 -0
  120. package/dist/dom-binding-program.js +1625 -0
  121. package/dist/dom-binding-projections.d.ts +15 -0
  122. package/dist/dom-binding-projections.js +187 -0
  123. package/dist/dom-binding-protocol.d.ts +13 -0
  124. package/dist/dom-binding-protocol.js +88 -0
  125. package/dist/dom-binding-signals.d.ts +21 -0
  126. package/dist/dom-binding-signals.js +109 -0
  127. package/dist/dom-binding-styles.d.ts +17 -0
  128. package/dist/dom-binding-styles.js +272 -0
  129. package/dist/dom-bindings.d.ts +89 -0
  130. package/dist/dom-bindings.js +670 -0
  131. package/dist/dom-focused-move.d.ts +6 -0
  132. package/dist/dom-focused-move.js +42 -0
  133. package/dist/dom-stage.d.ts +52 -0
  134. package/dist/dom-stage.js +872 -0
  135. package/dist/dom-tables.d.ts +1 -3
  136. package/dist/dom-tables.js +1 -91
  137. package/dist/error-codes.client.generated.d.ts +9 -0
  138. package/dist/error-codes.client.generated.js +33 -0
  139. package/dist/error-codes.server.generated.d.ts +6 -0
  140. package/dist/error-codes.server.generated.js +24 -0
  141. package/dist/hydration/control-capture.d.ts +62 -0
  142. package/dist/hydration/control-capture.js +223 -0
  143. package/dist/hydration/document-lifecycle.d.ts +23 -0
  144. package/dist/hydration/document-lifecycle.js +80 -0
  145. package/dist/hydration/event-capture.d.ts +20 -1
  146. package/dist/hydration/event-capture.js +128 -42
  147. package/dist/hydration/independent-island.d.ts +45 -0
  148. package/dist/hydration/independent-island.js +233 -0
  149. package/dist/hydration/index.d.ts +8 -1
  150. package/dist/hydration/index.js +55 -1
  151. package/dist/hydration/interaction-config.d.ts +5 -0
  152. package/dist/hydration/interaction-config.js +44 -1
  153. package/dist/hydration/stream-delivery.d.ts +32 -0
  154. package/dist/hydration/stream-delivery.js +333 -0
  155. package/dist/hydration/stream-receiver.d.ts +26 -0
  156. package/dist/hydration/stream-receiver.js +204 -0
  157. package/dist/hydration/stream-result-receiver.d.ts +70 -0
  158. package/dist/hydration/stream-result-receiver.js +246 -0
  159. package/dist/hydration/streamed-signals.d.ts +44 -0
  160. package/dist/hydration/streamed-signals.js +186 -0
  161. package/dist/hydration/types.d.ts +6 -0
  162. package/dist/hydration-markers.d.ts +7 -0
  163. package/dist/hydration-markers.js +10 -0
  164. package/dist/independent-hydration-protocol.d.ts +31 -0
  165. package/dist/independent-hydration-protocol.js +80 -0
  166. package/dist/index.d.ts +3 -0
  167. package/dist/index.js +6 -0
  168. package/dist/internal/signal-read.d.ts +1 -0
  169. package/dist/internal/signal-read.js +4 -0
  170. package/dist/jsx-runtime-strong.d.ts +33 -0
  171. package/dist/jsx-runtime.d.ts +58 -2
  172. package/dist/public-types.d.ts +15 -1
  173. package/dist/runtime.d.ts +297 -23
  174. package/dist/runtime.js +6405 -1679
  175. package/dist/runtime.server.d.ts +110 -27
  176. package/dist/runtime.server.js +1458 -213
  177. package/dist/server/early-signals.d.ts +14 -0
  178. package/dist/server/early-signals.js +34 -0
  179. package/dist/server/index.d.ts +11 -2
  180. package/dist/server/index.js +50 -1
  181. package/dist/server/rpc-batch.d.ts +17 -0
  182. package/dist/server/rpc-batch.js +129 -0
  183. package/dist/server/rpc-stream.d.ts +9 -0
  184. package/dist/server/rpc-stream.js +125 -0
  185. package/dist/server/rpc.d.ts +5 -1
  186. package/dist/server/rpc.js +37 -13
  187. package/dist/server/signal-query-observation.d.ts +3 -0
  188. package/dist/server/signal-query-observation.js +134 -0
  189. package/dist/server/streamed-region.d.ts +15 -0
  190. package/dist/server/streamed-region.js +30 -0
  191. package/dist/server/streamed-signals.d.ts +45 -0
  192. package/dist/server/streamed-signals.js +488 -0
  193. package/dist/server-call.d.ts +41 -0
  194. package/dist/server-call.js +75 -0
  195. package/dist/server-rpc-batch-client.d.ts +7 -0
  196. package/dist/server-rpc-batch-client.js +119 -0
  197. package/dist/server-rpc-client.d.ts +3 -1
  198. package/dist/server-rpc-client.js +99 -27
  199. package/dist/server-rpc-protocol.d.ts +47 -0
  200. package/dist/server-rpc-protocol.js +109 -0
  201. package/dist/server-rpc-stream-client.d.ts +5 -0
  202. package/dist/server-rpc-stream-client.js +98 -0
  203. package/dist/signals/actions.d.ts +12 -0
  204. package/dist/signals/actions.js +466 -0
  205. package/dist/signals/client.d.ts +2 -0
  206. package/dist/signals/client.js +1 -0
  207. package/dist/signals/computations.d.ts +37 -0
  208. package/dist/signals/computations.js +455 -0
  209. package/dist/signals/control-binding.d.ts +31 -0
  210. package/dist/signals/control-binding.js +353 -0
  211. package/dist/signals/derived-facade.d.ts +3 -0
  212. package/dist/signals/derived-facade.js +25 -0
  213. package/dist/signals/document-owner.d.ts +10 -0
  214. package/dist/signals/document-owner.js +43 -0
  215. package/dist/signals/early-values.d.ts +24 -0
  216. package/dist/signals/early-values.js +124 -0
  217. package/dist/signals/encoding.d.ts +1 -3
  218. package/dist/signals/encoding.js +3 -93
  219. package/dist/signals/engine.d.ts +53 -4
  220. package/dist/signals/engine.js +275 -16
  221. package/dist/signals/errors.d.ts +7 -0
  222. package/dist/signals/errors.js +16 -0
  223. package/dist/signals/facade.d.ts +74 -0
  224. package/dist/signals/facade.js +452 -0
  225. package/dist/signals/graph.d.ts +52 -6
  226. package/dist/signals/graph.js +174 -19
  227. package/dist/signals/handle-protocol.d.ts +4 -0
  228. package/dist/signals/handle-protocol.js +11 -0
  229. package/dist/signals/index.d.ts +12 -3
  230. package/dist/signals/index.js +71 -2
  231. package/dist/signals/native-read-client.d.ts +3 -0
  232. package/dist/signals/native-read-client.js +10 -0
  233. package/dist/signals/owner-context.d.ts +22 -0
  234. package/dist/signals/owner-context.js +77 -0
  235. package/dist/signals/query-attempt-observer.d.ts +49 -0
  236. package/dist/signals/query-attempt-observer.js +41 -0
  237. package/dist/signals/query-facade.d.ts +3 -0
  238. package/dist/signals/query-facade.js +49 -0
  239. package/dist/signals/read-protocol.d.ts +37 -2
  240. package/dist/signals/read-protocol.js +44 -0
  241. package/dist/signals/requests.d.ts +38 -5
  242. package/dist/signals/requests.js +298 -15
  243. package/dist/signals/scalar-computations.d.ts +2 -0
  244. package/dist/signals/scalar-computations.js +60 -0
  245. package/dist/signals/server.d.ts +2 -0
  246. package/dist/signals/server.js +1 -0
  247. package/dist/signals/transition-candidate.d.ts +71 -0
  248. package/dist/signals/transition-candidate.js +557 -0
  249. package/dist/signals/transition-state.d.ts +51 -0
  250. package/dist/signals/transition-state.js +70 -0
  251. package/dist/signals/types.d.ts +95 -5
  252. package/dist/signals/types.js +20 -0
  253. package/dist/stream-protocol.d.ts +0 -5
  254. package/dist/stream-protocol.js +3 -0
  255. package/dist/streamed-signals-protocol.d.ts +60 -0
  256. package/dist/streamed-signals-protocol.js +130 -0
  257. package/dist/style-values.d.ts +3 -0
  258. package/dist/style-values.js +96 -0
  259. package/dist/trusted-html.d.ts +17 -0
  260. package/dist/trusted-html.js +6 -0
  261. package/dist/universal-core.d.ts +6 -0
  262. package/dist/universal-core.js +214 -202
  263. package/dist/version.js +1 -1
  264. package/package.json +48 -5
@@ -0,0 +1,118 @@
1
+ // Shared syntax-level recognition for runtime server registration and editor call signatures.
2
+ export function unwrapServerFunctionInitializer(node) {
3
+ let current = node;
4
+ while (
5
+ current &&
6
+ (current.type === 'TSAsExpression' ||
7
+ current.type === 'TSSatisfiesExpression' ||
8
+ current.type === 'TSNonNullExpression' ||
9
+ current.type === 'TypeCastExpression' ||
10
+ current.type === 'ParenthesizedExpression')
11
+ ) {
12
+ current = current.expression;
13
+ }
14
+ return current;
15
+ }
16
+
17
+ export function collectServerFunctionNodes(statements) {
18
+ const functions = new Map();
19
+ const aliases = new Map();
20
+ for (const statement of statements) {
21
+ const declaration =
22
+ statement.type === 'ExportNamedDeclaration' ? statement.declaration : statement;
23
+ if (!declaration) continue;
24
+ if (declaration.type === 'FunctionDeclaration' && declaration.id) {
25
+ functions.set(declaration.id.name, declaration);
26
+ continue;
27
+ }
28
+ if (declaration.type !== 'VariableDeclaration') continue;
29
+ for (const item of declaration.declarations ?? []) {
30
+ if (item.id?.type !== 'Identifier' || !item.init) continue;
31
+ const init = unwrapServerFunctionInitializer(item.init);
32
+ if (init?.type === 'FunctionExpression' || init?.type === 'ArrowFunctionExpression') {
33
+ functions.set(item.id.name, init);
34
+ } else if (init?.type === 'Identifier') {
35
+ aliases.set(item.id.name, init.name);
36
+ }
37
+ }
38
+ }
39
+ for (const [local, target] of aliases) {
40
+ let name = target;
41
+ const seen = new Set([local]);
42
+ while (!seen.has(name)) {
43
+ seen.add(name);
44
+ const fn = functions.get(name);
45
+ if (fn !== undefined) {
46
+ functions.set(local, fn);
47
+ break;
48
+ }
49
+ name = aliases.get(name);
50
+ if (name === undefined) break;
51
+ }
52
+ }
53
+ return functions;
54
+ }
55
+
56
+ export function serverContextTypeImports(statements) {
57
+ const named = new Set();
58
+ const namespaces = new Set();
59
+ for (const statement of statements) {
60
+ if (statement.type !== 'ImportDeclaration' || statement.source?.value !== 'octane/server') {
61
+ continue;
62
+ }
63
+ for (const specifier of statement.specifiers ?? []) {
64
+ if (specifier.type === 'ImportNamespaceSpecifier') {
65
+ namespaces.add(specifier.local.name);
66
+ continue;
67
+ }
68
+ if (specifier.type !== 'ImportSpecifier') continue;
69
+ const imported = specifier.imported?.name ?? specifier.imported?.value;
70
+ if (imported === 'ServerCallContext') named.add(specifier.local.name);
71
+ }
72
+ }
73
+ return { named, namespaces };
74
+ }
75
+
76
+ export function serverContextTypeName(type, imports) {
77
+ while (
78
+ type &&
79
+ (type.type === 'TSTypeAnnotation' ||
80
+ type.type === 'TSParenthesizedType' ||
81
+ type.type === 'TSOptionalType')
82
+ ) {
83
+ type = type.typeAnnotation;
84
+ }
85
+ if (type?.type !== 'TSTypeReference') return false;
86
+ const name = type.typeName;
87
+ if (name?.type === 'Identifier') return imports.named.has(name.name);
88
+ if (name?.type === 'TSQualifiedName') {
89
+ return (
90
+ name.left?.type === 'Identifier' &&
91
+ imports.namespaces.has(name.left.name) &&
92
+ name.right?.name === 'ServerCallContext'
93
+ );
94
+ }
95
+ return (
96
+ name?.type === 'MemberExpression' &&
97
+ name.object?.type === 'Identifier' &&
98
+ imports.namespaces.has(name.object.name) &&
99
+ name.property?.name === 'ServerCallContext'
100
+ );
101
+ }
102
+
103
+ export function serverFunctionContextIndex(fn, imports, filename) {
104
+ let contextIndex = -1;
105
+ const params = fn.params ?? [];
106
+ for (let index = 0; index < params.length; index++) {
107
+ let parameter = params[index];
108
+ if (parameter.type === 'AssignmentPattern') parameter = parameter.left;
109
+ if (!serverContextTypeName(parameter.typeAnnotation, imports)) continue;
110
+ if (index !== params.length - 1) {
111
+ throw new Error(
112
+ `ServerCallContext must be the final parameter of a \`module server\` function (${filename}).`,
113
+ );
114
+ }
115
+ contextIndex = index;
116
+ }
117
+ return contextIndex;
118
+ }
@@ -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
+ }