octane 0.2.13 → 0.2.15

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 (36) hide show
  1. package/dist/cjs/constants.cjs +7 -7
  2. package/dist/cjs/dom-binding-claims.cjs +28 -0
  3. package/dist/cjs/dom-binding-handoff.cjs +2 -2
  4. package/dist/cjs/dom-bindings.cjs +105 -13
  5. package/dist/cjs/runtime.cjs +311 -55
  6. package/dist/cjs/server/streamed-signals.cjs +3 -2
  7. package/dist/cjs/signals/control-binding.cjs +33 -9
  8. package/dist/cjs/signals/control-handoff.cjs +39 -0
  9. package/dist/cjs/stream-protocol.cjs +6 -0
  10. package/dist/cjs/version.cjs +1 -1
  11. package/dist/compiler/compile.js +86 -24
  12. package/dist/compiler/dom-binding-program.js +30 -3
  13. package/dist/compiler/dom-bindings.js +115 -34
  14. package/dist/compiler/volar.js +58 -17
  15. package/dist/constants.d.ts +2 -2
  16. package/dist/constants.js +2 -2
  17. package/dist/dom-binding-claims.d.ts +1 -0
  18. package/dist/dom-binding-claims.js +4 -0
  19. package/dist/dom-binding-handoff.d.ts +6 -4
  20. package/dist/dom-binding-handoff.js +1 -1
  21. package/dist/dom-binding-program.d.ts +2 -1
  22. package/dist/dom-binding-program.js +16 -1
  23. package/dist/dom-bindings.d.ts +5 -2
  24. package/dist/dom-bindings.js +105 -13
  25. package/dist/runtime.d.ts +34 -3
  26. package/dist/runtime.js +314 -55
  27. package/dist/server/streamed-signals.js +3 -2
  28. package/dist/signals/control-binding.d.ts +9 -5
  29. package/dist/signals/control-binding.js +38 -6
  30. package/dist/signals/control-handoff.d.ts +26 -0
  31. package/dist/signals/control-handoff.js +12 -0
  32. package/dist/signals/index.d.ts +1 -0
  33. package/dist/stream-protocol.d.ts +4 -0
  34. package/dist/stream-protocol.js +4 -0
  35. package/dist/version.js +1 -1
  36. package/package.json +1 -1
package/dist/runtime.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { resolveHookPath } from "./hook-slot-cache.js";
2
+ import { domBindingClaims } from "./dom-binding-claims.js";
2
3
  import { DOMStage } from "./dom-stage.js";
3
4
  import { __normalizeBindingStyle } from "./dom-binding-styles.js";
4
5
  import {
@@ -9,6 +10,10 @@ import {
9
10
  releaseBindingHandoff
10
11
  } from "./dom-binding-handoff.js";
11
12
  import { bumpContextEpoch, contextEpochNow } from "./context-epoch.js";
13
+ import {
14
+ CONTROL_HANDOFF,
15
+ hasSignalControlBinding
16
+ } from "./signals/control-handoff.js";
12
17
  import {
13
18
  SUSPENSE_SCRIPT_ATTR,
14
19
  SUSPENSE_RESOLVED_COMMENT,
@@ -2257,7 +2262,9 @@ function rollbackRootRender(transaction) {
2257
2262
  }
2258
2263
  function retireDetachedBindingLeases(owner) {
2259
2264
  for (const lease of owner.bindingLeases) {
2260
- if (owner.bindingContainer.contains(lease.anchor)) continue;
2265
+ const preparation = lease.host !== void 0 && !lease.active() ? PRESENTATION_PREPARATIONS.get(lease) : void 0;
2266
+ if (preparation !== void 0 ? !preparation.scope.block.disposed : owner.bindingContainer.contains(lease.anchor))
2267
+ continue;
2261
2268
  owner.bindingLeases.delete(lease);
2262
2269
  PRESENTATION_PREPARATIONS.delete(lease);
2263
2270
  releaseBindingHandoff(lease);
@@ -6292,7 +6299,8 @@ function nativeEffectPublicationCurrent(entry, slot) {
6292
6299
  if (ROOT_RENDER_TRANSACTION !== null) journalObjectOnce(slot);
6293
6300
  slot.deps = void 0;
6294
6301
  slot.active = slot.connectedFn !== null;
6295
- invalidateRender(entry.scope.block, entry.scope.block);
6302
+ if (!entry.scope.block.idState.renderOwner?.disposed && !blockSubtreeDisposed(entry.scope.block))
6303
+ scheduleNativeRead(entry.scope.block);
6296
6304
  return false;
6297
6305
  }
6298
6306
  function drainMutationEffects() {
@@ -8662,6 +8670,7 @@ function releasePreservedHydrateActivation(state) {
8662
8670
  function pendingHydrateOwner(target) {
8663
8671
  let owner = null;
8664
8672
  for (let block = target; block !== null; block = block.parentBlock) {
8673
+ if (block.block !== block) continue;
8665
8674
  const state = block.slots[0];
8666
8675
  if (state?.__kind === "hydrateBlockSlot" && !state.hydrated && preservedHydrateActivations?.has(state))
8667
8676
  owner = state;
@@ -11409,7 +11418,7 @@ class HydrationCapability {
11409
11418
  const first = getFirstChild(el);
11410
11419
  if (first !== null && first.nodeType === 3) {
11411
11420
  const server = (STAGED_DOM?.view(first) ?? first).nodeValue;
11412
- if (server !== text && !isTextParserNormalizedMatch(server, text) && !isHydrationSuppressed(el)) {
11421
+ if (server !== text && domBindingClaims.get(el)?.get("#text") !== server && !isTextParserNormalizedMatch(server, text) && !isHydrationSuppressed(el)) {
11413
11422
  this.recordTextMismatch(first, loc || el.__oct_loc, server);
11414
11423
  (STAGED_DOM?.view(first) ?? first).nodeValue = text;
11415
11424
  }
@@ -11494,6 +11503,7 @@ class HydrationCapability {
11494
11503
  name.indexOf(":") >= 0 ? name.slice(name.indexOf(":") + 1) : name
11495
11504
  ) : (STAGED_DOM?.view(el) ?? el).getAttribute(name);
11496
11505
  if (server === next) return false;
11506
+ if (domBindingClaims.get(el)?.get(name) === server) return false;
11497
11507
  if (next !== null && isAttributeParserNormalizedMatch(server, next)) return false;
11498
11508
  const mode = hydrationMismatchMode(el);
11499
11509
  if (mode === 0) return true;
@@ -11504,10 +11514,11 @@ class HydrationCapability {
11504
11514
  }
11505
11515
  allowClass(el, next, absentIsEmpty = false) {
11506
11516
  const mode = hydrationMismatchMode(el);
11507
- if (mode === 0) return true;
11508
11517
  const rawServer = (STAGED_DOM?.view(el) ?? el).getAttribute("class");
11509
11518
  const server = absentIsEmpty && rawServer === null ? "" : rawServer;
11510
11519
  if (server === next) return true;
11520
+ if (domBindingClaims.get(el)?.get("class") === rawServer) return false;
11521
+ if (mode === 0) return true;
11511
11522
  if (mode === 1) return false;
11512
11523
  if (process.env.NODE_ENV !== "production")
11513
11524
  warnHydrationValueMismatch(el.__oct_loc, "attribute `class`", server, next);
@@ -11549,6 +11560,18 @@ class HydrationCapability {
11549
11560
  applyStyleProperty(el, expectedStyle, entries[i], entry);
11550
11561
  }
11551
11562
  }
11563
+ const claims = domBindingClaims.get(el);
11564
+ if (claims !== void 0) {
11565
+ for (const [channel, published] of claims) {
11566
+ if (!channel.startsWith("style:")) continue;
11567
+ const property = channel.slice(6);
11568
+ const value2 = style.getPropertyValue(property);
11569
+ const priority = style.getPropertyPriority(property);
11570
+ if ((value2 === "" ? null : value2 + (priority ? " !important" : "")) !== published) continue;
11571
+ if (value2 === "") expectedStyle.removeProperty(property);
11572
+ else expectedStyle.setProperty(property, value2, priority);
11573
+ }
11574
+ }
11552
11575
  const expected = expectedStyle.cssText;
11553
11576
  const expectsStyleAttribute = expected !== "";
11554
11577
  if (before === expected && hadStyleAttribute === expectsStyleAttribute) return true;
@@ -11856,7 +11879,7 @@ function isAdoptionControl(error) {
11856
11879
  }
11857
11880
  function presentationFailure(error) {
11858
11881
  const frame = PRESENTATION_HYDRATION;
11859
- if (frame?.revision !== void 0 && !isSuspenseException(error) && !isAdoptionControl(error)) {
11882
+ if (frame !== null && !isSuspenseException(error) && !isAdoptionControl(error)) {
11860
11883
  const failure = new PresentationAdoptionMiss(frame.lease, false);
11861
11884
  failure.failure = error;
11862
11885
  throw failure;
@@ -11867,12 +11890,25 @@ function presentationMiss(retry = true) {
11867
11890
  throw new PresentationAdoptionMiss(PRESENTATION_HYDRATION?.lease, retry);
11868
11891
  }
11869
11892
  function currentPresentation(frame) {
11870
- if (frame.revision === void 0) return true;
11893
+ if (frame.controls !== void 0 && [...frame.controls.values()].some(({ valid }) => !valid()))
11894
+ return false;
11895
+ if (frame.revision === void 0 && frame.controls === void 0) return true;
11871
11896
  const lease = frame.lease;
11872
- return lease.active() && lease.valid?.() !== false && lease.revision?.() === frame.revision && lease.root.parentNode !== null && lease.end?.parentNode === lease.root.parentNode && [...frame.witnesses.values()].every(
11897
+ return lease.active() && lease.valid?.() !== false && lease.revision?.() === frame.revision && lease.root.parentNode !== null && (lease.host === void 0 ? lease.end?.parentNode === lease.root.parentNode : lease.owner === frame.scope.block.idState.renderOwner && frame.scope.block.idState.renderOwner.bindingContainer.contains(lease.root)) && [...frame.witnesses.values()].every(
11873
11898
  (witness) => witness === null || validateNativeReadWitness(witness)
11874
11899
  );
11875
11900
  }
11901
+ function trackPresentationFrame(frame) {
11902
+ if (frame.current !== void 0) return;
11903
+ frame.current = currentPresentation;
11904
+ (WIP_CAPTURE.presentations ??= []).push(frame);
11905
+ if (frame.hydration === null) return;
11906
+ frame.hydration.presentation = true;
11907
+ for (let block = frame.scope.block; block !== null; block = block.parentBlock) {
11908
+ preserveRootCreatedDom(block);
11909
+ if (block === frame.hydration.rootBlock) break;
11910
+ }
11911
+ }
11876
11912
  function currentPresentations(capture) {
11877
11913
  return capture.presentations?.every((frame) => frame.current(frame)) !== false;
11878
11914
  }
@@ -11908,7 +11944,7 @@ function hasClosedPresentationView(lease, scope, id) {
11908
11944
  return false;
11909
11945
  }
11910
11946
  }
11911
- function beginPresentationHydration(scope, id, supported = false, structural = false, conditionalRest = false) {
11947
+ function beginPresentationHydration(scope, id, supported = false, structural = false, conditionalRest = false, host = false) {
11912
11948
  const owner = scope.block.idState.renderOwner;
11913
11949
  if (PRESENTATION_HYDRATION !== null && PRESENTATION_HYDRATION.scope.block.idState.renderOwner === owner) {
11914
11950
  if (conditionalRest) {
@@ -11920,15 +11956,17 @@ function beginPresentationHydration(scope, id, supported = false, structural = f
11920
11956
  if (owner?.bindingLeases === void 0 || WIP_CAPTURE === null) return null;
11921
11957
  const hydration = activeHydration();
11922
11958
  const lease = [...owner.bindingLeases].find(
11923
- (candidate) => candidate.id === id && (candidate.root === scope.block.startMarker || candidate.root === hydration?.node || candidate.root.nodeType === 8 && candidate.root.nextSibling === hydration?.node)
11959
+ (candidate) => candidate.id === id && (candidate.root === scope.block.startMarker || candidate.root === hydration?.node || candidate.root.nodeType === 8 && candidate.root.nextSibling === hydration?.node || candidate.host !== void 0 && PRESENTATION_PREPARATIONS.get(candidate)?.scope === scope)
11924
11960
  );
11925
11961
  if (lease === void 0) return null;
11962
+ if (!lease.active()) throw new PresentationAdoptionMiss(lease, false);
11963
+ if (host !== (lease.host !== void 0)) throw new PresentationAdoptionMiss(lease, false);
11926
11964
  if (conditionalRest ? !supported || !structural || !hasClosedPresentationView(lease, scope, id) : !supported)
11927
11965
  throw new PresentationAdoptionMiss(lease, false);
11928
11966
  const revision = lease.revision?.();
11929
- if (revision !== void 0 && revision >= 0 && lease.valid?.() === false)
11967
+ if ((revision === void 0 || revision >= 0) && lease.valid?.() === false)
11930
11968
  throw new PresentationAdoptionMiss(lease, false);
11931
- if (revision !== void 0 && (!structural || revision < 0))
11969
+ if (revision !== void 0 && (!structural && !host || revision < 0))
11932
11970
  throw new PresentationAdoptionMiss(lease, revision < 0, revision);
11933
11971
  const pending = PRESENTATION_PREPARATIONS.get(lease);
11934
11972
  const frame = {
@@ -11941,42 +11979,95 @@ function beginPresentationHydration(scope, id, supported = false, structural = f
11941
11979
  readToken: beginNativeReadScope(scope),
11942
11980
  witnessToken: beginNativeReadWitness(),
11943
11981
  witnesses: pending?.scope === scope ? new Map(pending.witnesses) : /* @__PURE__ */ new Map(),
11982
+ ...lease.host === void 0 ? {} : { hostSuccessors: new Map(pending?.scope === scope ? pending.hostSuccessors : void 0) },
11983
+ ...pending?.scope === scope && pending.controls !== void 0 ? { controls: new Map(pending.controls) } : {},
11944
11984
  completed: false,
11945
11985
  lease,
11946
- ...revision === void 0 ? {} : { revision, current: currentPresentation },
11986
+ ...revision === void 0 ? {} : { revision },
11947
11987
  hydration
11948
11988
  };
11949
11989
  PRESENTATION_HYDRATION = frame;
11950
- if (revision !== void 0 && hydration !== null) {
11951
- hydration.presentation = true;
11952
- for (let block = scope.block; block !== null; block = block.parentBlock) {
11953
- preserveRootCreatedDom(block);
11954
- if (block === hydration.rootBlock) break;
11955
- }
11956
- }
11990
+ if (revision !== void 0 || frame.controls !== void 0) trackPresentationFrame(frame);
11957
11991
  PRESENTATION_PREPARATIONS.set(lease, frame);
11958
- if (revision !== void 0) (WIP_CAPTURE.presentations ??= []).push(frame);
11992
+ const hostRefs = host ? WIP_CAPTURE.refs : void 0;
11959
11993
  (WIP_CAPTURE.renderCleanups ??= []).push((discarded) => {
11960
11994
  if (discarded || !frame.completed || owner.disposed || scope.block.disposed || PRESENTATION_PREPARATIONS.get(lease) !== frame)
11961
11995
  return;
11962
11996
  if (!currentPresentation(frame)) {
11963
- frame.hydration?.retryPresentation?.();
11997
+ if (frame.hydration?.retryPresentation !== void 0) frame.hydration.retryPresentation();
11998
+ else throw new PresentationAdoptionMiss(lease, false);
11964
11999
  return;
11965
12000
  }
11966
12001
  PRESENTATION_PREPARATIONS.delete(lease);
11967
12002
  owner.bindingLeases.delete(lease);
11968
12003
  releaseBindingHandoff(lease);
12004
+ let publicationStarted = false;
11969
12005
  let published = false;
12006
+ let hostFailed = false;
12007
+ let hostFailure;
11970
12008
  try {
11971
12009
  lease.retire(() => {
11972
- for (const writes of frame.writes.values()) for (const write of writes.values()) write();
12010
+ publicationStarted = true;
12011
+ try {
12012
+ for (const writes of frame.writes.values()) for (const write of writes.values()) write();
12013
+ } catch (error) {
12014
+ for (const stop of frame.hostSuccessors?.values() ?? []) {
12015
+ try {
12016
+ stop();
12017
+ } catch {
12018
+ }
12019
+ }
12020
+ frame.hostSuccessors?.clear();
12021
+ for (const [element, control] of frame.controls ?? []) {
12022
+ const binding = control.binding;
12023
+ if (binding.disposed && control.valid(true)) continue;
12024
+ (owner.controlLeases ??= /* @__PURE__ */ new Map()).set(element, control.lease);
12025
+ if (binding.disposed) continue;
12026
+ if (binding.input !== void 0) renderControlledState(element).v = UNCONTROLLED;
12027
+ control.stop = binding.unsubscribe;
12028
+ binding.unsubscribe = void 0;
12029
+ disposeDirectSignalBinding(binding);
12030
+ }
12031
+ for (const control of frame.controls?.values() ?? []) {
12032
+ try {
12033
+ control.stop?.();
12034
+ } catch {
12035
+ }
12036
+ }
12037
+ throw error;
12038
+ }
11973
12039
  published = true;
11974
12040
  });
11975
12041
  } catch (error) {
11976
- if (!published) throw error;
11977
- reportRendererOwnerError(scope, error);
12042
+ if (!publicationStarted) throw error;
12043
+ if (frame.hostSuccessors !== void 0) {
12044
+ hostFailed = true;
12045
+ hostFailure = error;
12046
+ } else reportRendererOwnerError(scope, error);
11978
12047
  } finally {
12048
+ if (frame.hostSuccessors !== void 0 && (!published || lease.valid?.() === false)) {
12049
+ published = false;
12050
+ owner.bindingLeases.add(lease);
12051
+ PRESENTATION_PREPARATIONS.set(lease, frame);
12052
+ for (const entry of hostRefs) if (entry.el === lease.root) entry.ref = null;
12053
+ for (const stop of frame.hostSuccessors.values()) {
12054
+ try {
12055
+ stop();
12056
+ } catch (error) {
12057
+ if (!hostFailed) {
12058
+ hostFailed = true;
12059
+ hostFailure = error;
12060
+ }
12061
+ }
12062
+ }
12063
+ if (!hostFailed) {
12064
+ hostFailed = true;
12065
+ hostFailure = new Error(formatClientError(77));
12066
+ }
12067
+ }
12068
+ frame.hostSuccessors?.clear();
11979
12069
  frame.writes.clear();
12070
+ frame.controls?.clear();
11980
12071
  if (published && !owner.disposed && owner.current !== null && [...frame.witnesses.values()].some(
11981
12072
  (witness) => witness !== null && !validateNativeReadWitness(witness)
11982
12073
  )) {
@@ -11986,6 +12077,7 @@ function beginPresentationHydration(scope, id, supported = false, structural = f
11986
12077
  }
11987
12078
  frame.witnesses.clear();
11988
12079
  }
12080
+ if (hostFailed) reportRendererOwnerError(scope, hostFailure);
11989
12081
  });
11990
12082
  return frame;
11991
12083
  }
@@ -12004,13 +12096,33 @@ function preparedPresentationAttribute(el, name, value, kind) {
12004
12096
  const result = coerceAttrValue(el, name, value);
12005
12097
  return result === "" && BOOLEAN_ATTR_PROPS.has(name.toLowerCase()) ? true : result;
12006
12098
  }
12007
- function preparePresentationSignalAttribute(args, frame) {
12008
- const [scope, previous, element, name, value, site, attributeKind = "attr"] = args;
12099
+ function preparePresentationSignalBinding(args, frame, text = false) {
12100
+ const [scope, previous, element] = args;
12101
+ const name = text ? void 0 : args[3];
12102
+ const value = args[text ? 3 : 4];
12103
+ const site = args[text ? 4 : 5];
12104
+ const attributeKind = text ? void 0 : args[6] ?? "attr";
12105
+ const kind = text ? args[5] ? "textOnlyChild" : "text" : "attribute";
12009
12106
  return runWithBlockSignalOwner(scope, () => {
12010
12107
  const handle = isSignalHandle(value) ? value : null;
12011
- const current = handle === null ? value : handle.get();
12012
- const prepared = preparedPresentationAttribute(element, name, current, attributeKind);
12108
+ const witness = handle !== null && frame.lease.host !== void 0 ? beginNativeReadWitness(true) : -1;
12109
+ let current;
12110
+ let readCompleted = false;
12111
+ try {
12112
+ current = handle === null ? value : handle.get();
12113
+ readCompleted = true;
12114
+ } finally {
12115
+ if (witness >= 0)
12116
+ frame.witnesses.set(handle, finishNativeReadWitness(witness, readCompleted));
12117
+ }
12118
+ if (text && (current !== null && (typeof current === "object" || typeof current === "function") || typeof args[7] !== "string"))
12119
+ presentationMiss(false);
12120
+ const prepared = text ? bindingText(element, current, args[7]) : preparedPresentationAttribute(element, name, current, attributeKind);
12013
12121
  if (handle === null) {
12122
+ if (text) {
12123
+ frame.writes.get(element)?.delete("signalText");
12124
+ return prepared;
12125
+ }
12014
12126
  preparePresentationOperation(
12015
12127
  frame,
12016
12128
  element,
@@ -12020,12 +12132,12 @@ function preparePresentationSignalAttribute(args, frame) {
12020
12132
  return value;
12021
12133
  }
12022
12134
  const prior = previous;
12023
- const reusable = prior?.[DIRECT_SIGNAL_BINDING] === true && !prior.disposed && prior.unsubscribe === void 0 && prior.handle === handle && prior.target === element && prior.site === site && prior.name === name && prior.attributeKind === attributeKind;
12135
+ const reusable = prior?.[DIRECT_SIGNAL_BINDING] === true && !prior.disposed && prior.unsubscribe === void 0 && prior.handle === handle && prior.target === element && prior.kind === kind && prior.site === site && prior.name === name && prior.attributeKind === attributeKind;
12024
12136
  const binding = reusable ? prior : {
12025
12137
  [DIRECT_SIGNAL_BINDING]: true,
12026
12138
  scope,
12027
12139
  target: element,
12028
- kind: "attribute",
12140
+ kind,
12029
12141
  site,
12030
12142
  name,
12031
12143
  attributeKind,
@@ -12033,16 +12145,114 @@ function preparePresentationSignalAttribute(args, frame) {
12033
12145
  value: current,
12034
12146
  disposed: false
12035
12147
  };
12148
+ if (text) binding.text = prepared;
12036
12149
  if (!reusable) registerHookCleanup(scope, () => disposeDirectSignalBinding(binding));
12037
- preparePresentationOperation(frame, element, name, () => {
12150
+ frame.hostSuccessors?.set(binding, () => disposeDirectSignalBinding(binding));
12151
+ preparePresentationOperation(frame, element, name ?? "signalText", () => {
12038
12152
  if (binding.disposed) return;
12039
- writeDirectSignalScalar(element, prepared, "attribute", previous, name, attributeKind);
12153
+ if (!text)
12154
+ writeDirectSignalScalar(element, prepared, "attribute", previous, name, attributeKind);
12040
12155
  binding.value = current;
12041
12156
  runWithBlockSignalOwner(scope, () => activateDirectSignalBinding(binding, void 0));
12042
12157
  });
12043
12158
  return binding;
12044
12159
  });
12045
12160
  }
12161
+ function preparePresentationSignalValue(args, frame) {
12162
+ const [scope, previous, element, handle, site] = args;
12163
+ return runWithBlockSignalOwner(scope, () => {
12164
+ if (element.localName !== "textarea" || element.namespaceURI !== HTML_NS || !isWritableSignal(handle))
12165
+ presentationMiss(false);
12166
+ validateDirectSignalControl(element, site);
12167
+ const value = handle.get();
12168
+ if (typeof value !== "string") presentationMiss(false);
12169
+ const owner = scope.block.idState.renderOwner;
12170
+ const lease = owner.controlLeases?.get(element);
12171
+ const valid = (retired = false) => owner.bindingContainer.contains(element) && (!retired || frame.lease.valid?.() !== false) && (lease !== void 0 && !retired ? lease.owner === owner && lease.active() && runWithBlockSignalOwner(scope, () => lease.matches(handle)) : !hasSignalControlBinding(element, "value") && !hasHydrationControlSignalWriter(element, "value"));
12172
+ if (!valid()) presentationMiss(false);
12173
+ const prior = previous;
12174
+ const reusable = prior?.[DIRECT_SIGNAL_BINDING] === true && !prior.disposed && prior.input === void 0 && prior.handle === handle && prior.target === element && prior.kind === "value" && prior.site === site;
12175
+ const binding = reusable ? prior : {
12176
+ [DIRECT_SIGNAL_BINDING]: true,
12177
+ scope,
12178
+ target: element,
12179
+ kind: "value",
12180
+ site,
12181
+ handle,
12182
+ value,
12183
+ disposed: false
12184
+ };
12185
+ (frame.controls ??= /* @__PURE__ */ new Map()).set(element, { valid, binding, lease });
12186
+ trackPresentationFrame(frame);
12187
+ if (!reusable) registerHookCleanup(scope, () => disposeDirectSignalBinding(binding));
12188
+ if (binding.unsubscribe === void 0) {
12189
+ const notify = Object.assign(
12190
+ () => {
12191
+ if (binding.controlWriterCleanup !== void 0) updateDirectSignalBinding(binding);
12192
+ },
12193
+ {
12194
+ [NATIVE_TRANSITION_CONSUMER]: {
12195
+ active: () => binding.controlWriterCleanup !== void 0 && !binding.disposed && !scope.block.disposed,
12196
+ prepare: () => prepareNativeTransitionBlock(scope.block)
12197
+ }
12198
+ }
12199
+ );
12200
+ const stop = handle[SIGNAL_BINDING_SUBSCRIBE](
12201
+ notify,
12202
+ () => disposeDirectSignalBinding(binding)
12203
+ );
12204
+ if (typeof stop !== "function") throw new TypeError(formatClientError(74));
12205
+ if (binding.disposed || scope.block.disposed) {
12206
+ stop();
12207
+ presentationMiss(false);
12208
+ }
12209
+ binding.unsubscribe = stop;
12210
+ }
12211
+ preparePresentationOperation(frame, element, "value", () => {
12212
+ if (binding.disposed || scope.block.disposed) return;
12213
+ runWithBlockSignalOwner(scope, () => {
12214
+ if (!valid()) throw new Error(formatClientError(77));
12215
+ const before = snapshotHydrationControl(element);
12216
+ const ctrl = armControlled(element);
12217
+ ctrl.composing = before.composing || lease?.composing() === true;
12218
+ if (ctrl.composing) element.addEventListener("blur", onCtrlCompositionEnd);
12219
+ ctrl.v = UNCONTROLLED;
12220
+ let failed = false;
12221
+ let failure;
12222
+ if (lease !== void 0) {
12223
+ try {
12224
+ lease.retire();
12225
+ } catch (error) {
12226
+ failed = true;
12227
+ failure = error;
12228
+ } finally {
12229
+ lease.owner = void 0;
12230
+ }
12231
+ }
12232
+ if (lease !== void 0 && !scope.block.disposed && !valid(true))
12233
+ throw failed ? failure : new Error(formatClientError(77));
12234
+ if (lease !== void 0) owner.controlLeases.delete(element);
12235
+ if (!binding.disposed && !scope.block.disposed) {
12236
+ const snapshot = snapshotHydrationControl(element);
12237
+ binding.value = element.value;
12238
+ binding.pendingControl = snapshot.editRevision > (lease === void 0 ? 0 : before.editRevision);
12239
+ installDirectSignalControl(binding);
12240
+ if (!binding.pendingControl) consumeHydrationControl(element, snapshot.revision);
12241
+ if (binding.pendingControl) queueDirectSignalControlAdoption(binding);
12242
+ else {
12243
+ const current = readSignalBinding(handle);
12244
+ ctrl.sawV = true;
12245
+ ctrl.v = value;
12246
+ binding.value = current;
12247
+ writeDirectSignalBinding(binding, current);
12248
+ }
12249
+ }
12250
+ if (failed) reportRendererOwnerError(scope, failure);
12251
+ });
12252
+ });
12253
+ return binding;
12254
+ });
12255
+ }
12046
12256
  function presentationHostWrite(writer, ...args) {
12047
12257
  const frame = PRESENTATION_HYDRATION;
12048
12258
  if (frame === null) return writer(...args);
@@ -12074,6 +12284,20 @@ function presentationHostWrite(writer, ...args) {
12074
12284
  function presentationWrite(writer, kind, ...args) {
12075
12285
  const frame = PRESENTATION_HYDRATION;
12076
12286
  if (frame === null) return writer(...args);
12287
+ if (frame.lease.host !== void 0) {
12288
+ const channels = frame.lease.host;
12289
+ const changed2 = kind.endsWith("IfChanged") || kind.startsWith("updateFresh");
12290
+ const element2 = kind === "bindSignalAttribute" || kind === "nativeStyleBinding" || kind === "nativeProjectionBinding" ? args[2] : args[changed2 ? 2 : 0];
12291
+ const keys = kind === "nativeProjectionBinding" ? args[4].map((field) => field === "className" ? "class" : field) : kind === "setStyleProperties" ? args[1].filter((_, index) => index % 2 === 0).map((key2) => "style:" + key2) : [
12292
+ kind === "nativeStyleBinding" || kind === "setStyle" ? "style" : kind === "setStyleProperty" ? "style:" + args[1] : kind.includes("Class") ? "class" : kind === "bindSignalAttribute" ? args[3] : args[changed2 ? 3 : 1]
12293
+ ];
12294
+ if (element2 !== frame.lease.root || keys.some((key2) => !channels.has(key2)) || kind === "bindSignalValue" || kind === "setEventHandler" || kind === "bindSignalText" || kind === "setText" || kind === "markDangerouslySetInnerHTMLChildren")
12295
+ presentationMiss(false);
12296
+ }
12297
+ if (kind === "queueNativeChangeDiagnostic") {
12298
+ preparePresentationOperation(frame, args[0], "nativeChangeDiagnostic", () => writer(...args));
12299
+ return void 0;
12300
+ }
12077
12301
  if (kind === "markDangerouslySetInnerHTMLChildren") {
12078
12302
  const element2 = args[0];
12079
12303
  if (!frame.writes.get(element2)?.has("hostProps") || element2[DANGER_HTML_ACTIVE] === true)
@@ -12081,28 +12305,32 @@ function presentationWrite(writer, kind, ...args) {
12081
12305
  preparePresentationOperation(frame, element2, "hostChildren", () => writer(element2));
12082
12306
  return void 0;
12083
12307
  }
12084
- if (kind === "bindSignalText") {
12085
- const value = args[3];
12086
- if (value !== null && (typeof value === "object" || typeof value === "function") || typeof args[7] !== "string")
12087
- presentationMiss(false);
12088
- return bindingText(args[2], value, args[7]);
12089
- }
12308
+ if (kind === "bindSignalText") return preparePresentationSignalBinding(args, frame, true);
12090
12309
  if (kind === "setText") {
12091
12310
  const text = coerceText(args[1]);
12092
12311
  preparePresentationOperation(frame, args[0], "text", () => writer(args[0], text));
12093
12312
  return void 0;
12094
12313
  }
12095
- if (kind === "bindSignalAttribute") return preparePresentationSignalAttribute(args, frame);
12314
+ if (kind === "bindSignalAttribute") return preparePresentationSignalBinding(args, frame);
12315
+ if (kind === "bindSignalValue") return preparePresentationSignalValue(args, frame);
12096
12316
  if (kind === "nativeStyleBinding" || kind === "nativeProjectionBinding") {
12097
12317
  const [owner, slot, el] = args;
12098
12318
  const style = kind === "nativeStyleBinding";
12099
- nativePresentationBinding(
12100
- owner,
12101
- slot,
12102
- el,
12103
- style ? preparedNativeStyleBody : preparedNativeProjectionBody,
12104
- style ? { el, value: args[3], frame } : { el, compute: args[3], fields: args[4], frame }
12105
- );
12319
+ try {
12320
+ nativePresentationBinding(
12321
+ owner,
12322
+ slot,
12323
+ el,
12324
+ style ? preparedNativeStyleBody : preparedNativeProjectionBody,
12325
+ style ? { el, value: args[3], frame } : { el, compute: args[3], fields: args[4], frame }
12326
+ );
12327
+ } finally {
12328
+ if (frame.hostSuccessors !== void 0) {
12329
+ const binding = owner.slots[slot];
12330
+ if (binding !== void 0)
12331
+ frame.hostSuccessors.set(binding, () => disposeNativeStyleBinding(binding));
12332
+ }
12333
+ }
12106
12334
  return void 0;
12107
12335
  }
12108
12336
  const prepared = args.slice();
@@ -12321,11 +12549,14 @@ function disposeDirectSignalBinding(binding) {
12321
12549
  if (DEFERRED_LAYOUT_DRIVER?.stageAction(() => disposeDirectSignalBinding(binding)) === true)
12322
12550
  return;
12323
12551
  binding.disposed = true;
12324
- binding.unsubscribe?.();
12325
- retireNativeTransitionBlock(binding.scope.block);
12326
- binding.controlWriterCleanup?.();
12327
- if (binding.input !== void 0) {
12328
- domNode(binding.target).removeEventListener("input", binding.input);
12552
+ try {
12553
+ binding.unsubscribe?.();
12554
+ } finally {
12555
+ retireNativeTransitionBlock(binding.scope.block);
12556
+ binding.controlWriterCleanup?.();
12557
+ if (binding.input !== void 0) {
12558
+ domNode(binding.target).removeEventListener("input", binding.input);
12559
+ }
12329
12560
  }
12330
12561
  }
12331
12562
  function directSignalControlValue(binding, snapshot) {
@@ -12490,6 +12721,8 @@ function activateDirectSignalBinding(binding, revision) {
12490
12721
  }
12491
12722
  }
12492
12723
  function createDirectSignalBinding(scope, target, value, site, kind, name, attributeKind, text) {
12724
+ if (kind === "value" && scope.block.idState.renderOwner?.controlLeases?.has(target))
12725
+ presentationMiss(false);
12493
12726
  const handle = isSignalHandle(value) ? value : null;
12494
12727
  const binding = {
12495
12728
  [DIRECT_SIGNAL_BINDING]: true,
@@ -14473,6 +14706,8 @@ function bindSignalHostPropSources(scope, previous, element, sources, site, hasN
14473
14706
  binding.sources = sources;
14474
14707
  }
14475
14708
  const valueControl = winningSignalHostControl(sources, "value");
14709
+ if (valueControl !== null && scope.block.idState.renderOwner?.controlLeases?.has(element))
14710
+ presentationMiss(false);
14476
14711
  const checkedControl = winningSignalHostControl(sources, "checked");
14477
14712
  const controlSnapshot = isWritableSignal(valueControl) || isWritableSignal(checkedControl) ? snapshotHydrationControl(element) : null;
14478
14713
  if (controlSnapshot !== null) {
@@ -16125,6 +16360,7 @@ function setNativeChangeDiagnosticMetadata(el, value) {
16125
16360
  if (value === "runtime") queueDevFormDiagnostic(el, CURRENT_SCOPE ?? void 0, true);
16126
16361
  }
16127
16362
  function setValue(el, value) {
16363
+ if (CURRENT_SCOPE?.block.idState.renderOwner?.controlLeases?.has(el)) presentationMiss(false);
16128
16364
  const input = el;
16129
16365
  const ctrl = armControlled(el);
16130
16366
  if (TRANSITION_JOURNAL !== null) journalControlled(el, "value", "defaultValue");
@@ -26884,6 +27120,11 @@ function makeRoot(container, rootBlock, currentBody, currentKey, idState, output
26884
27120
  }
26885
27121
  unmounted = true;
26886
27122
  renderOwner.disposed = true;
27123
+ if (renderOwner.controlLeases !== void 0) {
27124
+ for (const lease of renderOwner.controlLeases.values())
27125
+ if (lease !== void 0) lease.owner = void 0;
27126
+ renderOwner.controlLeases.clear();
27127
+ }
26887
27128
  if (renderOwner.bindingLeases !== void 0) {
26888
27129
  for (const lease of renderOwner.bindingLeases) {
26889
27130
  releaseBindingHandoff(lease);
@@ -26944,7 +27185,8 @@ function makeRoot(container, rootBlock, currentBody, currentKey, idState, output
26944
27185
  }
26945
27186
  function createRootWithOutputHandler(container, options, outputHandler) {
26946
27187
  assertValidRootContainer(container);
26947
- if (options?.bindingLeases !== void 0) throw new Error(formatClientError(76));
27188
+ if (options?.bindingLeases !== void 0 || options?.controlLeases !== void 0)
27189
+ throw new Error(formatClientError(76));
26948
27190
  options = warnCreateRootElementOption(options);
26949
27191
  const ownerToken = claimRootContainer(container);
26950
27192
  registerDelegationTarget(container, true);
@@ -27004,12 +27246,20 @@ function hydrateRoot(container, bodyOrElement, propsOrOptions, rootOptions) {
27004
27246
  const bindingLeases = rootOptions?.bindingLeases?.map((handle) => {
27005
27247
  const capability = handle;
27006
27248
  const lease = capability[BINDING_HANDOFF]?.();
27007
- if (lease === void 0 || !lease.active() || lease.owner !== void 0 || !container.contains(lease.root))
27249
+ if (lease === void 0 || !lease.active() || lease.host !== void 0 && lease.valid?.() === false || lease.owner !== void 0 || !container.contains(lease.root))
27008
27250
  throw new Error(formatClientError(77));
27009
27251
  return lease;
27010
27252
  });
27011
27253
  if (bindingLeases !== void 0 && new Set(bindingLeases).size !== bindingLeases.length)
27012
27254
  throw new Error(formatClientError(78));
27255
+ const controlLeases = rootOptions?.controlLeases?.map((handle) => {
27256
+ const lease = handle?.[CONTROL_HANDOFF]?.();
27257
+ if (!bindingLeases?.length || lease === void 0 || !lease.active() || lease.owner !== void 0 || lease.channel !== "value" || lease.control.localName !== "textarea" || !container.contains(lease.control))
27258
+ throw new Error(formatClientError(77));
27259
+ return lease;
27260
+ });
27261
+ if (controlLeases !== void 0 && new Set(controlLeases.map((lease) => lease.control)).size !== controlLeases.length)
27262
+ throw new Error(formatClientError(78));
27013
27263
  const nativeSidecar = findHydrateSeedSidecar(container, NATIVE_SIGNAL_SEED_ATTR);
27014
27264
  const nativeManifest = nativeSidecar === null ? void 0 : parseNativeSignalManifest(
27015
27265
  (STAGED_DOM?.view(nativeSidecar) ?? nativeSidecar).textContent || ""
@@ -27071,6 +27321,10 @@ function hydrateRoot(container, bodyOrElement, propsOrOptions, rootOptions) {
27071
27321
  rootOptions
27072
27322
  );
27073
27323
  const owner = idState.renderOwner;
27324
+ if (controlLeases !== void 0) {
27325
+ owner.controlLeases = new Map(controlLeases.map((lease) => [lease.control, lease]));
27326
+ for (const lease of controlLeases) lease.owner = owner;
27327
+ }
27074
27328
  if (bindingLeases !== void 0) {
27075
27329
  owner.bindingLeases = new Set(bindingLeases);
27076
27330
  owner.bindingContainer = container;
@@ -27104,7 +27358,7 @@ function hydrateRoot(container, bodyOrElement, propsOrOptions, rootOptions) {
27104
27358
  while (firstNode !== null && (firstNode.nodeType === 10 || isRendererHydrationStyle(firstNode)))
27105
27359
  firstNode = getNextSibling(firstNode);
27106
27360
  const hydration = new HydrationCapability(rootBlock, firstNode, seeds);
27107
- if (bindingLeases?.some((lease) => lease.revision !== void 0)) {
27361
+ if (bindingLeases?.length) {
27108
27362
  const attempted = rootBlock;
27109
27363
  hydration.retryPresentation = () => {
27110
27364
  if (owner.disposed || owner.current !== attempted) return;
@@ -27112,7 +27366,12 @@ function hydrateRoot(container, bodyOrElement, propsOrOptions, rootOptions) {
27112
27366
  collectVisibleSubtreeRefs(attempted, refs);
27113
27367
  withRefDetachSuppression(refs, () => unmountBlock(attempted, false));
27114
27368
  queueMicrotask(() => {
27115
- if (!owner.disposed && owner.current === attempted) adopt();
27369
+ if (owner.disposed || owner.current !== attempted) return;
27370
+ try {
27371
+ adopt();
27372
+ } catch (error) {
27373
+ if (!reportUncaughtError(rootBlock, error)) throw error;
27374
+ }
27116
27375
  });
27117
27376
  };
27118
27377
  }
@@ -1,4 +1,5 @@
1
1
  import { encodeSignalValue } from "../data-encoding.js";
2
+ import { STREAM_SCRIPT_ATTR } from "../stream-protocol.js";
2
3
  import {
3
4
  encodeStreamedRendererFrameForScript,
4
5
  isStreamFrameIdentity,
@@ -159,12 +160,12 @@ function escapeAttribute(value) {
159
160
  }
160
161
  function streamedRendererFrameScript(frame, nonce) {
161
162
  const encoded = encodeStreamedRendererFrameForScript(frame);
162
- return `<script${nonce === void 0 ? "" : ` nonce="${escapeAttribute(nonce)}"`}>globalThis.__octaneStreamedRenderer.receive(${encoded});</script>`;
163
+ return `<script ${STREAM_SCRIPT_ATTR}${nonce === void 0 ? "" : ` nonce="${escapeAttribute(nonce)}"`}>globalThis.__octaneStreamedRenderer.receive(${encoded});</script>`;
163
164
  }
164
165
  function streamedSignalSelectionScript(identity, nonce) {
165
166
  if (!isStreamFrameIdentity(identity)) throw new TypeError("Invalid streamed signal identity.");
166
167
  const encoded = JSON.stringify(identity).replace(/&/g, "\\u0026").replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
167
- return `<script${nonce === void 0 ? "" : ` nonce="${escapeAttribute(nonce)}"`}>(function(g){var k="__octaneStreamedRenderer",e=g[k];if(!e){var q=[];g[k]={version:1,frames:q,receive:function(f){if(q.length>=512){this.overflow=true;return;}q.push(f);}};}var z="__octaneStreamedSignalSelections",v=g[z];if(!v){var a=[];v=g[z]={version:1,identities:a,register:function(i){if(a.length>=256){this.overflow=true;return;}a.push(i);}};}v.register(${encoded});})(globalThis);</script>`;
168
+ return `<script ${STREAM_SCRIPT_ATTR}${nonce === void 0 ? "" : ` nonce="${escapeAttribute(nonce)}"`}>(function(g){var k="__octaneStreamedRenderer",e=g[k];if(!e){var q=[];g[k]={version:1,frames:q,receive:function(f){if(q.length>=512){this.overflow=true;return;}q.push(f);}};}var z="__octaneStreamedSignalSelections",v=g[z];if(!v){var a=[];v=g[z]={version:1,identities:a,register:function(i){if(a.length>=256){this.overflow=true;return;}a.push(i);}};}v.register(${encoded});})(globalThis);</script>`;
168
169
  }
169
170
  function createStreamedSignalInjection(identity, result, options = {}) {
170
171
  const budget = limits(options);