octane 0.1.45 → 0.1.47

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.
package/README.md CHANGED
@@ -25,6 +25,11 @@ Vite builds can fold proven immutable CSS-module class strings into templates.
25
25
  See the [CSS-module constants guide](https://github.com/octanejs/octane/blob/main/docs/compiler-css-module-constants.md)
26
26
  for the provider contract and stylesheet-loading guarantees.
27
27
 
28
+ Custom native integrations can opt into the experimental
29
+ [Valdi writer compiler](https://github.com/octanejs/octane/blob/main/docs/valdi-compiler.md).
30
+ It requires an application-provided adapter; Octane does not bundle a Valdi
31
+ runtime or native build integration.
32
+
28
33
  Direct Node or Bun server scripts can preload `octane/compiler/register` to
29
34
  compile imported Octane components without going through Vite. See the
30
35
  [SSR guide](https://github.com/octanejs/octane/blob/main/docs/ssr.md#run-an-ssg-script-directly).
@@ -131,9 +131,15 @@ function adoptionContext(adoption, event) {
131
131
  };
132
132
  return context;
133
133
  }
134
+ function finishPendingAdoption(adoption) {
135
+ if (!adoption.pending) return;
136
+ adoption.pending = false;
137
+ adoption.entry.pendingAdoptionCount--;
138
+ }
134
139
  function disposeAdoption(adoption) {
135
140
  if (adoption.disposed) return;
136
141
  adoption.disposed = true;
142
+ finishPendingAdoption(adoption);
137
143
  adoption.entry.adoptions.delete(adoption.element);
138
144
  adoption.controller.abort();
139
145
  for (const unlink of adoption.unlinks) unlink();
@@ -144,29 +150,47 @@ function disposeAdoption(adoption) {
144
150
  }
145
151
  function flushQueuedEvents(record) {
146
152
  if (record.status !== "active") return;
147
- while (record.queuedEvents.length !== 0) {
148
- const queued = record.queuedEvents[0];
149
- if (!contains(record.root, queued.element)) {
150
- record.queuedEvents.shift();
151
- continue;
152
- }
153
- const range = nearestRange(record.root, queued.element);
154
- if (range !== queued.range || !rangeMatches(record, range)) {
155
- record.queuedEvents.shift();
156
- continue;
153
+ const queue = record.queuedEvents;
154
+ record.queuedEventFlushDepth++;
155
+ try {
156
+ while (record.queuedEventHead < queue.length) {
157
+ const index = record.queuedEventHead;
158
+ const queued = queue[index];
159
+ if (!contains(record.root, queued.element)) {
160
+ record.queuedEventHead = index + 1;
161
+ continue;
162
+ }
163
+ const range = nearestRange(record.root, queued.element);
164
+ if (range !== queued.range || !rangeMatches(record, range)) {
165
+ record.queuedEventHead = index + 1;
166
+ continue;
167
+ }
168
+ if (range?.status === "pending") return;
169
+ let adoption = record.adoptions.get(queued.element);
170
+ if (adoption === void 0) {
171
+ adoption = adoptElement(record, queued.element, range, queued.event);
172
+ }
173
+ if (adoption === void 0 || adoption.pending) return;
174
+ if (record.queuedEventHead !== index || queue[index] !== queued) continue;
175
+ record.queuedEventHead = index + 1;
176
+ record.entry.handleEvent?.(
177
+ queued.event,
178
+ queued.element,
179
+ adoptionContext(adoption, queued.event)
180
+ );
157
181
  }
158
- if (range?.status === "pending") return;
159
- let adoption = record.adoptions.get(queued.element);
160
- if (adoption === void 0) {
161
- adoption = adoptElement(record, queued.element, range, queued.event);
182
+ } finally {
183
+ record.queuedEventFlushDepth--;
184
+ if (record.queuedEventFlushDepth === 0 && record.queuedEventHead !== 0) {
185
+ const consumed = record.queuedEventHead;
186
+ if (consumed >= queue.length) {
187
+ queue.length = 0;
188
+ record.queuedEventHead = 0;
189
+ } else if (consumed >= queue.length - consumed) {
190
+ queue.splice(0, consumed);
191
+ record.queuedEventHead = 0;
192
+ }
162
193
  }
163
- if (adoption === void 0 || adoption.pending) return;
164
- record.queuedEvents.shift();
165
- record.entry.handleEvent?.(
166
- queued.event,
167
- queued.element,
168
- adoptionContext(adoption, queued.event)
169
- );
170
194
  }
171
195
  }
172
196
  function completeBehavior(record) {
@@ -175,9 +199,7 @@ function completeBehavior(record) {
175
199
  if (range.status === "pending") return;
176
200
  record.waitingRanges.delete(range);
177
201
  }
178
- for (const adoption of record.adoptions.values()) {
179
- if (adoption.pending) return;
180
- }
202
+ if (record.pendingAdoptionCount !== 0) return;
181
203
  flushQueuedEvents(record);
182
204
  if (record.queuedEvents.length !== 0) return;
183
205
  record.readiness.resolve();
@@ -227,9 +249,10 @@ function adoptElement(record, element, range, event) {
227
249
  }
228
250
  if (typeof result === "object" && result !== null && typeof result.then === "function") {
229
251
  adoption.pending = true;
252
+ record.pendingAdoptionCount++;
230
253
  const settled = Promise.resolve(result).then(
231
254
  (cleanup) => {
232
- adoption.pending = false;
255
+ finishPendingAdoption(adoption);
233
256
  if (typeof cleanup === "function") {
234
257
  if (adoption.disposed || adoption.controller.signal.aborted) cleanup();
235
258
  else adoption.cleanup = cleanup;
@@ -240,7 +263,7 @@ function adoptElement(record, element, range, event) {
240
263
  }
241
264
  },
242
265
  (error) => {
243
- adoption.pending = false;
266
+ finishPendingAdoption(adoption);
244
267
  if (adoption.disposed || record.controller.signal.aborted) return;
245
268
  disposeAdoption(adoption);
246
269
  failBehavior(record, error);
@@ -375,6 +398,7 @@ function disposeBehavior(record) {
375
398
  record.root.behaviorsById.delete(record.entry.id);
376
399
  }
377
400
  record.queuedEvents.length = 0;
401
+ record.queuedEventHead = 0;
378
402
  record.waitingRanges.clear();
379
403
  let firstError;
380
404
  for (const adoption of [...record.adoptions.values()]) {
@@ -428,8 +452,11 @@ function registerBehavior(root, entry) {
428
452
  readiness,
429
453
  status: "pending",
430
454
  adoptions: /* @__PURE__ */ new Map(),
455
+ pendingAdoptionCount: 0,
431
456
  waitingRanges: /* @__PURE__ */ new Set(),
432
457
  queuedEvents: [],
458
+ queuedEventHead: 0,
459
+ queuedEventFlushDepth: 0,
433
460
  unlinks: [linkSignal(root.controller.signal, controller)],
434
461
  initialScanComplete: false
435
462
  };
@@ -154,9 +154,15 @@ function adoptionContext(adoption, event) {
154
154
  };
155
155
  return context;
156
156
  }
157
+ function finishPendingAdoption(adoption) {
158
+ if (!adoption.pending) return;
159
+ adoption.pending = false;
160
+ adoption.entry.pendingAdoptionCount--;
161
+ }
157
162
  function disposeAdoption(adoption) {
158
163
  if (adoption.disposed) return;
159
164
  adoption.disposed = true;
165
+ finishPendingAdoption(adoption);
160
166
  adoption.entry.adoptions.delete(adoption.element);
161
167
  adoption.controller.abort();
162
168
  for (const unlink of adoption.unlinks) unlink();
@@ -167,29 +173,47 @@ function disposeAdoption(adoption) {
167
173
  }
168
174
  function flushQueuedEvents(record) {
169
175
  if (record.status !== "active") return;
170
- while (record.queuedEvents.length !== 0) {
171
- const queued = record.queuedEvents[0];
172
- if (!contains(record.root, queued.element)) {
173
- record.queuedEvents.shift();
174
- continue;
175
- }
176
- const range = nearestRange(record.root, queued.element);
177
- if (range !== queued.range || !rangeMatches(record, range)) {
178
- record.queuedEvents.shift();
179
- continue;
176
+ const queue = record.queuedEvents;
177
+ record.queuedEventFlushDepth++;
178
+ try {
179
+ while (record.queuedEventHead < queue.length) {
180
+ const index = record.queuedEventHead;
181
+ const queued = queue[index];
182
+ if (!contains(record.root, queued.element)) {
183
+ record.queuedEventHead = index + 1;
184
+ continue;
185
+ }
186
+ const range = nearestRange(record.root, queued.element);
187
+ if (range !== queued.range || !rangeMatches(record, range)) {
188
+ record.queuedEventHead = index + 1;
189
+ continue;
190
+ }
191
+ if (range?.status === "pending") return;
192
+ let adoption = record.adoptions.get(queued.element);
193
+ if (adoption === void 0) {
194
+ adoption = adoptElement(record, queued.element, range, queued.event);
195
+ }
196
+ if (adoption === void 0 || adoption.pending) return;
197
+ if (record.queuedEventHead !== index || queue[index] !== queued) continue;
198
+ record.queuedEventHead = index + 1;
199
+ record.entry.handleEvent?.(
200
+ queued.event,
201
+ queued.element,
202
+ adoptionContext(adoption, queued.event)
203
+ );
180
204
  }
181
- if (range?.status === "pending") return;
182
- let adoption = record.adoptions.get(queued.element);
183
- if (adoption === void 0) {
184
- adoption = adoptElement(record, queued.element, range, queued.event);
205
+ } finally {
206
+ record.queuedEventFlushDepth--;
207
+ if (record.queuedEventFlushDepth === 0 && record.queuedEventHead !== 0) {
208
+ const consumed = record.queuedEventHead;
209
+ if (consumed >= queue.length) {
210
+ queue.length = 0;
211
+ record.queuedEventHead = 0;
212
+ } else if (consumed >= queue.length - consumed) {
213
+ queue.splice(0, consumed);
214
+ record.queuedEventHead = 0;
215
+ }
185
216
  }
186
- if (adoption === void 0 || adoption.pending) return;
187
- record.queuedEvents.shift();
188
- record.entry.handleEvent?.(
189
- queued.event,
190
- queued.element,
191
- adoptionContext(adoption, queued.event)
192
- );
193
217
  }
194
218
  }
195
219
  function completeBehavior(record) {
@@ -198,9 +222,7 @@ function completeBehavior(record) {
198
222
  if (range.status === "pending") return;
199
223
  record.waitingRanges.delete(range);
200
224
  }
201
- for (const adoption of record.adoptions.values()) {
202
- if (adoption.pending) return;
203
- }
225
+ if (record.pendingAdoptionCount !== 0) return;
204
226
  flushQueuedEvents(record);
205
227
  if (record.queuedEvents.length !== 0) return;
206
228
  record.readiness.resolve();
@@ -250,9 +272,10 @@ function adoptElement(record, element, range, event) {
250
272
  }
251
273
  if (typeof result === "object" && result !== null && typeof result.then === "function") {
252
274
  adoption.pending = true;
275
+ record.pendingAdoptionCount++;
253
276
  const settled = Promise.resolve(result).then(
254
277
  (cleanup) => {
255
- adoption.pending = false;
278
+ finishPendingAdoption(adoption);
256
279
  if (typeof cleanup === "function") {
257
280
  if (adoption.disposed || adoption.controller.signal.aborted) cleanup();
258
281
  else adoption.cleanup = cleanup;
@@ -263,7 +286,7 @@ function adoptElement(record, element, range, event) {
263
286
  }
264
287
  },
265
288
  (error) => {
266
- adoption.pending = false;
289
+ finishPendingAdoption(adoption);
267
290
  if (adoption.disposed || record.controller.signal.aborted) return;
268
291
  disposeAdoption(adoption);
269
292
  failBehavior(record, error);
@@ -398,6 +421,7 @@ function disposeBehavior(record) {
398
421
  record.root.behaviorsById.delete(record.entry.id);
399
422
  }
400
423
  record.queuedEvents.length = 0;
424
+ record.queuedEventHead = 0;
401
425
  record.waitingRanges.clear();
402
426
  let firstError;
403
427
  for (const adoption of [...record.adoptions.values()]) {
@@ -451,8 +475,11 @@ function registerBehavior(root, entry) {
451
475
  readiness,
452
476
  status: "pending",
453
477
  adoptions: /* @__PURE__ */ new Map(),
478
+ pendingAdoptionCount: 0,
454
479
  waitingRanges: /* @__PURE__ */ new Set(),
455
480
  queuedEvents: [],
481
+ queuedEventHead: 0,
482
+ queuedEventFlushDepth: 0,
456
483
  unlinks: [linkSignal(root.controller.signal, controller)],
457
484
  initialScanComplete: false
458
485
  };
@@ -8727,21 +8727,25 @@ function setHostPropSources(el, sources, prev, scope, hasNestedChildren = false)
8727
8727
  }
8728
8728
  }
8729
8729
  const values = /* @__PURE__ */ new Map();
8730
+ let needsWinningOrderSort = false;
8730
8731
  for (const writer of props.values()) {
8731
8732
  if (writer.rawName === "key") continue;
8732
8733
  const [identity, name] = normalizedHostProp(el, writer.rawName);
8733
8734
  const previous = values.get(identity);
8734
- if (previous === void 0 || previous[3] < writer.lastOrder) {
8735
- values.set(identity, [
8736
- process.env.NODE_ENV !== "production" && (writer.rawName === "tabIndex" || writer.rawName === "htmlFor") ? writer.rawName : name,
8737
- writer.value,
8738
- writer.firstOrder,
8739
- writer.lastOrder
8740
- ]);
8735
+ if (previous !== void 0) {
8736
+ if (previous[3] >= writer.lastOrder) continue;
8737
+ needsWinningOrderSort = true;
8741
8738
  }
8739
+ values.set(identity, [
8740
+ process.env.NODE_ENV !== "production" && (writer.rawName === "tabIndex" || writer.rawName === "htmlFor") ? writer.rawName : name,
8741
+ writer.value,
8742
+ writer.firstOrder,
8743
+ writer.lastOrder
8744
+ ]);
8742
8745
  }
8743
8746
  const resolved = /* @__PURE__ */ Object.create(null);
8744
- const ordered = [...values.values()].sort((a, b) => a[2] - b[2]);
8747
+ const ordered = [...values.values()];
8748
+ if (needsWinningOrderSort) ordered.sort((a, b) => a[2] - b[2]);
8745
8749
  for (const [name, value] of ordered) resolved[name] = value;
8746
8750
  const formHost = el.localName === "input" || el.localName === "textarea" || el.localName === "select";
8747
8751
  setSpread(el, resolved, prev, scope, true, formHost);
@@ -9593,6 +9597,9 @@ let CHECKED_RESTORE = null;
9593
9597
  let SELECT_SYNCS = [];
9594
9598
  let SELECT_DEFAULT_SYNCS = [];
9595
9599
  let DEV_FORM_CHECKS = process.env.NODE_ENV === "production" ? null : [];
9600
+ const DEV_FORM_CHECK_MARK = /* @__PURE__ */ Symbol("octane.devFormCheck");
9601
+ const DEV_FORM_CHECK_LINEAR_LIMIT = 8;
9602
+ let DEV_FORM_CHECK_GENERATION = 1;
9596
9603
  let AUTOFOCUS_QUEUE = [];
9597
9604
  function queueControlledCommit(queue, item) {
9598
9605
  queue.push(item);
@@ -9602,6 +9609,18 @@ function queueControlledCommit(queue, item) {
9602
9609
  if (index !== -1) queue.splice(index, 1);
9603
9610
  });
9604
9611
  }
9612
+ function queueDevFormCheck(queue, el) {
9613
+ queue.push(el);
9614
+ if (ROOT_RENDER_TRANSACTION !== null) {
9615
+ const host = el;
9616
+ const generation = DEV_FORM_CHECK_GENERATION;
9617
+ journalUndo(() => {
9618
+ const index = queue.lastIndexOf(el);
9619
+ if (index !== -1) queue.splice(index, 1);
9620
+ if (host[DEV_FORM_CHECK_MARK] === generation) delete host[DEV_FORM_CHECK_MARK];
9621
+ });
9622
+ }
9623
+ }
9605
9624
  function hasControlledSyncs() {
9606
9625
  return SELECT_SYNCS.length > 0 || SELECT_DEFAULT_SYNCS.length > 0 || DEV_FORM_CHECKS !== null && DEV_FORM_CHECKS.length > 0 || AUTOFOCUS_QUEUE.length > 0;
9607
9626
  }
@@ -9755,7 +9774,19 @@ function queueDevFormDiagnostic(el, scope, force = false) {
9755
9774
  const q = DEV_FORM_CHECKS;
9756
9775
  if (q === null || !force && !hasDevFormDiagnosticContext(el, scope)) return;
9757
9776
  if (!force && !hasPotentialFormDiagnostic(el)) return;
9758
- if (q.indexOf(el) === -1) queueControlledCommit(q, el);
9777
+ if (q.length < DEV_FORM_CHECK_LINEAR_LIMIT) {
9778
+ if (q.indexOf(el) !== -1) return;
9779
+ queueDevFormCheck(q, el);
9780
+ if (q.length < DEV_FORM_CHECK_LINEAR_LIMIT) return;
9781
+ for (let i = 0; i < q.length; i++) {
9782
+ q[i][DEV_FORM_CHECK_MARK] = DEV_FORM_CHECK_GENERATION;
9783
+ }
9784
+ return;
9785
+ }
9786
+ const host = el;
9787
+ if (host[DEV_FORM_CHECK_MARK] === DEV_FORM_CHECK_GENERATION) return;
9788
+ queueDevFormCheck(q, el);
9789
+ host[DEV_FORM_CHECK_MARK] = DEV_FORM_CHECK_GENERATION;
9759
9790
  }
9760
9791
  function queueFormAuthoringDiagnostic(el, kind, selected) {
9761
9792
  if (process.env.NODE_ENV === "production") return;
@@ -10170,6 +10201,7 @@ function drainDevFormDiagnostics() {
10170
10201
  const q = DEV_FORM_CHECKS;
10171
10202
  if (q === null || q.length === 0) return;
10172
10203
  DEV_FORM_CHECKS = [];
10204
+ DEV_FORM_CHECK_GENERATION++;
10173
10205
  for (let i = 0; i < q.length; i++) {
10174
10206
  const el = q[i];
10175
10207
  const authoring = getDevFormDiagnosticState(el);
@@ -12041,6 +12073,14 @@ function reconcileDeoptNode(prev, value, ownerBlock, ns) {
12041
12073
  return null;
12042
12074
  }
12043
12075
  function reconcileDeoptChildren(el, children, ownerBlock) {
12076
+ const type = typeof children;
12077
+ if (type === "string" && children !== "" || type === "number" || type === "bigint") {
12078
+ const first = getFirstChild(el);
12079
+ if (first !== null && first.nodeType === 3 && getNextSibling(first) === null && first.$$deoptKey === 0 && first.$$portalEnd == null) {
12080
+ updateTextValue(first, String(children));
12081
+ return;
12082
+ }
12083
+ }
12044
12084
  const childNs = deoptChildNamespace(el);
12045
12085
  const next = [];
12046
12086
  const nextKeys = [];
@@ -12165,7 +12205,7 @@ function deoptItemBody(item, scope) {
12165
12205
  const block = scope.block;
12166
12206
  const hydration = activeHydration();
12167
12207
  const existingChild = scope.slots[0];
12168
- const needsBlocks = descNeedsBlocks(item) || isHostDescriptor(item) && existingChild?.__kind === "childSlot" && existingChild.currentComp === hostElementBody;
12208
+ const needsBlocks = isHostDescriptor(item) && existingChild?.__kind === "childSlot" && existingChild.currentComp === hostElementBody || descNeedsBlocks(item);
12169
12209
  const sm = block.startMarker;
12170
12210
  if (sm !== null && sm === block.endMarker && sm.nodeType !== 8 && sm.parentNode !== null && (needsBlocks || !isHostDescriptor(item))) {
12171
12211
  const p = sm.parentNode;
@@ -12981,7 +13021,7 @@ function childSlot(parentScope, slotKey, domParent, value, anchor, ownEnd, ownsH
12981
13021
  if (iterable !== null) value = iterable;
12982
13022
  const preparedList = compiledMapBody !== void 0 ? { items: value, keys: null } : prepareDeoptList(value, false, includeKeyedSingle);
12983
13023
  let state = parentScope.slots[slotKey];
12984
- const pureHost = preparedList === null && isHostDescriptor(value) && !descNeedsBlocks(value) && state?.currentComp !== hostElementBody;
13024
+ const pureHost = preparedList === null && isHostDescriptor(value) && state?.currentComp !== hostElementBody && !descNeedsBlocks(value);
12985
13025
  let rootShapeChanged = false;
12986
13026
  if (state !== void 0 && ROOT_RENDER_TRANSACTION !== null) {
12987
13027
  const component = pureHost || preparedList !== null ? null : isHostDescriptor(value) ? hostElementBody : valueComponent;
@@ -17461,6 +17501,10 @@ function updateSurvivor(block, newItem, newIdx, itemBody, pure, lite, indexIndep
17461
17501
  }
17462
17502
  }
17463
17503
  }
17504
+ function consumeAdoptQueuePrefix(adopt, count) {
17505
+ if (count < adopt.length) adopt.copyWithin(0, count);
17506
+ adopt.length -= count;
17507
+ }
17464
17508
  function mountItemsLinear(parentBlock, state, items, getKey, itemBody, singleRoot, ssrMarkerless) {
17465
17509
  const newLen = items.length;
17466
17510
  if (newLen === 0) return;
@@ -17468,6 +17512,7 @@ function mountItemsLinear(parentBlock, state, items, getKey, itemBody, singleRoo
17468
17512
  const oldItems = state.items;
17469
17513
  const parentNode = state.end.parentNode;
17470
17514
  const adopt = state.adopt;
17515
+ let adoptIndex = 0;
17471
17516
  let prev = null;
17472
17517
  const mounted = [];
17473
17518
  try {
@@ -17476,9 +17521,14 @@ function mountItemsLinear(parentBlock, state, items, getKey, itemBody, singleRoo
17476
17521
  const key = getKey(item, i);
17477
17522
  let adoptNode = null;
17478
17523
  let anchor = state.end;
17479
- if (adopt !== null && adopt.length !== 0) {
17480
- if (adopt[0].key === key) adoptNode = adopt.shift().node;
17481
- else anchor = adopt[0].node;
17524
+ if (adopt !== null && adoptIndex < adopt.length) {
17525
+ const candidate = adopt[adoptIndex];
17526
+ if (candidate.key === key) {
17527
+ adoptNode = candidate.node;
17528
+ adoptIndex++;
17529
+ } else {
17530
+ anchor = candidate.node;
17531
+ }
17482
17532
  }
17483
17533
  const block = mountItem(
17484
17534
  parentBlock,
@@ -17514,6 +17564,7 @@ function mountItemsLinear(parentBlock, state, items, getKey, itemBody, singleRoo
17514
17564
  state.size = 0;
17515
17565
  throw error;
17516
17566
  }
17567
+ if (adoptIndex !== 0) consumeAdoptQueuePrefix(adopt, adoptIndex);
17517
17568
  }
17518
17569
  function reconcileKeyed(parentBlock, state, items, getKey, itemBody, pure, singleRoot, lite = false, indexIndependent = false, ssrMarkerless = false) {
17519
17570
  const oldItems = state.items;
@@ -1421,6 +1421,7 @@ function ssrAttrs(sources, tag, namespace = "html", skipFormControls = false) {
1421
1421
  }
1422
1422
  }
1423
1423
  const resolved = /* @__PURE__ */ new Map();
1424
+ let needsWinningOrderSort = false;
1424
1425
  for (const writer of props.values()) {
1425
1426
  const { rawName, value, firstOrder, lastOrder } = writer;
1426
1427
  if (rawName === "key" || rawName === "ref" || rawName === "children" || rawName === "dangerouslySetInnerHTML" || rawName === "suppressHydrationWarning" || rawName === "suppressContentEditableWarning" || rawName === "suppressNativeChangeWarning" || rawName === "__octaneNativeChangeDiagnostic")
@@ -1434,17 +1435,20 @@ function ssrAttrs(sources, tag, namespace = "html", skipFormControls = false) {
1434
1435
  if (!import_constants.VALID_ATTR_NAME.test(name)) continue;
1435
1436
  const identity = namespace === "html" ? name.toLowerCase() : name;
1436
1437
  const previous = resolved.get(identity);
1437
- if (previous === void 0 || previous[3] < lastOrder) {
1438
- resolved.set(identity, [
1439
- process.env.NODE_ENV !== "production" && (rawName === "tabIndex" || rawName === "htmlFor") ? rawName : name,
1440
- value,
1441
- firstOrder,
1442
- lastOrder
1443
- ]);
1438
+ if (previous !== void 0) {
1439
+ if (previous[3] >= lastOrder) continue;
1440
+ needsWinningOrderSort = true;
1444
1441
  }
1442
+ resolved.set(identity, [
1443
+ process.env.NODE_ENV !== "production" && (rawName === "tabIndex" || rawName === "htmlFor") ? rawName : name,
1444
+ value,
1445
+ firstOrder,
1446
+ lastOrder
1447
+ ]);
1445
1448
  }
1446
1449
  let out = "";
1447
- const ordered = [...resolved.values()].sort((a, b) => a[2] - b[2]);
1450
+ const ordered = [...resolved.values()];
1451
+ if (needsWinningOrderSort) ordered.sort((a, b) => a[2] - b[2]);
1448
1452
  if (process.env.NODE_ENV !== "production") {
1449
1453
  devValidateSsrAriaProps(
1450
1454
  ordered.map(([name]) => name),
@@ -1932,21 +1936,9 @@ function captureComponentReplayState(scope, frame) {
1932
1936
  streamNextId: stream?.nextId ?? 0,
1933
1937
  streamActiveTryKeys: stream?.activeTryKeys.slice() ?? [],
1934
1938
  streamActiveOwnerKeys: stream?.activeOwnerKeys.slice() ?? [],
1935
- streamPassBoundaryKeys: stream?.activePassBoundaryKeys === null || stream?.activePassBoundaryKeys === void 0 ? null : new Set(stream.activePassBoundaryKeys),
1939
+ streamPassBoundaryCount: stream?.activePassBoundaryKeys?.size ?? 0,
1936
1940
  asyncScope: ASYNC_SCOPE,
1937
- streamBoundaries: stream === null ? null : Array.from(stream.boundaries, ([key, entry]) => ({
1938
- key,
1939
- entry,
1940
- id: entry.id,
1941
- order: entry.order,
1942
- state: entry.state,
1943
- html: entry.html,
1944
- seeds: entry.seeds.slice(),
1945
- pendingIdOffset: entry.pendingIdOffset,
1946
- ancestors: entry.ancestors.slice(),
1947
- owners: entry.owners.slice(),
1948
- namespace: entry.namespace
1949
- })),
1941
+ streamReplayCheckpoint: stream?.replay?.length ?? 0,
1950
1942
  frameDeferred: frame?.deferred ?? false,
1951
1943
  frameNextChild: frame?.nextChild ?? 0,
1952
1944
  frameScopedChildren: frame?.scopedChildren === null || frame?.scopedChildren === void 0 ? null : new Map(frame.scopedChildren),
@@ -1996,30 +1988,19 @@ function rewindComponentReplayState(snapshot, scope, frame) {
1996
1988
  VT_SSR_STACK.push(entry.candidate);
1997
1989
  }
1998
1990
  const stream = snapshot.stream;
1999
- if (stream !== null && snapshot.streamBoundaries !== null) {
1991
+ if (stream !== null) {
2000
1992
  stream.nextId = snapshot.streamNextId;
2001
- if (stream.activePassBoundaryKeys !== null && snapshot.streamPassBoundaryKeys !== null) {
2002
- stream.activePassBoundaryKeys.clear();
2003
- for (const key of snapshot.streamPassBoundaryKeys) stream.activePassBoundaryKeys.add(key);
1993
+ if (stream.activePassBoundaryKeys !== null) {
1994
+ let index = 0;
1995
+ for (const key of stream.activePassBoundaryKeys) {
1996
+ if (index++ >= snapshot.streamPassBoundaryCount) stream.activePassBoundaryKeys.delete(key);
1997
+ }
2004
1998
  }
2005
1999
  stream.activeTryKeys.length = 0;
2006
2000
  stream.activeTryKeys.push(...snapshot.streamActiveTryKeys);
2007
2001
  stream.activeOwnerKeys.length = 0;
2008
2002
  stream.activeOwnerKeys.push(...snapshot.streamActiveOwnerKeys);
2009
- stream.boundaries.clear();
2010
- for (const saved of snapshot.streamBoundaries) {
2011
- const entry = saved.entry;
2012
- entry.id = saved.id;
2013
- entry.order = saved.order;
2014
- entry.state = saved.state;
2015
- entry.html = saved.html;
2016
- entry.seeds = saved.seeds.slice();
2017
- entry.pendingIdOffset = saved.pendingIdOffset;
2018
- entry.ancestors = saved.ancestors.slice();
2019
- entry.owners = saved.owners.slice();
2020
- entry.namespace = saved.namespace;
2021
- stream.boundaries.set(saved.key, entry);
2022
- }
2003
+ rewindStreamBoundaryReplay(stream, snapshot.streamReplayCheckpoint);
2023
2004
  }
2024
2005
  scope.$$ctxValues = snapshot.context;
2025
2006
  if (frame !== null) {
@@ -3920,6 +3901,43 @@ function renderToStaticMarkup(entryComponent, props, options) {
3920
3901
  const html = spliceHead(pass.body, pass.head);
3921
3902
  return { html: pass.vtCandidates ? vtSsrStrip(html) : html, css: pass.css };
3922
3903
  }
3904
+ function recordStreamBoundaryMutation(stream, key) {
3905
+ if (stream.replay === null) return;
3906
+ const boundary = stream.boundaries.get(key);
3907
+ stream.replay.push({
3908
+ key,
3909
+ boundary,
3910
+ value: boundary === void 0 ? void 0 : { ...boundary }
3911
+ });
3912
+ }
3913
+ function rewindStreamBoundaryReplay(stream, checkpoint) {
3914
+ const replay = stream.replay;
3915
+ if (replay === null) return;
3916
+ let restoredDeletion = false;
3917
+ while (replay.length > checkpoint) {
3918
+ const saved = replay.pop();
3919
+ if (saved.boundary === void 0) {
3920
+ stream.boundaries.delete(saved.key);
3921
+ } else {
3922
+ const boundary = saved.boundary;
3923
+ const value = saved.value;
3924
+ Object.assign(boundary, value);
3925
+ boundary.error = value.error;
3926
+ boundary.errorReported = value.errorReported;
3927
+ boundary.errorFlushed = value.errorFlushed;
3928
+ if (!stream.boundaries.has(saved.key)) restoredDeletion = true;
3929
+ stream.boundaries.set(saved.key, boundary);
3930
+ }
3931
+ }
3932
+ if (restoredDeletion) {
3933
+ const ordered = [...stream.boundaries].sort((a, b) => a[1].order - b[1].order);
3934
+ stream.boundaries.clear();
3935
+ for (const [key, boundary] of ordered) stream.boundaries.set(key, boundary);
3936
+ }
3937
+ }
3938
+ function recordStreamBoundaryOwners(stream, owners) {
3939
+ for (const owner of owners) stream.boundaryOwnerKeys.add(owner);
3940
+ }
3923
3941
  let STREAM_REALM_SALT = null;
3924
3942
  function streamRealmSalt() {
3925
3943
  if (STREAM_REALM_SALT !== null) return STREAM_REALM_SALT;
@@ -3933,6 +3951,7 @@ function createStreamToken() {
3933
3951
  }
3934
3952
  let STREAM = null;
3935
3953
  function pruneUnrepresentedStreamDescendants(stream, ownerKey, ownerHtml) {
3954
+ if (!stream.boundaryOwnerKeys.has(ownerKey)) return;
3936
3955
  let removed = true;
3937
3956
  while (removed) {
3938
3957
  removed = false;
@@ -3948,6 +3967,7 @@ function pruneUnrepresentedStreamDescendants(stream, ownerKey, ownerHtml) {
3948
3967
  }
3949
3968
  if (nearestOwner !== ownerKey) continue;
3950
3969
  if (ownerHtml.includes(import_constants.STREAM_BOUNDARY_ATTR + '="' + child.id + '"')) continue;
3970
+ recordStreamBoundaryMutation(stream, childKey);
3951
3971
  stream.boundaries.delete(childKey);
3952
3972
  removed = true;
3953
3973
  }
@@ -3987,7 +4007,11 @@ function ssrTry(scope, siteKey, tryFn, pendFn, catchFn, namespace = FRAME?.names
3987
4007
  ancestorKeys = stream.activeTryKeys.slice();
3988
4008
  ownerKeys = stream.activeOwnerKeys.slice();
3989
4009
  entry = stream.boundaries.get(key);
3990
- if (entry !== void 0) entry.namespace = namespace;
4010
+ if (entry !== void 0) {
4011
+ recordStreamBoundaryMutation(stream, key);
4012
+ if (ownerKeys.length !== 0) recordStreamBoundaryOwners(stream, ownerKeys);
4013
+ entry.namespace = namespace;
4014
+ }
3991
4015
  if (entry !== void 0 && entry.state === "pending") {
3992
4016
  entry.ancestors = ancestorKeys;
3993
4017
  entry.owners = ownerKeys;
@@ -4183,7 +4207,9 @@ function ssrTry(scope, siteKey, tryFn, pendFn, catchFn, namespace = FRAME?.names
4183
4207
  ancestors: ancestorKeys,
4184
4208
  owners: ownerKeys
4185
4209
  };
4210
+ recordStreamBoundaryMutation(stream, key);
4186
4211
  stream.boundaries.set(key, entry);
4212
+ if (ownerKeys.length !== 0) recordStreamBoundaryOwners(stream, ownerKeys);
4187
4213
  enterBoundaryIds(pendingIdOffset);
4188
4214
  } else {
4189
4215
  ID_COUNTER = entry.pendingIdOffset;
@@ -4235,7 +4261,9 @@ function ssrTry(scope, siteKey, tryFn, pendFn, catchFn, namespace = FRAME?.names
4235
4261
  ancestors: ancestorKeys,
4236
4262
  owners: ownerKeys
4237
4263
  };
4264
+ recordStreamBoundaryMutation(stream, key);
4238
4265
  stream.boundaries.set(key, entry);
4266
+ if (ownerKeys.length !== 0) recordStreamBoundaryOwners(stream, ownerKeys);
4239
4267
  enterBoundaryIds(pendingIdOffset);
4240
4268
  } else if (entry.state === "pending") {
4241
4269
  entry.state = "errored";
@@ -4333,16 +4361,20 @@ async function runStream(component, props, options, sink) {
4333
4361
  const resolved = newResolvedMap();
4334
4362
  const stream = {
4335
4363
  boundaries: /* @__PURE__ */ new Map(),
4364
+ boundaryOwnerKeys: /* @__PURE__ */ new Set(),
4336
4365
  nextId: 0,
4337
4366
  token: createStreamToken(),
4338
4367
  activePassBoundaryKeys: null,
4339
4368
  activeTryKeys: [],
4340
- activeOwnerKeys: []
4369
+ activeOwnerKeys: [],
4370
+ replay: null
4341
4371
  };
4342
4372
  const renderFullPass = () => {
4343
4373
  const boundaryKeys = /* @__PURE__ */ new Set();
4344
4374
  const previousBoundaryKeys = stream.activePassBoundaryKeys;
4375
+ const previousReplay = stream.replay;
4345
4376
  stream.activePassBoundaryKeys = boundaryKeys;
4377
+ stream.replay = [];
4346
4378
  try {
4347
4379
  return {
4348
4380
  pass: withStream(
@@ -4353,6 +4385,7 @@ async function runStream(component, props, options, sink) {
4353
4385
  };
4354
4386
  } finally {
4355
4387
  stream.activePassBoundaryKeys = previousBoundaryKeys;
4388
+ stream.replay = previousReplay;
4356
4389
  }
4357
4390
  };
4358
4391
  const injection = options?.injection;
@@ -21,7 +21,7 @@ __export(version_exports, {
21
21
  version: () => version
22
22
  });
23
23
  module.exports = __toCommonJS(version_exports);
24
- const version = "0.1.45";
24
+ const version = "0.1.47";
25
25
  // Annotate the CommonJS export names for ESM import in node:
26
26
  0 && (module.exports = {
27
27
  version