octane 0.4.2 → 0.5.0

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 (108) hide show
  1. package/dist/behavior-form-submissions.d.ts +12 -0
  2. package/dist/behavior-form-submissions.js +228 -0
  3. package/dist/behavior-root-types.d.ts +93 -0
  4. package/dist/behavior-root-types.js +0 -0
  5. package/dist/behavior-root.d.ts +10 -2
  6. package/dist/behavior-root.js +128 -119
  7. package/dist/cjs/behavior-form-submissions.cjs +232 -0
  8. package/dist/cjs/behavior-root.cjs +129 -119
  9. package/dist/cjs/context-identity.cjs +29 -0
  10. package/dist/cjs/dom-bindings.cjs +266 -19
  11. package/dist/cjs/dom-stage.cjs +24 -18
  12. package/dist/cjs/error-codes.server.generated.cjs +5 -0
  13. package/dist/cjs/form-submission.cjs +74 -0
  14. package/dist/cjs/hydration/control-capture.cjs +2 -1
  15. package/dist/cjs/hydration/event-capture.cjs +77 -4
  16. package/dist/cjs/hydration/native-intent.cjs +95 -0
  17. package/dist/cjs/hydration-markers.cjs +12 -0
  18. package/dist/cjs/index.cjs +5 -0
  19. package/dist/cjs/runtime.cjs +317 -401
  20. package/dist/cjs/runtime.server.cjs +193 -44
  21. package/dist/cjs/server/early-signals.cjs +20 -5
  22. package/dist/cjs/server/index.cjs +2 -0
  23. package/dist/cjs/server/streamed-region.cjs +2 -1
  24. package/dist/cjs/server/streamed-signals.cjs +41 -9
  25. package/dist/cjs/signals/client.cjs +1 -6
  26. package/dist/cjs/signals/engine.cjs +92 -197
  27. package/dist/cjs/signals/facade.cjs +6 -4
  28. package/dist/cjs/signals/native-read-seeds.cjs +131 -10
  29. package/dist/cjs/signals/requests.cjs +16 -16
  30. package/dist/cjs/signals/scope-streams.cjs +170 -0
  31. package/dist/cjs/version.cjs +1 -1
  32. package/dist/compiler/client-only-server.js +1 -28
  33. package/dist/compiler/compile-renderer-boundaries.js +1 -22
  34. package/dist/compiler/compile-universal.js +30 -31
  35. package/dist/compiler/compile.js +932 -401
  36. package/dist/compiler/dom-binding-fixed-props.js +11 -1
  37. package/dist/compiler/dom-binding-program.js +7 -5
  38. package/dist/compiler/dom-bindings.js +135 -21
  39. package/dist/compiler/generated-origin.js +46 -0
  40. package/dist/compiler/hydrate-boundaries.js +124 -52
  41. package/dist/compiler/plain-hook-memo.js +5 -1
  42. package/dist/compiler/pure-factories.js +66 -0
  43. package/dist/compiler/register.js +4 -1
  44. package/dist/compiler/slot-hooks.js +113 -186
  45. package/dist/compiler/typescript-lowering.js +763 -0
  46. package/dist/compiler/void-roots.js +239 -0
  47. package/dist/compiler/volar.LICENSES.txt +1 -1
  48. package/dist/compiler/volar.js +820 -175
  49. package/dist/context-identity.d.ts +14 -0
  50. package/dist/context-identity.js +28 -0
  51. package/dist/dom-binding-program.d.ts +2 -2
  52. package/dist/dom-binding-signals.d.ts +2 -0
  53. package/dist/dom-binding-signals.js +30 -1
  54. package/dist/dom-bindings.d.ts +8 -0
  55. package/dist/dom-bindings.js +265 -19
  56. package/dist/dom-stage.js +24 -18
  57. package/dist/error-codes.server.generated.d.ts +1 -0
  58. package/dist/error-codes.server.generated.js +5 -0
  59. package/dist/form-submission.d.ts +66 -0
  60. package/dist/form-submission.js +43 -0
  61. package/dist/hydration/control-capture.d.ts +4 -2
  62. package/dist/hydration/control-capture.js +2 -1
  63. package/dist/hydration/event-capture.d.ts +6 -0
  64. package/dist/hydration/event-capture.js +79 -4
  65. package/dist/hydration/idle.js +1 -0
  66. package/dist/hydration/independent-island.d.ts +7 -1
  67. package/dist/hydration/independent-island.js +91 -15
  68. package/dist/hydration/media.js +1 -0
  69. package/dist/hydration/native-intent.d.ts +29 -0
  70. package/dist/hydration/native-intent.js +68 -0
  71. package/dist/hydration/streamed-signals.d.ts +2 -2
  72. package/dist/hydration/streamed-signals.js +2 -0
  73. package/dist/hydration/types.d.ts +5 -0
  74. package/dist/hydration/visible.js +1 -0
  75. package/dist/hydration-markers.d.ts +8 -0
  76. package/dist/hydration-markers.js +8 -0
  77. package/dist/index.d.ts +2 -1
  78. package/dist/index.js +4 -0
  79. package/dist/runtime.d.ts +34 -41
  80. package/dist/runtime.js +320 -406
  81. package/dist/runtime.server.d.ts +33 -5
  82. package/dist/runtime.server.js +200 -46
  83. package/dist/server/early-signals.d.ts +3 -1
  84. package/dist/server/early-signals.js +25 -5
  85. package/dist/server/index.d.ts +1 -1
  86. package/dist/server/index.js +2 -0
  87. package/dist/server/streamed-region.d.ts +3 -0
  88. package/dist/server/streamed-region.js +2 -1
  89. package/dist/server/streamed-signals.js +41 -9
  90. package/dist/signals/client.js +3 -8
  91. package/dist/signals/engine.d.ts +12 -25
  92. package/dist/signals/engine.js +93 -204
  93. package/dist/signals/facade.js +6 -4
  94. package/dist/signals/native-read-seeds.d.ts +23 -4
  95. package/dist/signals/native-read-seeds.js +129 -10
  96. package/dist/signals/requests.d.ts +8 -5
  97. package/dist/signals/requests.js +16 -16
  98. package/dist/signals/scope-streams.d.ts +37 -0
  99. package/dist/signals/scope-streams.js +147 -0
  100. package/dist/tsrx-iterable.d.ts +7 -6
  101. package/dist/tsrx-iterable.js +3 -2
  102. package/dist/universal-core.js +16 -4
  103. package/dist/version.js +1 -1
  104. package/package.json +3 -3
  105. package/dist/cjs/signals/diagnostic-local-hook.cjs +0 -37
  106. package/dist/compiler/local-void-roots.js +0 -154
  107. package/dist/signals/diagnostic-local-hook.d.ts +0 -8
  108. package/dist/signals/diagnostic-local-hook.js +0 -12
@@ -1,6 +1,23 @@
1
1
  import { adoptBindings, mountBindings, unbound } from "./dom-bindings.js";
2
+ import { captureFormSubmissions } from "./behavior-form-submissions.js";
2
3
  const documentRegistries = /* @__PURE__ */ new WeakMap();
3
- const CANCELED = /* @__PURE__ */ Symbol("octane.behavior.canceled");
4
+ const DEFAULT_DOM = {
5
+ element: isElement,
6
+ parent: (node) => node.parentElement,
7
+ contains: (container, element, document) => element.ownerDocument === document && (element === container || container.contains(element)),
8
+ matches: (element, selector) => element.matches(selector),
9
+ query: (element, selector) => element.querySelectorAll(selector),
10
+ target: targetElement
11
+ };
12
+ function registryFor(document) {
13
+ let registry = documentRegistries.get(document);
14
+ if (registry === void 0) {
15
+ registry = { roots: /* @__PURE__ */ new Map(), ranges: /* @__PURE__ */ new Map() };
16
+ documentRegistries.set(document, registry);
17
+ }
18
+ return registry;
19
+ }
20
+ const CANCELED = /* @__PURE__ */ Symbol("behavior.canceled");
4
21
  function createReadiness() {
5
22
  let resolvePromise;
6
23
  let rejectPromise;
@@ -41,44 +58,30 @@ function linkSignal(signal, controller) {
41
58
  function waitUntilReady(source, signal) {
42
59
  if (signal.aborted) {
43
60
  void Promise.resolve(source).then(void 0, () => void 0);
44
- return Promise.resolve(CANCELED);
61
+ return Promise.resolve();
45
62
  }
46
63
  return new Promise((resolve, reject) => {
47
- let settled = false;
48
- const cancel = () => {
49
- if (settled) return;
50
- settled = true;
51
- resolve(CANCELED);
64
+ const complete = () => {
65
+ signal.removeEventListener("abort", complete);
66
+ resolve();
52
67
  };
53
- signal.addEventListener("abort", cancel, { once: true });
54
- Promise.resolve(source).then(
55
- (value) => {
56
- if (settled) return;
57
- settled = true;
58
- signal.removeEventListener("abort", cancel);
59
- resolve(value);
60
- },
61
- (error) => {
62
- if (settled) return;
63
- settled = true;
64
- signal.removeEventListener("abort", cancel);
65
- reject(error);
66
- }
67
- );
68
+ signal.addEventListener("abort", complete);
69
+ Promise.resolve(source).then(complete, (error) => {
70
+ signal.removeEventListener("abort", complete);
71
+ reject(error);
72
+ });
68
73
  });
69
74
  }
70
75
  function trackPending(root, promise) {
71
76
  root.pending.add(promise);
72
- void promise.then(
73
- () => root.pending.delete(promise),
74
- () => root.pending.delete(promise)
75
- );
77
+ const remove = () => root.pending.delete(promise);
78
+ void promise.then(remove, remove);
76
79
  }
77
80
  function isElement(value) {
78
81
  return typeof value === "object" && value !== null && value.nodeType === 1;
79
82
  }
80
- function contains(root, element) {
81
- return element.ownerDocument === root.document && (element === root.container || root.container.contains(element));
83
+ function contains(root, element, container = root.container) {
84
+ return root.dom.contains(container, element, root.document);
82
85
  }
83
86
  function assertActiveRoot(root) {
84
87
  if (root.disposed || root.controller.signal.aborted) {
@@ -86,7 +89,7 @@ function assertActiveRoot(root) {
86
89
  }
87
90
  }
88
91
  function assertContainedElement(root, element, label) {
89
- if (!isElement(element) || !contains(root, element)) {
92
+ if (!root.dom.element(element) || !contains(root, element)) {
90
93
  throw new Error(`${label} must belong to the behavior root container and document.`);
91
94
  }
92
95
  }
@@ -98,28 +101,28 @@ function nearestRange(root, element) {
98
101
  return range;
99
102
  }
100
103
  if (current === root.container) return void 0;
101
- current = current.parentElement;
104
+ current = root.dom.parent(current);
102
105
  }
103
106
  return void 0;
104
107
  }
105
108
  function matchesTarget(record, element) {
106
- return typeof record.entry.target === "string" ? element.matches(record.entry.target) : record.entry.target === element;
109
+ return typeof record.entry.target === "string" ? record.root.dom.matches(element, record.entry.target) : record.entry.target === element;
107
110
  }
108
111
  function rangeMatches(record, range) {
109
112
  return record.entry.owner === void 0 || range !== void 0 && Object.is(record.entry.owner, range.owner);
110
113
  }
111
114
  function dependsOnRange(record, range) {
112
- if (record.waitingRanges.has(range)) return true;
115
+ if (record.waiting.has(range)) return true;
113
116
  if (record.entry.owner === void 0 || !Object.is(record.entry.owner, range.owner) || !contains(record.root, range.element)) {
114
117
  return false;
115
118
  }
116
119
  if (typeof record.entry.target !== "string") {
117
- return range.element.contains(record.entry.target) && nearestRange(record.root, record.entry.target) === range;
120
+ return contains(record.root, record.entry.target, range.element) && nearestRange(record.root, record.entry.target) === range;
118
121
  }
119
- if (range.element.matches(record.entry.target) && nearestRange(record.root, range.element) === range) {
122
+ if (record.root.dom.matches(range.element, record.entry.target) && nearestRange(record.root, range.element) === range) {
120
123
  return true;
121
124
  }
122
- for (const element of range.element.querySelectorAll(record.entry.target)) {
125
+ for (const element of record.root.dom.query(range.element, record.entry.target)) {
123
126
  if (nearestRange(record.root, element) === range) return true;
124
127
  }
125
128
  return false;
@@ -135,7 +138,7 @@ function adoptionContext(adoption, event) {
135
138
  function finishPendingAdoption(adoption) {
136
139
  if (!adoption.pending) return;
137
140
  adoption.pending = false;
138
- adoption.entry.pendingAdoptionCount--;
141
+ adoption.entry.pendingCount--;
139
142
  }
140
143
  function disposeAdoption(adoption) {
141
144
  if (adoption.disposed) return;
@@ -151,19 +154,19 @@ function disposeAdoption(adoption) {
151
154
  }
152
155
  function flushQueuedEvents(record) {
153
156
  if (record.status !== "active" || record.captureDepth) return;
154
- const queue = record.queuedEvents;
155
- record.queuedEventFlushDepth++;
157
+ const queue = record.queue;
158
+ record.flushDepth++;
156
159
  try {
157
- while (record.queuedEventHead < queue.length) {
158
- const index = record.queuedEventHead;
160
+ while (record.head < queue.length) {
161
+ const index = record.head;
159
162
  const queued = queue[index];
160
- if (queued.payload === CANCELED || !contains(record.root, queued.element)) {
161
- record.queuedEventHead = index + 1;
163
+ if (queued.payload === CANCELED || !(queued.valid?.() ?? contains(record.root, queued.element))) {
164
+ record.head = index + 1;
162
165
  continue;
163
166
  }
164
167
  const range = nearestRange(record.root, queued.element);
165
168
  if (range !== queued.range || !rangeMatches(record, range)) {
166
- record.queuedEventHead = index + 1;
169
+ record.head = index + 1;
167
170
  continue;
168
171
  }
169
172
  if (range?.status === "pending") return;
@@ -172,8 +175,9 @@ function flushQueuedEvents(record) {
172
175
  adoption = adoptElement(record, queued.element, range, queued.event);
173
176
  }
174
177
  if (adoption === void 0 || adoption.pending) return;
175
- if (record.queuedEventHead !== index || queue[index] !== queued) continue;
176
- record.queuedEventHead = index + 1;
178
+ if (record.head !== index || queue[index] !== queued) continue;
179
+ record.head = index + 1;
180
+ if (queued.valid?.() === false) continue;
177
181
  record.entry.handleEvent?.(
178
182
  queued.event,
179
183
  queued.element,
@@ -182,34 +186,34 @@ function flushQueuedEvents(record) {
182
186
  );
183
187
  }
184
188
  } finally {
185
- record.queuedEventFlushDepth--;
186
- if (record.queuedEventFlushDepth === 0 && record.queuedEventHead !== 0) {
187
- const consumed = record.queuedEventHead;
189
+ record.flushDepth--;
190
+ if (record.flushDepth === 0 && record.head !== 0) {
191
+ const consumed = record.head;
188
192
  if (consumed >= queue.length) {
189
193
  queue.length = 0;
190
- record.queuedEventHead = 0;
194
+ record.head = 0;
191
195
  } else if (consumed >= queue.length - consumed) {
192
196
  queue.splice(0, consumed);
193
- record.queuedEventHead = 0;
197
+ record.head = 0;
194
198
  }
195
199
  }
196
200
  }
197
201
  }
198
202
  function completeBehavior(record) {
199
- if (record.status !== "active" || !record.initialScanComplete) return;
200
- for (const range of [...record.waitingRanges]) {
203
+ if (record.status !== "active" || !record.scanned) return;
204
+ for (const range of record.waiting) {
201
205
  if (range.status === "pending") return;
202
- record.waitingRanges.delete(range);
206
+ record.waiting.delete(range);
203
207
  }
204
- if (record.pendingAdoptionCount !== 0) return;
208
+ if (record.pendingCount !== 0) return;
205
209
  flushQueuedEvents(record);
206
- if (record.queuedEvents.length !== 0) return;
207
- record.readiness.resolve();
210
+ if (record.queue.length !== 0) return;
211
+ record.ready.resolve();
208
212
  }
209
213
  function failBehavior(record, error) {
210
214
  if (record.status === "disposed" || record.status === "failed") return;
211
215
  record.status = "failed";
212
- record.readiness.reject(error);
216
+ record.ready.reject(error);
213
217
  disposeBehavior(record);
214
218
  }
215
219
  function adoptElement(record, element, range, event) {
@@ -217,7 +221,7 @@ function adoptElement(record, element, range, event) {
217
221
  return void 0;
218
222
  }
219
223
  if (range?.status === "pending") {
220
- record.waitingRanges.add(range);
224
+ record.waiting.add(range);
221
225
  return void 0;
222
226
  }
223
227
  const existing = record.adoptions.get(element);
@@ -251,7 +255,7 @@ function adoptElement(record, element, range, event) {
251
255
  }
252
256
  if (typeof result === "object" && result !== null && typeof result.then === "function") {
253
257
  adoption.pending = true;
254
- record.pendingAdoptionCount++;
258
+ record.pendingCount++;
255
259
  const settled = Promise.resolve(result).then(
256
260
  (cleanup) => {
257
261
  finishPendingAdoption(adoption);
@@ -271,24 +275,26 @@ function adoptElement(record, element, range, event) {
271
275
  failBehavior(record, error);
272
276
  }
273
277
  );
274
- const cancelable = waitUntilReady(settled, adoption.controller.signal).then(() => void 0);
278
+ const cancelable = waitUntilReady(settled, adoption.controller.signal);
275
279
  trackPending(record.root, cancelable);
276
280
  }
277
281
  return adoption;
278
282
  }
279
283
  function reconcileBehavior(record) {
280
284
  if (record.status !== "active") return;
281
- record.waitingRanges.clear();
285
+ record.waiting.clear();
282
286
  for (const [element, adoption] of [...record.adoptions]) {
283
- const range = contains(record.root, element) ? nearestRange(record.root, element) : void 0;
284
- if (!contains(record.root, element) || !matchesTarget(record, element) || !rangeMatches(record, range) || range !== adoption.range || range?.status === "pending") {
287
+ const contained = contains(record.root, element);
288
+ const range = contained ? nearestRange(record.root, element) : void 0;
289
+ if (!contained || !matchesTarget(record, element) || !rangeMatches(record, range) || range !== adoption.range || range?.status === "pending") {
285
290
  disposeAdoption(adoption);
286
291
  }
287
292
  }
288
293
  let elements;
289
294
  if (typeof record.entry.target === "string") {
290
- elements = Array.from(record.root.container.querySelectorAll(record.entry.target));
291
- if (record.root.container.matches(record.entry.target)) elements.unshift(record.root.container);
295
+ elements = Array.from(record.root.dom.query(record.root.container, record.entry.target));
296
+ if (record.root.dom.matches(record.root.container, record.entry.target))
297
+ elements.unshift(record.root.container);
292
298
  } else {
293
299
  elements = contains(record.root, record.entry.target) ? [record.entry.target] : [];
294
300
  }
@@ -296,15 +302,16 @@ function reconcileBehavior(record) {
296
302
  const range = nearestRange(record.root, element);
297
303
  if (!rangeMatches(record, range)) continue;
298
304
  if (range?.status === "pending") {
299
- record.waitingRanges.add(range);
305
+ record.waiting.add(range);
300
306
  continue;
301
307
  }
302
308
  adoptElement(record, element, range);
303
309
  }
304
- record.initialScanComplete = true;
310
+ record.scanned = true;
305
311
  completeBehavior(record);
306
312
  }
307
313
  function refreshDocument(registry) {
314
+ registry.capture?.flush();
308
315
  for (const root of [...registry.roots.values()]) {
309
316
  if (root.disposed) continue;
310
317
  for (const record of [...root.behaviors]) reconcileBehavior(record);
@@ -327,7 +334,8 @@ function targetElement(event) {
327
334
  }
328
335
  function handleDelegatedEvent(root, event) {
329
336
  if (root.disposed) return;
330
- const target = targetElement(event);
337
+ if (root.registry.capture?.skip(root, event)) return;
338
+ const target = root.dom.target(event);
331
339
  if (target === null || !contains(root, target)) return;
332
340
  for (const record of [...root.behaviors]) {
333
341
  if (record.status === "disposed" || record.status === "failed" || !record.entry.events?.includes(event.type)) {
@@ -339,15 +347,15 @@ function handleDelegatedEvent(root, event) {
339
347
  matched = null;
340
348
  break;
341
349
  }
342
- matched = matched.parentElement;
350
+ matched = root.dom.parent(matched);
343
351
  }
344
352
  if (matched === null || !contains(root, matched)) continue;
345
353
  const range = nearestRange(root, matched);
346
354
  if (!rangeMatches(record, range)) continue;
347
355
  if (record.entry.captureEvent !== void 0) {
348
356
  const queued = { event, element: matched, range, payload: CANCELED };
349
- record.queuedEvents.push(queued);
350
- if (range?.status === "pending") record.waitingRanges.add(range);
357
+ record.queue.push(queued);
358
+ if (range?.status === "pending") record.waiting.add(range);
351
359
  record.captureDepth = (record.captureDepth ?? 0) + 1;
352
360
  try {
353
361
  const payload = record.entry.captureEvent(event, matched);
@@ -363,13 +371,13 @@ function handleDelegatedEvent(root, event) {
363
371
  continue;
364
372
  }
365
373
  if (record.status !== "active" || range?.status === "pending") {
366
- record.queuedEvents.push({ event, element: matched, range });
367
- if (range?.status === "pending") record.waitingRanges.add(range);
374
+ record.queue.push({ event, element: matched, range });
375
+ if (range?.status === "pending") record.waiting.add(range);
368
376
  continue;
369
377
  }
370
378
  const adoption = record.adoptions.get(matched) ?? adoptElement(record, matched, range, event);
371
- if (adoption === void 0 || adoption.pending || record.queuedEvents.length !== 0) {
372
- record.queuedEvents.push({ event, element: matched, range });
379
+ if (adoption === void 0 || adoption.pending || record.queue.length !== 0) {
380
+ record.queue.push({ event, element: matched, range });
373
381
  flushQueuedEvents(record);
374
382
  continue;
375
383
  }
@@ -408,18 +416,19 @@ function removeUnusedListeners(root) {
408
416
  }
409
417
  function disposeBehavior(record) {
410
418
  if (record.status === "disposed") return;
419
+ record.root.registry.capture?.release(record);
411
420
  const failed = record.status === "failed";
412
421
  record.status = "disposed";
413
422
  record.controller.abort();
414
423
  for (const unlink of record.unlinks) unlink();
415
424
  record.unlinks.length = 0;
416
425
  record.root.behaviors.delete(record);
417
- if (record.entry.id !== void 0 && record.root.behaviorsById.get(record.entry.id) === record) {
418
- record.root.behaviorsById.delete(record.entry.id);
426
+ if (record.entry.id !== void 0 && record.root.byId.get(record.entry.id) === record) {
427
+ record.root.byId.delete(record.entry.id);
419
428
  }
420
- record.queuedEvents.length = 0;
421
- record.queuedEventHead = 0;
422
- record.waitingRanges.clear();
429
+ record.queue.length = 0;
430
+ record.head = 0;
431
+ record.waiting.clear();
423
432
  let firstError;
424
433
  for (const adoption of [...record.adoptions.values()]) {
425
434
  try {
@@ -428,7 +437,7 @@ function disposeBehavior(record) {
428
437
  firstError ??= error;
429
438
  }
430
439
  }
431
- if (!failed) record.readiness.resolve();
440
+ if (!failed) record.ready.resolve();
432
441
  removeUnusedListeners(record.root);
433
442
  if (firstError !== void 0) throw firstError;
434
443
  }
@@ -445,14 +454,14 @@ function registerBehavior(root, entry) {
445
454
  if (typeof entry.target !== "string") {
446
455
  assertContainedElement(root, entry.target, "A behavior target");
447
456
  } else {
448
- root.container.matches(entry.target);
457
+ root.dom.matches(root.container, entry.target);
449
458
  }
450
- if (entry.id !== void 0 && root.behaviorsById.has(entry.id)) {
459
+ if (entry.id !== void 0 && root.byId.has(entry.id)) {
451
460
  throw new Error(`A behavior with identity ${JSON.stringify(entry.id)} is already registered.`);
452
461
  }
453
462
  const dependencies = [];
454
463
  for (const id of entry.dependencies ?? []) {
455
- const dependency = root.behaviorsById.get(id);
464
+ const dependency = root.byId.get(id);
456
465
  if (dependency === void 0) {
457
466
  throw new Error(`Behavior dependency ${JSON.stringify(id)} is not registered.`);
458
467
  }
@@ -469,16 +478,16 @@ function registerBehavior(root, entry) {
469
478
  root,
470
479
  entry,
471
480
  controller,
472
- readiness,
481
+ ready: readiness,
473
482
  status: "pending",
474
483
  adoptions: /* @__PURE__ */ new Map(),
475
- pendingAdoptionCount: 0,
476
- waitingRanges: /* @__PURE__ */ new Set(),
477
- queuedEvents: [],
478
- queuedEventHead: 0,
479
- queuedEventFlushDepth: 0,
484
+ pendingCount: 0,
485
+ waiting: /* @__PURE__ */ new Set(),
486
+ queue: [],
487
+ head: 0,
488
+ flushDepth: 0,
480
489
  unlinks: [linkSignal(root.controller.signal, controller)],
481
- initialScanComplete: false
490
+ scanned: false
482
491
  };
483
492
  const registration = {
484
493
  ...entry.id === void 0 ? {} : { id: entry.id },
@@ -492,11 +501,12 @@ function registerBehavior(root, entry) {
492
501
  return registration;
493
502
  }
494
503
  root.behaviors.add(record);
495
- if (entry.id !== void 0) root.behaviorsById.set(entry.id, record);
504
+ if (entry.id !== void 0) root.byId.set(entry.id, record);
496
505
  controller.signal.addEventListener("abort", () => disposeBehavior(record), { once: true });
497
506
  try {
498
507
  installListeners(root, entry.events);
499
508
  ensureObserver(root);
509
+ root.registry.capture?.flush();
500
510
  } catch (error) {
501
511
  disposeBehavior(record);
502
512
  throw error;
@@ -504,7 +514,7 @@ function registerBehavior(root, entry) {
504
514
  const blockers = [];
505
515
  if (entry.ready !== void 0) blockers.push(entry.ready);
506
516
  for (const dependency of dependencies) {
507
- if (!dependency.readiness.settled) blockers.push(dependency.readiness.promise);
517
+ if (!dependency.ready.settled) blockers.push(dependency.ready.promise);
508
518
  }
509
519
  if (blockers.length === 0) {
510
520
  activateBehavior(record);
@@ -512,10 +522,7 @@ function registerBehavior(root, entry) {
512
522
  const gate = Promise.all(
513
523
  blockers.map((blocker) => waitUntilReady(blocker, controller.signal))
514
524
  ).then(
515
- (results) => {
516
- if (results.includes(CANCELED) || controller.signal.aborted) return;
517
- activateBehavior(record);
518
- },
525
+ () => activateBehavior(record),
519
526
  (error) => {
520
527
  if (!controller.signal.aborted) failBehavior(record, error);
521
528
  }
@@ -535,7 +542,7 @@ function disposeRange(record) {
535
542
  if (record.root.registry.ranges.get(record.element) === record) {
536
543
  record.root.registry.ranges.delete(record.element);
537
544
  }
538
- if (!failed) record.readiness.resolve();
545
+ if (!failed) record.ready.resolve();
539
546
  if (!record.root.disposed) refreshDocument(record.root.registry);
540
547
  }
541
548
  function registerExternalRange(root, element, options) {
@@ -561,7 +568,7 @@ function registerExternalRange(root, element, options) {
561
568
  element,
562
569
  owner: options.owner,
563
570
  controller,
564
- readiness,
571
+ ready: readiness,
565
572
  status: options.ready === void 0 ? "ready" : "pending",
566
573
  unlinks: [linkSignal(root.controller.signal, controller)],
567
574
  publicRange: void 0
@@ -587,8 +594,8 @@ function registerExternalRange(root, element, options) {
587
594
  readiness.resolve();
588
595
  } else {
589
596
  const gate = waitUntilReady(options.ready, controller.signal).then(
590
- (result) => {
591
- if (result === CANCELED || record.status !== "pending") return;
597
+ () => {
598
+ if (record.status !== "pending") return;
592
599
  record.status = "ready";
593
600
  refreshDocument(root.registry);
594
601
  readiness.resolve();
@@ -621,6 +628,7 @@ function registerExternalRange(root, element, options) {
621
628
  }
622
629
  function disposeRoot(root, options = {}) {
623
630
  if (root.disposed) return;
631
+ root.registry.capture?.dispose(root);
624
632
  const releasedExternalOwnership = root.ranges.size !== 0;
625
633
  root.disposed = true;
626
634
  root.controller.abort();
@@ -664,40 +672,33 @@ function disposeRoot(root, options = {}) {
664
672
  if (firstError !== void 0) throw firstError;
665
673
  }
666
674
  function attachBehaviorRoot(container, options = {}) {
667
- if (!isElement(container) || container.ownerDocument === null) {
675
+ const capture = options.formSubmissions;
676
+ const document = capture === void 0 ? isElement(container) ? container.ownerDocument : null : capture.document(container);
677
+ if (document === null) {
668
678
  throw new Error("A behavior root requires an element with an owner document.");
669
679
  }
670
- const document = container.ownerDocument;
671
- let registry = documentRegistries.get(document);
672
- if (registry === void 0) {
673
- registry = { roots: /* @__PURE__ */ new Map(), ranges: /* @__PURE__ */ new Map() };
674
- documentRegistries.set(document, registry);
675
- }
680
+ let registry = registryFor(document);
676
681
  const existing = registry.roots.get(container);
677
- const canceledReplacement = existing !== void 0 && options.replace && options.signal?.aborted;
678
682
  if (existing !== void 0) {
679
683
  if (!options.replace) {
680
684
  throw new Error(
681
685
  "This container already has a behavior root; pass replace: true to replace it."
682
686
  );
683
687
  }
684
- if (!canceledReplacement) {
688
+ if (!options.signal?.aborted) {
685
689
  disposeRoot(existing);
686
- registry = documentRegistries.get(document);
687
- if (registry === void 0) {
688
- registry = { roots: /* @__PURE__ */ new Map(), ranges: /* @__PURE__ */ new Map() };
689
- documentRegistries.set(document, registry);
690
- }
690
+ registry = registryFor(document);
691
691
  }
692
692
  }
693
693
  const controller = createController(document);
694
694
  const record = {
695
695
  container,
696
696
  document,
697
+ dom: DEFAULT_DOM,
697
698
  registry,
698
699
  controller,
699
700
  behaviors: /* @__PURE__ */ new Set(),
700
- behaviorsById: /* @__PURE__ */ new Map(),
701
+ byId: /* @__PURE__ */ new Map(),
701
702
  ranges: /* @__PURE__ */ new Set(),
702
703
  pending: /* @__PURE__ */ new Set(),
703
704
  listeners: /* @__PURE__ */ new Map(),
@@ -710,8 +711,8 @@ function attachBehaviorRoot(container, options = {}) {
710
711
  signal: controller.signal,
711
712
  get ready() {
712
713
  const waiting = [...record.pending];
713
- for (const range of record.ranges) waiting.push(range.readiness.promise);
714
- for (const behavior of record.behaviors) waiting.push(behavior.readiness.promise);
714
+ for (const range of record.ranges) waiting.push(range.ready.promise);
715
+ for (const behavior of record.behaviors) waiting.push(behavior.ready.promise);
715
716
  const ready = Promise.all(waiting).then(() => void 0);
716
717
  void ready.catch(() => void 0);
717
718
  return ready;
@@ -720,18 +721,26 @@ function attachBehaviorRoot(container, options = {}) {
720
721
  registerBehavior: (entry) => registerBehavior(record, entry),
721
722
  dispose: (disposeOptions) => disposeRoot(record, disposeOptions)
722
723
  };
723
- if (!canceledReplacement) registry.roots.set(container, record);
724
724
  if (options.signal !== void 0) record.unlinkSignal = linkSignal(options.signal, controller);
725
725
  if (controller.signal.aborted) {
726
726
  disposeRoot(record);
727
727
  } else {
728
+ registry.roots.set(container, record);
729
+ try {
730
+ capture?.(record, flushQueuedEvents, failBehavior, nearestRange, rangeMatches, CANCELED);
731
+ } catch (error) {
732
+ disposeRoot(record);
733
+ throw error;
734
+ }
728
735
  controller.signal.addEventListener("abort", () => disposeRoot(record), { once: true });
736
+ if (controller.signal.aborted) disposeRoot(record);
729
737
  }
730
738
  return root;
731
739
  }
732
740
  export {
733
741
  adoptBindings,
734
742
  attachBehaviorRoot,
743
+ captureFormSubmissions,
735
744
  mountBindings,
736
745
  unbound
737
746
  };