@farm.js/react 0.1.0-beta.3 → 0.1.0-beta.4

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.
@@ -7,6 +7,23 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
7
 
8
8
  var React__default = /*#__PURE__*/_interopDefault(React);
9
9
 
10
+ // src/compiler-runtime.tsx
11
+
12
+ // src/iterable.ts
13
+ var oneShotSnapshots = /* @__PURE__ */ new WeakMap();
14
+ function materializeIterable(source) {
15
+ if (source == null) return [];
16
+ const iterator = source[Symbol.iterator]();
17
+ const oneShot = iterator === source && (typeof source === "object" || typeof source === "function");
18
+ if (oneShot) {
19
+ const cached = oneShotSnapshots.get(source);
20
+ if (cached) return cached;
21
+ }
22
+ const items = Array.from({ [Symbol.iterator]: () => iterator });
23
+ if (oneShot) oneShotSnapshots.set(source, items);
24
+ return items;
25
+ }
26
+
10
27
  // src/compiler-runtime.tsx
11
28
  var COMPILER_HMR_REGISTRY = /* @__PURE__ */ Symbol.for("@farm.js/react/compiler-hmr-registry");
12
29
  function getCompilerHmrRegistry() {
@@ -22,6 +39,11 @@ function renderTextValue(value) {
22
39
  if (value === null || value === void 0 || typeof value === "boolean") return "";
23
40
  return String(value);
24
41
  }
42
+ function isCompilerPrimitive(value) {
43
+ if (value === null) return true;
44
+ const kind = typeof value;
45
+ return kind === "string" || kind === "number" || kind === "boolean" || kind === "bigint" || kind === "undefined";
46
+ }
25
47
  function findBindingTarget(root, path, blockRoots) {
26
48
  let current = root;
27
49
  for (const index of path) {
@@ -260,14 +282,19 @@ function createCompilerHostElement(document, descriptor) {
260
282
  updateAttribute(element, attribute.name, attribute.value);
261
283
  }
262
284
  for (const style of descriptor.styles) updateStyle(element, style.name, style.value);
263
- for (const child of descriptor.children) {
285
+ const appendChild = (child) => {
286
+ if (Array.isArray(child)) {
287
+ for (const nested of child) appendChild(nested);
288
+ return;
289
+ }
264
290
  if (isCompilerHostElement(child)) {
265
291
  element.append(createCompilerHostElement(document, child));
266
- continue;
292
+ return;
267
293
  }
268
294
  const text = renderTextValue(child);
269
295
  if (text) element.append(document.createTextNode(text));
270
- }
296
+ };
297
+ for (const child of materializeCompilerHostChildren(descriptor)) appendChild(child);
271
298
  for (const attribute of descriptor.attributes) {
272
299
  if (attribute.name === "value" && (isSelectElement(element) || isTextAreaElement(element))) {
273
300
  updateAttribute(element, attribute.name, attribute.value);
@@ -277,18 +304,110 @@ function createCompilerHostElement(document, descriptor) {
277
304
  }
278
305
  function matchesCompilerHostElement(element, descriptor, opaquePaths = /* @__PURE__ */ new Set(), path = []) {
279
306
  if (element.tagName.toLowerCase() !== descriptor.tag.toLowerCase()) return false;
280
- if (opaquePaths.has(path.join("."))) return true;
281
- const expectedChildren = descriptor.children.filter(isCompilerHostElement);
307
+ if (opaquePaths.has(path.join(".")) || descriptor.block) return true;
308
+ const expectedChildren = flattenCompilerHostElements(descriptor.children);
282
309
  if (element.children.length !== expectedChildren.length) return false;
283
310
  return expectedChildren.every(
284
311
  (child, index) => matchesCompilerHostElement(element.children[index], child, opaquePaths, [...path, index])
285
312
  );
286
313
  }
314
+ function flattenCompilerHostElements(children) {
315
+ const elements = [];
316
+ const visit = (child) => {
317
+ if (Array.isArray(child)) {
318
+ for (const nested of child) visit(nested);
319
+ } else if (isCompilerHostElement(child)) {
320
+ elements.push(child);
321
+ }
322
+ };
323
+ for (const child of children) visit(child);
324
+ return elements;
325
+ }
326
+ function materializeCompilerHostChildren(descriptor) {
327
+ const block = descriptor.block;
328
+ if (block?.kind !== "keyed-ranges" || !block.staticChildrenOnly) {
329
+ return descriptor.children;
330
+ }
331
+ const staticChildren = flattenCompilerHostElements(descriptor.children);
332
+ const children = [];
333
+ let cursor = 0;
334
+ for (const range of block.ranges) {
335
+ children.push(...staticChildren.slice(cursor, cursor + range.before));
336
+ cursor += range.before;
337
+ const items = materializeIterable(range.items());
338
+ children.push(...items.map((item, index) => range.create(item, index)));
339
+ }
340
+ children.push(...staticChildren.slice(cursor, cursor + block.trailing));
341
+ return children;
342
+ }
343
+ function collectCompilerHostBlockIds(descriptor, ids) {
344
+ for (const child of flattenCompilerHostElements(descriptor.children)) {
345
+ collectCompilerHostBlockIds(child, ids);
346
+ }
347
+ const block = descriptor.block;
348
+ if (!block || ids.has(block.id)) return;
349
+ ids.add(block.id);
350
+ if (block.kind === "conditional-ranges") {
351
+ for (const range of block.ranges) {
352
+ if (range.truthy) collectCompilerHostBlockIds(range.truthy.create(), ids);
353
+ if (range.falsy) collectCompilerHostBlockIds(range.falsy.create(), ids);
354
+ }
355
+ } else if (block.kind === "keyed-ranges") {
356
+ for (const range of block.ranges) {
357
+ const first = materializeIterable(range.items())[0];
358
+ if (first !== void 0) collectCompilerHostBlockIds(range.create(first, 0), ids);
359
+ }
360
+ } else {
361
+ for (const range of block.ranges) {
362
+ if (range.kind === "conditional") {
363
+ if (range.truthy) collectCompilerHostBlockIds(range.truthy.create(), ids);
364
+ if (range.falsy) collectCompilerHostBlockIds(range.falsy.create(), ids);
365
+ } else {
366
+ const first = materializeIterable(range.items())[0];
367
+ if (first !== void 0) collectCompilerHostBlockIds(range.create(first, 0), ids);
368
+ }
369
+ }
370
+ }
371
+ }
287
372
  function findCompilerHostTarget(root, path) {
288
373
  let current = root;
289
374
  for (const index of path) current = current?.children[index] || null;
290
375
  return current;
291
376
  }
377
+ var UNSET_STATIC_RANGE_BINDING = /* @__PURE__ */ Symbol("unset static range binding");
378
+ function normalizedStaticRangeBindingValue(binding, value) {
379
+ return binding.kind === "text" ? renderTextValue(value) : value;
380
+ }
381
+ function applyStaticRangeBindings(bindings, segments, values) {
382
+ const activeBindings = bindings || [];
383
+ if (values.length === 0 && activeBindings.length > 0) {
384
+ values.push(...activeBindings.map(() => UNSET_STATIC_RANGE_BINDING));
385
+ }
386
+ if (values.length !== activeBindings.length) return false;
387
+ for (let index = 0; index < activeBindings.length; index += 1) {
388
+ const binding = activeBindings[index];
389
+ if (!Number.isSafeInteger(binding.segment) || binding.segment < 0 || !Number.isSafeInteger(binding.sibling) || binding.sibling < 0) {
390
+ return false;
391
+ }
392
+ const sibling = segments[binding.segment]?.[binding.sibling];
393
+ const target = sibling && findCompilerHostTarget(sibling, binding.path);
394
+ if (!target) return false;
395
+ const rawValue = binding.read();
396
+ const value = normalizedStaticRangeBindingValue(binding, rawValue);
397
+ if (Object.is(values[index], value)) continue;
398
+ values[index] = value;
399
+ if (binding.kind === "text") {
400
+ target.textContent = value;
401
+ } else if (binding.kind === "style" && binding.name) {
402
+ updateStyle(target, binding.name, rawValue);
403
+ } else if (binding.kind === "attribute" && binding.name) {
404
+ updateAttribute(target, binding.name, rawValue);
405
+ } else {
406
+ return false;
407
+ }
408
+ }
409
+ return true;
410
+ }
292
411
  var UNSET_KEYED_ROW_BINDING = /* @__PURE__ */ Symbol("unset interactive keyed-row binding");
293
412
  function keyedRowIdentity(key) {
294
413
  return String(key);
@@ -303,20 +422,23 @@ function readKeyedRowBindingValues(props, item, index) {
303
422
  }
304
423
  function applyKeyedRowBindings(props, instance, item, index) {
305
424
  for (let bindingIndex = 0; bindingIndex < props.bindings.length; bindingIndex += 1) {
306
- const binding = props.bindings[bindingIndex];
307
- const rawValue = binding.read(item, index);
308
- const value = normalizedKeyedRowBindingValue(binding, rawValue);
309
- if (Object.is(instance.values[bindingIndex], value)) continue;
310
- instance.values[bindingIndex] = value;
311
- const target = findCompilerHostTarget(instance.element, binding.path);
312
- if (!target) continue;
313
- if (binding.kind === "text") {
314
- target.textContent = value;
315
- } else if (binding.kind === "style" && binding.name) {
316
- updateStyle(target, binding.name, rawValue);
317
- } else if (binding.kind === "attribute" && binding.name) {
318
- updateAttribute(target, binding.name, rawValue);
319
- }
425
+ applyKeyedRowBinding(props, instance, item, index, bindingIndex);
426
+ }
427
+ }
428
+ function applyKeyedRowBinding(props, instance, item, index, bindingIndex) {
429
+ const binding = props.bindings[bindingIndex];
430
+ const rawValue = binding.read(item, index);
431
+ const value = normalizedKeyedRowBindingValue(binding, rawValue);
432
+ if (Object.is(instance.values[bindingIndex], value)) return;
433
+ instance.values[bindingIndex] = value;
434
+ const target = findCompilerHostTarget(instance.element, binding.path);
435
+ if (!target) return;
436
+ if (binding.kind === "text") {
437
+ target.textContent = value;
438
+ } else if (binding.kind === "style" && binding.name) {
439
+ updateStyle(target, binding.name, rawValue);
440
+ } else if (binding.kind === "attribute" && binding.name) {
441
+ updateAttribute(target, binding.name, rawValue);
320
442
  }
321
443
  }
322
444
  function keyedRowConditionalSnapshot(conditional, item, index) {
@@ -335,13 +457,15 @@ function keyedRowConditionalSnapshot(conditional, item, index) {
335
457
  ];
336
458
  }
337
459
  function readKeyedRowConditionalValues(props, item, index) {
460
+ if (!props.conditionals?.length) return EMPTY_KEYED_ROW_CONDITIONAL_VALUES;
338
461
  return new Map(
339
- (props.conditionals || []).map((conditional) => [
462
+ props.conditionals.map((conditional) => [
340
463
  conditional.id,
341
464
  keyedRowConditionalSnapshot(conditional, item, index)
342
465
  ])
343
466
  );
344
467
  }
468
+ var EMPTY_KEYED_ROW_CONDITIONAL_VALUES = /* @__PURE__ */ new Map();
345
469
  function keyedRowConditionalChanged(previous, next) {
346
470
  return !previous || previous.length !== next.length || next.some((value, index) => !Object.is(value, previous[index]));
347
471
  }
@@ -358,11 +482,6 @@ function hostConditionalSelection(props) {
358
482
  function normalizedHostConditionalBindingValue(binding, value) {
359
483
  return binding.kind === "text" ? renderTextValue(value) : value;
360
484
  }
361
- function readHostConditionalBindingValues(branch) {
362
- return branch.bindings.map(
363
- (binding) => normalizedHostConditionalBindingValue(binding, binding.read())
364
- );
365
- }
366
485
  function applyHostConditionalBindings(branch, instance) {
367
486
  for (let bindingIndex = 0; bindingIndex < branch.bindings.length; bindingIndex += 1) {
368
487
  const binding = branch.bindings[bindingIndex];
@@ -381,6 +500,903 @@ function applyHostConditionalBindings(branch, instance) {
381
500
  }
382
501
  }
383
502
  }
503
+ var UNSET_HOST_CONDITIONAL_BINDING = /* @__PURE__ */ Symbol("unset host conditional binding");
504
+ function mountCompilerHostTree(owner, element, descriptor, onFallback) {
505
+ if (!matchesCompilerHostElement(element, descriptor)) return null;
506
+ if (descriptor.block?.kind === "conditional-ranges") {
507
+ const controller = new CompilerNestedConditionalRanges(
508
+ owner,
509
+ element,
510
+ descriptor,
511
+ descriptor.block,
512
+ onFallback
513
+ );
514
+ try {
515
+ if (controller.adopt()) return controller;
516
+ controller.cleanup();
517
+ return null;
518
+ } catch (error) {
519
+ controller.cleanup();
520
+ throw error;
521
+ }
522
+ }
523
+ if (descriptor.block?.kind === "keyed-ranges") {
524
+ const controller = new CompilerNestedKeyedRanges(
525
+ owner,
526
+ element,
527
+ descriptor,
528
+ descriptor.block,
529
+ onFallback
530
+ );
531
+ try {
532
+ if (controller.adopt()) return controller;
533
+ controller.cleanup();
534
+ return null;
535
+ } catch (error) {
536
+ controller.cleanup();
537
+ throw error;
538
+ }
539
+ }
540
+ if (descriptor.block?.kind === "mixed-ranges") {
541
+ const controller = new CompilerNestedMixedRanges(
542
+ owner,
543
+ element,
544
+ descriptor,
545
+ descriptor.block,
546
+ onFallback
547
+ );
548
+ try {
549
+ if (controller.adopt()) return controller;
550
+ controller.cleanup();
551
+ return null;
552
+ } catch (error) {
553
+ controller.cleanup();
554
+ throw error;
555
+ }
556
+ }
557
+ const descriptors = flattenCompilerHostElements(descriptor.children);
558
+ if (descriptors.length !== element.children.length) return null;
559
+ const scopes = [];
560
+ for (let index = 0; index < descriptors.length; index += 1) {
561
+ const scope = mountCompilerHostTree(
562
+ owner,
563
+ element.children[index],
564
+ descriptors[index],
565
+ onFallback
566
+ );
567
+ if (!scope) {
568
+ for (const mounted of scopes) mounted.cleanup();
569
+ return null;
570
+ }
571
+ scopes.push(scope);
572
+ }
573
+ let active = true;
574
+ return {
575
+ update(nextDescriptor) {
576
+ if (!active || nextDescriptor.block || !matchesCompilerHostElement(element, nextDescriptor)) {
577
+ return false;
578
+ }
579
+ const nextChildren = flattenCompilerHostElements(nextDescriptor.children);
580
+ if (nextChildren.length !== scopes.length) return false;
581
+ return scopes.every((scope, index) => scope.update(nextChildren[index]));
582
+ },
583
+ cleanup() {
584
+ if (!active) return;
585
+ active = false;
586
+ for (const scope of scopes) scope.cleanup();
587
+ }
588
+ };
589
+ }
590
+ function mountCompilerHostInstance(owner, element, descriptor, branch, onFallback) {
591
+ const scope = mountCompilerHostTree(owner, element, descriptor, onFallback);
592
+ if (!scope) return null;
593
+ const instance = {
594
+ element,
595
+ scope,
596
+ values: branch.bindings.map(() => UNSET_HOST_CONDITIONAL_BINDING)
597
+ };
598
+ try {
599
+ applyHostConditionalBindings(branch, instance);
600
+ } catch (error) {
601
+ scope.cleanup();
602
+ throw error;
603
+ }
604
+ return instance;
605
+ }
606
+ var CompilerNestedConditionalRanges = class {
607
+ constructor(owner, root, host, block, onFallback) {
608
+ this.owner = owner;
609
+ this.root = root;
610
+ this.host = host;
611
+ this.block = block;
612
+ this.onFallback = onFallback;
613
+ this.instances = [];
614
+ this.staticSegments = [];
615
+ this.staticScopes = [];
616
+ this.staticValues = [];
617
+ this.active = true;
618
+ this.refresh = (afterCommit) => {
619
+ if (!this.active) {
620
+ afterCommit?.();
621
+ return;
622
+ }
623
+ const selections = this.readSelections();
624
+ if (!selections || selections.length !== this.instances.length) {
625
+ this.onFallback();
626
+ afterCommit?.();
627
+ return;
628
+ }
629
+ if (!applyStaticRangeBindings(this.block.bindings, this.staticSegments, this.staticValues)) {
630
+ this.onFallback();
631
+ afterCommit?.();
632
+ return;
633
+ }
634
+ for (let index = this.instances.length - 1; index >= 0; index -= 1) {
635
+ if (!this.reconcileRange(index, selections[index])) {
636
+ this.onFallback();
637
+ afterCommit?.();
638
+ return;
639
+ }
640
+ }
641
+ afterCommit?.();
642
+ };
643
+ }
644
+ readSelections() {
645
+ const selections = [];
646
+ for (const range of this.block.ranges) {
647
+ const selection = hostConditionalSelection(range);
648
+ if (selection.kind === "fallback") return null;
649
+ selections.push(selection);
650
+ }
651
+ return selections;
652
+ }
653
+ mountStatic(elements, descriptors) {
654
+ if (elements.length !== descriptors.length) return false;
655
+ for (let index = 0; index < elements.length; index += 1) {
656
+ const scope = mountCompilerHostTree(
657
+ this.owner,
658
+ elements[index],
659
+ descriptors[index],
660
+ this.onFallback
661
+ );
662
+ if (!scope) return false;
663
+ this.staticScopes.push(scope);
664
+ }
665
+ return true;
666
+ }
667
+ adopt() {
668
+ if (!Number.isSafeInteger(this.block.trailing) || this.block.trailing < 0) return false;
669
+ const selections = this.readSelections();
670
+ if (!selections) return false;
671
+ const elements = [...this.root.children];
672
+ const descriptors = flattenCompilerHostElements(this.host.children);
673
+ if (elements.length !== descriptors.length) return false;
674
+ let cursor = 0;
675
+ for (let index = 0; index < this.block.ranges.length; index += 1) {
676
+ const range = this.block.ranges[index];
677
+ if (!Number.isSafeInteger(range.before) || range.before < 0) {
678
+ this.cleanup();
679
+ return false;
680
+ }
681
+ const staticEnd = cursor + range.before;
682
+ if (staticEnd > elements.length || !this.mountStatic(elements.slice(cursor, staticEnd), descriptors.slice(cursor, staticEnd))) {
683
+ this.cleanup();
684
+ return false;
685
+ }
686
+ this.staticSegments.push(elements.slice(cursor, staticEnd));
687
+ cursor = staticEnd;
688
+ const selection = selections[index];
689
+ if (selection.kind === "empty") {
690
+ this.instances.push(null);
691
+ continue;
692
+ }
693
+ const element = elements[cursor];
694
+ if (!element) {
695
+ this.cleanup();
696
+ return false;
697
+ }
698
+ const descriptor = selection.branch.create();
699
+ const host = mountCompilerHostInstance(
700
+ this.owner,
701
+ element,
702
+ descriptor,
703
+ selection.branch,
704
+ this.onFallback
705
+ );
706
+ if (!host) {
707
+ this.cleanup();
708
+ return false;
709
+ }
710
+ this.instances.push({ key: selection.key, host });
711
+ cursor += 1;
712
+ }
713
+ const trailingEnd = cursor + this.block.trailing;
714
+ if (trailingEnd !== elements.length || !this.mountStatic(elements.slice(cursor), descriptors.slice(cursor))) {
715
+ this.cleanup();
716
+ return false;
717
+ }
718
+ this.staticSegments.push(elements.slice(cursor));
719
+ if (!applyStaticRangeBindings(this.block.bindings, this.staticSegments, this.staticValues)) {
720
+ this.cleanup();
721
+ return false;
722
+ }
723
+ this.unsubscribe = this.owner.subscribe(this.block.id, this.refresh);
724
+ return true;
725
+ }
726
+ anchorAfter(rangeIndex) {
727
+ for (let next = rangeIndex + 1; next < this.instances.length; next += 1) {
728
+ const staticAnchor = this.staticSegments[next]?.[0];
729
+ if (staticAnchor) return staticAnchor;
730
+ const rangeAnchor = this.instances[next]?.host.element;
731
+ if (rangeAnchor) return rangeAnchor;
732
+ }
733
+ return this.staticSegments[this.instances.length]?.[0] || null;
734
+ }
735
+ reconcileRange(rangeIndex, selection) {
736
+ const previous = this.instances[rangeIndex];
737
+ if (selection.kind === "empty") {
738
+ previous?.host.scope?.cleanup();
739
+ previous?.host.element.remove();
740
+ this.instances[rangeIndex] = null;
741
+ return true;
742
+ }
743
+ if (previous?.key === selection.key) {
744
+ const descriptor2 = selection.branch.create();
745
+ if (!previous.host.scope?.update(descriptor2)) return false;
746
+ applyHostConditionalBindings(selection.branch, previous.host);
747
+ return true;
748
+ }
749
+ const descriptor = selection.branch.create();
750
+ const element = createCompilerHostElement(this.root.ownerDocument, descriptor);
751
+ const host = mountCompilerHostInstance(
752
+ this.owner,
753
+ element,
754
+ descriptor,
755
+ selection.branch,
756
+ this.onFallback
757
+ );
758
+ if (!host) return false;
759
+ previous?.host.scope?.cleanup();
760
+ if (previous) previous.host.element.replaceWith(element);
761
+ else this.root.insertBefore(element, this.anchorAfter(rangeIndex));
762
+ this.instances[rangeIndex] = { key: selection.key, host };
763
+ return true;
764
+ }
765
+ updateStaticScopes(descriptor, selections) {
766
+ const descriptors = flattenCompilerHostElements(descriptor.children);
767
+ let descriptorIndex = 0;
768
+ let scopeIndex = 0;
769
+ for (let rangeIndex = 0; rangeIndex < this.block.ranges.length; rangeIndex += 1) {
770
+ const range = this.block.ranges[rangeIndex];
771
+ for (let index = 0; index < range.before; index += 1) {
772
+ const scope = this.staticScopes[scopeIndex++];
773
+ const child = descriptors[descriptorIndex++];
774
+ if (!scope || !child || !scope.update(child)) return false;
775
+ }
776
+ if (selections[rangeIndex].kind === "branch") descriptorIndex += 1;
777
+ }
778
+ for (let index = 0; index < this.block.trailing; index += 1) {
779
+ const scope = this.staticScopes[scopeIndex++];
780
+ const child = descriptors[descriptorIndex++];
781
+ if (!scope || !child || !scope.update(child)) return false;
782
+ }
783
+ return scopeIndex === this.staticScopes.length && descriptorIndex === descriptors.length;
784
+ }
785
+ update(descriptor) {
786
+ const block = descriptor.block;
787
+ if (!this.active || block?.kind !== "conditional-ranges" || block.id !== this.block.id || block.ranges.length !== this.block.ranges.length || block.trailing !== this.block.trailing || block.ranges.some((range, index) => range.before !== this.block.ranges[index].before)) {
788
+ return false;
789
+ }
790
+ this.host = descriptor;
791
+ this.block = block;
792
+ const selections = this.readSelections();
793
+ if (!selections || !this.updateStaticScopes(descriptor, selections) || !applyStaticRangeBindings(this.block.bindings, this.staticSegments, this.staticValues)) {
794
+ return false;
795
+ }
796
+ for (let index = this.instances.length - 1; index >= 0; index -= 1) {
797
+ if (!this.reconcileRange(index, selections[index])) return false;
798
+ }
799
+ return true;
800
+ }
801
+ cleanup() {
802
+ if (!this.active) return;
803
+ this.active = false;
804
+ this.unsubscribe?.();
805
+ for (const instance of this.instances) instance?.host.scope?.cleanup();
806
+ for (const scope of this.staticScopes) scope.cleanup();
807
+ this.instances.length = 0;
808
+ this.staticSegments.length = 0;
809
+ this.staticScopes.length = 0;
810
+ this.staticValues.length = 0;
811
+ }
812
+ };
813
+ var CompilerNestedKeyedRanges = class {
814
+ constructor(owner, root, host, block, onFallback) {
815
+ this.owner = owner;
816
+ this.root = root;
817
+ this.host = host;
818
+ this.block = block;
819
+ this.onFallback = onFallback;
820
+ this.instances = [];
821
+ this.staticSegments = [];
822
+ this.staticScopes = [];
823
+ this.staticValues = [];
824
+ this.active = true;
825
+ this.refresh = (afterCommit) => {
826
+ if (!this.active) {
827
+ afterCommit?.();
828
+ return;
829
+ }
830
+ const rowsByRange = this.readRanges();
831
+ if (!rowsByRange || rowsByRange.length !== this.instances.length) {
832
+ this.onFallback();
833
+ afterCommit?.();
834
+ return;
835
+ }
836
+ if (!applyStaticRangeBindings(this.block.bindings, this.staticSegments, this.staticValues)) {
837
+ this.onFallback();
838
+ afterCommit?.();
839
+ return;
840
+ }
841
+ const activeElement = this.root.ownerDocument.activeElement;
842
+ const restoreFocus = Boolean(activeElement && this.root.contains(activeElement));
843
+ for (let index = rowsByRange.length - 1; index >= 0; index -= 1) {
844
+ if (!this.reconcileRange(index, rowsByRange[index])) {
845
+ this.onFallback();
846
+ afterCommit?.();
847
+ return;
848
+ }
849
+ }
850
+ if (restoreFocus && activeElement?.isConnected && activeElement.ownerDocument.activeElement !== activeElement && "focus" in activeElement) {
851
+ activeElement.focus({ preventScroll: true });
852
+ }
853
+ afterCommit?.();
854
+ };
855
+ }
856
+ readRange(range) {
857
+ const items = materializeIterable(range.items());
858
+ const keys = items.map((item, index) => keyedRowIdentity(range.rowKey(item, index)));
859
+ return new Set(keys).size === keys.length ? { items, keys } : null;
860
+ }
861
+ readRanges() {
862
+ const ranges = [];
863
+ for (const range of this.block.ranges) {
864
+ const rows = this.readRange(range);
865
+ if (!rows) return null;
866
+ ranges.push(rows);
867
+ }
868
+ return ranges;
869
+ }
870
+ mountStatic(elements, descriptors) {
871
+ if (elements.length !== descriptors.length) return false;
872
+ for (let index = 0; index < elements.length; index += 1) {
873
+ const scope = mountCompilerHostTree(
874
+ this.owner,
875
+ elements[index],
876
+ descriptors[index],
877
+ this.onFallback
878
+ );
879
+ if (!scope) return false;
880
+ this.staticScopes.push(scope);
881
+ }
882
+ return true;
883
+ }
884
+ adopt() {
885
+ if (!Number.isSafeInteger(this.block.trailing) || this.block.trailing < 0) return false;
886
+ const rowsByRange = this.readRanges();
887
+ if (!rowsByRange) return false;
888
+ const elements = [...this.root.children];
889
+ const descriptors = flattenCompilerHostElements(materializeCompilerHostChildren(this.host));
890
+ if (elements.length !== descriptors.length) return false;
891
+ let cursor = 0;
892
+ for (let rangeIndex = 0; rangeIndex < this.block.ranges.length; rangeIndex += 1) {
893
+ const range = this.block.ranges[rangeIndex];
894
+ const rows = rowsByRange[rangeIndex];
895
+ if (!Number.isSafeInteger(range.before) || range.before < 0) {
896
+ this.cleanup();
897
+ return false;
898
+ }
899
+ const staticEnd = cursor + range.before;
900
+ if (staticEnd > elements.length || !this.mountStatic(elements.slice(cursor, staticEnd), descriptors.slice(cursor, staticEnd))) {
901
+ this.cleanup();
902
+ return false;
903
+ }
904
+ this.staticSegments.push(elements.slice(cursor, staticEnd));
905
+ cursor = staticEnd;
906
+ const instances = /* @__PURE__ */ new Map();
907
+ for (let index = 0; index < rows.items.length; index += 1) {
908
+ const element = elements[cursor + index];
909
+ if (!element) {
910
+ this.cleanup();
911
+ return false;
912
+ }
913
+ const descriptor = range.create(rows.items[index], index);
914
+ const scope = mountCompilerHostTree(this.owner, element, descriptor, this.onFallback);
915
+ if (!scope) {
916
+ this.cleanup();
917
+ return false;
918
+ }
919
+ instances.set(rows.keys[index], {
920
+ key: rows.keys[index],
921
+ element,
922
+ scope,
923
+ values: range.bindings.map(() => UNSET_KEYED_ROW_BINDING),
924
+ item: rows.items[index],
925
+ index,
926
+ conditionalValues: /* @__PURE__ */ new Map()
927
+ });
928
+ applyKeyedRowBindings(range, instances.get(rows.keys[index]), rows.items[index], index);
929
+ }
930
+ this.instances.push(instances);
931
+ cursor += rows.items.length;
932
+ }
933
+ const trailingEnd = cursor + this.block.trailing;
934
+ if (trailingEnd !== elements.length || !this.mountStatic(elements.slice(cursor), descriptors.slice(cursor))) {
935
+ this.cleanup();
936
+ return false;
937
+ }
938
+ this.staticSegments.push(elements.slice(cursor));
939
+ if (!applyStaticRangeBindings(this.block.bindings, this.staticSegments, this.staticValues)) {
940
+ this.cleanup();
941
+ return false;
942
+ }
943
+ this.unsubscribe = this.owner.subscribe(this.block.id, this.refresh);
944
+ return true;
945
+ }
946
+ anchorAfter(rangeIndex) {
947
+ for (let next = rangeIndex + 1; next < this.instances.length; next += 1) {
948
+ const staticAnchor = this.staticSegments[next]?.[0];
949
+ if (staticAnchor) return staticAnchor;
950
+ const rowAnchor = this.instances[next].values().next().value?.element;
951
+ if (rowAnchor) return rowAnchor;
952
+ }
953
+ return this.staticSegments[this.instances.length]?.[0] || null;
954
+ }
955
+ reconcileRange(rangeIndex, rows) {
956
+ const range = this.block.ranges[rangeIndex];
957
+ const previous = this.instances[rangeIndex];
958
+ const oldIndices = /* @__PURE__ */ new Map();
959
+ [...previous.keys()].forEach((key, index) => oldIndices.set(key, index));
960
+ const nextKeys = new Set(rows.keys);
961
+ for (const [key, instance] of previous) {
962
+ if (nextKeys.has(key)) continue;
963
+ instance.scope?.cleanup();
964
+ instance.element.remove();
965
+ }
966
+ const sequence = rows.keys.map((key) => oldIndices.get(key) ?? -1);
967
+ const stablePositions = longestIncreasingSubsequencePositions(sequence);
968
+ const nextInstances = [];
969
+ for (let index = 0; index < rows.items.length; index += 1) {
970
+ const key = rows.keys[index];
971
+ const existing = previous.get(key);
972
+ if (existing) {
973
+ const descriptor2 = range.create(rows.items[index], index);
974
+ if (!existing.scope?.update(descriptor2)) return false;
975
+ applyKeyedRowBindings(range, existing, rows.items[index], index);
976
+ existing.item = rows.items[index];
977
+ existing.index = index;
978
+ nextInstances.push(existing);
979
+ continue;
980
+ }
981
+ const descriptor = range.create(rows.items[index], index);
982
+ const element = createCompilerHostElement(this.root.ownerDocument, descriptor);
983
+ const scope = mountCompilerHostTree(this.owner, element, descriptor, this.onFallback);
984
+ if (!scope) return false;
985
+ nextInstances.push({
986
+ key,
987
+ element,
988
+ scope,
989
+ values: readKeyedRowBindingValues(range, rows.items[index], index),
990
+ item: rows.items[index],
991
+ index,
992
+ conditionalValues: /* @__PURE__ */ new Map()
993
+ });
994
+ }
995
+ let anchor = this.anchorAfter(rangeIndex);
996
+ for (let index = nextInstances.length - 1; index >= 0; index -= 1) {
997
+ const instance = nextInstances[index];
998
+ if (sequence[index] < 0 || !stablePositions.has(index) && instance.element.nextSibling !== anchor) {
999
+ this.root.insertBefore(instance.element, anchor);
1000
+ }
1001
+ anchor = instance.element;
1002
+ }
1003
+ this.instances[rangeIndex] = new Map(nextInstances.map((instance) => [instance.key, instance]));
1004
+ return true;
1005
+ }
1006
+ updateStaticScopes(descriptor, rowsByRange) {
1007
+ const descriptors = flattenCompilerHostElements(materializeCompilerHostChildren(descriptor));
1008
+ let descriptorIndex = 0;
1009
+ let scopeIndex = 0;
1010
+ for (let rangeIndex = 0; rangeIndex < this.block.ranges.length; rangeIndex += 1) {
1011
+ const range = this.block.ranges[rangeIndex];
1012
+ for (let index = 0; index < range.before; index += 1) {
1013
+ const scope = this.staticScopes[scopeIndex++];
1014
+ const child = descriptors[descriptorIndex++];
1015
+ if (!scope || !child || !scope.update(child)) return false;
1016
+ }
1017
+ descriptorIndex += rowsByRange[rangeIndex].items.length;
1018
+ }
1019
+ for (let index = 0; index < this.block.trailing; index += 1) {
1020
+ const scope = this.staticScopes[scopeIndex++];
1021
+ const child = descriptors[descriptorIndex++];
1022
+ if (!scope || !child || !scope.update(child)) return false;
1023
+ }
1024
+ return scopeIndex === this.staticScopes.length && descriptorIndex === descriptors.length;
1025
+ }
1026
+ update(descriptor) {
1027
+ const block = descriptor.block;
1028
+ if (!this.active || block?.kind !== "keyed-ranges" || block.id !== this.block.id || block.ranges.length !== this.block.ranges.length || block.trailing !== this.block.trailing || block.ranges.some((range, index) => range.before !== this.block.ranges[index].before)) {
1029
+ return false;
1030
+ }
1031
+ this.host = descriptor;
1032
+ this.block = block;
1033
+ const rowsByRange = this.readRanges();
1034
+ if (!rowsByRange || rowsByRange.length !== this.instances.length || !this.updateStaticScopes(descriptor, rowsByRange) || !applyStaticRangeBindings(this.block.bindings, this.staticSegments, this.staticValues)) {
1035
+ return false;
1036
+ }
1037
+ for (let index = rowsByRange.length - 1; index >= 0; index -= 1) {
1038
+ if (!this.reconcileRange(index, rowsByRange[index])) return false;
1039
+ }
1040
+ return true;
1041
+ }
1042
+ cleanup() {
1043
+ if (!this.active) return;
1044
+ this.active = false;
1045
+ this.unsubscribe?.();
1046
+ for (const instances of this.instances) {
1047
+ for (const instance of instances.values()) instance.scope?.cleanup();
1048
+ }
1049
+ for (const scope of this.staticScopes) scope.cleanup();
1050
+ this.instances.length = 0;
1051
+ this.staticSegments.length = 0;
1052
+ this.staticScopes.length = 0;
1053
+ this.staticValues.length = 0;
1054
+ }
1055
+ };
1056
+ var CompilerNestedMixedRanges = class {
1057
+ constructor(owner, root, host, block, onFallback) {
1058
+ this.owner = owner;
1059
+ this.root = root;
1060
+ this.host = host;
1061
+ this.block = block;
1062
+ this.onFallback = onFallback;
1063
+ this.instances = [];
1064
+ this.staticSegments = [];
1065
+ this.staticScopes = [];
1066
+ this.staticValues = [];
1067
+ this.active = true;
1068
+ this.refresh = (afterCommit) => {
1069
+ if (!this.active) {
1070
+ afterCommit?.();
1071
+ return;
1072
+ }
1073
+ const snapshots = this.readSnapshots();
1074
+ if (!snapshots || snapshots.length !== this.instances.length) {
1075
+ this.onFallback();
1076
+ afterCommit?.();
1077
+ return;
1078
+ }
1079
+ if (!applyStaticRangeBindings(this.block.bindings, this.staticSegments, this.staticValues)) {
1080
+ this.onFallback();
1081
+ afterCommit?.();
1082
+ return;
1083
+ }
1084
+ const activeElement = this.root.ownerDocument.activeElement;
1085
+ const restoreFocus = Boolean(activeElement && this.root.contains(activeElement));
1086
+ for (let index = snapshots.length - 1; index >= 0; index -= 1) {
1087
+ if (!this.reconcileRange(index, snapshots[index])) {
1088
+ this.onFallback();
1089
+ afterCommit?.();
1090
+ return;
1091
+ }
1092
+ }
1093
+ if (restoreFocus && activeElement?.isConnected && activeElement.ownerDocument.activeElement !== activeElement && "focus" in activeElement) {
1094
+ activeElement.focus({ preventScroll: true });
1095
+ }
1096
+ afterCommit?.();
1097
+ };
1098
+ }
1099
+ readSnapshots() {
1100
+ const snapshots = [];
1101
+ for (const range of this.block.ranges) {
1102
+ if (range.kind === "conditional") {
1103
+ const selection = hostConditionalSelection(range);
1104
+ if (selection.kind === "fallback") return null;
1105
+ snapshots.push({ kind: "conditional", selection });
1106
+ continue;
1107
+ }
1108
+ const items = materializeIterable(range.items());
1109
+ const keys = items.map((item, index) => keyedRowIdentity(range.rowKey(item, index)));
1110
+ if (new Set(keys).size !== keys.length) return null;
1111
+ snapshots.push({ kind: "keyed", rows: { items, keys } });
1112
+ }
1113
+ return snapshots;
1114
+ }
1115
+ mountStatic(elements, descriptors) {
1116
+ if (elements.length !== descriptors.length) return false;
1117
+ for (let index = 0; index < elements.length; index += 1) {
1118
+ const scope = mountCompilerHostTree(
1119
+ this.owner,
1120
+ elements[index],
1121
+ descriptors[index],
1122
+ this.onFallback
1123
+ );
1124
+ if (!scope) return false;
1125
+ this.staticScopes.push(scope);
1126
+ }
1127
+ return true;
1128
+ }
1129
+ adopt() {
1130
+ if (!Number.isSafeInteger(this.block.trailing) || this.block.trailing < 0) return false;
1131
+ const snapshots = this.readSnapshots();
1132
+ if (!snapshots) return false;
1133
+ const elements = [...this.root.children];
1134
+ const descriptors = flattenCompilerHostElements(this.host.children);
1135
+ if (elements.length !== descriptors.length) return false;
1136
+ let cursor = 0;
1137
+ for (let rangeIndex = 0; rangeIndex < this.block.ranges.length; rangeIndex += 1) {
1138
+ const range = this.block.ranges[rangeIndex];
1139
+ const snapshot = snapshots[rangeIndex];
1140
+ if (!Number.isSafeInteger(range.before) || range.before < 0 || range.kind !== snapshot.kind) {
1141
+ this.cleanup();
1142
+ return false;
1143
+ }
1144
+ const staticEnd = cursor + range.before;
1145
+ if (staticEnd > elements.length || !this.mountStatic(elements.slice(cursor, staticEnd), descriptors.slice(cursor, staticEnd))) {
1146
+ this.cleanup();
1147
+ return false;
1148
+ }
1149
+ this.staticSegments.push(elements.slice(cursor, staticEnd));
1150
+ cursor = staticEnd;
1151
+ if (range.kind === "conditional" && snapshot.kind === "conditional") {
1152
+ if (snapshot.selection.kind === "empty") {
1153
+ this.instances.push({ kind: "conditional", value: null });
1154
+ continue;
1155
+ }
1156
+ const element = elements[cursor];
1157
+ if (!element) {
1158
+ this.cleanup();
1159
+ return false;
1160
+ }
1161
+ const descriptor = snapshot.selection.branch.create();
1162
+ const host = mountCompilerHostInstance(
1163
+ this.owner,
1164
+ element,
1165
+ descriptor,
1166
+ snapshot.selection.branch,
1167
+ this.onFallback
1168
+ );
1169
+ if (!host) {
1170
+ this.cleanup();
1171
+ return false;
1172
+ }
1173
+ this.instances.push({
1174
+ kind: "conditional",
1175
+ value: { key: snapshot.selection.key, host }
1176
+ });
1177
+ cursor += 1;
1178
+ continue;
1179
+ }
1180
+ if (range.kind !== "keyed" || snapshot.kind !== "keyed") {
1181
+ this.cleanup();
1182
+ return false;
1183
+ }
1184
+ const keyedInstances = /* @__PURE__ */ new Map();
1185
+ for (let index = 0; index < snapshot.rows.items.length; index += 1) {
1186
+ const element = elements[cursor + index];
1187
+ if (!element) {
1188
+ this.cleanup();
1189
+ return false;
1190
+ }
1191
+ const descriptor = range.create(snapshot.rows.items[index], index);
1192
+ const scope = mountCompilerHostTree(this.owner, element, descriptor, this.onFallback);
1193
+ if (!scope) {
1194
+ this.cleanup();
1195
+ return false;
1196
+ }
1197
+ const instance = {
1198
+ key: snapshot.rows.keys[index],
1199
+ element,
1200
+ scope,
1201
+ values: range.bindings.map(() => UNSET_KEYED_ROW_BINDING),
1202
+ item: snapshot.rows.items[index],
1203
+ index,
1204
+ conditionalValues: /* @__PURE__ */ new Map()
1205
+ };
1206
+ try {
1207
+ applyKeyedRowBindings(range, instance, snapshot.rows.items[index], index);
1208
+ } catch (error) {
1209
+ scope.cleanup();
1210
+ throw error;
1211
+ }
1212
+ keyedInstances.set(snapshot.rows.keys[index], instance);
1213
+ }
1214
+ this.instances.push({ kind: "keyed", value: keyedInstances });
1215
+ cursor += snapshot.rows.items.length;
1216
+ }
1217
+ const trailingEnd = cursor + this.block.trailing;
1218
+ if (trailingEnd !== elements.length || !this.mountStatic(elements.slice(cursor), descriptors.slice(cursor))) {
1219
+ this.cleanup();
1220
+ return false;
1221
+ }
1222
+ this.staticSegments.push(elements.slice(cursor));
1223
+ if (!applyStaticRangeBindings(this.block.bindings, this.staticSegments, this.staticValues)) {
1224
+ this.cleanup();
1225
+ return false;
1226
+ }
1227
+ this.unsubscribe = this.owner.subscribe(this.block.id, this.refresh);
1228
+ return true;
1229
+ }
1230
+ firstInstanceElement(instance) {
1231
+ if (instance.kind === "conditional") return instance.value?.host.element || null;
1232
+ return instance.value.values().next().value?.element || null;
1233
+ }
1234
+ anchorAfter(rangeIndex) {
1235
+ for (let next = rangeIndex + 1; next < this.instances.length; next += 1) {
1236
+ const staticAnchor = this.staticSegments[next]?.[0];
1237
+ if (staticAnchor) return staticAnchor;
1238
+ const rangeAnchor = this.firstInstanceElement(this.instances[next]);
1239
+ if (rangeAnchor) return rangeAnchor;
1240
+ }
1241
+ return this.staticSegments[this.instances.length]?.[0] || null;
1242
+ }
1243
+ reconcileConditional(rangeIndex, range, selection) {
1244
+ const instance = this.instances[rangeIndex];
1245
+ if (instance.kind !== "conditional") return false;
1246
+ const previous = instance.value;
1247
+ if (selection.kind === "empty") {
1248
+ previous?.host.scope?.cleanup();
1249
+ previous?.host.element.remove();
1250
+ instance.value = null;
1251
+ return true;
1252
+ }
1253
+ if (previous?.key === selection.key) {
1254
+ const descriptor2 = selection.branch.create();
1255
+ if (!previous.host.scope?.update(descriptor2)) return false;
1256
+ applyHostConditionalBindings(selection.branch, previous.host);
1257
+ return true;
1258
+ }
1259
+ const descriptor = selection.branch.create();
1260
+ const element = createCompilerHostElement(this.root.ownerDocument, descriptor);
1261
+ const host = mountCompilerHostInstance(
1262
+ this.owner,
1263
+ element,
1264
+ descriptor,
1265
+ selection.branch,
1266
+ this.onFallback
1267
+ );
1268
+ if (!host) return false;
1269
+ previous?.host.scope?.cleanup();
1270
+ if (previous) previous.host.element.replaceWith(element);
1271
+ else this.root.insertBefore(element, this.anchorAfter(rangeIndex));
1272
+ instance.value = { key: selection.key, host };
1273
+ return true;
1274
+ }
1275
+ reconcileKeyed(rangeIndex, range, rows) {
1276
+ const instance = this.instances[rangeIndex];
1277
+ if (instance.kind !== "keyed") return false;
1278
+ const previous = instance.value;
1279
+ const oldIndices = /* @__PURE__ */ new Map();
1280
+ [...previous.keys()].forEach((key, index) => oldIndices.set(key, index));
1281
+ const nextKeys = new Set(rows.keys);
1282
+ for (const [key, row] of previous) {
1283
+ if (nextKeys.has(key)) continue;
1284
+ row.scope?.cleanup();
1285
+ row.element.remove();
1286
+ }
1287
+ const sequence = rows.keys.map((key) => oldIndices.get(key) ?? -1);
1288
+ const stablePositions = longestIncreasingSubsequencePositions(sequence);
1289
+ const nextInstances = [];
1290
+ for (let index = 0; index < rows.items.length; index += 1) {
1291
+ const key = rows.keys[index];
1292
+ const existing = previous.get(key);
1293
+ if (existing) {
1294
+ const descriptor2 = range.create(rows.items[index], index);
1295
+ if (!existing.scope?.update(descriptor2)) return false;
1296
+ applyKeyedRowBindings(range, existing, rows.items[index], index);
1297
+ existing.item = rows.items[index];
1298
+ existing.index = index;
1299
+ nextInstances.push(existing);
1300
+ continue;
1301
+ }
1302
+ const descriptor = range.create(rows.items[index], index);
1303
+ const element = createCompilerHostElement(this.root.ownerDocument, descriptor);
1304
+ const scope = mountCompilerHostTree(this.owner, element, descriptor, this.onFallback);
1305
+ if (!scope) return false;
1306
+ let values;
1307
+ try {
1308
+ values = readKeyedRowBindingValues(range, rows.items[index], index);
1309
+ } catch (error) {
1310
+ scope.cleanup();
1311
+ throw error;
1312
+ }
1313
+ nextInstances.push({
1314
+ key,
1315
+ element,
1316
+ scope,
1317
+ values,
1318
+ item: rows.items[index],
1319
+ index,
1320
+ conditionalValues: /* @__PURE__ */ new Map()
1321
+ });
1322
+ }
1323
+ let anchor = this.anchorAfter(rangeIndex);
1324
+ for (let index = nextInstances.length - 1; index >= 0; index -= 1) {
1325
+ const row = nextInstances[index];
1326
+ if (sequence[index] < 0 || !stablePositions.has(index) && row.element.nextSibling !== anchor) {
1327
+ this.root.insertBefore(row.element, anchor);
1328
+ }
1329
+ anchor = row.element;
1330
+ }
1331
+ instance.value = new Map(nextInstances.map((row) => [row.key, row]));
1332
+ return true;
1333
+ }
1334
+ reconcileRange(rangeIndex, snapshot) {
1335
+ const range = this.block.ranges[rangeIndex];
1336
+ if (range.kind !== snapshot.kind) return false;
1337
+ return range.kind === "conditional" && snapshot.kind === "conditional" ? this.reconcileConditional(rangeIndex, range, snapshot.selection) : range.kind === "keyed" && snapshot.kind === "keyed" ? this.reconcileKeyed(rangeIndex, range, snapshot.rows) : false;
1338
+ }
1339
+ updateStaticScopes(descriptor, snapshots) {
1340
+ const descriptors = flattenCompilerHostElements(descriptor.children);
1341
+ let descriptorIndex = 0;
1342
+ let scopeIndex = 0;
1343
+ for (let rangeIndex = 0; rangeIndex < this.block.ranges.length; rangeIndex += 1) {
1344
+ const range = this.block.ranges[rangeIndex];
1345
+ for (let index = 0; index < range.before; index += 1) {
1346
+ const scope = this.staticScopes[scopeIndex++];
1347
+ const child = descriptors[descriptorIndex++];
1348
+ if (!scope || !child || !scope.update(child)) return false;
1349
+ }
1350
+ const snapshot = snapshots[rangeIndex];
1351
+ if (snapshot.kind === "conditional") {
1352
+ if (snapshot.selection.kind === "branch") descriptorIndex += 1;
1353
+ } else {
1354
+ descriptorIndex += snapshot.rows.items.length;
1355
+ }
1356
+ }
1357
+ for (let index = 0; index < this.block.trailing; index += 1) {
1358
+ const scope = this.staticScopes[scopeIndex++];
1359
+ const child = descriptors[descriptorIndex++];
1360
+ if (!scope || !child || !scope.update(child)) return false;
1361
+ }
1362
+ return scopeIndex === this.staticScopes.length && descriptorIndex === descriptors.length;
1363
+ }
1364
+ update(descriptor) {
1365
+ const block = descriptor.block;
1366
+ if (!this.active || block?.kind !== "mixed-ranges" || block.id !== this.block.id || block.ranges.length !== this.block.ranges.length || block.trailing !== this.block.trailing || block.ranges.some(
1367
+ (range, index) => range.kind !== this.block.ranges[index].kind || range.before !== this.block.ranges[index].before
1368
+ )) {
1369
+ return false;
1370
+ }
1371
+ this.host = descriptor;
1372
+ this.block = block;
1373
+ const snapshots = this.readSnapshots();
1374
+ if (!snapshots || snapshots.length !== this.instances.length || !this.updateStaticScopes(descriptor, snapshots) || !applyStaticRangeBindings(this.block.bindings, this.staticSegments, this.staticValues)) {
1375
+ return false;
1376
+ }
1377
+ for (let index = snapshots.length - 1; index >= 0; index -= 1) {
1378
+ if (!this.reconcileRange(index, snapshots[index])) return false;
1379
+ }
1380
+ return true;
1381
+ }
1382
+ cleanup() {
1383
+ if (!this.active) return;
1384
+ this.active = false;
1385
+ this.unsubscribe?.();
1386
+ for (const instance of this.instances) {
1387
+ if (instance.kind === "conditional") {
1388
+ instance.value?.host.scope?.cleanup();
1389
+ } else {
1390
+ for (const row of instance.value.values()) row.scope?.cleanup();
1391
+ }
1392
+ }
1393
+ for (const scope of this.staticScopes) scope.cleanup();
1394
+ this.instances.length = 0;
1395
+ this.staticSegments.length = 0;
1396
+ this.staticScopes.length = 0;
1397
+ this.staticValues.length = 0;
1398
+ }
1399
+ };
384
1400
  function createHostConditionalBlockComponent(owner) {
385
1401
  class FarmHostConditionalBlock extends React__default.default.Component {
386
1402
  constructor() {
@@ -392,8 +1408,13 @@ function createHostConditionalBlockComponent(owner) {
392
1408
  this.fallbackVersion = 0;
393
1409
  this.propSyncQueued = false;
394
1410
  this.currentProps = this.props;
1411
+ this.fallbackUnsubscribers = [];
395
1412
  this.activeBranch = null;
396
1413
  this.instance = null;
1414
+ this.requestNestedFallback = () => {
1415
+ this.instance?.scope?.cleanup();
1416
+ this.activateFallback();
1417
+ };
397
1418
  this.captureRoot = (root) => {
398
1419
  this.root = root;
399
1420
  };
@@ -401,6 +1422,26 @@ function createHostConditionalBlockComponent(owner) {
401
1422
  this.reconcile(afterCommit);
402
1423
  };
403
1424
  }
1425
+ subscribeFallbackDescendants() {
1426
+ for (const unsubscribe of this.fallbackUnsubscribers) unsubscribe();
1427
+ this.fallbackUnsubscribers = [];
1428
+ const ids = /* @__PURE__ */ new Set();
1429
+ if (this.currentProps.truthy) {
1430
+ collectCompilerHostBlockIds(this.currentProps.truthy.create(), ids);
1431
+ }
1432
+ if (this.currentProps.falsy) {
1433
+ collectCompilerHostBlockIds(this.currentProps.falsy.create(), ids);
1434
+ }
1435
+ ids.delete(this.currentProps.id);
1436
+ for (const id of ids) {
1437
+ this.fallbackUnsubscribers.push(
1438
+ owner.subscribe(id, (afterCommit) => {
1439
+ this.fallbackVersion += 1;
1440
+ this.forceUpdate(afterCommit);
1441
+ })
1442
+ );
1443
+ }
1444
+ }
404
1445
  adopt() {
405
1446
  if (!this.root) return false;
406
1447
  const selection = hostConditionalSelection(this.currentProps);
@@ -417,11 +1458,16 @@ function createHostConditionalBlockComponent(owner) {
417
1458
  const descriptor = selection.branch.create();
418
1459
  const element = this.root.firstElementChild;
419
1460
  if (!matchesCompilerHostElement(element, descriptor)) return false;
420
- this.activeBranch = selection.key;
421
- this.instance = {
1461
+ const instance = mountCompilerHostInstance(
1462
+ owner,
422
1463
  element,
423
- values: readHostConditionalBindingValues(selection.branch)
424
- };
1464
+ descriptor,
1465
+ selection.branch,
1466
+ this.requestNestedFallback
1467
+ );
1468
+ if (!instance) return false;
1469
+ this.activeBranch = selection.key;
1470
+ this.instance = instance;
425
1471
  return true;
426
1472
  }
427
1473
  activateFallback(afterCommit) {
@@ -430,6 +1476,10 @@ function createHostConditionalBlockComponent(owner) {
430
1476
  return;
431
1477
  }
432
1478
  this.fallbackRequested = true;
1479
+ this.instance?.scope?.cleanup();
1480
+ this.instance = null;
1481
+ this.activeBranch = null;
1482
+ this.subscribeFallbackDescendants();
433
1483
  this.fallbackVersion += 1;
434
1484
  this.setState({ fallback: true }, afterCommit);
435
1485
  }
@@ -448,6 +1498,7 @@ function createHostConditionalBlockComponent(owner) {
448
1498
  return;
449
1499
  }
450
1500
  if (selection.kind === "empty") {
1501
+ this.instance?.scope?.cleanup();
451
1502
  if (this.instance) this.instance.element.remove();
452
1503
  this.activeBranch = null;
453
1504
  this.instance = null;
@@ -455,17 +1506,40 @@ function createHostConditionalBlockComponent(owner) {
455
1506
  return;
456
1507
  }
457
1508
  if (this.activeBranch === selection.key && this.instance) {
1509
+ const descriptor2 = selection.branch.create();
1510
+ const scope = mountCompilerHostTree(
1511
+ owner,
1512
+ this.instance.element,
1513
+ descriptor2,
1514
+ this.requestNestedFallback
1515
+ );
1516
+ if (!scope) {
1517
+ this.activateFallback(afterCommit);
1518
+ return;
1519
+ }
1520
+ this.instance.scope?.cleanup();
1521
+ this.instance.scope = scope;
458
1522
  applyHostConditionalBindings(selection.branch, this.instance);
459
1523
  afterCommit?.();
460
1524
  return;
461
1525
  }
462
- const element = createCompilerHostElement(this.root.ownerDocument, selection.branch.create());
1526
+ const descriptor = selection.branch.create();
1527
+ const element = createCompilerHostElement(this.root.ownerDocument, descriptor);
1528
+ const instance = mountCompilerHostInstance(
1529
+ owner,
1530
+ element,
1531
+ descriptor,
1532
+ selection.branch,
1533
+ this.requestNestedFallback
1534
+ );
1535
+ if (!instance) {
1536
+ this.activateFallback(afterCommit);
1537
+ return;
1538
+ }
1539
+ this.instance?.scope?.cleanup();
463
1540
  this.root.replaceChildren(element);
464
1541
  this.activeBranch = selection.key;
465
- this.instance = {
466
- element,
467
- values: readHostConditionalBindingValues(selection.branch)
468
- };
1542
+ this.instance = instance;
469
1543
  afterCommit?.();
470
1544
  }
471
1545
  schedulePropSync() {
@@ -490,6 +1564,9 @@ function createHostConditionalBlockComponent(owner) {
490
1564
  componentWillUnmount() {
491
1565
  this.mounted = false;
492
1566
  this.unsubscribe?.();
1567
+ for (const unsubscribe of this.fallbackUnsubscribers) unsubscribe();
1568
+ this.fallbackUnsubscribers = [];
1569
+ this.instance?.scope?.cleanup();
493
1570
  this.root = null;
494
1571
  this.activeBranch = null;
495
1572
  this.instance = null;
@@ -521,8 +1598,14 @@ function createConditionalRangesBlockComponent(owner) {
521
1598
  this.fallbackVersion = 0;
522
1599
  this.propFallbackQueued = false;
523
1600
  this.currentProps = this.props;
1601
+ this.fallbackUnsubscribers = [];
524
1602
  this.rangeInstances = [];
525
1603
  this.staticSegments = [];
1604
+ this.staticValues = [];
1605
+ this.requestNestedFallback = () => {
1606
+ for (const instance of this.rangeInstances) instance?.host.scope?.cleanup();
1607
+ this.activateFallback();
1608
+ };
526
1609
  this.captureRoot = (root) => {
527
1610
  this.root = root;
528
1611
  this.currentProps.rootRef?.(root);
@@ -531,6 +1614,24 @@ function createConditionalRangesBlockComponent(owner) {
531
1614
  this.reconcile(afterCommit);
532
1615
  };
533
1616
  }
1617
+ subscribeFallbackDescendants() {
1618
+ for (const unsubscribe of this.fallbackUnsubscribers) unsubscribe();
1619
+ this.fallbackUnsubscribers = [];
1620
+ const ids = /* @__PURE__ */ new Set();
1621
+ for (const range of this.currentProps.ranges) {
1622
+ if (range.truthy) collectCompilerHostBlockIds(range.truthy.create(), ids);
1623
+ if (range.falsy) collectCompilerHostBlockIds(range.falsy.create(), ids);
1624
+ }
1625
+ ids.delete(this.currentProps.id);
1626
+ for (const id of ids) {
1627
+ this.fallbackUnsubscribers.push(
1628
+ owner.subscribe(id, (afterCommit) => {
1629
+ this.fallbackVersion += 1;
1630
+ this.forceUpdate(afterCommit);
1631
+ })
1632
+ );
1633
+ }
1634
+ }
534
1635
  readSelections(props) {
535
1636
  const selections = [];
536
1637
  for (const range of props.ranges) {
@@ -547,12 +1648,21 @@ function createConditionalRangesBlockComponent(owner) {
547
1648
  const elements = [...this.root.children];
548
1649
  const staticSegments = [];
549
1650
  const instances = [];
1651
+ const cleanupInstances = () => {
1652
+ for (const instance of instances) instance?.host.scope?.cleanup();
1653
+ };
550
1654
  let cursor = 0;
551
1655
  for (let rangeIndex = 0; rangeIndex < props.ranges.length; rangeIndex += 1) {
552
1656
  const range = props.ranges[rangeIndex];
553
- if (!Number.isSafeInteger(range.before) || range.before < 0) return false;
1657
+ if (!Number.isSafeInteger(range.before) || range.before < 0) {
1658
+ cleanupInstances();
1659
+ return false;
1660
+ }
554
1661
  const staticEnd = cursor + range.before;
555
- if (staticEnd > elements.length) return false;
1662
+ if (staticEnd > elements.length) {
1663
+ cleanupInstances();
1664
+ return false;
1665
+ }
556
1666
  staticSegments.push(elements.slice(cursor, staticEnd));
557
1667
  cursor = staticEnd;
558
1668
  const selection = selections[rangeIndex];
@@ -561,22 +1671,45 @@ function createConditionalRangesBlockComponent(owner) {
561
1671
  continue;
562
1672
  }
563
1673
  const element = elements[cursor];
564
- if (!element) return false;
1674
+ if (!element) {
1675
+ cleanupInstances();
1676
+ return false;
1677
+ }
565
1678
  const descriptor = selection.branch.create();
566
- if (!matchesCompilerHostElement(element, descriptor)) return false;
1679
+ if (!matchesCompilerHostElement(element, descriptor)) {
1680
+ cleanupInstances();
1681
+ return false;
1682
+ }
1683
+ const host = mountCompilerHostInstance(
1684
+ owner,
1685
+ element,
1686
+ descriptor,
1687
+ selection.branch,
1688
+ this.requestNestedFallback
1689
+ );
1690
+ if (!host) {
1691
+ cleanupInstances();
1692
+ return false;
1693
+ }
567
1694
  instances.push({
568
1695
  key: selection.key,
569
- host: {
570
- element,
571
- values: readHostConditionalBindingValues(selection.branch)
572
- }
1696
+ host
573
1697
  });
574
1698
  cursor += 1;
575
1699
  }
576
- if (cursor + props.trailing !== elements.length) return false;
1700
+ if (cursor + props.trailing !== elements.length) {
1701
+ cleanupInstances();
1702
+ return false;
1703
+ }
577
1704
  staticSegments.push(elements.slice(cursor));
578
1705
  this.staticSegments = staticSegments;
579
1706
  this.rangeInstances = instances;
1707
+ if (!applyStaticRangeBindings(props.bindings, this.staticSegments, this.staticValues)) {
1708
+ cleanupInstances();
1709
+ this.rangeInstances = [];
1710
+ this.staticSegments = [];
1711
+ return false;
1712
+ }
580
1713
  return true;
581
1714
  }
582
1715
  anchorAfter(rangeIndex) {
@@ -589,27 +1722,52 @@ function createConditionalRangesBlockComponent(owner) {
589
1722
  return this.staticSegments[this.rangeInstances.length]?.[0] || null;
590
1723
  }
591
1724
  reconcileRange(rangeIndex, selection) {
592
- if (!this.root) return;
1725
+ if (!this.root) return false;
593
1726
  const previous = this.rangeInstances[rangeIndex];
594
1727
  if (selection.kind === "empty") {
1728
+ previous?.host.scope?.cleanup();
595
1729
  previous?.host.element.remove();
596
1730
  this.rangeInstances[rangeIndex] = null;
597
- return;
1731
+ return true;
598
1732
  }
599
1733
  if (previous?.key === selection.key) {
1734
+ const descriptor2 = selection.branch.create();
1735
+ const scope = mountCompilerHostTree(
1736
+ owner,
1737
+ previous.host.element,
1738
+ descriptor2,
1739
+ this.requestNestedFallback
1740
+ );
1741
+ if (!scope) {
1742
+ this.requestNestedFallback();
1743
+ return false;
1744
+ }
1745
+ previous.host.scope?.cleanup();
1746
+ previous.host.scope = scope;
600
1747
  applyHostConditionalBindings(selection.branch, previous.host);
601
- return;
1748
+ return true;
602
1749
  }
603
- const element = createCompilerHostElement(this.root.ownerDocument, selection.branch.create());
1750
+ const descriptor = selection.branch.create();
1751
+ const element = createCompilerHostElement(this.root.ownerDocument, descriptor);
1752
+ const host = mountCompilerHostInstance(
1753
+ owner,
1754
+ element,
1755
+ descriptor,
1756
+ selection.branch,
1757
+ this.requestNestedFallback
1758
+ );
1759
+ if (!host) {
1760
+ this.requestNestedFallback();
1761
+ return false;
1762
+ }
1763
+ previous?.host.scope?.cleanup();
604
1764
  if (previous) previous.host.element.replaceWith(element);
605
1765
  else this.root.insertBefore(element, this.anchorAfter(rangeIndex));
606
1766
  this.rangeInstances[rangeIndex] = {
607
1767
  key: selection.key,
608
- host: {
609
- element,
610
- values: readHostConditionalBindingValues(selection.branch)
611
- }
1768
+ host
612
1769
  };
1770
+ return true;
613
1771
  }
614
1772
  reconcile(afterCommit) {
615
1773
  if (!this.mounted || !this.root) {
@@ -630,8 +1788,19 @@ function createConditionalRangesBlockComponent(owner) {
630
1788
  this.activateFallback(afterCommit);
631
1789
  return;
632
1790
  }
1791
+ if (!applyStaticRangeBindings(
1792
+ this.currentProps.bindings,
1793
+ this.staticSegments,
1794
+ this.staticValues
1795
+ )) {
1796
+ this.activateFallback(afterCommit);
1797
+ return;
1798
+ }
633
1799
  for (let rangeIndex = this.rangeInstances.length - 1; rangeIndex >= 0; rangeIndex -= 1) {
634
- this.reconcileRange(rangeIndex, selections[rangeIndex]);
1800
+ if (!this.reconcileRange(rangeIndex, selections[rangeIndex])) {
1801
+ afterCommit?.();
1802
+ return;
1803
+ }
635
1804
  }
636
1805
  afterCommit?.();
637
1806
  }
@@ -641,6 +1810,8 @@ function createConditionalRangesBlockComponent(owner) {
641
1810
  return;
642
1811
  }
643
1812
  this.fallbackRequested = true;
1813
+ for (const instance of this.rangeInstances) instance?.host.scope?.cleanup();
1814
+ this.subscribeFallbackDescendants();
644
1815
  this.setState({ fallback: true }, afterCommit);
645
1816
  }
646
1817
  schedulePropFallback() {
@@ -665,9 +1836,13 @@ function createConditionalRangesBlockComponent(owner) {
665
1836
  componentWillUnmount() {
666
1837
  this.mounted = false;
667
1838
  this.unsubscribe?.();
1839
+ for (const unsubscribe of this.fallbackUnsubscribers) unsubscribe();
1840
+ this.fallbackUnsubscribers = [];
1841
+ for (const instance of this.rangeInstances) instance?.host.scope?.cleanup();
668
1842
  this.root = null;
669
1843
  this.rangeInstances = [];
670
1844
  this.staticSegments = [];
1845
+ this.staticValues.length = 0;
671
1846
  }
672
1847
  render() {
673
1848
  const container = this.currentProps.render();
@@ -709,6 +1884,10 @@ function longestIncreasingSubsequencePositions(sequence) {
709
1884
  return positions;
710
1885
  }
711
1886
  function createKeyedRowsBlockComponent(owner) {
1887
+ const rowHostScopeOwner = {
1888
+ subscribe: () => () => {
1889
+ }
1890
+ };
712
1891
  class FarmKeyedRowConditional extends React__default.default.Component {
713
1892
  constructor() {
714
1893
  super(...arguments);
@@ -765,16 +1944,23 @@ function createKeyedRowsBlockComponent(owner) {
765
1944
  this.renderVersion = 0;
766
1945
  this.eventHandlers = /* @__PURE__ */ new Map();
767
1946
  this.conditionalListeners = /* @__PURE__ */ new Map();
1947
+ this.instancesByElement = /* @__PURE__ */ new WeakMap();
768
1948
  this.conditionalOwner = {
769
1949
  subscribe: (key, id, refresh) => this.subscribeToRowConditional(key, id, refresh)
770
1950
  };
771
1951
  this.captureRoot = (root) => {
772
1952
  this.root = root;
773
1953
  };
1954
+ this.requestHostScopeFallback = () => {
1955
+ this.activateFallback();
1956
+ };
774
1957
  this.rowEvent = (item, index, eventId) => {
775
1958
  if (this.state.fallback) {
776
1959
  return (event) => this.currentProps.events?.[eventId]?.invoke(item, index, event);
777
1960
  }
1961
+ if (this.usesDelegatedEvents()) {
1962
+ return void 0;
1963
+ }
778
1964
  const key = keyedRowIdentity(this.currentProps.rowKey(item, index));
779
1965
  let handlers = this.eventHandlers.get(key);
780
1966
  if (!handlers) {
@@ -792,6 +1978,26 @@ function createKeyedRowsBlockComponent(owner) {
792
1978
  handlers.set(eventId, handler);
793
1979
  return handler;
794
1980
  };
1981
+ this.dispatchDelegatedEvent = (name, event) => {
1982
+ const instance = this.eventRowInstance(event);
1983
+ const eventTarget = event.target;
1984
+ if (!instance || !eventTarget) return;
1985
+ const capture = name.endsWith("Capture");
1986
+ const matches = (this.currentProps.events || []).filter((descriptor) => descriptor.name === name && descriptor.path !== void 0).map((descriptor) => ({
1987
+ descriptor,
1988
+ target: findCompilerHostTarget(instance.element, descriptor.path || [])
1989
+ })).filter(
1990
+ (match) => Boolean(
1991
+ match.target && (match.target === eventTarget || match.target.contains(eventTarget))
1992
+ )
1993
+ ).sort(
1994
+ (left, right) => capture ? (left.descriptor.path?.length || 0) - (right.descriptor.path?.length || 0) : (right.descriptor.path?.length || 0) - (left.descriptor.path?.length || 0)
1995
+ );
1996
+ for (const match of matches) {
1997
+ this.invokeDelegatedEvent(match.descriptor, instance, event, match.target);
1998
+ if (event.isPropagationStopped()) break;
1999
+ }
2000
+ };
795
2001
  this.rowConditional = (item, index, conditionalId, render) => {
796
2002
  if (this.state.fallback) return render(item, index);
797
2003
  const key = keyedRowIdentity(this.currentProps.rowKey(item, index));
@@ -806,17 +2012,65 @@ function createKeyedRowsBlockComponent(owner) {
806
2012
  render
807
2013
  });
808
2014
  };
809
- this.refresh = (afterCommit) => {
2015
+ this.refresh = (afterCommit, dirtyState) => {
2016
+ if (dirtyState && this.refreshDirtyBindings(dirtyState, afterCommit)) return;
810
2017
  this.reconcile(afterCommit);
811
2018
  };
812
2019
  }
813
2020
  readRows(props) {
814
- const source = props.items();
815
- const items = source ? Array.from(source) : [];
2021
+ const items = materializeIterable(props.items());
816
2022
  const keys = items.map((item, index) => keyedRowIdentity(props.rowKey(item, index)));
817
2023
  if (new Set(keys).size !== keys.length) return null;
818
2024
  return { items, keys };
819
2025
  }
2026
+ usesDelegatedEvents(props = this.currentProps) {
2027
+ return Boolean(
2028
+ props.delegateEvents && props.events?.length && props.events.every((event) => event.path !== void 0)
2029
+ );
2030
+ }
2031
+ rebuildElementIndex(instances) {
2032
+ this.instancesByElement = /* @__PURE__ */ new WeakMap();
2033
+ for (const instance of instances.values()) {
2034
+ this.instancesByElement.set(instance.element, instance);
2035
+ }
2036
+ }
2037
+ eventRowInstance(event) {
2038
+ if (!this.root) return void 0;
2039
+ const target = event.target;
2040
+ if (!target) return void 0;
2041
+ let element = target.nodeType === 1 ? target : target.parentElement;
2042
+ while (element && element.parentElement !== this.root) element = element.parentElement;
2043
+ return element?.parentElement === this.root ? this.instancesByElement.get(element) : void 0;
2044
+ }
2045
+ invokeDelegatedEvent(descriptor, instance, event, currentTarget) {
2046
+ const ownCurrentTarget = Object.getOwnPropertyDescriptor(event, "currentTarget");
2047
+ try {
2048
+ Object.defineProperty(event, "currentTarget", {
2049
+ configurable: true,
2050
+ value: currentTarget
2051
+ });
2052
+ descriptor.invoke(instance.item, instance.index, event);
2053
+ } finally {
2054
+ if (ownCurrentTarget) Object.defineProperty(event, "currentTarget", ownCurrentTarget);
2055
+ else Reflect.deleteProperty(event, "currentTarget");
2056
+ }
2057
+ }
2058
+ delegatedContainerProps(container) {
2059
+ if (this.state.fallback || !this.usesDelegatedEvents()) return {};
2060
+ const props = container.props;
2061
+ return Object.fromEntries(
2062
+ [...new Set((this.currentProps.events || []).map((event) => event.name))].map((name) => {
2063
+ const existing = props[name];
2064
+ const capture = name.endsWith("Capture");
2065
+ const handler = (event) => {
2066
+ if (capture) existing?.(event);
2067
+ if (!event.isPropagationStopped()) this.dispatchDelegatedEvent(name, event);
2068
+ if (!capture && !event.isPropagationStopped()) existing?.(event);
2069
+ };
2070
+ return [name, handler];
2071
+ })
2072
+ );
2073
+ }
820
2074
  subscribeToRowConditional(key, id, refresh) {
821
2075
  let listeners = this.conditionalListeners.get(key);
822
2076
  if (!listeners) {
@@ -832,7 +2086,21 @@ function createKeyedRowsBlockComponent(owner) {
832
2086
  };
833
2087
  }
834
2088
  hasReactOwnedRows(props = this.currentProps) {
835
- return Boolean(props.events?.length || props.conditionals?.length);
2089
+ return Boolean(
2090
+ props.conditionals?.length || props.events?.length && !this.usesDelegatedEvents(props)
2091
+ );
2092
+ }
2093
+ cleanupHostScopes(instances = this.instances) {
2094
+ for (const instance of instances.values()) instance.scope?.cleanup();
2095
+ }
2096
+ mountRowHostScope(element, descriptor, props = this.currentProps) {
2097
+ if (!props.hostBlocks) return void 0;
2098
+ return mountCompilerHostTree(
2099
+ rowHostScopeOwner,
2100
+ element,
2101
+ descriptor,
2102
+ this.requestHostScopeFallback
2103
+ );
836
2104
  }
837
2105
  pruneEventHandlers(keys) {
838
2106
  const active = new Set(keys);
@@ -856,16 +2124,20 @@ function createKeyedRowsBlockComponent(owner) {
856
2124
  );
857
2125
  const instances = /* @__PURE__ */ new Map();
858
2126
  for (let index = 0; index < rows.items.length; index += 1) {
859
- if (this.hasReactOwnedRows() && !matchesCompilerHostElement(
860
- elements[index],
861
- this.currentProps.create(rows.items[index], index),
862
- opaqueConditionalPaths
863
- )) {
2127
+ const descriptor = this.currentProps.create(rows.items[index], index);
2128
+ if (this.hasReactOwnedRows() && !matchesCompilerHostElement(elements[index], descriptor, opaqueConditionalPaths)) {
2129
+ this.cleanupHostScopes(instances);
2130
+ return false;
2131
+ }
2132
+ const scope = this.mountRowHostScope(elements[index], descriptor);
2133
+ if (this.currentProps.hostBlocks && !scope) {
2134
+ this.cleanupHostScopes(instances);
864
2135
  return false;
865
2136
  }
866
2137
  const instance = {
867
2138
  key: rows.keys[index],
868
2139
  element: elements[index],
2140
+ scope: scope || void 0,
869
2141
  values: this.hasReactOwnedRows() ? this.currentProps.bindings.map(() => UNSET_KEYED_ROW_BINDING) : readKeyedRowBindingValues(this.currentProps, rows.items[index], index),
870
2142
  item: rows.items[index],
871
2143
  index,
@@ -880,7 +2152,9 @@ function createKeyedRowsBlockComponent(owner) {
880
2152
  }
881
2153
  instances.set(rows.keys[index], instance);
882
2154
  }
2155
+ this.cleanupHostScopes();
883
2156
  this.instances = instances;
2157
+ this.rebuildElementIndex(instances);
884
2158
  this.pruneEventHandlers(rows.keys);
885
2159
  this.pruneConditionalListeners(rows.keys);
886
2160
  return true;
@@ -899,6 +2173,7 @@ function createKeyedRowsBlockComponent(owner) {
899
2173
  }
900
2174
  this.fallbackRequested = true;
901
2175
  this.fallbackKeysWereUnsafe = this.hasUnsafeFallbackKeys();
2176
+ this.cleanupHostScopes();
902
2177
  this.fallbackVersion += 1;
903
2178
  this.setState({ fallback: true }, afterCommit);
904
2179
  }
@@ -933,6 +2208,116 @@ function createKeyedRowsBlockComponent(owner) {
933
2208
  }
934
2209
  });
935
2210
  }
2211
+ refreshDirtyBindings(dirtyState, afterCommit) {
2212
+ if (!this.mounted || !this.root || this.state.fallback || this.hasReactOwnedRows() || this.currentProps.hostBlocks || !this.currentProps.structureDependencies || this.currentProps.structureDependencies.some((dependency) => dirtyState.has(dependency)) || this.currentProps.bindings.some((binding) => binding.dependencies === void 0)) {
2213
+ return false;
2214
+ }
2215
+ const affectedBindingIndices = [];
2216
+ for (let bindingIndex = 0; bindingIndex < this.currentProps.bindings.length; bindingIndex += 1) {
2217
+ const dependencies = this.currentProps.bindings[bindingIndex].dependencies || [];
2218
+ if (dependencies.some((dependency) => dirtyState.has(dependency))) {
2219
+ affectedBindingIndices.push(bindingIndex);
2220
+ }
2221
+ }
2222
+ for (const instance of this.instances.values()) {
2223
+ for (const bindingIndex of affectedBindingIndices) {
2224
+ applyKeyedRowBinding(
2225
+ this.currentProps,
2226
+ instance,
2227
+ instance.item,
2228
+ instance.index,
2229
+ bindingIndex
2230
+ );
2231
+ }
2232
+ }
2233
+ afterCommit?.();
2234
+ return true;
2235
+ }
2236
+ reconcileStableRows(rows, afterCommit) {
2237
+ if (rows.keys.length !== this.instances.size) return false;
2238
+ let index = 0;
2239
+ for (const key of this.instances.keys()) {
2240
+ if (rows.keys[index] !== key) return false;
2241
+ index += 1;
2242
+ }
2243
+ const conditionalChanges = [];
2244
+ for (index = 0; index < rows.items.length; index += 1) {
2245
+ const key = rows.keys[index];
2246
+ const existing = this.instances.get(key);
2247
+ if (!existing) return false;
2248
+ if (this.currentProps.hostBlocks) {
2249
+ const descriptor = this.currentProps.create(rows.items[index], index);
2250
+ if (!existing.scope?.update(descriptor)) {
2251
+ this.activateFallback(afterCommit);
2252
+ return true;
2253
+ }
2254
+ }
2255
+ applyKeyedRowBindings(this.currentProps, existing, rows.items[index], index);
2256
+ const conditionalValues = readKeyedRowConditionalValues(
2257
+ this.currentProps,
2258
+ rows.items[index],
2259
+ index
2260
+ );
2261
+ for (const [id, values] of conditionalValues) {
2262
+ if (keyedRowConditionalChanged(existing.conditionalValues.get(id), values)) {
2263
+ conditionalChanges.push({ key, id, item: rows.items[index], index });
2264
+ }
2265
+ }
2266
+ existing.conditionalValues = conditionalValues;
2267
+ existing.item = rows.items[index];
2268
+ existing.index = index;
2269
+ }
2270
+ this.notifyConditionalChanges(conditionalChanges, afterCommit);
2271
+ return true;
2272
+ }
2273
+ reconcileSingleRemoval(rows, afterCommit) {
2274
+ if (this.hasReactOwnedRows() || rows.keys.length + 1 !== this.instances.size) return false;
2275
+ const previousKeys = [...this.instances.keys()];
2276
+ let removedIndex = 0;
2277
+ while (removedIndex < rows.keys.length && previousKeys[removedIndex] === rows.keys[removedIndex]) {
2278
+ removedIndex += 1;
2279
+ }
2280
+ for (let index = removedIndex; index < rows.keys.length; index += 1) {
2281
+ if (previousKeys[index + 1] !== rows.keys[index]) return false;
2282
+ }
2283
+ const removedKey = previousKeys[removedIndex];
2284
+ const removed = this.instances.get(removedKey);
2285
+ if (!removed) return false;
2286
+ const conditionalChanges = [];
2287
+ for (let index = 0; index < rows.items.length; index += 1) {
2288
+ const key = rows.keys[index];
2289
+ const existing = this.instances.get(key);
2290
+ if (!existing) return false;
2291
+ if (this.currentProps.hostBlocks) {
2292
+ const descriptor = this.currentProps.create(rows.items[index], index);
2293
+ if (!existing.scope?.update(descriptor)) {
2294
+ this.activateFallback(afterCommit);
2295
+ return true;
2296
+ }
2297
+ }
2298
+ applyKeyedRowBindings(this.currentProps, existing, rows.items[index], index);
2299
+ const conditionalValues = readKeyedRowConditionalValues(
2300
+ this.currentProps,
2301
+ rows.items[index],
2302
+ index
2303
+ );
2304
+ for (const [id, values] of conditionalValues) {
2305
+ if (keyedRowConditionalChanged(existing.conditionalValues.get(id), values)) {
2306
+ conditionalChanges.push({ key, id, item: rows.items[index], index });
2307
+ }
2308
+ }
2309
+ existing.conditionalValues = conditionalValues;
2310
+ existing.item = rows.items[index];
2311
+ existing.index = index;
2312
+ }
2313
+ removed.scope?.cleanup();
2314
+ removed.element.remove();
2315
+ this.instances.delete(removedKey);
2316
+ this.eventHandlers.delete(removedKey);
2317
+ this.conditionalListeners.delete(removedKey);
2318
+ this.notifyConditionalChanges(conditionalChanges, afterCommit);
2319
+ return true;
2320
+ }
936
2321
  reconcile(afterCommit) {
937
2322
  if (!this.mounted || !this.root) {
938
2323
  afterCommit?.();
@@ -952,27 +2337,54 @@ function createKeyedRowsBlockComponent(owner) {
952
2337
  this.activateFallback(afterCommit);
953
2338
  return;
954
2339
  }
955
- const previousKeys = [...this.instances.keys()];
956
- if (this.hasReactOwnedRows() && (rows.keys.length !== previousKeys.length || rows.keys.some((key, index) => key !== previousKeys[index]))) {
957
- this.synchronizeReactOwnedRows(afterCommit);
2340
+ if (this.hasReactOwnedRows()) {
2341
+ const previousKeys = [...this.instances.keys()];
2342
+ if (rows.keys.length !== previousKeys.length || rows.keys.some((key, index) => key !== previousKeys[index])) {
2343
+ this.synchronizeReactOwnedRows(afterCommit);
2344
+ return;
2345
+ }
2346
+ }
2347
+ if (rows.items.length === 0) {
2348
+ this.cleanupHostScopes();
2349
+ this.root.replaceChildren();
2350
+ this.instances = /* @__PURE__ */ new Map();
2351
+ this.instancesByElement = /* @__PURE__ */ new WeakMap();
2352
+ this.eventHandlers.clear();
2353
+ this.conditionalListeners.clear();
2354
+ afterCommit?.();
958
2355
  return;
959
2356
  }
960
- const oldIndices = /* @__PURE__ */ new Map();
2357
+ if (this.reconcileStableRows(rows, afterCommit)) return;
2358
+ if (this.reconcileSingleRemoval(rows, afterCommit)) return;
961
2359
  const activeElement = this.root.ownerDocument.activeElement;
962
2360
  const restoreFocus = Boolean(activeElement && this.root.contains(activeElement));
963
- [...this.instances].forEach(([key], index) => oldIndices.set(key, index));
964
2361
  const nextKeys = new Set(rows.keys);
2362
+ const hasSurvivingRows = rows.keys.some((key) => this.instances.has(key));
2363
+ const oldIndices = /* @__PURE__ */ new Map();
2364
+ if (hasSurvivingRows) {
2365
+ [...this.instances].forEach(([key], index) => oldIndices.set(key, index));
2366
+ }
965
2367
  for (const [key, instance] of this.instances) {
966
- if (!nextKeys.has(key)) instance.element.remove();
2368
+ if (!nextKeys.has(key)) {
2369
+ instance.scope?.cleanup();
2370
+ if (hasSurvivingRows) instance.element.remove();
2371
+ }
967
2372
  }
968
- const sequence = rows.keys.map((key) => oldIndices.get(key) ?? -1);
969
- const stablePositions = longestIncreasingSubsequencePositions(sequence);
2373
+ const sequence = hasSurvivingRows ? rows.keys.map((key) => oldIndices.get(key) ?? -1) : rows.keys.map(() => -1);
2374
+ const stablePositions = hasSurvivingRows ? longestIncreasingSubsequencePositions(sequence) : /* @__PURE__ */ new Set();
970
2375
  const nextInstances = [];
971
2376
  const conditionalChanges = [];
972
2377
  for (let index = 0; index < rows.items.length; index += 1) {
973
2378
  const key = rows.keys[index];
974
2379
  const existing = this.instances.get(key);
975
2380
  if (existing) {
2381
+ if (this.currentProps.hostBlocks) {
2382
+ const descriptor2 = this.currentProps.create(rows.items[index], index);
2383
+ if (!existing.scope?.update(descriptor2)) {
2384
+ this.activateFallback(afterCommit);
2385
+ return;
2386
+ }
2387
+ }
976
2388
  applyKeyedRowBindings(this.currentProps, existing, rows.items[index], index);
977
2389
  const conditionalValues = readKeyedRowConditionalValues(
978
2390
  this.currentProps,
@@ -990,13 +2402,20 @@ function createKeyedRowsBlockComponent(owner) {
990
2402
  nextInstances.push(existing);
991
2403
  continue;
992
2404
  }
993
- const element = createCompilerHostElement(
994
- this.root.ownerDocument,
995
- this.currentProps.create(rows.items[index], index)
996
- );
2405
+ const descriptor = this.currentProps.create(rows.items[index], index);
2406
+ const element = createCompilerHostElement(this.root.ownerDocument, descriptor);
2407
+ const scope = this.mountRowHostScope(element, descriptor);
2408
+ if (this.currentProps.hostBlocks && !scope) {
2409
+ for (const instance of nextInstances) {
2410
+ if (!this.instances.has(instance.key)) instance.scope?.cleanup();
2411
+ }
2412
+ this.activateFallback(afterCommit);
2413
+ return;
2414
+ }
997
2415
  nextInstances.push({
998
2416
  key,
999
2417
  element,
2418
+ scope: scope || void 0,
1000
2419
  values: readKeyedRowBindingValues(this.currentProps, rows.items[index], index),
1001
2420
  item: rows.items[index],
1002
2421
  index,
@@ -1007,17 +2426,34 @@ function createKeyedRowsBlockComponent(owner) {
1007
2426
  )
1008
2427
  });
1009
2428
  }
1010
- let anchor = null;
1011
- for (let index = nextInstances.length - 1; index >= 0; index -= 1) {
1012
- const instance = nextInstances[index];
1013
- if (sequence[index] < 0) {
1014
- this.root.insertBefore(instance.element, anchor);
1015
- } else if (!stablePositions.has(index) && instance.element.nextSibling !== anchor) {
1016
- this.root.insertBefore(instance.element, anchor);
2429
+ if (!hasSurvivingRows) {
2430
+ const fragment = this.root.ownerDocument.createDocumentFragment();
2431
+ for (const instance of nextInstances) fragment.append(instance.element);
2432
+ this.root.replaceChildren(fragment);
2433
+ } else {
2434
+ let anchor = null;
2435
+ for (let index = nextInstances.length - 1; index >= 0; index -= 1) {
2436
+ const instance = nextInstances[index];
2437
+ if (sequence[index] < 0) {
2438
+ let start = index;
2439
+ while (start > 0 && sequence[start - 1] < 0) start -= 1;
2440
+ const fragment = this.root.ownerDocument.createDocumentFragment();
2441
+ for (let itemIndex = start; itemIndex <= index; itemIndex += 1) {
2442
+ fragment.append(nextInstances[itemIndex].element);
2443
+ }
2444
+ this.root.insertBefore(fragment, anchor);
2445
+ anchor = nextInstances[start].element;
2446
+ index = start;
2447
+ } else {
2448
+ if (!stablePositions.has(index) && instance.element.nextSibling !== anchor) {
2449
+ this.root.insertBefore(instance.element, anchor);
2450
+ }
2451
+ anchor = instance.element;
2452
+ }
1017
2453
  }
1018
- anchor = instance.element;
1019
2454
  }
1020
2455
  this.instances = new Map(nextInstances.map((instance) => [instance.key, instance]));
2456
+ this.rebuildElementIndex(this.instances);
1021
2457
  this.pruneEventHandlers(rows.keys);
1022
2458
  this.pruneConditionalListeners(rows.keys);
1023
2459
  if (restoreFocus && activeElement?.isConnected && activeElement.ownerDocument.activeElement !== activeElement && "focus" in activeElement) {
@@ -1060,7 +2496,9 @@ function createKeyedRowsBlockComponent(owner) {
1060
2496
  this.mounted = false;
1061
2497
  this.unsubscribe?.();
1062
2498
  this.root = null;
2499
+ this.cleanupHostScopes();
1063
2500
  this.instances.clear();
2501
+ this.instancesByElement = /* @__PURE__ */ new WeakMap();
1064
2502
  this.eventHandlers.clear();
1065
2503
  this.conditionalListeners.clear();
1066
2504
  }
@@ -1073,6 +2511,7 @@ function createKeyedRowsBlockComponent(owner) {
1073
2511
  );
1074
2512
  }
1075
2513
  return React__default.default.cloneElement(container, {
2514
+ ...this.delegatedContainerProps(container),
1076
2515
  key: this.state.fallback ? `react-${this.fallbackVersion}` : "compiled",
1077
2516
  ref: this.captureRoot
1078
2517
  });
@@ -1094,6 +2533,7 @@ function createKeyedRangesBlockComponent(owner) {
1094
2533
  this.currentProps = this.props;
1095
2534
  this.rangeInstances = [];
1096
2535
  this.staticSegments = [];
2536
+ this.staticValues = [];
1097
2537
  this.captureRoot = (root) => {
1098
2538
  this.root = root;
1099
2539
  this.currentProps.rootRef?.(root);
@@ -1103,8 +2543,7 @@ function createKeyedRangesBlockComponent(owner) {
1103
2543
  };
1104
2544
  }
1105
2545
  readRange(range) {
1106
- const source = range.items();
1107
- const items = source ? Array.from(source) : [];
2546
+ const items = materializeIterable(range.items());
1108
2547
  const keys = items.map((item, index) => keyedRowIdentity(range.rowKey(item, index)));
1109
2548
  if (new Set(keys).size !== keys.length) return null;
1110
2549
  return { items, keys };
@@ -1158,6 +2597,11 @@ function createKeyedRangesBlockComponent(owner) {
1158
2597
  staticSegments.push(elements.slice(cursor));
1159
2598
  this.staticSegments = staticSegments;
1160
2599
  this.rangeInstances = instancesByRange;
2600
+ if (!applyStaticRangeBindings(props.bindings, this.staticSegments, this.staticValues)) {
2601
+ this.rangeInstances = [];
2602
+ this.staticSegments = [];
2603
+ return false;
2604
+ }
1161
2605
  return true;
1162
2606
  }
1163
2607
  anchorAfter(rangeIndex) {
@@ -1234,6 +2678,14 @@ function createKeyedRangesBlockComponent(owner) {
1234
2678
  this.activateFallback(afterCommit);
1235
2679
  return;
1236
2680
  }
2681
+ if (!applyStaticRangeBindings(
2682
+ this.currentProps.bindings,
2683
+ this.staticSegments,
2684
+ this.staticValues
2685
+ )) {
2686
+ this.activateFallback(afterCommit);
2687
+ return;
2688
+ }
1237
2689
  const activeElement = this.root.ownerDocument.activeElement;
1238
2690
  const restoreFocus = Boolean(activeElement && this.root.contains(activeElement));
1239
2691
  for (let rangeIndex = rowsByRange.length - 1; rangeIndex >= 0; rangeIndex -= 1) {
@@ -1277,6 +2729,7 @@ function createKeyedRangesBlockComponent(owner) {
1277
2729
  this.root = null;
1278
2730
  this.rangeInstances = [];
1279
2731
  this.staticSegments = [];
2732
+ this.staticValues.length = 0;
1280
2733
  }
1281
2734
  render() {
1282
2735
  const container = this.currentProps.render();
@@ -1294,6 +2747,128 @@ function createKeyedRangesBlockComponent(owner) {
1294
2747
  FarmKeyedRangesBlock.displayName = "FarmCompiledKeyedRanges";
1295
2748
  return FarmKeyedRangesBlock;
1296
2749
  }
2750
+ function createMixedRangesBlockComponent(owner) {
2751
+ class FarmMixedRangesBlock extends React__default.default.Component {
2752
+ constructor() {
2753
+ super(...arguments);
2754
+ this.state = { fallback: false };
2755
+ this.root = null;
2756
+ this.mounted = false;
2757
+ this.fallbackRequested = false;
2758
+ this.fallbackVersion = 0;
2759
+ this.propFallbackQueued = false;
2760
+ this.currentProps = this.props;
2761
+ this.controller = null;
2762
+ this.fallbackUnsubscribers = [];
2763
+ this.captureRoot = (root) => {
2764
+ this.root = root;
2765
+ this.currentProps.rootRef?.(root);
2766
+ };
2767
+ this.activateFallback = (afterCommit) => {
2768
+ if (!this.mounted || this.state.fallback || this.fallbackRequested) {
2769
+ afterCommit?.();
2770
+ return;
2771
+ }
2772
+ this.fallbackRequested = true;
2773
+ this.controller?.cleanup();
2774
+ this.controller = null;
2775
+ this.fallbackVersion += 1;
2776
+ this.setState({ fallback: true }, () => {
2777
+ this.subscribeFallbackBlocks();
2778
+ afterCommit?.();
2779
+ });
2780
+ };
2781
+ }
2782
+ clearFallbackSubscriptions() {
2783
+ for (const unsubscribe of this.fallbackUnsubscribers) unsubscribe();
2784
+ this.fallbackUnsubscribers = [];
2785
+ }
2786
+ subscribeFallbackBlocks() {
2787
+ this.clearFallbackSubscriptions();
2788
+ const descriptor = this.currentProps.create();
2789
+ const ids = /* @__PURE__ */ new Set();
2790
+ collectCompilerHostBlockIds(descriptor, ids);
2791
+ for (const id of ids) {
2792
+ this.fallbackUnsubscribers.push(
2793
+ owner.subscribe(id, (afterCommit) => {
2794
+ if (!this.mounted || !this.state.fallback) {
2795
+ afterCommit?.();
2796
+ return;
2797
+ }
2798
+ this.fallbackVersion += 1;
2799
+ this.forceUpdate(afterCommit);
2800
+ })
2801
+ );
2802
+ }
2803
+ }
2804
+ adopt() {
2805
+ if (!this.root) return false;
2806
+ const descriptor = this.currentProps.create();
2807
+ const block = descriptor.block;
2808
+ if (block?.kind !== "mixed-ranges" || block.id !== this.currentProps.id) return false;
2809
+ const controller = new CompilerNestedMixedRanges(
2810
+ owner,
2811
+ this.root,
2812
+ descriptor,
2813
+ block,
2814
+ this.activateFallback
2815
+ );
2816
+ try {
2817
+ if (!controller.adopt()) {
2818
+ controller.cleanup();
2819
+ return false;
2820
+ }
2821
+ } catch (error) {
2822
+ controller.cleanup();
2823
+ throw error;
2824
+ }
2825
+ this.controller = controller;
2826
+ return true;
2827
+ }
2828
+ schedulePropFallback() {
2829
+ if (this.propFallbackQueued) return;
2830
+ this.propFallbackQueued = true;
2831
+ queueMicrotask(() => {
2832
+ this.propFallbackQueued = false;
2833
+ if (this.mounted && !this.state.fallback) this.activateFallback();
2834
+ });
2835
+ }
2836
+ shouldComponentUpdate(nextProps, nextState) {
2837
+ this.currentProps = nextProps;
2838
+ if (nextState.fallback || this.state.fallback) return true;
2839
+ this.schedulePropFallback();
2840
+ return false;
2841
+ }
2842
+ componentDidMount() {
2843
+ this.mounted = true;
2844
+ if (!this.adopt()) this.activateFallback();
2845
+ }
2846
+ componentDidUpdate() {
2847
+ if (this.state.fallback) this.subscribeFallbackBlocks();
2848
+ }
2849
+ componentWillUnmount() {
2850
+ this.mounted = false;
2851
+ this.controller?.cleanup();
2852
+ this.controller = null;
2853
+ this.clearFallbackSubscriptions();
2854
+ this.root = null;
2855
+ }
2856
+ render() {
2857
+ const container = this.currentProps.render();
2858
+ if (!React__default.default.isValidElement(container) || typeof container.type !== "string") {
2859
+ throw new TypeError(
2860
+ `Compiled mixed ranges ${this.currentProps.id} must own one host container.`
2861
+ );
2862
+ }
2863
+ return React__default.default.cloneElement(container, {
2864
+ key: this.state.fallback ? `react-${this.fallbackVersion}` : "compiled",
2865
+ ref: this.captureRoot
2866
+ });
2867
+ }
2868
+ }
2869
+ FarmMixedRangesBlock.displayName = "FarmCompiledMixedRanges";
2870
+ return FarmMixedRangesBlock;
2871
+ }
1297
2872
  function createComponentBlockComponent(owner) {
1298
2873
  class FarmComponentBlock extends React__default.default.Component {
1299
2874
  constructor() {
@@ -1346,6 +2921,17 @@ function createCompiledComponent(definition) {
1346
2921
  this.hasBindingError = false;
1347
2922
  this.inputSelection = null;
1348
2923
  this.dirtyState = /* @__PURE__ */ new Set();
2924
+ this.indexedDefinition = null;
2925
+ this.staticBindingsByDependency = [];
2926
+ this.blockBindingsByDependency = [];
2927
+ this.blockBindings = /* @__PURE__ */ new Map();
2928
+ this.trackedBindingsByDependency = [];
2929
+ this.trackedDependenciesByBinding = /* @__PURE__ */ new Map();
2930
+ this.bindingValues = /* @__PURE__ */ new Map();
2931
+ this.activeBinding = null;
2932
+ this.activeDependencies = null;
2933
+ this.renderedDefinition = null;
2934
+ this.renderedElement = null;
1349
2935
  this.blockRefreshListeners = /* @__PURE__ */ new Map();
1350
2936
  this.blockRoots = /* @__PURE__ */ new Map();
1351
2937
  this.blockRootElements = /* @__PURE__ */ new Set();
@@ -1357,20 +2943,35 @@ function createCompiledComponent(definition) {
1357
2943
  this.refreshDefinition = () => {
1358
2944
  if (this.mounted) this.forceUpdate();
1359
2945
  };
1360
- this.cells = definitionReference.current.initialize(props).map((initialValue, index) => {
2946
+ const initialState = definitionReference.current.initialize(props);
2947
+ const initialProps = definitionReference.current.readProps?.(props) || [];
2948
+ this.propCellOffset = initialState.length;
2949
+ this.cells = [...initialState, ...initialProps].map((initialValue, index) => {
1361
2950
  let value = initialValue;
1362
2951
  const pending = [];
1363
2952
  return {
1364
- get: () => value,
2953
+ get: () => {
2954
+ if (this.activeBinding && this.activeDependencies) {
2955
+ this.activeDependencies.add(index);
2956
+ }
2957
+ return value;
2958
+ },
1365
2959
  set: (next) => {
1366
- pending.push(next);
2960
+ pending.push({ kind: "state", update: next });
1367
2961
  this.scheduleBindingFlush(index);
1368
2962
  },
2963
+ replace: (next) => {
2964
+ pending.push({ kind: "value", value: next });
2965
+ },
1369
2966
  flush: () => {
1370
2967
  if (pending.length === 0) return false;
1371
2968
  const previous = value;
1372
2969
  for (const next of pending.splice(0)) {
1373
- value = typeof next === "function" ? next(value) : next;
2970
+ if (next.kind === "value") {
2971
+ value = next.value;
2972
+ } else {
2973
+ value = typeof next.update === "function" ? next.update(value) : next.update;
2974
+ }
1374
2975
  }
1375
2976
  return !Object.is(previous, value);
1376
2977
  }
@@ -1396,12 +2997,113 @@ function createCompiledComponent(definition) {
1396
2997
  KeyedRanges: createKeyedRangesBlockComponent({
1397
2998
  subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
1398
2999
  }),
3000
+ MixedRanges: createMixedRangesBlockComponent({
3001
+ subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
3002
+ }),
1399
3003
  Component: createComponentBlockComponent({
1400
3004
  subscribe: (id, refresh) => this.subscribeToBlock(id, refresh)
1401
3005
  }),
1402
3006
  target: (id) => this.bindingTarget(id)
1403
3007
  };
1404
3008
  }
3009
+ usesHybridReactivity() {
3010
+ return definitionReference.current.reactivity === "hybrid";
3011
+ }
3012
+ canReuseRenderedElement(currentDefinition, propValues) {
3013
+ return Boolean(
3014
+ this.mounted && propValues && propValues.every(isCompilerPrimitive) && this.renderedDefinition === currentDefinition && this.renderedElement
3015
+ );
3016
+ }
3017
+ renderCellsForProps(propValues) {
3018
+ if (!propValues) return { cells: this.cells, commit: () => void 0 };
3019
+ let committed = false;
3020
+ const cells = this.cells.slice(0, this.propCellOffset);
3021
+ for (let propIndex = 0; propIndex < propValues.length; propIndex += 1) {
3022
+ const cell = this.cells[this.propCellOffset + propIndex];
3023
+ if (!cell) continue;
3024
+ cells.push({
3025
+ get: () => committed ? cell.get() : propValues[propIndex],
3026
+ set: (next) => cell.set(next)
3027
+ });
3028
+ }
3029
+ return {
3030
+ cells,
3031
+ commit: () => {
3032
+ committed = true;
3033
+ }
3034
+ };
3035
+ }
3036
+ tracksBinding(binding) {
3037
+ return this.usesHybridReactivity() && binding.tracking === "dynamic";
3038
+ }
3039
+ ensureBindingIndex() {
3040
+ const currentDefinition = definitionReference.current;
3041
+ if (this.indexedDefinition === currentDefinition) return;
3042
+ this.indexedDefinition = currentDefinition;
3043
+ this.staticBindingsByDependency = Array.from({ length: this.cells.length }, () => []);
3044
+ this.blockBindingsByDependency = Array.from({ length: this.cells.length }, () => []);
3045
+ this.trackedBindingsByDependency = Array.from(
3046
+ { length: this.cells.length },
3047
+ () => /* @__PURE__ */ new Set()
3048
+ );
3049
+ this.blockBindings.clear();
3050
+ this.trackedDependenciesByBinding.clear();
3051
+ this.bindingValues.clear();
3052
+ for (const binding of currentDefinition.bindings) {
3053
+ if (binding.kind === "block") this.blockBindings.set(binding.id, binding);
3054
+ for (const dependency of binding.dependencies) {
3055
+ if (dependency < 0 || dependency >= this.cells.length) continue;
3056
+ if (binding.kind === "block") {
3057
+ this.blockBindingsByDependency[dependency].push(binding);
3058
+ } else if (!this.tracksBinding(binding)) {
3059
+ this.staticBindingsByDependency[dependency].push(binding);
3060
+ }
3061
+ }
3062
+ }
3063
+ }
3064
+ updateTrackedDependencies(binding, nextDependencies) {
3065
+ const previousDependencies = this.trackedDependenciesByBinding.get(binding);
3066
+ if (previousDependencies) {
3067
+ for (const dependency of previousDependencies) {
3068
+ if (!nextDependencies.has(dependency)) {
3069
+ this.trackedBindingsByDependency[dependency]?.delete(binding);
3070
+ }
3071
+ }
3072
+ }
3073
+ for (const dependency of nextDependencies) {
3074
+ if (!previousDependencies?.has(dependency)) {
3075
+ this.trackedBindingsByDependency[dependency]?.add(binding);
3076
+ }
3077
+ }
3078
+ this.trackedDependenciesByBinding.set(binding, nextDependencies);
3079
+ }
3080
+ readBinding(binding) {
3081
+ if (!this.tracksBinding(binding)) return binding.read(this.props, this.cells);
3082
+ const previousBinding = this.activeBinding;
3083
+ const previousDependencies = this.activeDependencies;
3084
+ const nextDependencies = /* @__PURE__ */ new Set();
3085
+ this.activeBinding = binding;
3086
+ this.activeDependencies = nextDependencies;
3087
+ try {
3088
+ return binding.read(this.props, this.cells);
3089
+ } finally {
3090
+ this.activeBinding = previousBinding;
3091
+ this.activeDependencies = previousDependencies;
3092
+ this.updateTrackedDependencies(binding, nextDependencies);
3093
+ }
3094
+ }
3095
+ bindingValue(binding, value) {
3096
+ return binding.kind === "text" ? renderTextValue(value) : value;
3097
+ }
3098
+ primeHybridBindings() {
3099
+ if (!this.usesHybridReactivity()) return;
3100
+ for (const binding of definitionReference.current.bindings) {
3101
+ if (binding.kind === "block") continue;
3102
+ if (!this.tracksBinding(binding)) continue;
3103
+ const value = this.readBinding(binding);
3104
+ this.bindingValues.set(binding, this.bindingValue(binding, value));
3105
+ }
3106
+ }
1405
3107
  bindingTarget(id) {
1406
3108
  const existing = this.bindingTargetRefs.get(id);
1407
3109
  if (existing) return existing;
@@ -1458,83 +3160,118 @@ function createCompiledComponent(definition) {
1458
3160
  this.flushQueued = true;
1459
3161
  queueMicrotask(() => {
1460
3162
  this.flushQueued = false;
1461
- const inputSelection = this.inputSelection;
1462
- this.inputSelection = null;
1463
- if (!this.mounted) return;
1464
- const dirty = /* @__PURE__ */ new Set();
1465
- for (const index2 of this.dirtyState) {
1466
- if (this.cells[index2]?.flush()) dirty.add(index2);
1467
- }
1468
- this.dirtyState.clear();
1469
- if (dirty.size === 0) return;
1470
- try {
1471
- const bindings = definitionReference.current.bindings;
1472
- const blockBindings = /* @__PURE__ */ new Map();
1473
- const affectedBlockIds = /* @__PURE__ */ new Set();
1474
- for (const binding of bindings) {
1475
- if (binding.kind !== "block") continue;
1476
- blockBindings.set(binding.id, binding);
1477
- if (binding.dependencies.some((dependency) => dirty.has(dependency))) {
1478
- affectedBlockIds.add(binding.id);
1479
- }
3163
+ this.flushBindingUpdates();
3164
+ });
3165
+ }
3166
+ flushBindingUpdates() {
3167
+ const inputSelection = this.inputSelection;
3168
+ this.inputSelection = null;
3169
+ if (!this.mounted) return;
3170
+ const dirty = /* @__PURE__ */ new Set();
3171
+ for (const index of this.dirtyState) {
3172
+ if (this.cells[index]?.flush()) dirty.add(index);
3173
+ }
3174
+ this.dirtyState.clear();
3175
+ if (dirty.size === 0) return;
3176
+ try {
3177
+ this.ensureBindingIndex();
3178
+ const affectedBlockIds = /* @__PURE__ */ new Set();
3179
+ const affectedBindings = /* @__PURE__ */ new Set();
3180
+ for (const dependency of dirty) {
3181
+ for (const binding of this.blockBindingsByDependency[dependency] || []) {
3182
+ affectedBlockIds.add(binding.id);
1480
3183
  }
1481
- const hasAffectedMountedAncestor = (binding) => {
1482
- let parent = binding.parent;
1483
- while (parent !== void 0) {
1484
- if (affectedBlockIds.has(parent) && this.blockRefreshListeners.has(parent)) {
1485
- return true;
1486
- }
1487
- parent = blockBindings.get(parent)?.parent;
1488
- }
1489
- return false;
1490
- };
1491
- const blockRefreshes = [...affectedBlockIds].map((id) => {
1492
- const binding = blockBindings.get(id);
1493
- const refresh = this.blockRefreshListeners.get(id);
1494
- return binding && refresh && !hasAffectedMountedAncestor(binding) ? refresh : void 0;
1495
- }).filter(
1496
- (refresh) => refresh !== void 0
1497
- );
1498
- for (const binding of bindings) {
1499
- if (binding.kind !== "block" && binding.dependencies.some((dependency) => dirty.has(dependency))) {
1500
- this.applyBinding(binding);
3184
+ for (const binding of this.staticBindingsByDependency[dependency] || []) {
3185
+ affectedBindings.add(binding);
3186
+ }
3187
+ if (this.usesHybridReactivity()) {
3188
+ for (const binding of this.trackedBindingsByDependency[dependency] || []) {
3189
+ affectedBindings.add(binding);
1501
3190
  }
1502
3191
  }
1503
- let pendingBlockCommits = blockRefreshes.length;
1504
- for (const refresh of blockRefreshes) {
1505
- refresh(() => {
1506
- pendingBlockCommits -= 1;
1507
- if (pendingBlockCommits === 0) this.restoreInputSelection(inputSelection);
1508
- });
3192
+ }
3193
+ const hasAffectedMountedAncestor = (binding) => {
3194
+ let parent = binding.parent;
3195
+ while (parent !== void 0) {
3196
+ if (affectedBlockIds.has(parent) && this.blockRefreshListeners.has(parent)) {
3197
+ return true;
3198
+ }
3199
+ parent = this.blockBindings.get(parent)?.parent;
1509
3200
  }
1510
- if (blockRefreshes.length === 0) this.restoreInputSelection(inputSelection);
1511
- } catch (error) {
1512
- this.bindingError = error;
1513
- this.hasBindingError = true;
1514
- this.forceUpdate();
3201
+ return false;
3202
+ };
3203
+ const blockRefreshes = [...affectedBlockIds].map((id) => {
3204
+ const binding = this.blockBindings.get(id);
3205
+ const refresh = this.blockRefreshListeners.get(id);
3206
+ return binding && refresh && !hasAffectedMountedAncestor(binding) ? refresh : void 0;
3207
+ }).filter((refresh) => refresh !== void 0);
3208
+ for (const binding of affectedBindings) {
3209
+ if (binding.kind !== "block") this.applyBinding(binding);
1515
3210
  }
1516
- });
3211
+ let pendingBlockCommits = blockRefreshes.length;
3212
+ for (const refresh of blockRefreshes) {
3213
+ refresh(() => {
3214
+ pendingBlockCommits -= 1;
3215
+ if (pendingBlockCommits === 0) this.restoreInputSelection(inputSelection);
3216
+ }, dirty);
3217
+ }
3218
+ if (blockRefreshes.length === 0) this.restoreInputSelection(inputSelection);
3219
+ } catch (error) {
3220
+ this.bindingError = error;
3221
+ this.hasBindingError = true;
3222
+ this.forceUpdate();
3223
+ }
1517
3224
  }
1518
- applyBinding(binding) {
3225
+ applyBinding(binding, force = false) {
1519
3226
  if (!this.root || binding.kind === "block") return;
1520
3227
  const target = binding.target === void 0 ? findBindingTarget(this.root, binding.path, this.blockRootElements) : binding.path.length === 0 ? this.root : this.bindingTargets.get(binding.target) || null;
1521
3228
  if (!target) return;
1522
- const value = binding.read(this.props, this.cells);
3229
+ const value = this.readBinding(binding);
3230
+ const comparableValue = this.bindingValue(binding, value);
3231
+ if (!force && this.bindingValues.has(binding)) {
3232
+ if (Object.is(this.bindingValues.get(binding), comparableValue)) return;
3233
+ }
1523
3234
  if (binding.kind === "text") {
1524
- target.textContent = renderTextValue(value);
3235
+ target.textContent = comparableValue;
1525
3236
  } else if (binding.kind === "style") {
1526
3237
  updateStyle(target, binding.name, value);
1527
3238
  } else {
1528
3239
  updateAttribute(target, binding.name, value);
1529
3240
  }
3241
+ this.bindingValues.set(binding, comparableValue);
1530
3242
  }
1531
3243
  componentDidMount() {
1532
3244
  this.mounted = true;
1533
3245
  refreshListeners.add(this.refreshDefinition);
3246
+ this.ensureBindingIndex();
3247
+ this.primeHybridBindings();
1534
3248
  }
1535
- componentDidUpdate() {
3249
+ getSnapshotBeforeUpdate() {
3250
+ const currentDefinition = definitionReference.current;
3251
+ const propValues = currentDefinition.readProps?.(this.props) || null;
3252
+ return this.canReuseRenderedElement(currentDefinition, propValues);
3253
+ }
3254
+ componentDidUpdate(_previousProps, _previousState, reusedRenderedElement = false) {
3255
+ const propValues = definitionReference.current.readProps?.(this.props) || null;
3256
+ if (propValues) {
3257
+ if (reusedRenderedElement) this.captureInputSelection();
3258
+ for (let propIndex = 0; propIndex < propValues.length; propIndex += 1) {
3259
+ const cellIndex = this.propCellOffset + propIndex;
3260
+ const cell = this.cells[cellIndex];
3261
+ if (!cell) continue;
3262
+ cell.replace(propValues[propIndex]);
3263
+ if (reusedRenderedElement) {
3264
+ this.dirtyState.add(cellIndex);
3265
+ } else {
3266
+ cell.flush();
3267
+ }
3268
+ }
3269
+ this.flushBindingUpdates();
3270
+ return;
3271
+ }
3272
+ this.ensureBindingIndex();
1536
3273
  for (const binding of definitionReference.current.bindings) {
1537
- if (binding.kind !== "block") this.applyBinding(binding);
3274
+ if (binding.kind !== "block") this.applyBinding(binding, true);
1538
3275
  }
1539
3276
  }
1540
3277
  componentWillUnmount() {
@@ -1547,36 +3284,68 @@ function createCompiledComponent(definition) {
1547
3284
  this.blockRootElements.clear();
1548
3285
  this.bindingTargets.clear();
1549
3286
  this.bindingTargetRefs.clear();
3287
+ this.indexedDefinition = null;
3288
+ this.staticBindingsByDependency = [];
3289
+ this.blockBindingsByDependency = [];
3290
+ this.blockBindings.clear();
3291
+ this.trackedBindingsByDependency = [];
3292
+ this.trackedDependenciesByBinding.clear();
3293
+ this.bindingValues.clear();
3294
+ this.activeBinding = null;
3295
+ this.activeDependencies = null;
1550
3296
  refreshListeners.delete(this.refreshDefinition);
1551
3297
  }
1552
3298
  render() {
1553
3299
  if (this.hasBindingError) throw this.bindingError;
1554
3300
  const currentDefinition = definitionReference.current;
1555
- const element = currentDefinition.render(this.props, this.cells, this.blockRuntime);
3301
+ const propValues = currentDefinition.readProps?.(this.props) || null;
3302
+ if (this.canReuseRenderedElement(currentDefinition, propValues)) {
3303
+ return this.renderedElement;
3304
+ }
3305
+ const renderCells = this.renderCellsForProps(propValues);
3306
+ const element = currentDefinition.render(this.props, renderCells.cells, this.blockRuntime);
1556
3307
  if (!React__default.default.isValidElement(element)) {
1557
3308
  throw new TypeError(
1558
3309
  `Compiled component ${currentDefinition.displayName} must return one host element.`
1559
3310
  );
1560
3311
  }
3312
+ let renderedElement;
3313
+ const captureCommittedRoot = (root) => {
3314
+ this.captureRoot(root);
3315
+ if (!root) return;
3316
+ renderCells.commit();
3317
+ this.renderedDefinition = currentDefinition;
3318
+ this.renderedElement = renderedElement;
3319
+ };
1561
3320
  if (element.type === this.blockRuntime.KeyedRanges) {
1562
- return React__default.default.cloneElement(element, {
1563
- rootRef: this.captureRoot
1564
- });
1565
- }
1566
- if (element.type === this.blockRuntime.ConditionalRanges) {
1567
- return React__default.default.cloneElement(
3321
+ renderedElement = React__default.default.cloneElement(
1568
3322
  element,
1569
- { rootRef: this.captureRoot }
3323
+ {
3324
+ rootRef: captureCommittedRoot
3325
+ }
1570
3326
  );
1571
- }
1572
- if (typeof element.type !== "string") {
3327
+ } else if (element.type === this.blockRuntime.ConditionalRanges) {
3328
+ renderedElement = React__default.default.cloneElement(
3329
+ element,
3330
+ { rootRef: captureCommittedRoot }
3331
+ );
3332
+ } else if (element.type === this.blockRuntime.MixedRanges) {
3333
+ renderedElement = React__default.default.cloneElement(
3334
+ element,
3335
+ {
3336
+ rootRef: captureCommittedRoot
3337
+ }
3338
+ );
3339
+ } else if (typeof element.type !== "string") {
1573
3340
  throw new TypeError(
1574
3341
  `Compiled component ${currentDefinition.displayName} must return one host element.`
1575
3342
  );
3343
+ } else {
3344
+ renderedElement = React__default.default.cloneElement(element, {
3345
+ ref: captureCommittedRoot
3346
+ });
1576
3347
  }
1577
- return React__default.default.cloneElement(element, {
1578
- ref: this.captureRoot
1579
- });
3348
+ return renderedElement;
1580
3349
  }
1581
3350
  }
1582
3351
  FarmCompiledComponent.displayName = `FarmCompiled(${definition.displayName})`;