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
@@ -7,7 +7,7 @@
7
7
  * module cache is required. This is important for Rspack, whose loader is
8
8
  * intentionally instantiated afresh for every resource query.
9
9
  */
10
- import { builders as b, parseModule } from '@tsrx/core';
10
+ import { builders as b, parseModule, strongHash } from '@tsrx/core';
11
11
 
12
12
  export const HYDRATE_QUERY_PARAM = 'octane-hydrate';
13
13
 
@@ -117,6 +117,49 @@ function jsxExpressionAttribute(name, expression, origin) {
117
117
  );
118
118
  }
119
119
 
120
+ function objectProperty(name, value) {
121
+ return b.prop('init', b.id(name), value);
122
+ }
123
+
124
+ function independentManifestExpression(manifest, captures, origin) {
125
+ const template = b.object([
126
+ objectProperty('version', b.literal(1)),
127
+ objectProperty(
128
+ 'boundaryId',
129
+ b.literal(manifest.boundaryId, JSON.stringify(manifest.boundaryId)),
130
+ ),
131
+ objectProperty(
132
+ 'exportName',
133
+ b.literal(manifest.exportName, JSON.stringify(manifest.exportName)),
134
+ ),
135
+ objectProperty(
136
+ 'captureSchema',
137
+ b.array(
138
+ manifest.captureSchema.map((capture) =>
139
+ b.object([
140
+ objectProperty('name', b.literal(capture.name, JSON.stringify(capture.name))),
141
+ objectProperty('type', b.literal('json', "'json'")),
142
+ ]),
143
+ ),
144
+ ),
145
+ ),
146
+ objectProperty('hookSeed', b.literal(manifest.hookSeed)),
147
+ objectProperty('idSeed', b.literal(manifest.idSeed)),
148
+ objectProperty(
149
+ 'signalSites',
150
+ b.array(manifest.signalSites.map((site) => b.literal(site, JSON.stringify(site)))),
151
+ ),
152
+ objectProperty('parentDependencies', b.literal(false, 'false')),
153
+ ]);
154
+ return inheritGeneratedOrigin(
155
+ b.object([
156
+ objectProperty('manifestTemplate', template),
157
+ objectProperty('captures', b.array(captures.map((name) => b.id(name)))),
158
+ ]),
159
+ origin,
160
+ );
161
+ }
162
+
120
163
  function hydrateLoaderExpression(boundary, request) {
121
164
  const query = `${request}?${HYDRATE_QUERY_PARAM}=${encodeURIComponent(boundary.path)}`;
122
165
  return inheritGeneratedOrigin(
@@ -340,6 +383,52 @@ function literalSplitDisabled(node) {
340
383
  return value?.type === 'Literal' && value.value === false;
341
384
  }
342
385
 
386
+ function literalIndependentEnabled(node, filename) {
387
+ let independent = null;
388
+ let hasSpread = false;
389
+ for (const attribute of node.openingElement?.attributes ?? []) {
390
+ if (attribute.type === 'JSXSpreadAttribute' || attribute.type === 'SpreadAttribute') {
391
+ hasSpread = true;
392
+ continue;
393
+ }
394
+ if (jsxAttributeName(attribute) === 'independent') independent = attribute;
395
+ }
396
+ if (independent === null) return false;
397
+ const raw =
398
+ independent.value == null
399
+ ? null
400
+ : independent.value.type === 'JSXExpressionContainer'
401
+ ? unwrapExpression(independent.value.expression)
402
+ : independent.value;
403
+ const enabled = independent.value == null || (raw?.type === 'Literal' && raw.value === true);
404
+ const disabled = raw?.type === 'Literal' && raw.value === false;
405
+ if (!enabled && !disabled) {
406
+ throw extractionError(
407
+ 'OCTANE_HYDRATE_INDEPENDENT_LITERAL',
408
+ filename,
409
+ independent,
410
+ '`independent` must be the literal `true` or `false` so standalone ownership is decided at build time',
411
+ );
412
+ }
413
+ if (enabled && hasSpread) {
414
+ throw extractionError(
415
+ 'OCTANE_HYDRATE_INDEPENDENT_SPREAD',
416
+ filename,
417
+ independent,
418
+ 'an independent boundary cannot combine its ownership request with Hydrate prop spreads; author the boundary options directly',
419
+ );
420
+ }
421
+ if (enabled && literalSplitDisabled(node)) {
422
+ throw extractionError(
423
+ 'OCTANE_HYDRATE_INDEPENDENT_SPLIT',
424
+ filename,
425
+ independent,
426
+ 'an independent boundary requires its own split client entry; remove `split={false}` or remove `independent`',
427
+ );
428
+ }
429
+ return enabled;
430
+ }
431
+
343
432
  /**
344
433
  * Recognize only the exact server-only form whose client subtree may be erased.
345
434
  * Spreads and indirect strategies stay on the ordinary persistent-wrapper path:
@@ -719,9 +808,11 @@ export function analyzeHydrateBoundaries(source, filename = 'unknown.tsrx', pars
719
808
  const siblings = parentBoundary === null ? roots : parentBoundary.children;
720
809
  const index = siblings.length;
721
810
  const path = parentBoundary === null ? String(index) : `${parentBoundary.path}.${index}`;
811
+ const independent = literalIndependentEnabled(node, filename);
722
812
  boundary = {
723
813
  children: [],
724
814
  disabled: literalSplitDisabled(node),
815
+ independent,
725
816
  permanentStatic: isPermanentStaticBoundary(node, imports.neverNames, shadowed),
726
817
  node,
727
818
  parent: parentBoundary,
@@ -1024,6 +1115,158 @@ function collectCaptures(
1024
1115
  return [...captures];
1025
1116
  }
1026
1117
 
1118
+ function bindingInitializers(ast) {
1119
+ const initializers = new Map();
1120
+ const seen = new WeakSet();
1121
+ const visit = (node) => {
1122
+ if (!node || typeof node !== 'object' || seen.has(node)) return;
1123
+ seen.add(node);
1124
+ if (Array.isArray(node)) {
1125
+ for (const child of node) visit(child);
1126
+ return;
1127
+ }
1128
+ if (node.type === 'VariableDeclarator') {
1129
+ const names = new Set();
1130
+ collectBindingNames(node.id, names);
1131
+ for (const name of names) initializers.set(name, node.init ?? null);
1132
+ }
1133
+ for (const [key, child] of Object.entries(node)) {
1134
+ if (!SKIP_KEYS.has(key)) visit(child);
1135
+ }
1136
+ };
1137
+ visit(ast);
1138
+ return initializers;
1139
+ }
1140
+
1141
+ function assignedNamesOutsideBoundary(ast, boundary) {
1142
+ const assigned = new Set();
1143
+ const inside = new WeakSet();
1144
+ collectSubtreeNodes(boundary.node.children, inside);
1145
+ const seen = new WeakSet();
1146
+ const visit = (node) => {
1147
+ if (!node || typeof node !== 'object' || seen.has(node) || inside.has(node)) return;
1148
+ seen.add(node);
1149
+ if (Array.isArray(node)) {
1150
+ for (const child of node) visit(child);
1151
+ return;
1152
+ }
1153
+ if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression') {
1154
+ const names = new Set();
1155
+ collectBindingNames(node.type === 'AssignmentExpression' ? node.left : node.argument, names);
1156
+ for (const name of names) assigned.add(name);
1157
+ }
1158
+ for (const [key, child] of Object.entries(node)) {
1159
+ if (!SKIP_KEYS.has(key)) visit(child);
1160
+ }
1161
+ };
1162
+ visit(ast);
1163
+ return assigned;
1164
+ }
1165
+
1166
+ function unsupportedIndependentInitializer(node, hookNames) {
1167
+ const value = unwrapExpression(node);
1168
+ if (value == null) return false;
1169
+ if (isFunction(value) || value.type === 'ClassExpression' || value.type === 'NewExpression') {
1170
+ return true;
1171
+ }
1172
+ if (value.type !== 'CallExpression' && value.type !== 'OptionalCallExpression') return false;
1173
+ if (isHookCall(value, hookNames)) return true;
1174
+ // Calls can create request, DOM, class, store, or closure identity. A future
1175
+ // proof may admit a pure JSON constructor; strict independent activation
1176
+ // must not serialize the result merely because the local has a friendly name.
1177
+ return true;
1178
+ }
1179
+
1180
+ function signalSitesInBoundary(boundary) {
1181
+ const sites = new Set();
1182
+ const seen = new WeakSet();
1183
+ const visit = (node) => {
1184
+ if (!node || typeof node !== 'object' || seen.has(node)) return;
1185
+ seen.add(node);
1186
+ if (Array.isArray(node)) {
1187
+ for (const child of node) visit(child);
1188
+ return;
1189
+ }
1190
+ if (node.type === 'CallExpression' || node.type === 'OptionalCallExpression') {
1191
+ const callee = node.callee;
1192
+ const name =
1193
+ callee?.type === 'Identifier'
1194
+ ? callee.name
1195
+ : callee?.type === 'MemberExpression' || callee?.type === 'OptionalMemberExpression'
1196
+ ? nameOf(callee.property)
1197
+ : null;
1198
+ const site = node.arguments?.[0];
1199
+ if (
1200
+ /^_?\$?__(?:signal|derived|query)At$/.test(name ?? '') &&
1201
+ site?.type === 'Literal' &&
1202
+ typeof site.value === 'string'
1203
+ ) {
1204
+ sites.add(site.value);
1205
+ }
1206
+ }
1207
+ for (const [key, child] of Object.entries(node)) {
1208
+ if (!SKIP_KEYS.has(key)) visit(child);
1209
+ }
1210
+ };
1211
+ visit(boundary.node.children);
1212
+ return [...sites].sort();
1213
+ }
1214
+
1215
+ function independentWidgetMetadata(analysis, filename, moduleMovePlan) {
1216
+ const initializers = bindingInitializers(analysis.ast);
1217
+ const widgets = [];
1218
+ for (const boundary of analysis.boundaries) {
1219
+ if (!boundary.independent) continue;
1220
+ if (hasPermanentStaticAncestor(boundary)) {
1221
+ throw extractionError(
1222
+ 'OCTANE_HYDRATE_INDEPENDENT_STATIC_PARENT',
1223
+ filename,
1224
+ boundary.node,
1225
+ 'an independent boundary cannot be nested below a permanently static Hydrate owner',
1226
+ );
1227
+ }
1228
+ assertDirectChildren(boundary, filename);
1229
+ validateBoundary(boundary, filename, analysis.imports.hookNames);
1230
+ const moduleBindings = moduleMovePlan.bindingsByPath.get(boundary.path) ?? new Set();
1231
+ const captures = collectCaptures(
1232
+ boundary.node.children,
1233
+ analysis.imports.importBindings,
1234
+ boundary.shadowedImports,
1235
+ moduleBindings.size === 0 ? null : moduleBindings,
1236
+ );
1237
+ const assigned = assignedNamesOutsideBoundary(analysis.ast, boundary);
1238
+ for (const capture of captures) {
1239
+ const initializer = initializers.get(capture);
1240
+ if (
1241
+ assigned.has(capture) ||
1242
+ unsupportedIndependentInitializer(initializer, analysis.imports.hookNames)
1243
+ ) {
1244
+ throw extractionError(
1245
+ 'OCTANE_HYDRATE_INDEPENDENT_OWNER_CAPTURE',
1246
+ filename,
1247
+ boundary.node,
1248
+ `independent activation cannot preserve parent-owned runtime capture \`${capture}\`. Pass serializable data, move the lifecycle into the widget, or use ordinary parent-first Hydrate`,
1249
+ );
1250
+ }
1251
+ }
1252
+ const boundaryId = `w:${strongHash(`octane:independent-boundary:1\0${filename}\0${boundary.path}`)}`;
1253
+ widgets.push({
1254
+ version: 1,
1255
+ boundaryId,
1256
+ moduleId: filename,
1257
+ exportName: 'default',
1258
+ request: `${sameSourceRequest(filename)}?${HYDRATE_QUERY_PARAM}=${encodeURIComponent(boundary.path)}`,
1259
+ captureSchema: captures.map((name) => ({ name, type: 'json' })),
1260
+ hookSeed: Number.parseInt(strongHash(`${boundaryId}\0hook`), 16),
1261
+ idSeed: Number.parseInt(strongHash(`${boundaryId}\0id`), 16),
1262
+ signalSites: signalSitesInBoundary(boundary),
1263
+ styles: [],
1264
+ parentDependencies: false,
1265
+ });
1266
+ }
1267
+ return widgets;
1268
+ }
1269
+
1027
1270
  function declarationBindingSet(node) {
1028
1271
  const bindings = new Set();
1029
1272
  if (node?.type === 'VariableDeclaration') {
@@ -1286,6 +1529,7 @@ function hydrateBoundaryElement(
1286
1529
  request,
1287
1530
  importBindings,
1288
1531
  additionalModuleBindings = new Set(),
1532
+ referencesOnly = false,
1289
1533
  ) {
1290
1534
  const node = boundary.node;
1291
1535
  const opening = node.openingElement;
@@ -1320,18 +1564,41 @@ function hydrateBoundaryElement(
1320
1564
  boundary.shadowedImports,
1321
1565
  additionalModuleBindings.size === 0 ? null : additionalModuleBindings,
1322
1566
  );
1323
- const attributes = [
1324
- ...(opening.attributes ?? []),
1325
- jsxExpressionAttribute('__load', hydrateLoaderExpression(boundary, request), opening),
1326
- ];
1327
- if (captures.length > 0) {
1567
+ const attributes = [...(opening.attributes ?? [])];
1568
+ if (boundary.independent) {
1328
1569
  attributes.push(
1329
1570
  jsxExpressionAttribute(
1330
- '__data',
1331
- inheritGeneratedOrigin(b.array(captures.map((name) => b.id(name))), opening),
1571
+ '__independent',
1572
+ // Move planning precedes manifest validation, but still needs every capture
1573
+ // reference to distinguish eager declarations from widget-owned code.
1574
+ referencesOnly
1575
+ ? inheritGeneratedOrigin(b.array(captures.map((name) => b.id(name))), opening)
1576
+ : independentManifestExpression(boundary.independentManifest, captures, opening),
1332
1577
  opening,
1333
1578
  ),
1334
1579
  );
1580
+ attributes.push(
1581
+ jsxExpressionAttribute(
1582
+ '__independentLoad',
1583
+ hydrateLoaderExpression(boundary, request),
1584
+ opening,
1585
+ ),
1586
+ );
1587
+ } else {
1588
+ attributes.push(
1589
+ jsxExpressionAttribute('__load', hydrateLoaderExpression(boundary, request), opening),
1590
+ );
1591
+ }
1592
+ if (captures.length > 0) {
1593
+ if (!boundary.independent) {
1594
+ attributes.push(
1595
+ jsxExpressionAttribute(
1596
+ '__data',
1597
+ inheritGeneratedOrigin(b.array(captures.map((name) => b.id(name))), opening),
1598
+ opening,
1599
+ ),
1600
+ );
1601
+ }
1335
1602
  }
1336
1603
  attributes.push(jsxExpressionAttribute('children', b.literal(null, 'null'), opening));
1337
1604
  return {
@@ -1347,6 +1614,7 @@ function transformHydrateAst(
1347
1614
  request,
1348
1615
  bindingsByPath = new Map(),
1349
1616
  removedRecords = [],
1617
+ referencesOnly = false,
1350
1618
  ) {
1351
1619
  const replacements = new WeakMap();
1352
1620
  for (const boundary of extractionFrontier(analysis.roots)) {
@@ -1357,6 +1625,7 @@ function transformHydrateAst(
1357
1625
  request,
1358
1626
  analysis.imports.importBindings,
1359
1627
  bindingsByPath.get(boundary.path),
1628
+ referencesOnly,
1360
1629
  ),
1361
1630
  );
1362
1631
  }
@@ -1424,6 +1693,7 @@ function extractedModuleAst(
1424
1693
  const pathName = boundary.path.replace(/[^A-Za-z0-9_$]/g, '_');
1425
1694
  const componentName = uniqueGeneratedName(source, `__OctaneHydrateBoundary_${pathName}`);
1426
1695
  const captureName = uniqueGeneratedName(source, `__octaneHydrateCaptures_${pathName}`);
1696
+ const activatorName = uniqueGeneratedName(source, '__octaneCreateIndependentHydrateActivator');
1427
1697
  const nestedAnalysis = {
1428
1698
  ...analysis,
1429
1699
  roots: boundary.children,
@@ -1438,6 +1708,8 @@ function extractedModuleAst(
1438
1708
  nestedAnalysis,
1439
1709
  request,
1440
1710
  moduleBindingsByPath,
1711
+ [],
1712
+ true,
1441
1713
  ).body;
1442
1714
  const setup =
1443
1715
  captures.length === 0
@@ -1457,16 +1729,37 @@ function extractedModuleAst(
1457
1729
  },
1458
1730
  boundary.node,
1459
1731
  );
1460
- const generatedExport = inheritGeneratedOrigin(
1461
- b.export_default(b.function_declaration(b.id(componentName), [b.id(captureName)], codeBlock)),
1732
+ const generatedComponent = inheritGeneratedOrigin(
1733
+ b.function_declaration(b.id(componentName), [b.id(captureName)], codeBlock),
1462
1734
  boundary.node,
1463
1735
  );
1736
+ const generatedNodes = boundary.independent
1737
+ ? [
1738
+ generatedComponent,
1739
+ inheritGeneratedOrigin(
1740
+ b.export_default(b.call(b.id(activatorName), b.id(componentName))),
1741
+ boundary.node,
1742
+ ),
1743
+ ]
1744
+ : [inheritGeneratedOrigin(b.export_default(generatedComponent), boundary.node)];
1745
+ const activatorImport = boundary.independent
1746
+ ? [
1747
+ inheritGeneratedOrigin(
1748
+ b.import_declaration(
1749
+ [b.import_specifier('createIndependentHydrateActivator', activatorName)],
1750
+ 'octane/internal/client',
1751
+ ),
1752
+ boundary.node,
1753
+ ),
1754
+ ]
1755
+ : [];
1464
1756
  const program = {
1465
1757
  ...analysis.ast,
1466
1758
  body: [
1467
1759
  ...analysis.imports.declarations,
1760
+ ...activatorImport,
1468
1761
  ...(moduleDeclarationsByPath.get(boundary.path) ?? []).map((record) => record.node),
1469
- generatedExport,
1762
+ ...generatedNodes,
1470
1763
  ],
1471
1764
  };
1472
1765
  return pruneUnusedImportsAst(program, false);
@@ -1522,6 +1815,7 @@ function createModuleMovePlanAst(analysis, request) {
1522
1815
  request,
1523
1816
  allBindingsByPath,
1524
1817
  candidates.records,
1818
+ true,
1525
1819
  );
1526
1820
  const eagerReferences = new Set(
1527
1821
  collectCaptures(
@@ -1760,6 +2054,13 @@ export function prepareHydrateBoundaries(source, filename, boundaryPath = null,
1760
2054
  }
1761
2055
  const request = sameSourceRequest(filename);
1762
2056
  const moduleMovePlan = createModuleMovePlanAst(analysis, request);
2057
+ const independentWidgets = independentWidgetMetadata(analysis, filename, moduleMovePlan);
2058
+ let independentIndex = 0;
2059
+ for (const boundary of analysis.boundaries) {
2060
+ if (boundary.independent) {
2061
+ boundary.independentManifest = independentWidgets[independentIndex++];
2062
+ }
2063
+ }
1763
2064
  const permanentStaticRemoved =
1764
2065
  boundaryPath === null
1765
2066
  ? createPermanentStaticRemovalPlanAst(analysis, request, moduleMovePlan)
@@ -1793,6 +2094,7 @@ export function prepareHydrateBoundaries(source, filename, boundaryPath = null,
1793
2094
  return {
1794
2095
  ast,
1795
2096
  boundaryPath,
2097
+ independentWidgets,
1796
2098
  };
1797
2099
  }
1798
2100
 
@@ -1893,13 +2195,33 @@ export function prepareServerHydrateBoundaries(source, filename, parsedAst = nul
1893
2195
  if (!source.includes('Hydrate') || !source.includes('octane')) return null;
1894
2196
  const analysis = analyzeHydrateBoundaries(source, filename, parsedAst);
1895
2197
  if (analysis.boundaries.length === 0) return null;
2198
+ for (const boundary of analysis.boundaries) {
2199
+ boundary.filename = filename;
2200
+ boundary.hookNames = analysis.imports.hookNames;
2201
+ boundary.ast = analysis.ast;
2202
+ }
2203
+ const request = sameSourceRequest(filename);
2204
+ const moduleMovePlan = createModuleMovePlanAst(analysis, request);
2205
+ const independentWidgets = independentWidgetMetadata(analysis, filename, moduleMovePlan);
2206
+ let independentIndex = 0;
2207
+ for (const boundary of analysis.boundaries) {
2208
+ if (!boundary.independent) continue;
2209
+ boundary.independentManifest = independentWidgets[independentIndex++];
2210
+ const moduleBindings = moduleMovePlan.bindingsByPath.get(boundary.path) ?? new Set();
2211
+ boundary.independentCaptures = collectCaptures(
2212
+ boundary.node.children,
2213
+ analysis.imports.importBindings,
2214
+ boundary.shadowedImports,
2215
+ moduleBindings.size === 0 ? null : moduleBindings,
2216
+ );
2217
+ }
1896
2218
  const constObjects = collectSingleUseConstObjects(analysis.ast);
1897
2219
  const elementUpdates = new Map();
1898
2220
  const objectUpdates = new Map();
1899
2221
  const updateElement = (node) => {
1900
2222
  let update = elementUpdates.get(node);
1901
2223
  if (update === undefined) {
1902
- update = { permanentStatic: false, removedAttributes: new Set() };
2224
+ update = { permanentStatic: false, independent: null, removedAttributes: new Set() };
1903
2225
  elementUpdates.set(node, update);
1904
2226
  }
1905
2227
  return update;
@@ -1914,6 +2236,12 @@ export function prepareServerHydrateBoundaries(source, filename, parsedAst = nul
1914
2236
  }
1915
2237
  };
1916
2238
  for (const boundary of analysis.boundaries) {
2239
+ if (boundary.independent) {
2240
+ updateElement(boundary.node).independent = {
2241
+ manifest: boundary.independentManifest,
2242
+ captures: boundary.independentCaptures,
2243
+ };
2244
+ }
1917
2245
  if (boundary.permanentStatic) {
1918
2246
  updateElement(boundary.node).permanentStatic = true;
1919
2247
  }
@@ -1947,13 +2275,27 @@ export function prepareServerHydrateBoundaries(source, filename, parsedAst = nul
1947
2275
  const elementUpdate = elementUpdates.get(original);
1948
2276
  if (elementUpdate !== undefined) {
1949
2277
  const opening = node.openingElement;
2278
+ const attributes = (opening.attributes ?? []).filter(
2279
+ (attribute) => !elementUpdate.removedAttributes.has(attribute),
2280
+ );
2281
+ if (elementUpdate.independent !== null) {
2282
+ attributes.push(
2283
+ jsxExpressionAttribute(
2284
+ '__independent',
2285
+ independentManifestExpression(
2286
+ elementUpdate.independent.manifest,
2287
+ elementUpdate.independent.captures,
2288
+ opening,
2289
+ ),
2290
+ opening,
2291
+ ),
2292
+ );
2293
+ }
1950
2294
  return {
1951
2295
  ...node,
1952
2296
  openingElement: {
1953
2297
  ...opening,
1954
- attributes: (opening.attributes ?? []).filter(
1955
- (attribute) => !elementUpdate.removedAttributes.has(attribute),
1956
- ),
2298
+ attributes,
1957
2299
  name: elementUpdate.permanentStatic
1958
2300
  ? permanentStaticName(opening.name, opening.name)
1959
2301
  : opening.name,
@@ -1979,5 +2321,6 @@ export function prepareServerHydrateBoundaries(source, filename, parsedAst = nul
1979
2321
  return {
1980
2322
  ast,
1981
2323
  boundaryPath: null,
2324
+ independentWidgets,
1982
2325
  };
1983
2326
  }
@@ -33,6 +33,9 @@ export interface OctaneCssModuleConstants {
33
33
  /** The version checked by compiler-emitted external Valdi adapter calls. */
34
34
  export const VALDI_COMPILER_ABI_VERSION: 1;
35
35
 
36
+ /** Compiler-owned native presentation, known-shape spreads, and direct signal artifacts. */
37
+ export const DOM_BINDING_COMPILER_ABI_VERSION: 1;
38
+
36
39
  export type ValdiWriterEffectiveType = 'boolean' | 'number' | 'string' | 'function' | 'style';
37
40
 
38
41
  /** An exact authored-expression fact supplied by an integration's type checker. */
@@ -51,6 +54,25 @@ export interface ValdiWriterFacts {
51
54
  expressions: readonly ValdiWriterExpressionFact[];
52
55
  }
53
56
 
57
+ /**
58
+ * A provider guarantee for a pure factory returning nullish or an object with
59
+ * exactly these own data fields. Result fields must remain stable throughout
60
+ * the render; getters or a shared result mutated during render are not valid.
61
+ */
62
+ export interface KnownAttributeSpread {
63
+ /** Exact module specifier and imported export (`*` for a namespace). */
64
+ source: string;
65
+ imported: string;
66
+ /** Statically named member path from the imported binding to the factory. */
67
+ members?: readonly string[];
68
+ /** Exact stable own native presentation data fields; never accessors. */
69
+ fields: readonly string[];
70
+ /** Use signal-aware style-object bindings; fields must include style. Omitted retains CSS text. */
71
+ style?: 'object';
72
+ /** Opt-in native JSX expression shorthand, passed as one argument to this factory. */
73
+ jsxAttribute?: string;
74
+ }
75
+
54
76
  export interface CompileOptions {
55
77
  mode?: 'client' | 'server';
56
78
  hmr?: boolean | 'vite' | 'webpack';
@@ -67,6 +89,8 @@ export interface CompileOptions {
67
89
  dataCallbackHooks?: readonly string[];
68
90
  /** Exact authored-source facts from octane/compiler/typescript. */
69
91
  textTypeFacts?: TextTypeFacts;
92
+ /** Trusted provider contracts for pure, fixed-shape native attribute factories. */
93
+ knownAttributeSpreads?: readonly KnownAttributeSpread[];
70
94
  /** Optional exact attribute-expression proofs; used only by the Valdi target. */
71
95
  valdiWriterFacts?: ValdiWriterFacts;
72
96
  /**
@@ -107,7 +131,7 @@ export interface CompilePosition {
107
131
 
108
132
  export interface CompileDiagnostic {
109
133
  code: string;
110
- severity: 'warning' | 'error';
134
+ severity: 'warning' | 'error' | 'hint';
111
135
  message: string;
112
136
  filename: string;
113
137
  start: CompilePosition;
@@ -203,6 +227,14 @@ export interface CompileResult {
203
227
  diagnostics: readonly CompileDiagnostic[];
204
228
  inspect?: CompileInspection;
205
229
  universalRuntime?: CompileOptions['universalRuntime'];
230
+ /** This module contains compiler-proven signal declarations or native reads. */
231
+ streamedSignals?: true;
232
+ /** Production client constants used by compiled binding views; build-time adapter metadata. */
233
+ bindingConstants?: {
234
+ version: 1;
235
+ source: string;
236
+ names: readonly string[];
237
+ };
206
238
  }
207
239
 
208
240
  /** Compile authored TSRX/JSX to Octane client or server JavaScript. */
@@ -217,7 +249,12 @@ export function compile(source: string, filename: string, options?: CompileOptio
217
249
  export function compileToVolarMappings(
218
250
  source: string,
219
251
  filename?: string,
220
- options?: { loose?: boolean; renderers?: unknown; strong?: boolean },
252
+ options?: {
253
+ loose?: boolean;
254
+ renderers?: unknown;
255
+ strong?: boolean;
256
+ knownAttributeSpreads?: readonly KnownAttributeSpread[];
257
+ },
221
258
  ): VolarCompileResult;
222
259
 
223
260
  /** @internal Shared authored-JSX diagnostic analysis for compiler integrations. */
@@ -3,6 +3,7 @@
3
3
  // `@octanejs/vite-plugin`), which is where its `node:fs`/`node:path` graph
4
4
  // belongs.
5
5
  export { compile } from './compile.js';
6
+ export { DOM_BINDING_COMPILER_ABI_VERSION } from './dom-bindings.js';
6
7
  export { VALDI_COMPILER_ABI_VERSION } from './compile-valdi.js';
7
8
  export { analyzeNativeChangeDiagnostics as __analyzeNativeChangeDiagnostics } from './native-change-diagnostics.js';
8
9
  export { compileToVolarMappings } from './volar.js';