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