octane 0.6.2 → 0.6.3

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 (174) hide show
  1. package/dist/cjs/dom-binding-handoff.cjs +2 -1
  2. package/dist/cjs/dom-binding-protocol.cjs +4 -3
  3. package/dist/cjs/dom-binding-styles.cjs +3 -2
  4. package/dist/cjs/dom-bindings.cjs +24 -23
  5. package/dist/cjs/error-codes.client.generated.cjs +2 -1
  6. package/dist/cjs/error-codes.server.generated.cjs +2 -1
  7. package/dist/cjs/hydration/event-capture.cjs +2 -1
  8. package/dist/cjs/independent-hydration-protocol.cjs +3 -2
  9. package/dist/cjs/runtime.cjs +169 -155
  10. package/dist/cjs/runtime.server.cjs +40 -34
  11. package/dist/cjs/signals/control-binding.cjs +7 -6
  12. package/dist/cjs/signals/derived-facade.cjs +2 -1
  13. package/dist/cjs/signals/document-owner.cjs +4 -3
  14. package/dist/cjs/signals/facade.cjs +10 -9
  15. package/dist/cjs/signals/graph.cjs +5 -4
  16. package/dist/cjs/signals/native-read-client.cjs +4 -3
  17. package/dist/cjs/signals/owner-context.cjs +4 -3
  18. package/dist/cjs/signals/query-attempt-observer.cjs +2 -1
  19. package/dist/cjs/signals/query-facade.cjs +2 -1
  20. package/dist/cjs/signals/scalar-computations.cjs +2 -1
  21. package/dist/cjs/signals/transition-action.cjs +10 -9
  22. package/dist/cjs/signals/transition-candidate.cjs +2 -1
  23. package/dist/cjs/version.cjs +1 -1
  24. package/dist/compiler/compile.js +380 -12
  25. package/dist/node/aria-diagnostics.js +92 -0
  26. package/dist/node/behavior-form-submissions.js +228 -0
  27. package/dist/node/behavior-root-types.js +0 -0
  28. package/dist/node/behavior-root.js +746 -0
  29. package/dist/node/class-names.js +25 -0
  30. package/dist/node/compatibility.js +10 -0
  31. package/dist/node/component-flags.js +23 -0
  32. package/dist/node/constants.js +113 -0
  33. package/dist/node/context-epoch.js +13 -0
  34. package/dist/node/context-identity.js +46 -0
  35. package/dist/node/css.js +80 -0
  36. package/dist/node/data-encoding.js +134 -0
  37. package/dist/node/devtools-hook.js +229 -0
  38. package/dist/node/dom-binding-claims.js +4 -0
  39. package/dist/node/dom-binding-classes.js +152 -0
  40. package/dist/node/dom-binding-controls.js +6 -0
  41. package/dist/node/dom-binding-handoff.js +77 -0
  42. package/dist/node/dom-binding-program.js +1617 -0
  43. package/dist/node/dom-binding-projections.js +204 -0
  44. package/dist/node/dom-binding-protocol.js +130 -0
  45. package/dist/node/dom-binding-signals.js +135 -0
  46. package/dist/node/dom-binding-styles.js +280 -0
  47. package/dist/node/dom-bindings.js +1087 -0
  48. package/dist/node/dom-focused-move.js +46 -0
  49. package/dist/node/dom-stage.js +878 -0
  50. package/dist/node/dom-tables.js +382 -0
  51. package/dist/node/error-codes.client.generated.js +1066 -0
  52. package/dist/node/error-codes.server.generated.js +736 -0
  53. package/dist/node/error-message.js +27 -0
  54. package/dist/node/event-names.js +15 -0
  55. package/dist/node/form-diagnostics.js +96 -0
  56. package/dist/node/form-submission.js +43 -0
  57. package/dist/node/has-own.js +4 -0
  58. package/dist/node/head-ownership.js +31 -0
  59. package/dist/node/hook-slot-cache.js +91 -0
  60. package/dist/node/host-property-diagnostics.js +189 -0
  61. package/dist/node/html-tree-validation.js +193 -0
  62. package/dist/node/hydration/condition.js +15 -0
  63. package/dist/node/hydration/control-capture.js +237 -0
  64. package/dist/node/hydration/document-lifecycle.js +89 -0
  65. package/dist/node/hydration/event-capture.js +362 -0
  66. package/dist/node/hydration/idle.js +23 -0
  67. package/dist/node/hydration/independent-island.js +317 -0
  68. package/dist/node/hydration/index.js +74 -0
  69. package/dist/node/hydration/interaction-config.js +61 -0
  70. package/dist/node/hydration/interaction.js +35 -0
  71. package/dist/node/hydration/load.js +15 -0
  72. package/dist/node/hydration/media.js +33 -0
  73. package/dist/node/hydration/native-intent.js +68 -0
  74. package/dist/node/hydration/never.js +12 -0
  75. package/dist/node/hydration/stream-delivery.js +345 -0
  76. package/dist/node/hydration/stream-receiver.js +190 -0
  77. package/dist/node/hydration/stream-result-receiver.js +276 -0
  78. package/dist/node/hydration/streamed-signals.js +186 -0
  79. package/dist/node/hydration/types.js +0 -0
  80. package/dist/node/hydration/visible.js +69 -0
  81. package/dist/node/hydration-markers.js +44 -0
  82. package/dist/node/independent-hydration-protocol.js +91 -0
  83. package/dist/node/index.js +461 -0
  84. package/dist/node/internal/client.js +7 -0
  85. package/dist/node/internal/context.js +5 -0
  86. package/dist/node/internal/server.js +5 -0
  87. package/dist/node/internal/signal-read.js +4 -0
  88. package/dist/node/method-dep.js +25 -0
  89. package/dist/node/profiling.js +432 -0
  90. package/dist/node/public-types.js +0 -0
  91. package/dist/node/react/fiber-adapter.js +57 -0
  92. package/dist/node/react/index.js +397 -0
  93. package/dist/node/react/react-compat-envelope.js +78 -0
  94. package/dist/node/react/react-compat-server.js +129 -0
  95. package/dist/node/react/react-compat-shared.js +104 -0
  96. package/dist/node/react/react-compat.js +223 -0
  97. package/dist/node/react/server.js +81 -0
  98. package/dist/node/react/shared.js +75 -0
  99. package/dist/node/renderer-bridge.js +35 -0
  100. package/dist/node/resize-observer.js +105 -0
  101. package/dist/node/resource-hint-diagnostics.js +67 -0
  102. package/dist/node/runtime-tags.js +22 -0
  103. package/dist/node/runtime.js +28881 -0
  104. package/dist/node/runtime.server.js +8016 -0
  105. package/dist/node/sanitize-url.js +34 -0
  106. package/dist/node/server/early-signals.js +54 -0
  107. package/dist/node/server/index.js +336 -0
  108. package/dist/node/server/rpc-batch.js +129 -0
  109. package/dist/node/server/rpc-stream.js +125 -0
  110. package/dist/node/server/rpc.js +48 -0
  111. package/dist/node/server/signal-query-observation.js +134 -0
  112. package/dist/node/server/streamed-region.js +31 -0
  113. package/dist/node/server/streamed-signals.js +557 -0
  114. package/dist/node/server-call.js +75 -0
  115. package/dist/node/server-rpc-batch-client.js +119 -0
  116. package/dist/node/server-rpc-client.js +108 -0
  117. package/dist/node/server-rpc-protocol.js +109 -0
  118. package/dist/node/server-rpc-stream-client.js +98 -0
  119. package/dist/node/shared-value-helpers.js +40 -0
  120. package/dist/node/signals/actions.js +463 -0
  121. package/dist/node/signals/client.js +22 -0
  122. package/dist/node/signals/computations.js +452 -0
  123. package/dist/node/signals/control-binding.js +388 -0
  124. package/dist/node/signals/control-handoff.js +12 -0
  125. package/dist/node/signals/derived-facade.js +25 -0
  126. package/dist/node/signals/document-owner.js +48 -0
  127. package/dist/node/signals/early-values.js +124 -0
  128. package/dist/node/signals/encoding.js +6 -0
  129. package/dist/node/signals/engine.js +668 -0
  130. package/dist/node/signals/errors.js +54 -0
  131. package/dist/node/signals/facade.js +441 -0
  132. package/dist/node/signals/graph.js +817 -0
  133. package/dist/node/signals/handle-protocol.js +11 -0
  134. package/dist/node/signals/index.js +87 -0
  135. package/dist/node/signals/native-read-client.js +319 -0
  136. package/dist/node/signals/native-read-collector.js +132 -0
  137. package/dist/node/signals/native-read-events.js +45 -0
  138. package/dist/node/signals/native-read-inspection.js +20 -0
  139. package/dist/node/signals/native-read-retry.js +30 -0
  140. package/dist/node/signals/native-read-seeds.js +243 -0
  141. package/dist/node/signals/native-read-server.js +99 -0
  142. package/dist/node/signals/owner-context.js +88 -0
  143. package/dist/node/signals/query-attempt-observer.js +42 -0
  144. package/dist/node/signals/query-facade.js +41 -0
  145. package/dist/node/signals/read-protocol.js +125 -0
  146. package/dist/node/signals/requests.js +716 -0
  147. package/dist/node/signals/scalar-computations.js +57 -0
  148. package/dist/node/signals/scope-streams.js +147 -0
  149. package/dist/node/signals/server.js +22 -0
  150. package/dist/node/signals/transition-action.js +555 -0
  151. package/dist/node/signals/transition-candidate.js +101 -0
  152. package/dist/node/signals/transition-coordinator.js +255 -0
  153. package/dist/node/signals/transition-state.js +82 -0
  154. package/dist/node/signals/types.js +20 -0
  155. package/dist/node/static/index.js +10 -0
  156. package/dist/node/stream-protocol.js +114 -0
  157. package/dist/node/streamed-signals-protocol.js +130 -0
  158. package/dist/node/style-values.js +96 -0
  159. package/dist/node/sub-slot.js +35 -0
  160. package/dist/node/testing.js +14 -0
  161. package/dist/node/trusted-html.js +6 -0
  162. package/dist/node/tsrx-iterable.js +5 -0
  163. package/dist/node/tsrx-spread.js +10 -0
  164. package/dist/node/universal-core.js +10010 -0
  165. package/dist/node/universal-dom-boundary.js +237 -0
  166. package/dist/node/universal-native.js +28 -0
  167. package/dist/node/universal.js +17 -0
  168. package/dist/node/version.js +4 -0
  169. package/dist/runtime.d.ts +18 -0
  170. package/dist/runtime.js +13 -2
  171. package/dist/runtime.server.d.ts +7 -0
  172. package/dist/runtime.server.js +4 -0
  173. package/dist/version.js +1 -1
  174. package/package.json +62 -1
@@ -27,6 +27,7 @@ var import_facade = require("./facade.cjs");
27
27
  var import_owner_context = require("./owner-context.cjs");
28
28
  var import_requests = require("./requests.cjs");
29
29
  var import_types = require("./types.cjs");
30
+ const __octaneDev = process.env.NODE_ENV !== "production";
30
31
  function __octaneNoArgError(code) {
31
32
  return "Minified Octane error #" + code + "; visit https://octanejs.dev/errors/" + code + " for the full message or use a development build for full errors and additional helpful warnings.";
32
33
  }
@@ -43,7 +44,7 @@ class QueryDescriptor extends import_facade.Descriptor {
43
44
  }
44
45
  function __queryAt(site, select, load, options) {
45
46
  if (typeof select !== "function" || typeof load !== "function") {
46
- throw new TypeError(process.env.NODE_ENV !== "production" ? "query$ requires selector and loader functions." : __octaneNoArgError(193));
47
+ throw new TypeError(__octaneDev ? "query$ requires selector and loader functions." : __octaneNoArgError(193));
47
48
  }
48
49
  const explicit = (0, import_facade.signalOptionsKey)(options);
49
50
  site ??= explicit;
@@ -23,6 +23,7 @@ __export(scalar_computations_exports, {
23
23
  module.exports = __toCommonJS(scalar_computations_exports);
24
24
  var import_engine = require("./engine.cjs");
25
25
  var import_graph = require("./graph.cjs");
26
+ const __octaneDev = process.env.NODE_ENV !== "production";
26
27
  function __octaneNoArgError(code) {
27
28
  return "Minified Octane error #" + code + "; visit https://octanejs.dev/errors/" + code + " for the full message or use a development build for full errors and additional helpful warnings.";
28
29
  }
@@ -53,7 +54,7 @@ class ScalarBinding {
53
54
  compute;
54
55
  forkCandidate(target) {
55
56
  if (this.frozen || this.owner.readBarrier || !this.compute) {
56
- throw new import_graph.CandidateUnsupportedError(process.env.NODE_ENV !== "production" ? "Frozen scalar candidates are not supported." : __octaneNoArgError(205));
57
+ throw new import_graph.CandidateUnsupportedError(__octaneDev ? "Frozen scalar candidates are not supported." : __octaneNoArgError(205));
57
58
  }
58
59
  target.compute = this.node.compute;
59
60
  }
@@ -25,6 +25,7 @@ module.exports = __toCommonJS(transition_action_exports);
25
25
  var import_errors = require("./errors.cjs");
26
26
  var import_read_protocol = require("./read-protocol.cjs");
27
27
  var import_transition_state = require("./transition-state.cjs");
28
+ const __octaneDev = process.env.NODE_ENV !== "production";
28
29
  function __octaneNoArgError(code) {
29
30
  return "Minified Octane error #" + code + "; visit https://octanejs.dev/errors/" + code + " for the full message or use a development build for full errors and additional helpful warnings.";
30
31
  }
@@ -70,9 +71,9 @@ class SignalActionFrame {
70
71
  demand;
71
72
  run(callback) {
72
73
  if (!this.active)
73
- throw new TypeError(process.env.NODE_ENV !== "production" ? "The signal candidate has retired." : __octaneNoArgError(203));
74
+ throw new TypeError(__octaneDev ? "The signal candidate has retired." : __octaneNoArgError(203));
74
75
  if (import_transition_state.activeCandidate && import_transition_state.activeCandidate !== this) {
75
- throw new import_transition_state.CandidateUnsupportedError(process.env.NODE_ENV !== "production" ? "Nested signal candidates are not supported." : __octaneNoArgError(206));
76
+ throw new import_transition_state.CandidateUnsupportedError(__octaneDev ? "Nested signal candidates are not supported." : __octaneNoArgError(206));
76
77
  }
77
78
  const previous = (0, import_transition_state.swapActiveSignalCandidate)(this);
78
79
  try {
@@ -83,7 +84,7 @@ class SignalActionFrame {
83
84
  }
84
85
  resolve(node) {
85
86
  if (!this.active)
86
- throw new TypeError(process.env.NODE_ENV !== "production" ? "The signal candidate has retired." : __octaneNoArgError(203));
87
+ throw new TypeError(__octaneDev ? "The signal candidate has retired." : __octaneNoArgError(203));
87
88
  const original = this.originals?.get(node);
88
89
  if (original) {
89
90
  this.demand?.add(original);
@@ -94,7 +95,7 @@ class SignalActionFrame {
94
95
  if (existing)
95
96
  return existing.target;
96
97
  if (import_transition_state.candidateGraph.historical() || node.owner.readBarrier || !node.owner.forkCandidate) {
97
- throw new import_transition_state.CandidateUnsupportedError(process.env.NODE_ENV !== "production" ? "Only live candidate-capable owners are supported." : __octaneNoArgError(207));
98
+ throw new import_transition_state.CandidateUnsupportedError(__octaneDev ? "Only live candidate-capable owners are supported." : __octaneNoArgError(207));
98
99
  }
99
100
  import_transition_state.candidateGraph.assertAlive(node.owner);
100
101
  this.invalidatePreparation();
@@ -130,7 +131,7 @@ class SignalActionFrame {
130
131
  }
131
132
  write(node, value) {
132
133
  if (!this.validate())
133
- throw new TypeError(process.env.NODE_ENV !== "production" ? "Rebase the signal candidate before writing." : __octaneNoArgError(208));
134
+ throw new TypeError(__octaneDev ? "Rebase the signal candidate before writing." : __octaneNoArgError(208));
134
135
  const target = this.resolve(node);
135
136
  const current = import_transition_state.candidateGraph.strictValue(import_transition_state.candidateGraph.refreshNode(target), node.key);
136
137
  let previous = current;
@@ -175,7 +176,7 @@ class SignalActionFrame {
175
176
  /** Only observed candidates allocate forwarding sources; ordinary reads keep their identity. */
176
177
  nativeSource(target, read, source) {
177
178
  if (!nativeExtension)
178
- throw new import_transition_state.CandidateUnsupportedError(process.env.NODE_ENV !== "production" ? "Native signal presentation is not installed." : __octaneNoArgError(209));
179
+ throw new import_transition_state.CandidateUnsupportedError(__octaneDev ? "Native signal presentation is not installed." : __octaneNoArgError(209));
179
180
  return nativeExtension.source(this.entries.get(this.originals.get(target)), read, source);
180
181
  }
181
182
  validate() {
@@ -259,7 +260,7 @@ class SignalActionFrame {
259
260
  /** Urgent writes win their own cells without restarting unaffected producer leases. */
260
261
  rebase() {
261
262
  if (!this.active)
262
- throw new TypeError(process.env.NODE_ENV !== "production" ? "The signal candidate has retired." : __octaneNoArgError(203));
263
+ throw new TypeError(__octaneDev ? "The signal candidate has retired." : __octaneNoArgError(203));
263
264
  const hadWrites = this.hasWrites();
264
265
  this.generation++;
265
266
  this.invalidatePreparation();
@@ -267,7 +268,7 @@ class SignalActionFrame {
267
268
  for (const entry of this.entries?.values() ?? []) {
268
269
  import_transition_state.candidateGraph.assertAlive(entry.node.owner);
269
270
  if (entry.epoch !== entry.node.owner.epoch || entry.node.owner.readBarrier) {
270
- throw new TypeError(process.env.NODE_ENV !== "production" ? "The candidate owner changed lifetime." : __octaneNoArgError(210));
271
+ throw new TypeError(__octaneDev ? "The candidate owner changed lifetime." : __octaneNoArgError(210));
271
272
  }
272
273
  const operations = entry.urgentOperations;
273
274
  if (!entry.withdrawn && !operations && entry.revision === entry.node.revision)
@@ -417,7 +418,7 @@ class SignalActionFrame {
417
418
  for (let link = target.deps; link; link = link.nextDep) {
418
419
  const original = this.originals.get(link.dep);
419
420
  if (!original)
420
- throw new TypeError(process.env.NODE_ENV !== "production" ? "Candidate dependency escaped its frame." : __octaneNoArgError(211));
421
+ throw new TypeError(__octaneDev ? "Candidate dependency escaped its frame." : __octaneNoArgError(211));
421
422
  import_transition_state.candidateGraph.link(original, node);
422
423
  }
423
424
  const currentChanged = node.state?.snapshot !== state.snapshot || !import_transition_state.candidateGraph.sameState(node.state, state);
@@ -23,6 +23,7 @@ __export(transition_candidate_exports, {
23
23
  module.exports = __toCommonJS(transition_candidate_exports);
24
24
  var import_transition_state = require("./transition-state.cjs");
25
25
  var import_transition_action = require("./transition-action.cjs");
26
+ const __octaneDev = process.env.NODE_ENV !== "production";
26
27
  function __octaneNoArgError(code) {
27
28
  return "Minified Octane error #" + code + "; visit https://octanejs.dev/errors/" + code + " for the full message or use a development build for full errors and additional helpful warnings.";
28
29
  }
@@ -36,7 +37,7 @@ function createNativeReadCandidateSource(candidate) {
36
37
  getVersion: () => target === void 0 ? NaN : !accepted ? target.getVersion() : target.getVersion() === canonicalVersion ? acceptedVersion : NaN,
37
38
  subscribe(notify) {
38
39
  if (target === void 0)
39
- throw new TypeError(process.env.NODE_ENV !== "production" ? "The native read candidate has retired." : __octaneNoArgError(212));
40
+ throw new TypeError(__octaneDev ? "The native read candidate has retired." : __octaneNoArgError(212));
40
41
  const subscription = { notify, dispose: target.subscribe(notify) };
41
42
  subscriptions.add(subscription);
42
43
  return () => {
@@ -21,7 +21,7 @@ __export(version_exports, {
21
21
  version: () => version
22
22
  });
23
23
  module.exports = __toCommonJS(version_exports);
24
- const version = "0.6.2";
24
+ const version = "0.6.3";
25
25
  // Annotate the CommonJS export names for ESM import in node:
26
26
  0 && (module.exports = {
27
27
  version
@@ -1452,6 +1452,8 @@ const INTERNAL_CLIENT_RUNTIME_HELPERS = new Set([
1452
1452
  'enableSignalBindings',
1453
1453
  'createElementAt',
1454
1454
  'createElementFromConfig',
1455
+ 'isRenderCall',
1456
+ 'deferRecord',
1455
1457
  'bindSignalText',
1456
1458
  'bindSignalChild',
1457
1459
  'bindSignalAttribute',
@@ -1491,6 +1493,7 @@ const INTERNAL_SERVER_RUNTIME_HELPERS = new Set([
1491
1493
  'enableServerSignalBindings',
1492
1494
  'createElementAt',
1493
1495
  'createElementFromConfig',
1496
+ 'isRenderCall',
1494
1497
  'ssrSignalValue',
1495
1498
  'ssrSignalControlValue',
1496
1499
  'ssrSignalControlAttrs',
@@ -12087,6 +12090,11 @@ function compileServerComponent(node, ctx) {
12087
12090
  const beforeCss = ctx.cssInjections.length;
12088
12091
  const scoping = applyStyleScopes(node, ctx);
12089
12092
  node = scoping.node;
12093
+ // A plain return-JSX declaration can also be called directly; its returned
12094
+ // JSX then needs the same deferred value the client emits.
12095
+ if (node.body?.type === 'BlockStatement' && !node._octaneBindingView) {
12096
+ node = { ...node, _octaneDirectCallReturn: true };
12097
+ }
12090
12098
  const cssHash = scoping.cssHash;
12091
12099
  const cssEntries = [...ctx.moduleCssInjections, ...ctx.cssInjections.slice(beforeCss)].sort(
12092
12100
  (a, b) => a.order - b.order,
@@ -12280,6 +12288,7 @@ function ssrCompileBodyWithMapTemps(
12280
12288
 
12281
12289
  let statements;
12282
12290
  let jsxNodes;
12291
+ let directCallRoot = null;
12283
12292
  if (node.body && node.body.type === 'JSXCodeBlock') {
12284
12293
  statements = returnedOutput
12285
12294
  ? (node.body.body || []).map(normalizeOwnRenderableReturns)
@@ -12298,6 +12307,9 @@ function ssrCompileBodyWithMapTemps(
12298
12307
  jsxNodes = [];
12299
12308
  for (const child of bodyRewritten) {
12300
12309
  if (child.type === 'ReturnStatement' && child.argument && isJsxNode(child.argument)) {
12310
+ if (directCallRoot === null && node._octaneDirectCallReturn === true) {
12311
+ directCallRoot = child.argument;
12312
+ }
12301
12313
  // return-JSX form: the returned host element (+ its directive children) is
12302
12314
  // the render output — route it to jsxNodes so it flows through ssrEmitNode
12303
12315
  // (byte-identical SSR to the `@{}` form), not printed as `return <jsx>`.
@@ -12425,6 +12437,53 @@ function ssrCompileBodyWithMapTemps(
12425
12437
  ctx._returnedFragmentTemplate = prevReturnedFragmentTemplate;
12426
12438
  ctx._tsxValuePos = prevValuePos;
12427
12439
  ctx._valueDirectiveLowering = prevValueDirectiveLowering;
12440
+ // Mirrors compileReturnJsxFunction: a direct call defers the record that a
12441
+ // body call renders now. Its fragment sub is declared only in that branch, and
12442
+ // it is lowered after the body template so body-call output is unchanged.
12443
+ let directCall = null;
12444
+ const directCallKind = directCallRoot === null ? null : directCallReturnKind(directCallRoot, ctx);
12445
+ if (directCallKind === 'value') {
12446
+ // Directives in the returned value fold into helpers owned by the direct
12447
+ // call, so a body call never declares them.
12448
+ const subs = [];
12449
+ const previousLowering = ctx._valueDirectiveLowering;
12450
+ ctx._valueDirectiveLowering = serverValueDirectiveFold(ctx, name, subs, cssHash);
12451
+ try {
12452
+ const returned = rewriteHookCalls(b.return(directCallRoot), ctx, name, localSetupSlots);
12453
+ directCall = { subs, value: rewriteJsxValues(returned.argument, ctx) };
12454
+ } finally {
12455
+ ctx._valueDirectiveLowering = previousLowering;
12456
+ }
12457
+ } else if (directCallKind === 'fragment') {
12458
+ const subs = [];
12459
+ const record = serverValueDirectiveFold(
12460
+ ctx,
12461
+ name,
12462
+ subs,
12463
+ cssHash,
12464
+ false,
12465
+ 'html',
12466
+ )(directCallRoot);
12467
+ ctx.runtimeNeeded.add('createScopedValue');
12468
+ directCall = {
12469
+ subs,
12470
+ value: inheritOriginLoc(
12471
+ b.call(rtAlias('createScopedValue'), b.arrow([], record)),
12472
+ directCallRoot,
12473
+ ),
12474
+ };
12475
+ }
12476
+ const renderCallTest =
12477
+ directCall === null
12478
+ ? null
12479
+ : () =>
12480
+ inheritOriginLoc(
12481
+ b.call(
12482
+ requireRuntimeForContext(ctx, 'isRenderCall'),
12483
+ node.params.length > 1 ? secondArgument(node) : b.id('__s'),
12484
+ ),
12485
+ directCallRoot,
12486
+ );
12428
12487
 
12429
12488
  const body = [];
12430
12489
  body.push(...serverThemeTouchStatements(themeTouches ?? [], ctx, node));
@@ -12488,7 +12547,39 @@ function ssrCompileBodyWithMapTemps(
12488
12547
  }
12489
12548
  }
12490
12549
  }
12491
- body.push(...frontHead, ...rewritten, ...inlinedSubs, ...deferredHead);
12550
+ // A direct call's deferred fragment registers its own head elements when it
12551
+ // renders, so the body-call registrations run only for a body call.
12552
+ const guardHead = (statements) =>
12553
+ renderCallTest === null || statements.length === 0
12554
+ ? statements
12555
+ : [inheritOriginLoc(b.if(renderCallTest(), b.block(statements), null), statements[0])];
12556
+ body.push(...guardHead(frontHead), ...rewritten, ...inlinedSubs, ...guardHead(deferredHead));
12557
+ if (directCall !== null) {
12558
+ const scopeNames = collectFunctionScopeBindings(
12559
+ node.params.length > 0 ? node.params : [b.id('__props')],
12560
+ [...rewritten, ...inlinedSubs],
12561
+ new Set([...mapTemps, '__s', '__extra']),
12562
+ );
12563
+ const captures = directCallCaptures(node, scopeNames, directCall.subs, directCall.value);
12564
+ const branch =
12565
+ captures === null
12566
+ ? [...directCall.subs, b.return(directCall.value)]
12567
+ : [
12568
+ b.return(
12569
+ hoistCapturingHelper(
12570
+ ctx,
12571
+ `${name}$direct`,
12572
+ captures,
12573
+ directCall.subs,
12574
+ directCall.value,
12575
+ directCallRoot,
12576
+ ),
12577
+ ),
12578
+ ];
12579
+ body.push(
12580
+ inheritOriginLoc(b.if(b.unary('!', renderCallTest()), b.block(branch), null), directCallRoot),
12581
+ );
12582
+ }
12492
12583
  // PROPS-FIRST ABI (matches the client): `(…userParams, __s, __extra)`. A leading
12493
12584
  // `__props` placeholder stands in when there are no user params, so a verbatim
12494
12585
  // `function Foo(props)` and a compiled component both bind props from arg 0.
@@ -19990,6 +20081,9 @@ function compileReturnJsxFunction(node, ctx, options) {
19990
20081
  // their closure over setup locals/props — and only their values + the control
19991
20082
  // expression are threaded into the renderer as `props.hN` holes.
19992
20083
  const compInlinedSubs = [];
20084
+ // Set once a returned record needs a separate direct-call value.
20085
+ let renderCall = null;
20086
+ const returnedRecords = [];
19993
20087
  // Fold a directive found at value position into a hoisted renderer owned by THIS
19994
20088
  // body — the same fold the `@{}` body and the server emitter build.
19995
20089
  const lowerBodyValueDirective =
@@ -20121,7 +20215,23 @@ function compileReturnJsxFunction(node, ctx, options) {
20121
20215
  };
20122
20216
  }
20123
20217
  }
20124
- return { ...h, argument: lowerReturnJsx(h.argument, ctx, compInlinedSubs, cssHash) };
20218
+ const record = lowerReturnJsx(h.argument, ctx, compInlinedSubs, cssHash);
20219
+ if (node._octaneBindingView || !directCallNeedsClientBranch(h.argument, node, ctx)) {
20220
+ return { ...h, argument: record };
20221
+ }
20222
+ renderCall ??= renderCallScopeParam(node, ctx);
20223
+ // Built once the function's top-level bindings are all known.
20224
+ const placeholder = inheritOriginLoc(b.id('__octaneReturnedRecord'), h.argument);
20225
+ returnedRecords.push({
20226
+ placeholder,
20227
+ record,
20228
+ origin: h.argument,
20229
+ test: inheritOriginLoc(
20230
+ b.call(requireRuntimeForContext(ctx, 'isRenderCall'), cloneAstNode(renderCall.scope)),
20231
+ h.argument,
20232
+ ),
20233
+ });
20234
+ return { ...h, argument: placeholder };
20125
20235
  }
20126
20236
  return rewriteJsxValues(h, ctx);
20127
20237
  });
@@ -20133,6 +20243,20 @@ function compileReturnJsxFunction(node, ctx, options) {
20133
20243
  ctx.currentAutoCalculatedRenderableRefs = prevAutoCalculatedRenderableRefs;
20134
20244
  ctx.currentMapTemps = prevMapTemps;
20135
20245
  }
20246
+ if (returnedRecords.length > 0) {
20247
+ const scopeNames = collectFunctionScopeBindings(
20248
+ renderCall.params,
20249
+ [...compInlinedSubs, ...newStatements],
20250
+ new Set(mapTemps),
20251
+ );
20252
+ const replacements = new Map(
20253
+ returnedRecords.map((entry) => [
20254
+ entry.placeholder,
20255
+ splitReturnedRecord(ctx, node, name, scopeNames, entry),
20256
+ ]),
20257
+ );
20258
+ newStatements = mapAst(newStatements, (candidate) => replacements.get(candidate) ?? null);
20259
+ }
20136
20260
  if (
20137
20261
  ctx.autoMemo &&
20138
20262
  ctx.privateUnescapedComponents.has(name) &&
@@ -20184,7 +20308,7 @@ function compileReturnJsxFunction(node, ctx, options) {
20184
20308
  }
20185
20309
  const emittedFunction = b.function_declaration(
20186
20310
  node.id,
20187
- node.params,
20311
+ renderCall?.params ?? node.params,
20188
20312
  b.block(
20189
20313
  ctx.nativeReads ? wrapNativeReadScope(returnBody, b.void0, nativeReadNames(ctx)) : returnBody,
20190
20314
  ),
@@ -20532,6 +20656,238 @@ function requiresTemplateNormalization(
20532
20656
  );
20533
20657
  }
20534
20658
 
20659
+ // A named return-JSX declaration is also a component. Its body call evaluates
20660
+ // the returned record in the scope that renders it. A direct call returns a JSX
20661
+ // value instead, which must resolve where it renders, like the value an arrow
20662
+ // function returns. Both emitters classify the same authored root: 'fragment'
20663
+ // defers the compiled fragment record, 'value' uses ordinary value lowering, and
20664
+ // null marks static host markup that renders the same anywhere. The server's
20665
+ // body call renders the whole tree to HTML, so every non-literal hole and every
20666
+ // component below the root needs deferral there.
20667
+ function directCallReturnKind(node, ctx, stableNames = null) {
20668
+ if (VALUE_DIRECTIVE_ARM_TYPES.has(node.type)) return 'fragment';
20669
+ if (requiresTemplateNormalization(node, 'html', true, ctx)) return 'fragment';
20670
+ if (node.type !== 'Element' && node.type !== 'JSXElement') {
20671
+ return jsxValueChildrenNeedRenderScope(node, false, stableNames) ? 'value' : null;
20672
+ }
20673
+ if (isComponentTag(node)) return 'value';
20674
+ return jsxValueRootNeedsRenderScope(node, stableNames) ||
20675
+ jsxValueChildrenNeedRenderScope(node, false, stableNames)
20676
+ ? 'fragment'
20677
+ : null;
20678
+ }
20679
+
20680
+ // Whether the client's body-call record evaluates anything a direct call must
20681
+ // defer. The client record only stores hole values and interprets them where
20682
+ // it mounts, so reading an unreassigned local is the same during the call and
20683
+ // at render. Value-lowered roots already defer, except a component's own props.
20684
+ function directCallNeedsClientBranch(node, fn, ctx) {
20685
+ const stable = directCallStableNames(fn);
20686
+ const kind = directCallReturnKind(node, ctx, stable);
20687
+ if (kind === 'fragment') return true;
20688
+ return (
20689
+ kind === 'value' &&
20690
+ (node.type === 'Element' || node.type === 'JSXElement') &&
20691
+ jsxValueRootNeedsRenderScope(node, stable)
20692
+ );
20693
+ }
20694
+
20695
+ // Names that code anywhere in the function assigns after declaring them.
20696
+ const DIRECT_CALL_ASSIGNED_NAMES = new WeakMap();
20697
+ function directCallAssignedNames(fn) {
20698
+ const cached = DIRECT_CALL_ASSIGNED_NAMES.get(fn);
20699
+ if (cached !== undefined) return cached;
20700
+ const assigned = new Set();
20701
+ const target = (node) => {
20702
+ if (node?.type === 'Identifier') assigned.add(node.name);
20703
+ else if (node != null && typeof node === 'object') collectBindings(node, assigned);
20704
+ };
20705
+ mapAst(fn.body, (node) => {
20706
+ if (node.type === 'AssignmentExpression') target(node.left);
20707
+ else if (node.type === 'UpdateExpression') target(node.argument);
20708
+ else if (
20709
+ (node.type === 'ForInStatement' || node.type === 'ForOfStatement') &&
20710
+ node.left?.type !== 'VariableDeclaration'
20711
+ ) {
20712
+ target(node.left);
20713
+ }
20714
+ return null;
20715
+ });
20716
+ DIRECT_CALL_ASSIGNED_NAMES.set(fn, assigned);
20717
+ return assigned;
20718
+ }
20719
+
20720
+ // Parameters and top-level declarations that nothing in the function assigns.
20721
+ // Reading one runs no user code and yields the same value during the call and
20722
+ // at render, so the client record needs no deferral for it.
20723
+ function directCallStableNames(fn) {
20724
+ const names = collectComponentLocals(fn);
20725
+ for (const name of directCallAssignedNames(fn)) names.delete(name);
20726
+ return names;
20727
+ }
20728
+
20729
+ // A direct call's deferred value reads the function's locals after the call has
20730
+ // returned. Declaring its closure inside the function would make those locals
20731
+ // context slots, allocating a context on every call, body calls included, so
20732
+ // the value is built by a module function that receives them as arguments.
20733
+ // Returns those arguments, or null when a copy could differ from the live
20734
+ // binding: a captured authored local that the function reassigns, or a read of
20735
+ // `this` or `arguments`.
20736
+ function directCallCaptures(fn, scopeNames, statements, value) {
20737
+ const body = b.block([...statements, b.return(value)]);
20738
+ let contextual = false;
20739
+ mapAst(body, (node) => {
20740
+ if (
20741
+ node.type === 'ThisExpression' ||
20742
+ node.type === 'Super' ||
20743
+ node.type === 'MetaProperty' ||
20744
+ (node.type === 'Identifier' && node.name === 'arguments')
20745
+ ) {
20746
+ contextual = true;
20747
+ }
20748
+ return contextual ? node : null;
20749
+ });
20750
+ if (contextual) return null;
20751
+ const captures = [...collectFreeIdentifiers(body, new Set())]
20752
+ .filter((identifier) => scopeNames.has(identifier))
20753
+ .sort();
20754
+ const assigned = directCallAssignedNames(fn);
20755
+ return captures.some((identifier) => assigned.has(identifier)) ? null : captures;
20756
+ }
20757
+
20758
+ // Declare `function helper(captures) { ...statements; return value; }` among the
20759
+ // module's hoisted helpers and return a call to it.
20760
+ function hoistCapturingHelper(ctx, preferred, captures, statements, value, origin) {
20761
+ const helper = allocCompilerName(ctx, preferred);
20762
+ const ids = () => captures.map((identifier) => inheritOriginLoc(b.id(identifier), origin));
20763
+ ctx.hoistedHelpers.push(
20764
+ inheritOriginLoc(
20765
+ b.function_declaration(b.id(helper), ids(), b.block([...statements, b.return(value)])),
20766
+ origin,
20767
+ ),
20768
+ );
20769
+ return inheritOriginLoc(b.call(helper, ...ids()), origin);
20770
+ }
20771
+
20772
+ // The client's returned record, split between a body call and a direct call.
20773
+ // The record is printed once, in a module function the body call invokes; a
20774
+ // direct call defers that same function until the value renders.
20775
+ function splitReturnedRecord(ctx, fn, name, scopeNames, { record, test, origin }) {
20776
+ const captures = directCallCaptures(fn, scopeNames, [], record);
20777
+ if (captures === null) {
20778
+ ctx.runtimeNeeded.add('createScopedValue');
20779
+ const direct = b.call(rtAlias('createScopedValue'), b.arrow([], cloneAstNode(record)));
20780
+ return inheritOriginLoc(b.conditional(test, record, inheritOriginLoc(direct, origin)), origin);
20781
+ }
20782
+ const read = hoistCapturingHelper(ctx, `${name}$record`, captures, [], record, origin);
20783
+ const direct = b.call(
20784
+ requireRuntimeForContext(ctx, 'deferRecord'),
20785
+ b.id(read.callee.name),
20786
+ ...read.arguments.map((argument) => cloneAstNode(argument)),
20787
+ );
20788
+ return inheritOriginLoc(b.conditional(test, read, inheritOriginLoc(direct, origin)), origin);
20789
+ }
20790
+
20791
+ // Names an emitted function body binds in its own scope: parameters, top-level
20792
+ // declarations, and `var` declarations in nested blocks.
20793
+ function collectFunctionScopeBindings(params, statements, into) {
20794
+ for (const param of params) collectBindings(param, into);
20795
+ const visit = (statement, topLevel) => {
20796
+ if (statement == null || typeof statement !== 'object') return;
20797
+ switch (statement.type) {
20798
+ case 'VariableDeclaration':
20799
+ if (topLevel || statement.kind === 'var') {
20800
+ for (const declaration of statement.declarations || []) {
20801
+ collectBindings(declaration.id, into);
20802
+ }
20803
+ }
20804
+ return;
20805
+ case 'FunctionDeclaration':
20806
+ case 'ClassDeclaration':
20807
+ if (topLevel && statement.id) into.add(statement.id.name);
20808
+ return;
20809
+ case 'BlockStatement':
20810
+ for (const child of statement.body || []) visit(child, false);
20811
+ return;
20812
+ case 'IfStatement':
20813
+ visit(statement.consequent, false);
20814
+ visit(statement.alternate, false);
20815
+ return;
20816
+ case 'ForStatement':
20817
+ visit(statement.init, false);
20818
+ visit(statement.body, false);
20819
+ return;
20820
+ case 'ForInStatement':
20821
+ case 'ForOfStatement':
20822
+ visit(statement.left, false);
20823
+ visit(statement.body, false);
20824
+ return;
20825
+ case 'WhileStatement':
20826
+ case 'DoWhileStatement':
20827
+ case 'LabeledStatement':
20828
+ visit(statement.body, false);
20829
+ return;
20830
+ case 'TryStatement':
20831
+ visit(statement.block, false);
20832
+ visit(statement.handler?.body, false);
20833
+ visit(statement.finalizer, false);
20834
+ return;
20835
+ case 'SwitchStatement':
20836
+ for (const clause of statement.cases || []) {
20837
+ for (const child of clause.consequent || []) visit(child, false);
20838
+ }
20839
+ return;
20840
+ default:
20841
+ return;
20842
+ }
20843
+ };
20844
+ for (const statement of statements) visit(statement, true);
20845
+ return into;
20846
+ }
20847
+
20848
+ // Argument 1 of a function whose own parameters already reach it: the second
20849
+ // parameter when the function never reassigns it, `arguments[1]` otherwise.
20850
+ function secondArgument(fn) {
20851
+ const params = fn.params || [];
20852
+ const second = params[1]?.type === 'AssignmentPattern' ? params[1].left : params[1];
20853
+ return second?.type === 'Identifier' &&
20854
+ params[0]?.type !== 'RestElement' &&
20855
+ !(params[0]?.type === 'Identifier' && params[0].name === 'this') &&
20856
+ !directCallAssignedNames(fn).has(second.name)
20857
+ ? b.id(second.name)
20858
+ : b.member(b.id('arguments'), b.literal(1), true);
20859
+ }
20860
+
20861
+ // The argument a body call receives its Scope in. A spare default parameter
20862
+ // keeps the function's `length` and avoids an `arguments` object; functions
20863
+ // that cannot take one read `arguments[1]`.
20864
+ function renderCallScopeParam(node, ctx) {
20865
+ const params = node.params || [];
20866
+ const statements = node.body?.body || [];
20867
+ const strictBody = statements.some(
20868
+ (statement) => statement.type === 'ExpressionStatement' && statement.directive === 'use strict',
20869
+ );
20870
+ if (
20871
+ params.length > 1 ||
20872
+ strictBody ||
20873
+ params.some(
20874
+ (param) =>
20875
+ param.type === 'RestElement' || (param.type === 'Identifier' && param.name === 'this'),
20876
+ )
20877
+ ) {
20878
+ return { params, scope: inheritOriginLoc(secondArgument(node), node) };
20879
+ }
20880
+ const scope = inheritOriginLoc(b.id(allocCompilerName(ctx, '__renderScope')), node);
20881
+ const spare = (identifier) => inheritOriginLoc(b.assignment_pattern(identifier, b.void0), node);
20882
+ return {
20883
+ params:
20884
+ params.length === 0
20885
+ ? [spare(inheritOriginLoc(b.id(allocCompilerName(ctx, '__props')), node)), spare(scope)]
20886
+ : [...params, spare(scope)],
20887
+ scope,
20888
+ };
20889
+ }
20890
+
20535
20891
  // Lower JSX at return position. Host roots always use the compiled-fragment
20536
20892
  // path. Any other root whose subtree contains template-only syntax uses it too.
20537
20893
  // Ordinary component/Fragment values retain descriptor lowering and its identity
@@ -22096,18 +22452,19 @@ function jsxNameToExpr(name) {
22096
22452
  // hit the TDZ, while a member, coercion, spread, or computed key can invoke
22097
22453
  // user code without containing a call. Function expressions are values here;
22098
22454
  // their bodies still run only when an event/render-prop consumer invokes them.
22099
- function isStaticJsxValueExpression(expression) {
22455
+ function isStaticJsxValueExpression(expression, stableNames = null) {
22100
22456
  const value = unwrapTsExpr(expression);
22101
22457
  return (
22102
22458
  isInvariantLiteral(value) ||
22103
22459
  value?.type === 'ArrowFunctionExpression' ||
22104
- value?.type === 'FunctionExpression'
22460
+ value?.type === 'FunctionExpression' ||
22461
+ (stableNames !== null && value?.type === 'Identifier' && stableNames.has(value.name))
22105
22462
  );
22106
22463
  }
22107
22464
 
22108
22465
  // A root member tag, spread, or dynamic prop can invoke user code before its
22109
22466
  // descriptor exists. Defer the complete record rather than just its children.
22110
- function jsxValueRootNeedsRenderScope(node) {
22467
+ function jsxValueRootNeedsRenderScope(node, stableNames = null) {
22111
22468
  const name = node.openingElement?.name || node.id;
22112
22469
  if (
22113
22470
  name?.type === 'MemberExpression' ||
@@ -22120,7 +22477,7 @@ function jsxValueRootNeedsRenderScope(node) {
22120
22477
  if (attr.type === 'SpreadAttribute' || attr.type === 'JSXSpreadAttribute') return true;
22121
22478
  if (attr.type !== 'Attribute' && attr.type !== 'JSXAttribute') continue;
22122
22479
  if (attr.value?.type !== 'JSXExpressionContainer') continue;
22123
- if (!isStaticJsxValueExpression(attr.value.expression)) return true;
22480
+ if (!isStaticJsxValueExpression(attr.value.expression, stableNames)) return true;
22124
22481
  }
22125
22482
  return false;
22126
22483
  }
@@ -22129,21 +22486,27 @@ function jsxValueRootNeedsRenderScope(node) {
22129
22486
  // syntax is effectful. Nested component/tag resolution and dynamic attributes
22130
22487
  // belong to their represented parent, so they make that parent's children lazy
22131
22488
  // even when the nested element has no expression children of its own.
22132
- function jsxValueChildrenNeedRenderScope(node, descendantElementsOwnChildren = false) {
22489
+ function jsxValueChildrenNeedRenderScope(
22490
+ node,
22491
+ descendantElementsOwnChildren = false,
22492
+ stableNames = null,
22493
+ ) {
22133
22494
  for (const child of node.children || []) {
22134
22495
  if (child == null || child.type === 'JSXText' || child.type === 'Text') continue;
22135
22496
  if (child.type === 'JSXExpressionContainer') {
22136
22497
  if (
22137
22498
  child.expression &&
22138
22499
  child.expression.type !== 'JSXEmptyExpression' &&
22139
- !isStaticJsxValueExpression(child.expression)
22500
+ !isStaticJsxValueExpression(child.expression, stableNames)
22140
22501
  ) {
22141
22502
  return true;
22142
22503
  }
22143
22504
  continue;
22144
22505
  }
22145
22506
  if (child.type === 'Fragment' || child.type === 'JSXFragment') {
22146
- if (jsxValueChildrenNeedRenderScope(child, descendantElementsOwnChildren)) return true;
22507
+ if (jsxValueChildrenNeedRenderScope(child, descendantElementsOwnChildren, stableNames)) {
22508
+ return true;
22509
+ }
22147
22510
  continue;
22148
22511
  }
22149
22512
  if (child.type !== 'Element' && child.type !== 'JSXElement') return true;
@@ -22152,11 +22515,16 @@ function jsxValueChildrenNeedRenderScope(node, descendantElementsOwnChildren = f
22152
22515
  if (attr.type === 'SpreadAttribute' || attr.type === 'JSXSpreadAttribute') return true;
22153
22516
  if (attr.type !== 'Attribute' && attr.type !== 'JSXAttribute') return true;
22154
22517
  if (attr.value?.type !== 'JSXExpressionContainer') continue;
22155
- if (!isStaticJsxValueExpression(attr.value.expression)) return true;
22518
+ if (!isStaticJsxValueExpression(attr.value.expression, stableNames)) return true;
22156
22519
  }
22157
22520
  // A fragment can retain its inspectable positional array when only a nested
22158
22521
  // element's descendants are dynamic: that element already owns their scope.
22159
- if (!descendantElementsOwnChildren && jsxValueChildrenNeedRenderScope(child)) return true;
22522
+ if (
22523
+ !descendantElementsOwnChildren &&
22524
+ jsxValueChildrenNeedRenderScope(child, false, stableNames)
22525
+ ) {
22526
+ return true;
22527
+ }
22160
22528
  }
22161
22529
  return false;
22162
22530
  }