@source-crafter/xzor-runtime 0.1.15 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/model.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  $inSvg,
3
3
  $isFragment,
4
+ $leave,
4
5
  ComponentBase,
5
6
  FINE,
6
7
  SVG,
@@ -8,7 +9,7 @@ import {
8
9
  patchIsland,
9
10
  setFineHooks,
10
11
  setRenderObserver
11
- } from "./index-k290gfdd.js";
12
+ } from "./index-45b6cjyv.js";
12
13
 
13
14
  // runtime/entity-set.ts
14
15
  class EntitySet extends Array {
@@ -226,18 +227,31 @@ var ATTR = 1;
226
227
  var REGION = 2;
227
228
  var LIST = 3;
228
229
  var ISLAND = 4;
230
+ var KEYED = 5;
231
+ var EFFECT = 6;
232
+ var MEMO = 7;
229
233
  var KIND = 7;
230
234
  var QUEUED = 8;
231
235
  var MANY = 16;
232
236
  var DEAD = 32;
233
237
  var LOOSE = 64;
234
238
  var ITEM = 128;
239
+ var OWNED = 256;
240
+ var FRESH = 512;
241
+ var SHOW = 1024;
242
+ var PAUSE = 2048;
243
+ var PAUSED = 4096;
244
+ var STALE = 8192;
235
245
  var bitOf = (index) => index === -1 ? 1 << 31 : index < 30 ? 1 << index : 1 << 30;
246
+ var MEMOS = Symbol("xzor.fine.memos");
247
+ var EMPTY = Symbol("xzor.fine.empty");
236
248
  var collecting;
237
249
  var holder;
238
250
  function collect(source, index) {
251
+ collectBits(source, bitOf(index));
252
+ }
253
+ function collectBits(source, bit) {
239
254
  const b = collecting;
240
- const bit = bitOf(index);
241
255
  const own = b.model;
242
256
  if (own === source) {
243
257
  b.mask |= bit;
@@ -296,8 +310,31 @@ class Binding {
296
310
  return (this.flags & (QUEUED | DEAD)) !== 0 ? 0 : -1;
297
311
  }
298
312
  set at(value) {
299
- if ((this.flags & DEAD) === 0)
300
- this.flags = value < 0 ? this.flags & ~QUEUED : this.flags | QUEUED;
313
+ if ((this.flags & DEAD) !== 0)
314
+ return;
315
+ if ((this.flags & KIND) === MEMO) {
316
+ if (value >= 0)
317
+ this.flags &= ~FRESH;
318
+ return;
319
+ }
320
+ this.flags = value < 0 ? this.flags & ~QUEUED : this.flags | QUEUED;
321
+ }
322
+ $memo() {
323
+ if ((this.flags & FRESH) === 0) {
324
+ this.data = this.#run();
325
+ if (this.model !== undefined && (this.flags & DEAD) === 0)
326
+ this.flags |= FRESH;
327
+ }
328
+ if (collecting !== undefined) {
329
+ const own = this.model;
330
+ if ((this.flags & MANY) === 0) {
331
+ if (own !== undefined)
332
+ collectBits(own, this.mask);
333
+ } else
334
+ for (const dep of own.values())
335
+ collectBits(dep.model, dep.mask);
336
+ }
337
+ return this.data;
301
338
  }
302
339
  #reset() {
303
340
  this.mask = 0;
@@ -330,7 +367,7 @@ class Binding {
330
367
  }
331
368
  }
332
369
  #loose() {
333
- if ((this.flags & (LOOSE | DEAD)) !== 0 || holder === undefined)
370
+ if ((this.flags & (LOOSE | DEAD | PAUSE)) !== 0 || (this.flags & KIND) >= EFFECT || holder === undefined)
334
371
  return;
335
372
  this.flags |= LOOSE;
336
373
  holder.ctx.loose.push(this);
@@ -351,8 +388,13 @@ class Binding {
351
388
  }
352
389
  stateHasChanged() {
353
390
  const flags = this.flags;
354
- if ((flags & DEAD) !== 0)
391
+ if ((flags & (DEAD | PAUSED)) !== 0) {
392
+ if ((flags & DEAD) === 0)
393
+ this.flags |= STALE;
355
394
  return;
395
+ }
396
+ if ((flags & SHOW) !== 0)
397
+ return this.#show();
356
398
  switch (flags & KIND) {
357
399
  case TEXT: {
358
400
  const value = this.#run();
@@ -365,26 +407,76 @@ class Binding {
365
407
  return;
366
408
  }
367
409
  case ATTR:
410
+ if ((flags & OWNED) !== 0)
411
+ return ownAttr(this.node, this.data, this.#run());
368
412
  return setAttr(this.node, (flags & ITEM) !== 0 ? this.read.$n : this.data, this.#run());
369
413
  case REGION: {
370
414
  const region = this.data;
415
+ const branch = region.branch;
416
+ if (branch !== undefined) {
417
+ const index = this.#run();
418
+ if (index === region.index)
419
+ return;
420
+ region.index = index;
421
+ }
371
422
  disposeAll(region);
372
423
  const end = this.node;
373
424
  const parent = end.parentNode;
374
- while (region.start.nextSibling !== end)
375
- parent.removeChild(region.start.nextSibling);
425
+ for (let node = region.start.nextSibling;node !== end; ) {
426
+ const next = node.nextSibling;
427
+ $leave(node);
428
+ node = next;
429
+ }
376
430
  const frag = document.createDocumentFragment();
377
- within(region, () => this.#run(frag));
431
+ within(region, () => branch !== undefined ? untracked(() => branch(frag)) : this.#run(frag));
378
432
  parent.insertBefore(frag, end);
379
433
  return;
380
434
  }
381
435
  case LIST:
382
436
  return reconcile(this, this.#run());
437
+ case KEYED: {
438
+ const keyed = this.data;
439
+ keyed.order = this.#run();
440
+ return;
441
+ }
383
442
  case ISLAND:
443
+ case EFFECT:
384
444
  this.#run();
385
445
  return;
446
+ case MEMO:
447
+ return;
386
448
  }
387
449
  }
450
+ #show() {
451
+ const show = this.data;
452
+ const index = this.#run();
453
+ if (index === show.index)
454
+ return;
455
+ const shown = show.branches[show.index];
456
+ if (shown !== undefined)
457
+ hideBranch(shown);
458
+ show.index = index;
459
+ let branch = show.branches[index];
460
+ if (branch !== undefined)
461
+ return showBranch(branch);
462
+ branch = { bindings: undefined, ctx: show.ctx, start: show.start, item: undefined, branch: undefined, index: -1, first: null, els: [], display: [] };
463
+ show.branches[index] = branch;
464
+ const frag = document.createDocumentFragment();
465
+ within(branch, () => untracked(() => show.build(frag)));
466
+ branch.first = frag.firstChild;
467
+ for (let node = frag.firstChild;node !== null; node = node.nextSibling)
468
+ if (node.nodeType === 1)
469
+ branch.els.push(node);
470
+ let before = this.node;
471
+ for (let i = index + 1;i < show.branches.length; i++) {
472
+ const next = show.branches[i]?.first;
473
+ if (next) {
474
+ before = next;
475
+ break;
476
+ }
477
+ }
478
+ before.parentNode.insertBefore(frag, before);
479
+ }
388
480
  #becomeIsland() {
389
481
  const read = this.read;
390
482
  const h = this.data;
@@ -407,15 +499,19 @@ class Binding {
407
499
  if ((this.flags & DEAD) !== 0)
408
500
  return;
409
501
  const kind = this.flags & KIND;
410
- if (kind === REGION)
502
+ if ((this.flags & SHOW) !== 0)
503
+ for (const branch of this.data.branches)
504
+ branch !== undefined && disposeAll(branch);
505
+ else if (kind === REGION)
411
506
  disposeAll(this.data);
412
- else if (kind === LIST)
507
+ else if (kind === LIST || kind === KEYED)
413
508
  for (const row of this.data.order)
414
509
  disposeAll(row);
415
510
  else if (kind === ISLAND) {
416
511
  disposeIsland(this.data.tree);
417
512
  this.data.tree = [];
418
- }
513
+ } else if ((this.flags & OWNED) !== 0)
514
+ undoAttr(this.node, this.data);
419
515
  this.#reset();
420
516
  this.#sweep();
421
517
  this.flags = DEAD;
@@ -448,6 +544,18 @@ function afterHandler(ctx, result) {
448
544
  if (result instanceof Promise)
449
545
  result.then(() => refreshLoose(ctx));
450
546
  }
547
+ function untracked(run) {
548
+ const prev = collecting;
549
+ const reader = Model.$reader;
550
+ collecting = undefined;
551
+ Model.$reader = undefined;
552
+ try {
553
+ return run();
554
+ } finally {
555
+ collecting = prev;
556
+ Model.$reader = reader;
557
+ }
558
+ }
451
559
  function within(h, run) {
452
560
  const prev = holder;
453
561
  holder = h;
@@ -470,14 +578,72 @@ function disposeAll(h) {
470
578
  }
471
579
  function refreshAll(h) {
472
580
  for (let b = h.bindings;b !== undefined; b = b.next) {
581
+ const kind = b.flags & KIND;
582
+ const kept = kind === REGION ? b.data.index : -1;
473
583
  b.stateHasChanged();
474
- if ((b.flags & KIND) === LIST)
584
+ if ((b.flags & SHOW) !== 0)
585
+ for (const branch of b.data.branches)
586
+ branch !== undefined && refreshAll(branch);
587
+ else if (kind === LIST || kind === KEYED)
475
588
  for (const row of b.data.order)
476
589
  refreshAll(row);
590
+ else if (kept !== -1 && kept === b.data.index)
591
+ refreshAll(b.data);
477
592
  }
478
593
  }
594
+ function under(b, f) {
595
+ const kind = b.flags & KIND;
596
+ if ((b.flags & SHOW) !== 0)
597
+ for (const branch of b.data.branches)
598
+ branch !== undefined && f(branch);
599
+ else if (kind === REGION)
600
+ f(b.data);
601
+ else if (kind === LIST || kind === KEYED)
602
+ for (const row of b.data.order)
603
+ f(row);
604
+ }
605
+ function pauseIn(h) {
606
+ for (let b = h.bindings;b !== undefined; b = b.next) {
607
+ if ((b.flags & PAUSE) !== 0)
608
+ under(b, freeze);
609
+ else
610
+ under(b, pauseIn);
611
+ }
612
+ }
613
+ function freeze(h) {
614
+ for (let b = h.bindings;b !== undefined; b = b.next) {
615
+ b.flags |= PAUSED;
616
+ under(b, freeze);
617
+ }
618
+ }
619
+ function thaw(h) {
620
+ for (let b = h.bindings;b !== undefined; b = b.next) {
621
+ const stale = (b.flags & STALE) !== 0;
622
+ b.flags &= ~(PAUSED | STALE);
623
+ if (stale)
624
+ b.stateHasChanged();
625
+ under(b, thaw);
626
+ }
627
+ }
628
+ function hideBranch(branch) {
629
+ for (let i = 0;i < branch.els.length; i++) {
630
+ const style = branch.els[i].style;
631
+ branch.display[i] = style.display;
632
+ style.display = "none";
633
+ }
634
+ pauseIn(branch);
635
+ }
636
+ function showBranch(branch) {
637
+ for (let i = 0;i < branch.els.length; i++)
638
+ branch.els[i].style.display = branch.display[i] ?? "";
639
+ thaw(branch);
640
+ }
479
641
  var textOf = (value) => value === null || value === undefined || value === false ? "" : String(value);
480
642
  function setAttr(el, name, value) {
643
+ if (typeof value === "boolean" && name.startsWith("aria-"))
644
+ value = String(value);
645
+ if (name.startsWith("class:"))
646
+ return void el.classList.toggle(name.slice(6), !!value);
481
647
  if (name.charCodeAt(0) === 45 && name.charCodeAt(1) === 45) {
482
648
  const style = el.style;
483
649
  const text2 = value === null || value === undefined || value === false ? "" : String(value);
@@ -504,48 +670,68 @@ function setAttr(el, name, value) {
504
670
  else if (name === "checked" && "checked" in el)
505
671
  el.checked = true;
506
672
  }
673
+ function ownAttr(el, s, value) {
674
+ if (s.name !== "class") {
675
+ if (s.prev === undefined)
676
+ s.prev = el.getAttribute(s.name);
677
+ return setAttr(el, s.name, value);
678
+ }
679
+ const text = value === null || value === undefined || value === false || value === true ? "" : String(value);
680
+ if (text === s.text)
681
+ return;
682
+ s.text = text;
683
+ for (const t of s.added)
684
+ el.classList.remove(t);
685
+ s.added = [];
686
+ for (const t of text.split(/\s+/))
687
+ if (t !== "" && !el.classList.contains(t)) {
688
+ el.classList.add(t);
689
+ s.added.push(t);
690
+ }
691
+ }
692
+ function undoAttr(el, s) {
693
+ if (s.name === "class")
694
+ for (const t of s.added)
695
+ el.classList.remove(t);
696
+ else if (s.prev !== undefined)
697
+ setAttr(el, s.name, s.prev);
698
+ }
507
699
  function removeRange(head, tail) {
508
- const parent = head.parentNode;
509
700
  for (let node = head;; ) {
510
701
  const next = node.nextSibling;
511
- parent.removeChild(node);
702
+ $leave(node);
512
703
  if (node === tail)
513
704
  return;
514
705
  node = next;
515
706
  }
516
707
  }
517
- function reconcile(list, items) {
518
- const state = list.data;
519
- const end = list.node;
708
+ function newRow(ctx, item, reads, build) {
709
+ const frag = document.createDocumentFragment();
710
+ const row = { bindings: undefined, ctx, head: frag, tail: frag, item, reads };
711
+ within(row, () => build(frag, item));
712
+ seal(row, frag);
713
+ return row;
714
+ }
715
+ function seal(row, frag) {
716
+ if (frag.firstChild === null)
717
+ frag.appendChild(document.createComment(""));
718
+ row.head = frag.firstChild;
719
+ row.tail = frag.lastChild;
720
+ }
721
+ function dropRow(row) {
722
+ disposeAll(row);
723
+ removeRange(row.head, row.tail);
724
+ }
725
+ function place(end, old, order) {
520
726
  const parent = end.parentNode;
521
- const next = Array.isArray(items) ? items : [...items];
522
- const keep = new Set(next);
523
- for (const [item, row] of state.rows)
524
- if (!keep.has(item)) {
525
- disposeAll(row);
526
- removeRange(row.head, row.tail);
527
- state.rows.delete(item);
528
- }
529
727
  let cursor = end;
530
- for (const row of state.order)
728
+ for (const row of old)
531
729
  if (row.head.parentNode === parent) {
532
730
  cursor = row.head;
533
731
  break;
534
732
  }
535
- const order = [];
536
- for (const item of next) {
537
- let row = state.rows.get(item);
538
- if (row === undefined) {
539
- const frag = document.createDocumentFragment();
540
- const made = { bindings: undefined, ctx: state.ctx, head: frag, tail: frag, item, reads: state.reads };
541
- within(made, () => state.build(frag, item));
542
- if (frag.firstChild === null)
543
- frag.appendChild(document.createComment(""));
544
- made.head = frag.firstChild;
545
- made.tail = frag.lastChild;
546
- parent.insertBefore(frag, cursor);
547
- state.rows.set(item, row = made);
548
- } else if (row.head !== cursor) {
733
+ for (const row of order) {
734
+ if (row.head !== cursor) {
549
735
  for (let node = row.head;; ) {
550
736
  const after = node.nextSibling;
551
737
  parent.insertBefore(node, cursor);
@@ -555,10 +741,114 @@ function reconcile(list, items) {
555
741
  }
556
742
  } else
557
743
  cursor = row.tail.nextSibling;
744
+ }
745
+ }
746
+ var duplicate = (key) => new Error(`xzor: duplicate @key ${String(key)}`);
747
+ function reconcile(list, items) {
748
+ const state = list.data;
749
+ const rows = state.rows;
750
+ let next = Array.isArray(items) ? items : [...items];
751
+ if (next.length === 0 && state.empty !== undefined)
752
+ next = [EMPTY];
753
+ const named = state.key === undefined ? $keyName(next[0]) : undefined;
754
+ const keyOf = state.key ?? (named === undefined ? undefined : (item) => item[named]);
755
+ let keys = next;
756
+ if (keyOf === undefined) {
757
+ const keep = new Set(next);
758
+ for (const [item, row] of rows)
759
+ if (!keep.has(item)) {
760
+ dropRow(row);
761
+ rows.delete(item);
762
+ }
763
+ } else {
764
+ const at = new Map;
765
+ keys = next.map((item) => item === EMPTY ? EMPTY : keyOf(item));
766
+ for (let i = 0;i < next.length; i++) {
767
+ if (at.has(keys[i]))
768
+ throw duplicate(keys[i]);
769
+ at.set(keys[i], next[i]);
770
+ }
771
+ for (const [key, row] of rows)
772
+ if (!at.has(key) || at.get(key) !== row.item) {
773
+ dropRow(row);
774
+ rows.delete(key);
775
+ }
776
+ }
777
+ const order = [];
778
+ for (let i = 0;i < next.length; i++) {
779
+ const item = next[i];
780
+ let row = rows.get(keys[i]);
781
+ if (row === undefined) {
782
+ row = item === EMPTY ? newRow(state.ctx, item, undefined, state.empty) : newRow(state.ctx, item, state.reads, state.build);
783
+ rows.set(keys[i], row);
784
+ }
558
785
  order.push(row);
559
786
  }
787
+ place(list.node, state.order, order);
560
788
  state.order = order;
561
789
  }
790
+ function sameValues(a, b) {
791
+ if (a.length !== b.length)
792
+ return false;
793
+ for (let i = 0;i < a.length; i++)
794
+ if (!Object.is(a[i], b[i]))
795
+ return false;
796
+ return true;
797
+ }
798
+ function runKeyed(s, end, run) {
799
+ const order = [];
800
+ const seen = new Set;
801
+ const outer = holder;
802
+ let building;
803
+ const finish = () => {
804
+ if (building === undefined)
805
+ return;
806
+ seal(building, building.head);
807
+ building = undefined;
808
+ holder = outer;
809
+ };
810
+ try {
811
+ run((key, values) => {
812
+ finish();
813
+ if (seen.has(key))
814
+ throw duplicate(key);
815
+ seen.add(key);
816
+ let row = s.rows.get(key);
817
+ if (row !== undefined && !sameValues(row.item, values)) {
818
+ dropRow(row);
819
+ row = undefined;
820
+ }
821
+ if (row !== undefined) {
822
+ order.push(row);
823
+ return;
824
+ }
825
+ const frag = document.createDocumentFragment();
826
+ row = { bindings: undefined, ctx: s.ctx, head: frag, tail: frag, item: values, reads: undefined };
827
+ s.rows.set(key, row);
828
+ order.push(row);
829
+ building = row;
830
+ holder = row;
831
+ return frag;
832
+ });
833
+ finish();
834
+ } finally {
835
+ holder = outer;
836
+ }
837
+ if (order.length === 0 && s.empty !== undefined) {
838
+ let row = s.rows.get(EMPTY);
839
+ if (row === undefined)
840
+ s.rows.set(EMPTY, row = newRow(s.ctx, EMPTY, undefined, s.empty));
841
+ order.push(row);
842
+ seen.add(EMPTY);
843
+ }
844
+ for (const [key, row] of s.rows)
845
+ if (!seen.has(key)) {
846
+ dropRow(row);
847
+ s.rows.delete(key);
848
+ }
849
+ place(end, s.order, order);
850
+ return order;
851
+ }
562
852
  function make(spec, svg = false) {
563
853
  if (spec === null)
564
854
  return document.createComment("");
@@ -612,15 +902,50 @@ function on(el, event, handler, h) {
612
902
  } else
613
903
  el.addEventListener(event, (e) => afterHandler(h.ctx, handler(e)));
614
904
  }
615
- function region(start, end, build, item, flags) {
616
- const region2 = { bindings: undefined, ctx: holder.ctx, start, item };
905
+ function region(start, end, build, item, flags, branch) {
906
+ const region2 = { bindings: undefined, ctx: holder.ctx, start, item, branch, index: -1 };
617
907
  track(new Binding(REGION | flags, end, build, region2));
618
908
  }
619
- function list(end, items, reads, build) {
620
- const state = { build, ctx: holder.ctx, rows: new Map, order: [], reads };
909
+ function list(end, items, reads, build, empty, key) {
910
+ const state = { build, ctx: holder.ctx, rows: new Map, order: [], reads, empty, key };
621
911
  track(new Binding(LIST, end, items, state));
622
912
  }
623
913
  var anchor = (parent) => parent.appendChild(document.createComment(""));
914
+ function awaiting(end, pending, then, fail) {
915
+ const state = { phase: 0, value: undefined, seq: 0 };
916
+ const view = new Binding(REGION, end, (p) => {
917
+ if (state.phase === 0)
918
+ pending?.(p);
919
+ else if (state.phase === 1)
920
+ then?.(p, state.value);
921
+ else
922
+ fail?.(p, state.value);
923
+ }, { bindings: undefined, ctx: holder.ctx, start: end.previousSibling, item: undefined, branch: undefined, index: -1 });
924
+ const show = (phase, value) => {
925
+ if ((view.flags & DEAD) !== 0)
926
+ return;
927
+ state.phase = phase;
928
+ state.value = value;
929
+ view.stateHasChanged();
930
+ };
931
+ track(view);
932
+ return (result) => {
933
+ const seq = ++state.seq;
934
+ if (typeof result?.then !== "function")
935
+ return show(1, result);
936
+ if (state.phase !== 0)
937
+ show(0, undefined);
938
+ result.then((value) => seq === state.seq && show(1, value), (error) => {
939
+ if (seq !== state.seq)
940
+ return;
941
+ show(2, error);
942
+ if (fail === undefined)
943
+ queueMicrotask(() => {
944
+ throw error;
945
+ });
946
+ });
947
+ };
948
+ }
624
949
  var fine = {
625
950
  template(specs) {
626
951
  let node;
@@ -682,12 +1007,93 @@ var fine = {
682
1007
  read.$n = name;
683
1008
  track(new Binding(ATTR | ITEM, el, read, row));
684
1009
  },
1010
+ attrOf(el, name, read) {
1011
+ track(new Binding(ATTR | OWNED, el, read, { name, text: undefined, prev: undefined, added: [] }));
1012
+ },
685
1013
  region(parent, build) {
686
1014
  region(anchor(parent), anchor(parent), build, undefined, 0);
687
1015
  },
688
1016
  regionAt(end, build) {
689
1017
  region(end.previousSibling, end, build, undefined, 0);
690
1018
  },
1019
+ branchAt(end, run) {
1020
+ region(end.previousSibling, end, run, undefined, 0, run);
1021
+ },
1022
+ showAt(end, run) {
1023
+ const show = { ctx: holder.ctx, start: end.previousSibling, index: -1, build: run, branches: [] };
1024
+ track(new Binding(REGION | SHOW, end, run, show));
1025
+ },
1026
+ pauseAt(end, build) {
1027
+ region(end.previousSibling, end, () => 0, undefined, PAUSE, build);
1028
+ },
1029
+ textOnce(node, read) {
1030
+ node.data = textOf(untracked(read));
1031
+ },
1032
+ onceAt(end, build) {
1033
+ const frag = document.createDocumentFragment();
1034
+ untracked(() => build(frag));
1035
+ end.parentNode.insertBefore(frag, end);
1036
+ },
1037
+ set(el, name, value) {
1038
+ setAttr(el, name, value);
1039
+ },
1040
+ setText(node, value) {
1041
+ node.data = textOf(value);
1042
+ },
1043
+ anchor,
1044
+ memo(cls, names) {
1045
+ const proto = cls.prototype;
1046
+ names.forEach((name, i) => {
1047
+ const desc = Object.getOwnPropertyDescriptor(proto, name);
1048
+ const get = desc?.get;
1049
+ if (get === undefined)
1050
+ return;
1051
+ const reader = Symbol(name);
1052
+ Object.defineProperty(proto, name, {
1053
+ ...desc,
1054
+ get() {
1055
+ const memo = this[MEMOS]?.[i];
1056
+ if (memo !== undefined)
1057
+ return memo.$memo();
1058
+ const by = collecting;
1059
+ if (by === undefined)
1060
+ return get.call(this);
1061
+ const first = this[reader];
1062
+ if (first === undefined || first === by || (first.flags & DEAD) !== 0) {
1063
+ this[reader] = by;
1064
+ return get.call(this);
1065
+ }
1066
+ this[reader] = undefined;
1067
+ const b = new Binding(MEMO | QUEUED, null, () => get.call(this), undefined);
1068
+ (this[MEMOS] ??= [])[i] = b;
1069
+ return b.$memo();
1070
+ }
1071
+ });
1072
+ });
1073
+ },
1074
+ awaitAt(end, read, pending, then, fail) {
1075
+ const show = awaiting(end, pending, then, fail);
1076
+ track(new Binding(EFFECT, end, () => show(read()), undefined));
1077
+ },
1078
+ awaitCallAt(end, args, call, pending, then, fail) {
1079
+ const show = awaiting(end, pending, then, fail);
1080
+ let last;
1081
+ const request = new Binding(EFFECT, end, () => show(call(...last)), undefined);
1082
+ track(new Binding(EFFECT, end, () => {
1083
+ const next = args();
1084
+ if (last !== undefined && next.every((v, i) => Object.is(v, last[i])))
1085
+ return;
1086
+ const first = last === undefined;
1087
+ last = next;
1088
+ if (!first)
1089
+ request.stateHasChanged();
1090
+ }, undefined));
1091
+ track(request);
1092
+ },
1093
+ anchors(parent) {
1094
+ anchor(parent);
1095
+ return anchor(parent);
1096
+ },
691
1097
  regionItem(end, i) {
692
1098
  const row = holder;
693
1099
  region(end.previousSibling, end, row.reads[i], row.item, ITEM);
@@ -695,8 +1101,12 @@ var fine = {
695
1101
  list(parent, items, build) {
696
1102
  list(anchor(parent), items, undefined, build);
697
1103
  },
698
- listAt(end, items, reads, build) {
699
- list(end, items, reads, build);
1104
+ listAt(end, items, reads, build, empty, key) {
1105
+ list(end, items, reads, build, empty, key);
1106
+ },
1107
+ keyedAt(end, run, empty) {
1108
+ const state = { ctx: holder.ctx, rows: new Map, order: [], empty };
1109
+ track(new Binding(KEYED, end, () => runKeyed(state, end, run), state));
700
1110
  },
701
1111
  island(parent, build) {
702
1112
  fine.islandAt(anchor(parent), build);
@@ -765,8 +1175,22 @@ setFineHooks({
765
1175
  root.end = frag.appendChild(document.createComment(""));
766
1176
  return root;
767
1177
  },
768
- refresh: (root) => refreshAll(root),
769
- dispose: (root) => disposeAll(root),
1178
+ refresh(root) {
1179
+ const memos = root.ctx.owner[MEMOS];
1180
+ if (memos !== undefined) {
1181
+ for (const m of memos)
1182
+ if (m !== undefined)
1183
+ m.flags &= ~FRESH;
1184
+ }
1185
+ refreshAll(root);
1186
+ },
1187
+ dispose(root) {
1188
+ const memos = root.ctx.owner[MEMOS];
1189
+ if (memos !== undefined)
1190
+ for (const m of memos)
1191
+ m?.dispose();
1192
+ disposeAll(root);
1193
+ },
770
1194
  handled(owner, result) {
771
1195
  const ctx = owner[CTX];
772
1196
  if (ctx !== undefined)
@@ -804,6 +1228,7 @@ var meta = Symbol("xzor.model.meta");
804
1228
  var empty = { names: [], tracked: new Set, persisted: [], ignored: new Set, validators: [], serialized: [] };
805
1229
  var pendingPersist;
806
1230
  var pendingIgnored;
1231
+ var pendingKey;
807
1232
  var pendingValidators;
808
1233
  var silent = 0;
809
1234
  function metaOf(model) {
@@ -835,8 +1260,10 @@ class Model extends EventTarget {
835
1260
  tracked: new Set(all),
836
1261
  persisted: base ? base.persisted.slice() : [],
837
1262
  ignored: new Set(base?.ignored),
838
- validators: base ? base.validators.map(([name, list2]) => [name, list2.slice()]) : []
1263
+ validators: base ? base.validators.map(([name, list2]) => [name, list2.slice()]) : [],
1264
+ key: pendingKey ?? base?.key
839
1265
  };
1266
+ pendingKey = undefined;
840
1267
  if (pendingValidators) {
841
1268
  for (const [name, list2] of pendingValidators) {
842
1269
  const known = own.validators.find((entry) => entry[0] === name);
@@ -1230,6 +1657,12 @@ function Ignore(_member, context) {
1230
1657
  (pendingIgnored ??= []).push(context.name);
1231
1658
  }
1232
1659
  function Untracked(_value, _context) {}
1660
+ function Key(_member, context) {
1661
+ pendingKey = context.name;
1662
+ }
1663
+ function $keyName(item) {
1664
+ return item instanceof Model ? metaOf(item).key : undefined;
1665
+ }
1233
1666
  function isLike(a, b) {
1234
1667
  return Model.same(a, b);
1235
1668
  }
@@ -1440,10 +1873,12 @@ setRenderObserver({
1440
1873
  });
1441
1874
  export {
1442
1875
  $flush,
1876
+ $keyName,
1443
1877
  $notify,
1444
1878
  EntitySet,
1445
1879
  FineComponent,
1446
1880
  Ignore,
1881
+ Key,
1447
1882
  Model,
1448
1883
  ModelChangeEvent,
1449
1884
  Persist,
@@ -1465,5 +1900,5 @@ export {
1465
1900
  requiredIdValue
1466
1901
  };
1467
1902
 
1468
- //# debugId=1D6B4E24F03EAD3F64756E2164756E21
1903
+ //# debugId=E7409C03C0112EDB64756E2164756E21
1469
1904
  //# sourceMappingURL=model.js.map