octane 0.2.6 → 0.2.7

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.
@@ -70,7 +70,7 @@ interface Frame {
70
70
  deferred: boolean;
71
71
  asyncScope: string;
72
72
  /** Parser context supplied by, or inherited through, the component call site. */
73
- namespace?: 'html' | 'svg' | 'mathml';
73
+ namespace: 'html' | 'svg' | 'mathml' | undefined;
74
74
  }
75
75
  /** Compiler ABI: validate and scope one native element during a DEV SSR render. */
76
76
  export declare function ssrElement(tag: string, location: string | undefined, render: () => string, htmlIntegrationPoint?: boolean): string;
@@ -2297,7 +2297,8 @@ function ssrComponent(parent, comp, props, inherit, key, identityScoped) {
2297
2297
  occ: null,
2298
2298
  path: null,
2299
2299
  deferred: false,
2300
- asyncScope: ASYNC_SCOPE
2300
+ asyncScope: ASYNC_SCOPE,
2301
+ namespace: explicitNamespace ?? void 0
2301
2302
  } : {
2302
2303
  parent: pf,
2303
2304
  seg: nextChildSegment(pf),
@@ -2306,9 +2307,9 @@ function ssrComponent(parent, comp, props, inherit, key, identityScoped) {
2306
2307
  occ: null,
2307
2308
  path: null,
2308
2309
  deferred: false,
2309
- asyncScope: ASYNC_SCOPE
2310
+ asyncScope: ASYNC_SCOPE,
2311
+ namespace: explicitNamespace ?? pf.namespace
2310
2312
  };
2311
- frame.namespace = explicitNamespace ?? pf?.namespace;
2312
2313
  return renderComponentFramed(comp, props, parent, frame, inherit);
2313
2314
  } finally {
2314
2315
  if (identityScoped !== true) ASYNC_SCOPE = previousIdentityScope;
@@ -2550,6 +2551,7 @@ function vtSsrAnnotate(html, attrs) {
2550
2551
  return html.slice(0, insertion) + inject + html.slice(insertion);
2551
2552
  }
2552
2553
  function vtSsrClaimArm(html, kind) {
2554
+ if (!VT_SSR_HAS_CANDIDATES) return html;
2553
2555
  const start = vtSsrFirstVisibleOpenTag(html);
2554
2556
  if (start === -1) return html;
2555
2557
  const end = vtSsrOpenTagEnd(html, start + 1);
@@ -3688,14 +3690,12 @@ function namespaceHeadElement(headKey, tag, attrs, text, authoredKey) {
3688
3690
  return createElement(namespaceHead, config);
3689
3691
  }
3690
3692
  function spliceHead(body, head) {
3691
- if (head === "" && !isDocumentRoot(body)) return body;
3693
+ if (!isDocumentRoot(body)) return head === "" ? body : head + body;
3692
3694
  const headClose = body.indexOf("</head>");
3693
3695
  if (headClose !== -1) return body.slice(0, headClose) + head + body.slice(headClose);
3694
- if (isDocumentRoot(body)) {
3695
- const openingEnd = documentTagEnd(body, body.indexOf("<html") + 5);
3696
- if (openingEnd !== -1)
3697
- return body.slice(0, openingEnd) + "<head>" + head + "</head>" + body.slice(openingEnd);
3698
- }
3696
+ const openingEnd = documentTagEnd(body, body.indexOf("<html") + 5);
3697
+ if (openingEnd !== -1)
3698
+ return body.slice(0, openingEnd) + "<head>" + head + "</head>" + body.slice(openingEnd);
3699
3699
  if (head === "") return body;
3700
3700
  return head + body;
3701
3701
  }
@@ -3955,7 +3955,8 @@ function runFullFramedPass(component, props, resolved, nonceAttr = "", identifie
3955
3955
  occ: null,
3956
3956
  path: "",
3957
3957
  deferred: false,
3958
- asyncScope: ""
3958
+ asyncScope: "",
3959
+ namespace: void 0
3959
3960
  };
3960
3961
  CURRENT_COMP = component;
3961
3962
  CURRENT_PROPS = props;
@@ -4061,7 +4062,8 @@ function runDiscoveryRound(jobs, resolved, identifierPrefix) {
4061
4062
  occ: null,
4062
4063
  path: null,
4063
4064
  deferred: false,
4064
- asyncScope: job.frame.asyncScope
4065
+ asyncScope: job.frame.asyncScope,
4066
+ namespace: void 0
4065
4067
  };
4066
4068
  try {
4067
4069
  renderComponentFramed(job.comp, job.props, job.parentScope, frame);
@@ -1,4 +1,4 @@
1
- /** Experimental scoped signals. This entry does not import a renderer. */
1
+ /** Stable scoped signals. This entry does not import a renderer. */
2
2
  export { createScope } from './engine.js';
3
3
  export { query } from './requests.js';
4
4
  export { ScopeDisposedError, SignalCycleError, SignalFrameError, SignalSerializationError, SignalWriteError, } from './errors.js';
@@ -1,4 +1,4 @@
1
- /** Nominal marker for the experimental native signal API; it has no runtime payload. */
1
+ /** Nominal marker for the native signal API; it has no runtime payload. */
2
2
  export declare const SIGNAL_HANDLE: unique symbol;
3
3
  export declare const QUERY_REQUEST: unique symbol;
4
4
  export type ConnectionState = 'none' | 'connecting' | 'open' | 'closed';
@@ -779,6 +779,7 @@ function draftOwner(record, parent, replayPath) {
779
779
  componentRevision: record.componentRevision,
780
780
  parent,
781
781
  replayPath,
782
+ priorClaimedSibling: parent?.children[parent.children.length - 1] ?? null,
782
783
  hooks: new Map(record.hooks),
783
784
  clonedHooks: /* @__PURE__ */ new Set(),
784
785
  seenEffects: [],
@@ -788,7 +789,6 @@ function draftOwner(record, parent, replayPath) {
788
789
  sequentialClaimCursor: 0,
789
790
  childOwnerBuckets: null,
790
791
  childClaimCursors: null,
791
- childReplayOrdinals: null,
792
792
  contextValues: record.contextValues === null ? null : new Map(record.contextValues),
793
793
  appliedUpdates: /* @__PURE__ */ new Map(),
794
794
  needsRender: false,
@@ -802,11 +802,25 @@ function draftOwner(record, parent, replayPath) {
802
802
  contextStable: null
803
803
  };
804
804
  }
805
- function childReplayPath(parent, component, identityPath, key) {
806
- const ordinals = parent.childReplayOrdinals ??= createOwnerIdentityIndex();
807
- const ordinal = readOwnerIdentity(ordinals, component, identityPath, key) ?? 0;
808
- writeOwnerIdentity(ordinals, component, identityPath, key, ordinal + 1);
809
- return [...parent.replayPath, { component, identityPath, key, ordinal }];
805
+ function ownerReplayPath(owner) {
806
+ if (owner.replayPath !== null) return owner.replayPath;
807
+ const record = owner.record;
808
+ let ordinal = 0;
809
+ for (let sibling = owner.priorClaimedSibling; sibling !== null; sibling = sibling.priorClaimedSibling) {
810
+ const previous = sibling.record;
811
+ if (previous.component === record.component && Object.is(previous.key, record.key) && identityPathsEqual(previous.identityPath, record.identityPath)) {
812
+ ordinal++;
813
+ }
814
+ }
815
+ return owner.replayPath = [
816
+ ...ownerReplayPath(owner.parent),
817
+ {
818
+ component: record.component,
819
+ identityPath: record.identityPath,
820
+ key: record.key,
821
+ ordinal
822
+ }
823
+ ];
810
824
  }
811
825
  function childOwnerBucket(parent, component, identityPath, key) {
812
826
  let buckets = parent.childOwnerBuckets;
@@ -859,7 +873,7 @@ function findClaimableChildRecord(parent, component, identityPath, key) {
859
873
  function adoptChildOwner(parent, record, component, identityPath, key) {
860
874
  const attempt = currentAttempt();
861
875
  parent.claimedChildren.add(record);
862
- const draft = draftOwner(record, parent, childReplayPath(parent, component, identityPath, key));
876
+ const draft = draftOwner(record, parent, null);
863
877
  parent.children.push(draft);
864
878
  attempt.owners.push(draft);
865
879
  return draft;
@@ -944,14 +958,13 @@ function executeOwner(owner, build, initialRenderCount = 0) {
944
958
  for (let renderCount = initialRenderCount; ; renderCount++) {
945
959
  ACTIVE_UNIVERSAL_WARM_PLANS.length = warmPlanCheckpoint;
946
960
  if (renderCount === 25) throw new Error("Too many universal render-phase updates.");
947
- if (renderCount > 0) resetDraftChildren(owner);
948
- owner.seenEffects = [];
949
- owner.children = [];
961
+ if (renderCount > 0) {
962
+ resetDraftChildren(owner);
963
+ owner.seenEffects = [];
964
+ }
950
965
  owner.retainedChildren = null;
951
- owner.claimedChildren = /* @__PURE__ */ new Set();
952
966
  owner.sequentialClaimCursor = 0;
953
967
  owner.childClaimCursors = null;
954
- owner.childReplayOrdinals = null;
955
968
  owner.contextStable = null;
956
969
  owner.needsRender = false;
957
970
  owner.implicitSlot = 0;
@@ -1135,7 +1148,6 @@ function resetDraftChildren(owner) {
1135
1148
  owner.claimedChildren = /* @__PURE__ */ new Set();
1136
1149
  owner.sequentialClaimCursor = 0;
1137
1150
  owner.childClaimCursors = null;
1138
- owner.childReplayOrdinals = null;
1139
1151
  }
1140
1152
  function retainCommittedOwnerTree(owner) {
1141
1153
  owner.hooks = new Map(owner.record.hooks);
@@ -1146,13 +1158,8 @@ function retainCommittedOwnerTree(owner) {
1146
1158
  owner.claimedChildren = new Set(owner.record.children);
1147
1159
  owner.sequentialClaimCursor = 0;
1148
1160
  owner.childClaimCursors = null;
1149
- owner.childReplayOrdinals = null;
1150
1161
  for (const childRecord of owner.record.children) {
1151
- const child = draftOwner(
1152
- childRecord,
1153
- owner,
1154
- childReplayPath(owner, childRecord.component, childRecord.identityPath, childRecord.key)
1155
- );
1162
+ const child = draftOwner(childRecord, owner, null);
1156
1163
  owner.children.push(child);
1157
1164
  currentAttempt().owners.push(child);
1158
1165
  retainCommittedOwnerTree(child);
@@ -1229,11 +1236,7 @@ function retainCommittedTryArm(owner) {
1229
1236
  const range = findLogicalRange(owner.record.root.rootRecordForRetention(), childRecord.rangeKey);
1230
1237
  if (range === null) return null;
1231
1238
  resetDraftChildren(owner);
1232
- const child = draftOwner(
1233
- childRecord,
1234
- owner,
1235
- childReplayPath(owner, childRecord.component, childRecord.identityPath, childRecord.key)
1236
- );
1239
+ const child = draftOwner(childRecord, owner, null);
1237
1240
  owner.children.push(child);
1238
1241
  owner.claimedChildren.add(childRecord);
1239
1242
  currentAttempt().owners.push(child);
@@ -1250,6 +1253,7 @@ function retainCommittedActivity(owner) {
1250
1253
  const range = record.mounted ? record.range ?? findLogicalRange(attempt.root.rootRecordForRetention(), record.rangeKey) : null;
1251
1254
  resetDraftChildren(owner);
1252
1255
  const retained = draftOwner(record, parent, owner.replayPath);
1256
+ retained.priorClaimedSibling = owner.priorClaimedSibling;
1253
1257
  retained.visibility = owner.visibility;
1254
1258
  retained.canHandleSuspense = owner.canHandleSuspense;
1255
1259
  retained.boundaryThenable = owner.boundaryThenable;
@@ -2779,7 +2783,7 @@ function isThenable(value) {
2779
2783
  function findSuspendedMemo(owner, slot, deps) {
2780
2784
  if (deps === null) return null;
2781
2785
  for (const entry of currentAttempt().replayEntries) {
2782
- if (Object.is(entry.slot, slot) && depsEqual(entry.deps, deps) && suspendedOwnerPathEqual(entry.ownerPath, owner.replayPath)) {
2786
+ if (Object.is(entry.slot, slot) && depsEqual(entry.deps, deps) && suspendedOwnerPathEqual(entry.ownerPath, ownerReplayPath(owner))) {
2783
2787
  return entry;
2784
2788
  }
2785
2789
  }
@@ -2794,12 +2798,12 @@ function collectSuspendedMemos(attempt) {
2794
2798
  continue;
2795
2799
  }
2796
2800
  if (entries.some(
2797
- (entry) => Object.is(entry.slot, slot) && suspendedOwnerPathEqual(entry.ownerPath, owner.replayPath)
2801
+ (entry) => Object.is(entry.slot, slot) && suspendedOwnerPathEqual(entry.ownerPath, ownerReplayPath(owner))
2798
2802
  )) {
2799
2803
  continue;
2800
2804
  }
2801
2805
  entries.push({
2802
- ownerPath: owner.replayPath,
2806
+ ownerPath: ownerReplayPath(owner),
2803
2807
  slot,
2804
2808
  deps: hook.deps,
2805
2809
  value: hook.value
@@ -4318,7 +4322,7 @@ function runOwnedCommit(owner, work) {
4318
4322
  if (!reportUniversalUncaughtError(owner.root, error)) throw error;
4319
4323
  }
4320
4324
  }
4321
- function cloneSerializableValue(value, seen = /* @__PURE__ */ new WeakSet()) {
4325
+ function cloneSerializableValue(value, seen) {
4322
4326
  if (value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
4323
4327
  return value;
4324
4328
  }
@@ -4328,6 +4332,7 @@ function cloneSerializableValue(value, seen = /* @__PURE__ */ new WeakSet()) {
4328
4332
  if (value.$$kind === "octane.universal.resource") {
4329
4333
  throw new TypeError("A resource handle must use the resource encoding branch.");
4330
4334
  }
4335
+ seen ??= /* @__PURE__ */ new WeakSet();
4331
4336
  if (seen.has(value)) throw new TypeError("Serializable host values cannot contain cycles.");
4332
4337
  seen.add(value);
4333
4338
  try {
@@ -4503,6 +4508,7 @@ class UniversalRootImpl {
4503
4508
  passiveTasks = [];
4504
4509
  hostAttachments = null;
4505
4510
  collapsedTemplates = null;
4511
+ codecResourceHandle = null;
4506
4512
  initializeHostAttachments() {
4507
4513
  const capability = this.driver.attachments;
4508
4514
  if (capability === void 0) return;
@@ -5083,25 +5089,24 @@ class UniversalRootImpl {
5083
5089
  if (codec === void 0) {
5084
5090
  return this.transport === null ? value : cloneSerializableValue(value);
5085
5091
  }
5092
+ const createResourceHandle = this.codecResourceHandle ??= (id) => {
5093
+ if (typeof id !== "string" && typeof id !== "number" || String(id).length === 0) {
5094
+ throw new TypeError("A universal resource handle ID must be a non-empty string or number.");
5095
+ }
5096
+ return Object.freeze({
5097
+ $$kind: "octane.universal.resource",
5098
+ renderer: this.renderer,
5099
+ root: this.resourceRoot,
5100
+ id
5101
+ });
5102
+ };
5086
5103
  const result = codec.encode({
5087
5104
  container: this.container,
5088
5105
  renderer: this.renderer,
5089
5106
  hostType,
5090
5107
  name,
5091
5108
  value,
5092
- createResourceHandle: (id) => {
5093
- if (typeof id !== "string" && typeof id !== "number" || String(id).length === 0) {
5094
- throw new TypeError(
5095
- "A universal resource handle ID must be a non-empty string or number."
5096
- );
5097
- }
5098
- return Object.freeze({
5099
- $$kind: "octane.universal.resource",
5100
- renderer: this.renderer,
5101
- root: this.resourceRoot,
5102
- id
5103
- });
5104
- }
5109
+ createResourceHandle
5105
5110
  });
5106
5111
  if (result === null || typeof result !== "object") {
5107
5112
  throw new TypeError(
@@ -7997,11 +8002,6 @@ class UniversalRootImpl {
7997
8002
  }
7998
8003
  if ((treeFeatures & UNIVERSAL_TREE_EVENT) !== 0) {
7999
8004
  const handlers = this.handlers;
8000
- for (const listener of previousReplacedEventListeners) {
8001
- EVENT_DISPATCHERS.delete(listener);
8002
- this.publishedListeners.delete(listener);
8003
- handlers.delete(listener);
8004
- }
8005
8005
  for (const listener of previousCollapsedEventListeners) {
8006
8006
  EVENT_DISPATCHERS.delete(listener);
8007
8007
  this.publishedListeners.delete(listener);
@@ -8010,14 +8010,22 @@ class UniversalRootImpl {
8010
8010
  for (const [record, events] of stagedEvents) {
8011
8011
  if (!stagedVisibleEventRecords.has(record)) continue;
8012
8012
  for (const event of events.values()) {
8013
+ previousReplacedEventListeners.delete(event.listener);
8013
8014
  handlers.set(event.listener, event);
8014
- this.publishedListeners.add(event.listener);
8015
- EVENT_DISPATCHERS.set(
8016
- event.listener,
8017
- (payload) => this.dispatchEvent(event.listener, payload)
8018
- );
8015
+ if (!this.publishedListeners.has(event.listener)) {
8016
+ this.publishedListeners.add(event.listener);
8017
+ EVENT_DISPATCHERS.set(
8018
+ event.listener,
8019
+ (payload) => this.dispatchEvent(event.listener, payload)
8020
+ );
8021
+ }
8019
8022
  }
8020
8023
  }
8024
+ for (const listener of previousReplacedEventListeners) {
8025
+ EVENT_DISPATCHERS.delete(listener);
8026
+ this.publishedListeners.delete(listener);
8027
+ handlers.delete(listener);
8028
+ }
8021
8029
  for (const event of nextCollapsedEvents) {
8022
8030
  handlers.set(event.listener, event);
8023
8031
  if (!this.publishedListeners.has(event.listener)) {
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const version = "0.2.6";
1
+ const version = "0.2.7";
2
2
  export {
3
3
  version
4
4
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "octane",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": [