tutuca 0.13.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -128,8 +128,8 @@ function jestMatchers(chai2, utils) {
128
128
  expected
129
129
  );
130
130
  });
131
- m("toHaveProperty", function(path, ...rest) {
132
- const keys = Array.isArray(path) ? path : String(path).split(".");
131
+ m("toHaveProperty", function(path2, ...rest) {
132
+ const keys = Array.isArray(path2) ? path2 : String(path2).split(".");
133
133
  let cur = this._obj;
134
134
  let found = true;
135
135
  for (const k of keys) {
@@ -144,7 +144,7 @@ function jestMatchers(chai2, utils) {
144
144
  pass,
145
145
  "expected #{this} to have property #{exp}",
146
146
  "expected #{this} not to have property #{exp}",
147
- path
147
+ path2
148
148
  );
149
149
  });
150
150
  m("toBeInstanceOf", function(ctor) {
@@ -234,8 +234,11 @@ var Step = class {
234
234
  }
235
235
  setDraftValue(_root, _v) {
236
236
  }
237
- enterFrame(stack, next) {
238
- return stack.enter(next, {}, true);
237
+ // Re-enter this step while rebuilding a stack. `renderPath` is the dispatch
238
+ // position AFTER this step: a rebuilt frame must carry the same render path
239
+ // the renderer had there, or the provides it publishes would be located wrong.
240
+ enterFrame(stack, next, renderPath) {
241
+ return stack.enter(next, {}, true, renderPath);
239
242
  }
240
243
  toAbstractPathStep() {
241
244
  return this;
@@ -260,8 +263,8 @@ var BindStep = class _BindStep extends Step {
260
263
  lookup(v, _dval) {
261
264
  return v;
262
265
  }
263
- enterFrame(stack, next) {
264
- return stack.enter(next, { ...this.binds }, false);
266
+ enterFrame(stack, next, renderPath) {
267
+ return stack.enter(next, { ...this.binds }, false, renderPath);
265
268
  }
266
269
  withIndex(i) {
267
270
  return new _BindStep({ ...this.binds, key: i });
@@ -278,9 +281,9 @@ var ScopeBindStep = class _ScopeBindStep extends BindStep {
278
281
  super(binds);
279
282
  this.val = val;
280
283
  }
281
- enterFrame(stack, next) {
284
+ enterFrame(stack, next, renderPath) {
282
285
  const dyn = this.val.evalAsHandler(stack)?.call(stack.it) ?? {};
283
- return stack.enter(next, { ...this.binds, ...dyn }, false);
286
+ return stack.enter(next, { ...this.binds, ...dyn }, false, renderPath);
284
287
  }
285
288
  withIndex(i) {
286
289
  return new _ScopeBindStep(this.val, { ...this.binds, key: i });
@@ -323,8 +326,8 @@ var SeqStep = class extends Step {
323
326
  const seq = readKey(root, this.field, null);
324
327
  if (seq != null) writeSeqKey(seq, this.key, v);
325
328
  }
326
- enterFrame(stack, next) {
327
- return stack.enter(next, { key: this.key }, true);
329
+ enterFrame(stack, next, renderPath) {
330
+ return stack.enter(next, { key: this.key }, true, renderPath);
328
331
  }
329
332
  toKey() {
330
333
  return { field: this.field, key: this.key };
@@ -370,60 +373,52 @@ var EachBindStep = class extends Step {
370
373
  }
371
374
  // Replay the renderer's per-item binds (key, value + any @enrich-with binds)
372
375
  // so a rebuilt stack matches the one @each rendered with.
373
- enterFrame(stack, next) {
374
- return stack.enter(next, this.iterInfo.enrichBinds(stack, this.key), false);
376
+ enterFrame(stack, next, renderPath) {
377
+ return stack.enter(next, this.iterInfo.enrichBinds(stack, this.key), false, renderPath);
375
378
  }
376
379
  toAbstractPathStep() {
377
380
  return null;
378
381
  }
379
382
  };
380
383
  var EachRenderItStep = class extends SeqStep {
381
- enterFrame(stack, next) {
382
- return stack.enter(next, { key: this.key, value: next }, false).enter(next, {}, true);
384
+ enterFrame(stack, next, renderPath) {
385
+ return stack.enter(next, { key: this.key, value: next }, false, renderPath).enter(next, {}, true, renderPath);
383
386
  }
384
387
  toAbstractPathStep() {
385
388
  return new SeqStep(this.field, this.key);
386
389
  }
387
390
  };
388
- function warnRawDynStep(op, step) {
389
- console.warn(`Path.${op} reached a DynStep: call toTransactionPath() first`, step);
391
+ function stepToJson(step) {
392
+ if (step instanceof SeqStep) return { f: step.field, k: step.key };
393
+ if (step instanceof FieldStep) return { f: step.field };
394
+ if (step instanceof SeqAccessStep) return { f: step.seqField, a: step.keyField };
395
+ return null;
390
396
  }
391
- var DynStep = class extends Step {
392
- constructor(producerCompId, producerSteps) {
393
- super();
394
- this.producerCompId = producerCompId;
395
- this.producerSteps = producerSteps;
396
- this.interiorCids = /* @__PURE__ */ new Set();
397
- }
398
- // Steps spliced into the transaction path in place of this marker.
399
- teleportSteps() {
400
- return this.producerSteps;
401
- }
402
- lookup(_v, dval = null) {
403
- warnRawDynStep("lookup", this);
404
- return dval;
405
- }
406
- enterFrame(stack, _next) {
407
- warnRawDynStep("enterFrame", this);
408
- return stack;
409
- }
410
- };
411
- var DynEachStep = class extends DynStep {
412
- constructor(producerCompId, producerSteps, key) {
413
- super(producerCompId, producerSteps);
414
- this.key = key;
397
+ function stepFromJson(j) {
398
+ if (j == null || typeof j.f !== "string") return null;
399
+ if (j.k !== void 0) return new SeqStep(j.f, j.k);
400
+ if (j.a !== void 0) return new SeqAccessStep(j.f, j.a);
401
+ return new FieldStep(j.f);
402
+ }
403
+ function pathToJson(path2) {
404
+ const out = [];
405
+ for (const step of path2.steps) {
406
+ const j = stepToJson(step);
407
+ if (j === null) return null;
408
+ out.push(j);
415
409
  }
416
- teleportSteps() {
417
- const { producerSteps, key } = this;
418
- if (producerSteps.length === 0) return producerSteps;
419
- const last = producerSteps[producerSteps.length - 1];
420
- if (!(last instanceof FieldStep)) {
421
- console.warn("DynEachStep: seq-access dynamic cannot be iterated", this);
422
- return producerSteps;
423
- }
424
- return producerSteps.slice(0, -1).concat(new SeqStep(last.field, key));
410
+ return out;
411
+ }
412
+ function pathFromJson(j) {
413
+ if (!Array.isArray(j)) return null;
414
+ const steps = [];
415
+ for (const item of j) {
416
+ const step = stepFromJson(item);
417
+ if (step === null) return null;
418
+ steps.push(step);
425
419
  }
426
- };
420
+ return new Path(steps);
421
+ }
427
422
  var Path = class _Path {
428
423
  constructor(steps = []) {
429
424
  this.steps = steps;
@@ -434,40 +429,13 @@ var Path = class _Path {
434
429
  popStep() {
435
430
  return new _Path(this.steps.slice(0, -1));
436
431
  }
437
- // The dispatch path: frame-only steps removed, one step per crossed component
438
- // (DynStep included). `popStep` over it bubbles through every component.
432
+ // Frame-only steps removed, one step per crossed component: `popStep` over the
433
+ // result bubbles through every component.
439
434
  compact() {
440
435
  const out = [];
441
436
  for (const step of this.steps) {
442
437
  const s = step.toAbstractPathStep();
443
- if (s !== null) {
444
- if (s !== step) s._originCid = step._originCid;
445
- out.push(s);
446
- }
447
- }
448
- return new _Path(out);
449
- }
450
- // The abstract path used to apply a transaction: every DynStep is teleported —
451
- // the steps interior to its producer..consumer span are dropped and the
452
- // producer's own path spliced in — so a mutation lands on the data's real
453
- // location. A path with no DynStep is returned unchanged.
454
- toTransactionPath() {
455
- let hasDyn = false;
456
- for (const step of this.steps)
457
- if (step instanceof DynStep) {
458
- hasDyn = true;
459
- break;
460
- }
461
- if (!hasDyn) return this;
462
- const out = [];
463
- for (const step of this.steps) {
464
- if (step instanceof DynStep) {
465
- while (out.length > 0 && step.interiorCids.has(out[out.length - 1]._originCid)) out.pop();
466
- for (const ts of step.teleportSteps()) {
467
- ts._originCid = step.producerCompId;
468
- out.push(ts);
469
- }
470
- } else out.push(step);
438
+ if (s !== null) out.push(s);
471
439
  }
472
440
  return new _Path(out);
473
441
  }
@@ -475,7 +443,6 @@ var Path = class _Path {
475
443
  // key as it is *now* so a later lookup/setValue lands on the same item even if the
476
444
  // keyField changed meanwhile (e.g. the selected tab moved while an intent was in
477
445
  // flight). Returns a new Path with those steps replaced; `this` if nothing pinned.
478
- // Must be called on a transaction path (no DynSteps — call toTransactionPath first).
479
446
  pinKeys(root) {
480
447
  let curVal = root;
481
448
  let out = null;
@@ -498,8 +465,8 @@ var Path = class _Path {
498
465
  }
499
466
  // The values entered along the path, root→leaf (root included): index 0 is `root`,
500
467
  // the last entry is the leaf this path resolves to. Stops early at the first
501
- // unresolvable step. Call on a transaction path (no DynSteps). Used to walk the
502
- // component instances on a dispatch path (filter via Components.getCompFor).
468
+ // unresolvable step. Used to walk the component instances on a dispatch path
469
+ // (filter via Components.getCompFor).
503
470
  resolveChain(root) {
504
471
  const out = [root];
505
472
  let curVal = root;
@@ -513,7 +480,7 @@ var Path = class _Path {
513
480
  // A flat `[{ field, key? }]` list of the addressing steps, skipping frame-only
514
481
  // steps (binds). Generic path introspection so tooling (e.g. the storybook
515
482
  // activity log) can identify which subtree a transaction touched without
516
- // depending on Step internals. Call on a transaction path (no DynSteps).
483
+ // depending on Step internals.
517
484
  toKeys() {
518
485
  const out = [];
519
486
  for (const step of this.steps) {
@@ -534,62 +501,147 @@ var Path = class _Path {
534
501
  });
535
502
  }
536
503
  buildStack(stack) {
537
- let prev = stack.it;
538
- for (const step of this.steps) {
539
- const next = step.lookup(prev, NONE);
540
- if (next === NONE) {
541
- console.warn("bad PathItem", { root: stack.it, step, path: this });
542
- return null;
504
+ return walkItems(stack, this.steps, stack.renderPath ?? new DispatchPath(), this)?.[0] ?? null;
505
+ }
506
+ };
507
+ function walkItems(stack, items, renderPath, path2) {
508
+ let prev = stack.it;
509
+ for (const step of items) {
510
+ const next = step.lookup(prev, NONE);
511
+ if (next === NONE) {
512
+ console.warn("bad PathItem", { root: stack.it, step, path: path2 });
513
+ return null;
514
+ }
515
+ renderPath = renderPath.pushItem(step);
516
+ stack = step.enterFrame(stack, next, renderPath);
517
+ prev = next;
518
+ }
519
+ return [stack, renderPath];
520
+ }
521
+ var EMPTY_PATH = new Path([]);
522
+ var DispatchPath = class _DispatchPath {
523
+ constructor(frames = [{ base: EMPTY_PATH, items: [] }]) {
524
+ this.frames = frames;
525
+ }
526
+ // Plain addressing steps in one frame based at the root: what a caller means
527
+ // by "this position" when it has no continuation of its own.
528
+ static ofSteps(steps) {
529
+ return new _DispatchPath([{ base: EMPTY_PATH, items: steps.slice() }]);
530
+ }
531
+ get top() {
532
+ return this.frames[this.frames.length - 1];
533
+ }
534
+ _withTopItems(items) {
535
+ const frames = this.frames.slice();
536
+ frames[frames.length - 1] = { base: this.top.base, items };
537
+ return new _DispatchPath(frames);
538
+ }
539
+ concat(steps) {
540
+ if (this.frames.length === 0) return _DispatchPath.ofSteps(steps);
541
+ return this._withTopItems(this.top.items.concat(steps));
542
+ }
543
+ pushItem(step) {
544
+ return this.concat([step]);
545
+ }
546
+ pushFrame(base) {
547
+ return new _DispatchPath(this.frames.concat({ base, items: [] }));
548
+ }
549
+ // Whether bubbling has anywhere left to go: another step in this frame, or a
550
+ // visual caller underneath it.
551
+ canPop() {
552
+ const n = this.frames.length;
553
+ return n > 1 || n === 1 && this.frames[0].items.length > 0;
554
+ }
555
+ isRoot() {
556
+ return this.toTransactionPath().steps.length === 0;
557
+ }
558
+ // One component closer to the root. At the top of a frame that is popping back
559
+ // to the visual caller, not to the producer's own parent — the caller is where
560
+ // the `*name` was written, and where an unhandled message should keep going.
561
+ popStep() {
562
+ const n = this.frames.length;
563
+ if (n === 0) return this;
564
+ const top = this.frames[n - 1];
565
+ if (top.items.length > 0) return this._withTopItems(top.items.slice(0, -1));
566
+ if (n > 1) return new _DispatchPath(this.frames.slice(0, -1));
567
+ return this;
568
+ }
569
+ // Drop frame-only steps inside every frame independently; a frame's base is
570
+ // already addressing-only.
571
+ compact() {
572
+ return new _DispatchPath(
573
+ this.frames.map(({ base, items }) => ({ base, items: new Path(items).compact().steps }))
574
+ );
575
+ }
576
+ // A stable string for the ADDRESS this path denotes, for the render cache. The
577
+ // same immutable value can sit at two places in the tree, and a subtree rendered
578
+ // at one of them bakes that address in — the provides it publishes are located
579
+ // there. Frame-only steps address nothing, so they are compacted out: two sites
580
+ // that differ only in binds do render the same subtree.
581
+ get addressKey() {
582
+ this._addressKey ??= renderPathText(this.toTransactionPath().compact());
583
+ return this._addressKey;
584
+ }
585
+ // The active transaction address: the top frame's absolute base followed by
586
+ // its ordinary descendant steps. This is where a mutation lands.
587
+ toTransactionPath() {
588
+ const top = this.top;
589
+ return top === void 0 ? EMPTY_PATH : top.base.concat(top.items);
590
+ }
591
+ // Rebuild the render stack this path was dispatched from. A frame with a base
592
+ // re-enters at that absolute value first — replaying the resume a `*name`
593
+ // render performed — and then walks its ordinary items.
594
+ buildStack(stack) {
595
+ let renderPath = new _DispatchPath();
596
+ for (let i = 0; i < this.frames.length; i++) {
597
+ const { base, items } = this.frames[i];
598
+ if (i > 0 || base.steps.length > 0) {
599
+ const baseValue = base.lookup(stack.root, NONE);
600
+ if (baseValue === NONE) {
601
+ console.warn("bad frame base", { base, path: this });
602
+ return null;
603
+ }
604
+ renderPath = renderPath.pushFrame(base);
605
+ stack = stack.enter(baseValue, {}, true, renderPath);
543
606
  }
544
- stack = step.enterFrame(stack, next);
545
- prev = next;
607
+ const walked = walkItems(stack, items, renderPath, this);
608
+ if (walked === null) return null;
609
+ [stack, renderPath] = walked;
546
610
  }
547
611
  return stack;
548
612
  }
549
613
  static fromNodeAndEventName(node, eventName, rootNode, maxDepth, comps, stopOnNoEvent = true) {
550
- const pathSteps = [];
551
- const pendingDyns = [];
614
+ const parts = [];
552
615
  const bubbles = BUBBLING_EVENTS.has(eventName);
553
616
  let depth = 0;
554
617
  let eventIds = [];
555
618
  let handlers = null;
556
- let nodeIds = [];
619
+ let nodeRefs = [];
557
620
  let isLeafComponent = true;
558
621
  const crossComponent = (cidNum, vid) => {
559
622
  const comp = comps.getComponentForId(cidNum);
560
- let pushStep = true;
623
+ let pushPart = true;
561
624
  if (handlers === null && (isLeafComponent || bubbles)) {
562
625
  handlers = findHandlers(comp, eventIds, vid, eventName);
563
626
  if (handlers === null) {
564
627
  if (isLeafComponent && stopOnNoEvent && !bubbles) return false;
565
628
  } else if (!isLeafComponent) {
566
- pathSteps.length = 0;
567
- pendingDyns.length = 0;
568
- pushStep = false;
629
+ parts.length = 0;
630
+ pushPart = false;
569
631
  }
570
632
  }
571
633
  isLeafComponent = false;
572
- for (const dyn of pendingDyns) dyn.interiorCids.add(cidNum);
573
- if (pushStep) {
574
- const step = resolvePathStep(comp, nodeIds, vid);
575
- if (step) {
576
- step._originCid = cidNum;
577
- pathSteps.push(step);
578
- if (step instanceof DynStep) {
579
- step.interiorCids.add(cidNum);
580
- pendingDyns.push(step);
581
- }
582
- }
634
+ if (pushPart) {
635
+ const part = resolvePathPart(comp, nodeRefs, vid);
636
+ if (part) parts.push(part);
583
637
  }
584
- for (let i = pendingDyns.length - 1; i >= 0; i--)
585
- if (pendingDyns[i].producerCompId === cidNum) pendingDyns.splice(i, 1);
586
638
  eventIds = [];
587
- nodeIds = [];
639
+ nodeRefs = [];
588
640
  return true;
589
641
  };
590
642
  while (node && node !== rootNode && depth < maxDepth) {
591
643
  if (node?.dataset) {
592
- const { eid, cid, vid } = node.dataset;
644
+ const { eid, cid, vid, rp } = node.dataset;
593
645
  if (eid !== void 0) eventIds.push(eid);
594
646
  const metas = metaChain(node.previousSibling);
595
647
  let sawComp = false;
@@ -597,21 +649,47 @@ var Path = class _Path {
597
649
  if (m.$ === "Comp") {
598
650
  sawComp = true;
599
651
  if (!crossComponent(m.cid, m.vid)) return NO_EVENT_INFO;
600
- nodeIds.push({ nid: m.nid });
652
+ nodeRefs.push({ nid: m.nid, base: pathFromJson(m.base) });
601
653
  } else {
602
- nodeIds.push({ nid: m.nid, si: m.si, sk: m.sk });
654
+ nodeRefs.push({ nid: m.nid, si: m.si, sk: m.sk });
655
+ }
656
+ }
657
+ if (!sawComp && cid !== void 0) {
658
+ if (!crossComponent(+cid, vid)) return NO_EVENT_INFO;
659
+ if (rp !== void 0) {
660
+ const base = parseRenderPath(rp);
661
+ if (base !== null) nodeRefs.push({ base });
603
662
  }
604
663
  }
605
- if (!sawComp && cid !== void 0 && !crossComponent(+cid, vid)) return NO_EVENT_INFO;
606
664
  }
607
665
  depth += 1;
608
666
  node = node.parentNode;
609
667
  }
610
- if (pendingDyns.length > 0)
611
- console.warn("event reconstruction: dynamic-var producer not found", pendingDyns);
612
- return [new _Path(pathSteps.reverse()), handlers];
668
+ parts.reverse();
669
+ let path2 = new _DispatchPath();
670
+ for (const part of parts)
671
+ path2 = part.base !== void 0 ? path2.pushFrame(part.base) : path2.pushItem(part.step);
672
+ return [path2, handlers];
613
673
  }
614
674
  };
675
+ function keyedPath(path2, key) {
676
+ const steps = path2.steps;
677
+ const last = steps[steps.length - 1];
678
+ if (last instanceof SeqStep || last instanceof FieldStep)
679
+ return new Path(steps.slice(0, -1).concat(new SeqStep(last.field, key)));
680
+ return null;
681
+ }
682
+ function renderPathText(path2) {
683
+ return JSON.stringify(pathToJson(path2));
684
+ }
685
+ function parseRenderPath(text) {
686
+ try {
687
+ return pathFromJson(JSON.parse(text));
688
+ } catch (err) {
689
+ console.warn("bad render path", err, text);
690
+ return null;
691
+ }
692
+ }
615
693
  function metaChain(n) {
616
694
  const out = [];
617
695
  while (n?.nodeType === 8 && n.textContent[0] === "§") {
@@ -664,20 +742,27 @@ var StepCtx = class _StepCtx {
664
742
  return pi;
665
743
  }
666
744
  };
667
- function resolvePathStep(comp, nodeIds, vid) {
668
- for (let i = 0; i < nodeIds.length; i++) {
669
- const ctx = new StepCtx(comp, nodeIds, i, vid);
670
- const step = ctx.resolveNode().toPathStep(ctx);
671
- if (step !== null) return step;
745
+ function resolvePathPart(comp, nodeRefs, vid) {
746
+ for (let i = 0; i < nodeRefs.length; i++) {
747
+ const ref = nodeRefs[i];
748
+ if (ref.base != null) return { base: ref.base };
749
+ if (ref.nid === void 0 || ref.nid === null) continue;
750
+ const ctx = new StepCtx(comp, nodeRefs, i, vid);
751
+ const step = ctx.resolveNode()?.toPathStep(ctx) ?? null;
752
+ if (step !== null) return { step };
672
753
  }
673
754
  return null;
674
755
  }
675
756
  var NO_EVENT_INFO = [null, null];
676
757
  var BUBBLING_EVENTS = /* @__PURE__ */ new Set(["drop"]);
758
+ var path = () => new PathBuilder();
677
759
  var PathBuilder = class {
678
760
  constructor() {
679
761
  this.pathChanges = [];
680
762
  }
763
+ toPath() {
764
+ return new Path(this.pathChanges);
765
+ }
681
766
  add(pathChange) {
682
767
  this.pathChanges.push(pathChange);
683
768
  return this;
@@ -814,31 +899,14 @@ function parseBool(s, px) {
814
899
  const val = parseToken(tokens[0], px);
815
900
  return val !== null && kindOf(val) & G_BOOL ? val : null;
816
901
  }
817
- function parseText(s, px) {
818
- return _parseSingle(s, px, G_TEXT);
819
- }
820
- function parseComponent(s, px) {
821
- return _parseSingle(s, px, G_COMPONENT);
822
- }
823
- function parseSequence(s, px) {
824
- return _parseSingle(s, px, G_SEQUENCE);
825
- }
826
- function parseField(s, px) {
827
- return _parseSingle(s, px, G_FIELD);
828
- }
829
- function parseProvide(s, px) {
830
- return _parseSingle(s, px, G_PROVIDE);
831
- }
832
- function parseMacroAttr(s, px) {
833
- return _parseSingle(s, px, G_ALL);
834
- }
835
- function parseReceiveHandler(s, px) {
836
- return _parseHandler(s, px, "receive", true, true, false);
837
- }
838
- function parseAlterHandler(s, px) {
839
- const r = _parseHandler(s, px, "alter", false, false, true);
840
- return r === null ? null : r.handlerVal;
841
- }
902
+ var parseText = (s, px) => _parseSingle(s, px, G_TEXT);
903
+ var parseComponent = (s, px) => _parseSingle(s, px, G_COMPONENT);
904
+ var parseSequence = (s, px) => _parseSingle(s, px, G_SEQUENCE);
905
+ var parseField = (s, px) => _parseSingle(s, px, G_FIELD);
906
+ var parseProvide = (s, px) => _parseSingle(s, px, G_PROVIDE);
907
+ var parseMacroAttr = (s, px) => _parseSingle(s, px, G_ALL);
908
+ var parseReceiveHandler = (s, px) => _parseHandler(s, px, "receive", true, true, false);
909
+ var parseAlterHandler = (s, px) => _parseHandler(s, px, "alter", false, false, true)?.handlerVal ?? null;
842
910
  function _parseHandler(s, px, namespace, allowArgs, report, allowMethod) {
843
911
  const tokens = tokenizeValue(s.trim());
844
912
  const headTok = tokens[0] ?? "";
@@ -1527,19 +1595,9 @@ function childOpts(vnode, ns, opts) {
1527
1595
  const target = ns === SVG_NS && isForeignObject(vnode.tag) ? null : ns;
1528
1596
  return target === (opts.namespace ?? null) ? opts : { ...opts, namespace: target };
1529
1597
  }
1530
- var NEVER_ASSIGN = /* @__PURE__ */ new Set([
1531
- "width",
1532
- "height",
1533
- "href",
1534
- "list",
1535
- "form",
1536
- "tabIndex",
1537
- "download",
1538
- "rowSpan",
1539
- "colSpan",
1540
- "role",
1541
- "popover"
1542
- ]);
1598
+ var NEVER_ASSIGN = new Set(
1599
+ "width height href list form tabIndex download rowSpan colSpan role popover".split(" ")
1600
+ );
1543
1601
  var PROP_ATTR_NAME = { className: "class", htmlFor: "for" };
1544
1602
  function applyProperties(node, props) {
1545
1603
  const namespaced = isNamespaced(node);
@@ -1818,6 +1876,10 @@ function morphChildren(parentDom, oldChilds, newChilds, opts) {
1818
1876
  if (!used[i] && domNodes[i].parentNode === parentDom) parentDom.removeChild(domNodes[i]);
1819
1877
  }
1820
1878
  function render(vnode, container, options, prev) {
1879
+ if (vnode == null) {
1880
+ container.replaceChildren();
1881
+ return { vnode: null, dom: null };
1882
+ }
1821
1883
  const isFragment = vnode instanceof VFragment;
1822
1884
  if (prev && prev.vnode instanceof VFragment === isFragment) {
1823
1885
  const oldDom = isFragment ? container : prev.dom;
@@ -1857,13 +1919,16 @@ function h(tagName, properties, children, namespace) {
1857
1919
  }
1858
1920
 
1859
1921
  // src/anode.js
1860
- function resolveDynProducer(comp, name) {
1861
- const own = comp?.provide?.[name];
1862
- const producerComp = own != null ? comp : comp?.scope?.lookupProvider(name) ?? null;
1863
- const producerProvide = own ?? producerComp?.provide?.[name];
1864
- if (producerComp == null || producerProvide == null) return null;
1865
- const pi = producerProvide.val?.toPathItem?.() ?? null;
1866
- return { producerCompId: producerComp.id, producerSteps: pi ? [pi] : [] };
1922
+ function renderTarget(val, stack) {
1923
+ if (val instanceof DynVal) {
1924
+ const loc = stack.lookupDynamicLocated(val.name);
1925
+ if (loc?.path == null) return [loc?.value ?? null, stack.renderPath, null];
1926
+ return [loc.value, stack.renderPath.pushFrame(loc.path), loc.path];
1927
+ }
1928
+ const step = val.toPathItem?.() ?? null;
1929
+ if (step === null) return [val.eval(stack), stack.renderPath, null];
1930
+ const path2 = stack.renderPath.pushItem(step);
1931
+ return [val.eval(stack), path2, stack.pendingFrame ? path2.toTransactionPath() : null];
1867
1932
  }
1868
1933
  var BaseNode = class {
1869
1934
  render(_stack, _rx) {
@@ -2185,33 +2250,31 @@ var RenderViewId = class extends ANode {
2185
2250
  setDataAttr(_key, _val) {
2186
2251
  }
2187
2252
  };
2188
- function dynRenderStep(comp, name, key) {
2189
- const p = resolveDynProducer(comp, name);
2190
- if (!p) return null;
2191
- return key === void 0 ? new DynStep(p.producerCompId, p.producerSteps) : new DynEachStep(p.producerCompId, p.producerSteps, key);
2192
- }
2193
2253
  var RenderNode = class extends RenderViewId {
2194
2254
  render(stack, rx) {
2195
- const newStack = stack.enter(this.val.eval(stack), {}, true);
2196
- return rx.renderIt(newStack, this, "", this.evalViewName(stack));
2255
+ const [value, renderPath, base] = renderTarget(this.val, stack);
2256
+ const newStack = stack.enter(value, {}, true, renderPath, false);
2257
+ return rx.renderIt(newStack, this, "", this.evalViewName(stack), base);
2197
2258
  }
2259
+ // A `*name` target contributes no step: the site recorded the absolute base it
2260
+ // resumed at, and event reconstruction turns that into a continuation frame.
2198
2261
  toPathStep(ctx) {
2199
- if (this.val instanceof DynVal) return dynRenderStep(ctx.comp, this.val.name);
2262
+ if (this.val instanceof DynVal) return null;
2200
2263
  return super.toPathStep(ctx);
2201
2264
  }
2202
2265
  };
2203
2266
  var RenderItNode = class extends RenderViewId {
2204
2267
  render(stack, rx) {
2205
- const newStack = stack.enter(stack.it, {}, true);
2206
- return rx.renderIt(newStack, this, "", this.evalViewName(stack));
2268
+ const base = stack.pendingFrame ? stack.renderPath.toTransactionPath() : null;
2269
+ const newStack = stack.enter(stack.it, {}, true, stack.renderPath, false);
2270
+ return rx.renderIt(newStack, this, "", this.evalViewName(stack), base);
2207
2271
  }
2208
2272
  toPathStep(ctx) {
2209
2273
  const next = ctx.next();
2210
2274
  if (next === null) return null;
2211
2275
  const nextNode = next.resolveNode();
2212
2276
  if (nextNode instanceof EachNode && next.hasKey) {
2213
- if (nextNode.val instanceof DynVal)
2214
- return dynRenderStep(ctx.comp, nextNode.val.name, next.key);
2277
+ if (nextNode.val instanceof DynVal) return null;
2215
2278
  return new EachRenderItStep(nextNode.val.name, next.key);
2216
2279
  }
2217
2280
  return null;
@@ -2313,11 +2376,19 @@ var EachNode = class extends WrapperNode {
2313
2376
  this.iterInfo = new IterInfo(val, null, null, null);
2314
2377
  }
2315
2378
  render(stack, rx) {
2316
- return rx.renderEachWhen(stack, this.iterInfo, this.node, this.nodeId);
2379
+ return rx.renderEachWhen(stack, this);
2317
2380
  }
2318
2381
  toPathStep(ctx) {
2319
2382
  return ctx.hasKey ? new EachBindStep(this.iterInfo, ctx.key) : null;
2320
2383
  }
2384
+ // Where one item lives, for the render path. `@each` re-binds `it` to the item
2385
+ // whether or not the body is a component, so the position moves either way and
2386
+ // the step has to address it — `.rows` iterated at `key` IS `rows[key]`.
2387
+ // Null for a dynamic sequence: `*rows` carries its own absolute path and enters
2388
+ // a continuation frame instead (see Renderer.renderEachWhen).
2389
+ itemStep(key) {
2390
+ return this.val instanceof FieldVal ? new SeqStep(this.val.name, key) : null;
2391
+ }
2321
2392
  static register = true;
2322
2393
  };
2323
2394
  var IterInfo = class {
@@ -2656,6 +2727,215 @@ function collectAppClassesInSet(app) {
2656
2727
  return classes;
2657
2728
  }
2658
2729
 
2730
+ // src/stack.js
2731
+ var STOP = /* @__PURE__ */ Symbol("STOP");
2732
+ var NEXT = /* @__PURE__ */ Symbol("NEXT");
2733
+ var DEFAULT_ROUTE = ["dyn", "lex"];
2734
+ function routeLookup(route, lex, dyn) {
2735
+ for (let i = 0; i < route.length; i++) {
2736
+ const leg = route[i];
2737
+ if (leg === "dyn") {
2738
+ const v = dyn();
2739
+ if (v != null) return v;
2740
+ } else if (leg === "lex") {
2741
+ const v = lex();
2742
+ if (v != null) return v;
2743
+ } else {
2744
+ console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
2745
+ }
2746
+ }
2747
+ return null;
2748
+ }
2749
+ var isTypeName = (s) => {
2750
+ const c = s.charCodeAt(0);
2751
+ return c >= 65 && c <= 90;
2752
+ };
2753
+ function lookup(chain, name, dv = null) {
2754
+ let n = chain;
2755
+ while (n !== null) {
2756
+ const r = n[0].lookup(name);
2757
+ if (r === STOP) return dv;
2758
+ if (r !== NEXT) return r;
2759
+ n = n[1];
2760
+ }
2761
+ return dv;
2762
+ }
2763
+ var BindFrame = class {
2764
+ constructor(it, binds, isFrame) {
2765
+ this.it = it;
2766
+ this.binds = binds;
2767
+ this.isFrame = isFrame;
2768
+ }
2769
+ lookup(name) {
2770
+ const v = this.binds[name];
2771
+ return v === void 0 ? this.isFrame ? STOP : NEXT : v;
2772
+ }
2773
+ };
2774
+ var DynFrame = class {
2775
+ constructor(binds, types) {
2776
+ this.binds = binds;
2777
+ this.types = types;
2778
+ }
2779
+ lookup(name) {
2780
+ const v = (isTypeName(name) ? this.types : this.binds)[name];
2781
+ return v === void 0 ? NEXT : v;
2782
+ }
2783
+ };
2784
+ function computeViewsId(views) {
2785
+ let s = "";
2786
+ let n = views;
2787
+ while (n !== null) {
2788
+ s += n[0];
2789
+ n = n[1];
2790
+ }
2791
+ return s === "main" ? "" : s;
2792
+ }
2793
+ var Stack = class _Stack {
2794
+ constructor(fields) {
2795
+ Object.assign(this, fields);
2796
+ }
2797
+ _with(patch) {
2798
+ return new _Stack({ ...this, ...patch });
2799
+ }
2800
+ // Evaluate every provide the entered component publishes and push them as one
2801
+ // dynBinds frame, keyed by NAME. A value is published together with the absolute
2802
+ // path it lives at: the same declaration is read as `*name` AND resumed at by
2803
+ // `<x render="*name">`, so a consumer needs both halves. Types go in the same
2804
+ // frame's other map. No-op with no provides.
2805
+ _pushProvides() {
2806
+ const comp = this.comps.getCompFor(this.it);
2807
+ if (comp == null) return this;
2808
+ const { provide, provideType } = comp;
2809
+ const binds = {};
2810
+ const types = {};
2811
+ let has = false;
2812
+ const base = this._publishBase();
2813
+ for (const k in provide) {
2814
+ const step = provide[k].val.toPathItem?.() ?? null;
2815
+ if (step === null) continue;
2816
+ const value = provide[k].val.eval(this);
2817
+ binds[k] = { value, path: base === null ? null : base.concat([step]) };
2818
+ has = true;
2819
+ }
2820
+ for (const k in provideType) {
2821
+ types[k] = provideType[k];
2822
+ has = true;
2823
+ }
2824
+ if (!has) return this;
2825
+ return this._with({ dynBinds: [new DynFrame(binds, types), this.dynBinds] });
2826
+ }
2827
+ // The absolute address of the component being entered, or null when this
2828
+ // position cannot be written down as one. Frame-only steps carry bindings and
2829
+ // address nothing, so they are compacted away first; what is left is checked
2830
+ // against the value actually being rendered, because a scope CAN move the
2831
+ // render position without contributing an addressing step (a plain `@each`
2832
+ // body re-binds `it` to the item while its rebuild step is an identity).
2833
+ //
2834
+ // A null base still publishes the VALUE — `*name` reads it fine — but there is
2835
+ // nowhere to resume, so `<x render="*name">` renders nothing rather than
2836
+ // editing whatever happens to live at the address we guessed.
2837
+ _publishBase() {
2838
+ const base = this.renderPath.toTransactionPath().compact();
2839
+ return base.lookup(this.root, NOT_FOUND) === this.it ? base : null;
2840
+ }
2841
+ static root(comps, it, ctx = null) {
2842
+ return new _Stack({
2843
+ comps,
2844
+ root: it,
2845
+ it,
2846
+ binds: [new BindFrame(it, {}, true), null],
2847
+ dynBinds: [new DynFrame({}, {}), null],
2848
+ views: ["main", null],
2849
+ viewsId: "",
2850
+ renderPath: new DispatchPath(),
2851
+ pendingFrame: false,
2852
+ ctx
2853
+ })._pushProvides();
2854
+ }
2855
+ // `renderPath` defaults to this stack's own: an ordinary scope does not move.
2856
+ // `pendingFrame` clears on a component frame (which emits the base) and is
2857
+ // inherited by transparent scopes, which have to carry it to the next boundary.
2858
+ enter(it, bindings = {}, isFrame = true, renderPath = this.renderPath, pendingFrame = null) {
2859
+ const stack = this._with({
2860
+ it,
2861
+ binds: [new BindFrame(it, bindings, isFrame), this.binds],
2862
+ renderPath,
2863
+ pendingFrame: pendingFrame ?? (isFrame ? false : this.pendingFrame)
2864
+ });
2865
+ return isFrame ? stack._pushProvides() : stack;
2866
+ }
2867
+ pushViewName(name) {
2868
+ const views = [name, this.views];
2869
+ return this._with({ views, viewsId: computeViewsId(views) });
2870
+ }
2871
+ // Published types are stable per scope and would only churn the render cache, so
2872
+ // the cache key covers values alone.
2873
+ _pushDynBindValuesToArray(arr, comp) {
2874
+ for (const k in comp.provide) arr.push(this.lookupDynamic(k));
2875
+ for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
2876
+ }
2877
+ // `*name`: the nearest binding above (including this component's own provides,
2878
+ // pushed on entering it), else a path registered in the component's lexical
2879
+ // scope, else this component's declared default, else null.
2880
+ //
2881
+ // One chain walk and no producer resolution: a lookup names what it WANTS, so the
2882
+ // frame it wants is keyed by that name. The default belongs to the CONSUMER's
2883
+ // declaration and is evaluated against the consumer's stack, which is why it is
2884
+ // consulted only after the whole chain has missed.
2885
+ lookupDynamicLocated(name) {
2886
+ if (isTypeName(name)) return null;
2887
+ const v = lookup(this.dynBinds, name);
2888
+ if (v != null) return v;
2889
+ const comp = this.comps.getCompFor(this.it);
2890
+ if (comp == null) return null;
2891
+ const path2 = comp.scope?.lookupPath?.(name) ?? null;
2892
+ if (path2 !== null) {
2893
+ const value2 = path2.lookup(this.root, NOT_FOUND);
2894
+ if (value2 !== NOT_FOUND) return { value: value2, path: path2 };
2895
+ }
2896
+ const dval = comp.lookup[name]?.val ?? null;
2897
+ if (dval === null) return null;
2898
+ const step = dval.toPathItem?.() ?? null;
2899
+ const value = dval.eval(this);
2900
+ return step === null ? { value, path: null } : { value, path: this.renderPath.toTransactionPath().concat([step]) };
2901
+ }
2902
+ lookupDynamic(name) {
2903
+ if (isTypeName(name)) return lookup(this.dynBinds, name);
2904
+ return this.lookupDynamicLocated(name)?.value ?? null;
2905
+ }
2906
+ lookupBind(name) {
2907
+ return lookup(this.binds, name);
2908
+ }
2909
+ lookupFieldRaw(name) {
2910
+ return this.it[name] ?? null;
2911
+ }
2912
+ lookupMethod(name) {
2913
+ const fn = this.it[name];
2914
+ return fn instanceof Function ? fn.call(this.it) : null;
2915
+ }
2916
+ // The dispatched DOM event / drag info, read only by EventMemberVal's
2917
+ // `e.<member>` handler args. Null outside a live event transaction.
2918
+ lookupEvent() {
2919
+ return this.ctx?.event ?? null;
2920
+ }
2921
+ lookupDragInfo() {
2922
+ return this.ctx?.dragInfo ?? null;
2923
+ }
2924
+ getHandlerFor(name, key) {
2925
+ return this.comps.getHandlerFor(this.it, name, key);
2926
+ }
2927
+ lookupBestView(views, defaultViewName) {
2928
+ let n = this.views;
2929
+ while (n !== null) {
2930
+ const view = views[n[0]];
2931
+ if (view !== void 0) return view;
2932
+ n = n[1];
2933
+ }
2934
+ return views[defaultViewName];
2935
+ }
2936
+ };
2937
+ var NOT_FOUND = /* @__PURE__ */ Symbol("NOT_FOUND");
2938
+
2659
2939
  // src/components.js
2660
2940
  var COMPONENT = /* @__PURE__ */ Symbol.for("tutuca.component");
2661
2941
  var Components = class {
@@ -2694,12 +2974,14 @@ var ComponentStack = class _ComponentStack {
2694
2974
  this.byName = {};
2695
2975
  this.intentsByName = {};
2696
2976
  this.macros = {};
2977
+ this.paths = {};
2697
2978
  }
2698
2979
  enter() {
2699
2980
  return new _ComponentStack(this.comps, this);
2700
2981
  }
2701
2982
  registerComponents(comps, opts) {
2702
- const { aliases = {} } = opts ?? {};
2983
+ const { aliases = {}, paths } = opts ?? {};
2984
+ if (paths) this.registerPaths(paths);
2703
2985
  for (let i = 0; i < comps.length; i++) {
2704
2986
  const Comp = comps[i];
2705
2987
  Comp[COMPONENT].scope = this.enter();
@@ -2713,6 +2995,27 @@ var ComponentStack = class _ComponentStack {
2713
2995
  else console.warn("alias", alias, "to inexistent component", aliases[alias]);
2714
2996
  }
2715
2997
  }
2998
+ // Register lowercase names as absolute paths from the app state root. A
2999
+ // descendant that declares one in its `lookup` reads and renders `*name` without
3000
+ // anything above it publishing one — which is what makes a session, a theme or a
3001
+ // host-owned value available in its natural registration scope, instead of forcing
3002
+ // an application root whose only job is to `provide` it. Register on a nested
3003
+ // scope to narrow a name; nearest registration wins.
3004
+ //
3005
+ // Uppercase names are ignored: a component TYPE is what `lookupComponent` already
3006
+ // answers, and a type has no path.
3007
+ registerPaths(paths) {
3008
+ for (const name in paths) {
3009
+ if (isTypeName(name)) {
3010
+ console.warn("registerPaths: a type name has no path", name);
3011
+ continue;
3012
+ }
3013
+ this.paths[name] = paths[name].toPath?.() ?? paths[name];
3014
+ }
3015
+ }
3016
+ lookupPath(name) {
3017
+ return this.paths[name] ?? this.parent?.lookupPath(name) ?? null;
3018
+ }
2716
3019
  registerMacros(macros) {
2717
3020
  for (const key in macros) {
2718
3021
  const lower = key.toLowerCase();
@@ -2744,17 +3047,16 @@ var ComponentStack = class _ComponentStack {
2744
3047
  lookupComponent(name) {
2745
3048
  return this.byName[name] ?? this.parent?.lookupComponent(name) ?? null;
2746
3049
  }
2747
- // The component in this scope chain that provides `name`, innermost first. A lookup
2748
- // names a value without naming its producer, so the render-target teleport
2749
- // (resolveDynProducer) recovers the producer here instead. Sound because
2750
- // PROVIDE_NAME_COLLISION keeps a provide name to one producer per chain; with two,
2751
- // this returns the innermost and the linter has already reported the ambiguity.
2752
- lookupProvider(name) {
3050
+ // Whether anything in this scope chain provides `name`. Existence only: a lookup
3051
+ // names what it WANTS and takes whoever is nearest above it at render time, so
3052
+ // there is no producer to identify several components may publish one name and
3053
+ // the live render ancestry decides. Used by the linter to tell a lookup that can
3054
+ // be satisfied from one that never will be.
3055
+ hasProvider(name) {
2753
3056
  for (const compName in this.byName) {
2754
- const Comp = this.byName[compName];
2755
- if (Comp.provide?.[name] !== void 0) return Comp;
3057
+ if (this.byName[compName].provide?.[name] !== void 0) return true;
2756
3058
  }
2757
- return this.parent?.lookupProvider(name) ?? null;
3059
+ return this.parent?.hasProvider(name) ?? false;
2758
3060
  }
2759
3061
  lookupMacro(name) {
2760
3062
  return this.macros[name] ?? this.parent?.lookupMacro(name) ?? null;
@@ -2771,10 +3073,6 @@ var LookupInfo = class {
2771
3073
  }
2772
3074
  };
2773
3075
  var isString = (v) => typeof v === "string";
2774
- var isTypeName = (s) => {
2775
- const c = s.charCodeAt(0);
2776
- return c >= 65 && c <= 90;
2777
- };
2778
3076
  var _rawSpecKeys = "name view style commonStyle globalStyle receive intent alter views provide lookup fields methods statics";
2779
3077
  var KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
2780
3078
  var _compId = 0;
@@ -5138,7 +5436,6 @@ var DYN_VAL_NOT_DEFINED = "DYN_VAL_NOT_DEFINED";
5138
5436
  var DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED";
5139
5437
  var PROVIDE_NOT_ADDRESSABLE = "PROVIDE_NOT_ADDRESSABLE";
5140
5438
  var PROVIDE_TYPE_BAD_SHAPE = "PROVIDE_TYPE_BAD_SHAPE";
5141
- var PROVIDE_NAME_COLLISION = "PROVIDE_NAME_COLLISION";
5142
5439
  var LOOKUP_BAD_SHAPE = "LOOKUP_BAD_SHAPE";
5143
5440
  var LOOKUP_NO_PROVIDER = "LOOKUP_NO_PROVIDER";
5144
5441
  var RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP";
@@ -5261,7 +5558,6 @@ function checkComponent(Comp, lx = new LintContext(), { wellKnownExtras = EMPTY_
5261
5558
  checkFieldMethodNameCollisions(lx, Comp);
5262
5559
  checkProvidesAreAddressable(lx, Comp);
5263
5560
  checkProvidedTypes(lx, Comp);
5264
- checkProvideNameCollisions(lx, Comp);
5265
5561
  checkLookupShapes(lx, Comp);
5266
5562
  checkLookupTypesResolve(lx, Comp);
5267
5563
  checkLookupsHaveProviders(lx, Comp);
@@ -5945,19 +6241,6 @@ function checkProvidedTypes(lx, Comp) {
5945
6241
  if (raw !== "self") lx.error(PROVIDE_TYPE_BAD_SHAPE, { name, value: raw });
5946
6242
  }
5947
6243
  }
5948
- function checkProvideNameCollisions(lx, Comp) {
5949
- const scope = Comp.scope;
5950
- if (!scope) return;
5951
- for (const name in Comp.provide) {
5952
- for (let s = scope; s; s = s.parent) {
5953
- for (const otherName in s.byName) {
5954
- const Other = s.byName[otherName];
5955
- if (Other !== Comp && Other.provide?.[name] !== void 0)
5956
- lx.error(PROVIDE_NAME_COLLISION, { name, other: Other.name });
5957
- }
5958
- }
5959
- }
5960
- }
5961
6244
  var KNOWN_LOOKUP_KEYS = /* @__PURE__ */ new Set(["name", "default"]);
5962
6245
  function checkLookupShapes(lx, Comp) {
5963
6246
  const raw = Comp._rawLookup;
@@ -5999,7 +6282,7 @@ function checkLookupsHaveProviders(lx, Comp) {
5999
6282
  if (!scope) return;
6000
6283
  for (const name in Comp.lookup) {
6001
6284
  if (isTypeName2(name)) continue;
6002
- if (scope.lookupProvider?.(name)) continue;
6285
+ if (scope.hasProvider?.(name) || scope.lookupPath?.(name)) continue;
6003
6286
  const info = { name, hasDefault: Comp.lookup[name].val != null };
6004
6287
  if (info.hasDefault) lx.hint(LOOKUP_NO_PROVIDER, info);
6005
6288
  else lx.error(LOOKUP_NO_PROVIDER, info);
@@ -6121,9 +6404,22 @@ var CHECK_TYPE_LIST = Array.isArray;
6121
6404
  var CHECK_TYPE_OBJECT = isPlainObject;
6122
6405
  var CHECK_TYPE_MAP = (v) => v instanceof Map;
6123
6406
  var CHECK_TYPE_SET = (v) => v instanceof Set;
6407
+ var COERCE_NONE = (_v) => null;
6408
+ var COERCE_BOOL = (v) => !!v;
6409
+ var COERCE_STRING = (v) => v?.toString?.() ?? "";
6410
+ var COERCE_INT = (v) => Number.isFinite(v) ? Math.trunc(v) : null;
6411
+ var COERCE_LIST = (v) => Array.isArray(v) ? [...v] : null;
6412
+ var COERCE_OBJECT = (v) => isPlainObject(v) ? { ...v } : null;
6413
+ var COERCE_MAP = (v) => {
6414
+ if (v instanceof Map) return new Map(v);
6415
+ if (Array.isArray(v) || isPlainObject(v))
6416
+ return new Map(Array.isArray(v) ? v : Object.entries(v));
6417
+ return null;
6418
+ };
6419
+ var COERCE_SET = (v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null;
6124
6420
  var FieldBool = class extends Field {
6125
6421
  constructor(name, defaultValue = false) {
6126
- super("bool", name, CHECK_TYPE_BOOL, (v) => !!v, defaultValue);
6422
+ super("bool", name, CHECK_TYPE_BOOL, COERCE_BOOL, defaultValue);
6127
6423
  }
6128
6424
  };
6129
6425
  var FieldAny = class extends Field {
@@ -6133,23 +6429,17 @@ var FieldAny = class extends Field {
6133
6429
  };
6134
6430
  var FieldString = class extends Field {
6135
6431
  constructor(name, defaultValue = "") {
6136
- super("text", name, CHECK_TYPE_STRING, (v) => v?.toString?.() ?? "", defaultValue);
6432
+ super("text", name, CHECK_TYPE_STRING, COERCE_STRING, defaultValue);
6137
6433
  }
6138
6434
  };
6139
6435
  var FieldInt = class extends Field {
6140
6436
  constructor(name, defaultValue = 0) {
6141
- super(
6142
- "int",
6143
- name,
6144
- CHECK_TYPE_INT,
6145
- (v) => Number.isFinite(v) ? Math.trunc(v) : null,
6146
- defaultValue
6147
- );
6437
+ super("int", name, CHECK_TYPE_INT, COERCE_INT, defaultValue);
6148
6438
  }
6149
6439
  };
6150
6440
  var FieldFloat = class extends Field {
6151
6441
  constructor(name, defaultValue = 0) {
6152
- super("float", name, CHECK_TYPE_FLOAT, (_) => null, defaultValue);
6442
+ super("float", name, CHECK_TYPE_FLOAT, COERCE_NONE, defaultValue);
6153
6443
  }
6154
6444
  };
6155
6445
  var metaOf = (v) => v?.constructor?.[COMPONENT] ?? v?.constructor?.getMetaClass?.();
@@ -6162,45 +6452,22 @@ var FieldComp = class extends Field {
6162
6452
  };
6163
6453
  var FieldList = class extends Field {
6164
6454
  constructor(name, defaultValue = []) {
6165
- super("list", name, CHECK_TYPE_LIST, (v) => Array.isArray(v) ? [...v] : null, defaultValue);
6455
+ super("list", name, CHECK_TYPE_LIST, COERCE_LIST, defaultValue);
6166
6456
  }
6167
6457
  };
6168
6458
  var FieldObject = class extends Field {
6169
6459
  constructor(name, defaultValue = {}) {
6170
- super(
6171
- "object",
6172
- name,
6173
- CHECK_TYPE_OBJECT,
6174
- (v) => isPlainObject(v) ? { ...v } : null,
6175
- defaultValue
6176
- );
6460
+ super("object", name, CHECK_TYPE_OBJECT, COERCE_OBJECT, defaultValue);
6177
6461
  }
6178
6462
  };
6179
6463
  var FieldMap = class extends Field {
6180
6464
  constructor(name, defaultValue = /* @__PURE__ */ new Map()) {
6181
- super(
6182
- "map",
6183
- name,
6184
- CHECK_TYPE_MAP,
6185
- (v) => {
6186
- if (v instanceof Map) return new Map(v);
6187
- if (Array.isArray(v) || isPlainObject(v))
6188
- return new Map(Array.isArray(v) ? v : Object.entries(v));
6189
- return null;
6190
- },
6191
- defaultValue
6192
- );
6465
+ super("map", name, CHECK_TYPE_MAP, COERCE_MAP, defaultValue);
6193
6466
  }
6194
6467
  };
6195
6468
  var FieldSet = class extends Field {
6196
6469
  constructor(name, defaultValue = /* @__PURE__ */ new Set()) {
6197
- super(
6198
- "set",
6199
- name,
6200
- CHECK_TYPE_SET,
6201
- (v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null,
6202
- defaultValue
6203
- );
6470
+ super("set", name, CHECK_TYPE_SET, COERCE_SET, defaultValue);
6204
6471
  }
6205
6472
  };
6206
6473
  function mkCompField(field, scope, args) {
@@ -6298,8 +6565,7 @@ function classFromData(name, { fields = {}, methods, statics }) {
6298
6565
  const value = fields[field];
6299
6566
  const type = typeof value;
6300
6567
  if (type === "string") b.addField(field, value, FieldString);
6301
- else if (type === "number")
6302
- b.addField(field, value, Number.isInteger(value) ? FieldInt : FieldFloat);
6568
+ else if (type === "number") b.addField(field, value, FieldFloat);
6303
6569
  else if (type === "boolean") b.addField(field, value, FieldBool);
6304
6570
  else if (Array.isArray(value)) b.addField(field, [...value], FieldList);
6305
6571
  else if (value instanceof Set) b.addField(field, new Set(value), FieldSet);
@@ -6376,160 +6642,6 @@ Component.fromSpec = (opts) => {
6376
6642
  };
6377
6643
  var component = (opts) => Component.fromSpec(opts);
6378
6644
 
6379
- // src/stack.js
6380
- var STOP = /* @__PURE__ */ Symbol("STOP");
6381
- var NEXT = /* @__PURE__ */ Symbol("NEXT");
6382
- var DEFAULT_ROUTE = ["dyn", "lex"];
6383
- function routeLookup(route, lex, dyn) {
6384
- for (let i = 0; i < route.length; i++) {
6385
- const leg = route[i];
6386
- if (leg === "dyn") {
6387
- const v = dyn();
6388
- if (v != null) return v;
6389
- } else if (leg === "lex") {
6390
- const v = lex();
6391
- if (v != null) return v;
6392
- } else {
6393
- console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
6394
- }
6395
- }
6396
- return null;
6397
- }
6398
- function lookup(chain, name, dv = null) {
6399
- let n = chain;
6400
- while (n !== null) {
6401
- const r = n[0].lookup(name);
6402
- if (r === STOP) return dv;
6403
- if (r !== NEXT) return r;
6404
- n = n[1];
6405
- }
6406
- return dv;
6407
- }
6408
- var BindFrame = class {
6409
- constructor(it, binds, isFrame) {
6410
- this.it = it;
6411
- this.binds = binds;
6412
- this.isFrame = isFrame;
6413
- }
6414
- lookup(name) {
6415
- const v = this.binds[name];
6416
- return v === void 0 ? this.isFrame ? STOP : NEXT : v;
6417
- }
6418
- };
6419
- var ObjectFrame = class {
6420
- constructor(binds) {
6421
- this.binds = binds;
6422
- }
6423
- lookup(key) {
6424
- const v = this.binds[key];
6425
- return v === void 0 ? NEXT : v;
6426
- }
6427
- };
6428
- function computeViewsId(views) {
6429
- let s = "";
6430
- let n = views;
6431
- while (n !== null) {
6432
- s += n[0];
6433
- n = n[1];
6434
- }
6435
- return s === "main" ? "" : s;
6436
- }
6437
- var Stack = class _Stack {
6438
- constructor(comps, it, binds, dynBinds, views, viewsId, ctx = null) {
6439
- this.comps = comps;
6440
- this.it = it;
6441
- this.binds = binds;
6442
- this.dynBinds = dynBinds;
6443
- this.views = views;
6444
- this.viewsId = viewsId;
6445
- this.ctx = ctx;
6446
- }
6447
- // Evaluate every provide the entered component publishes and push them as one
6448
- // dynBinds frame, keyed by NAME. Published types go in the same frame: a type name
6449
- // starts A-Z and a value name does not, so the two namespaces cannot collide and
6450
- // nearest-ancestor-wins falls out of frame order for both. No-op with no provides.
6451
- _pushProvides() {
6452
- const comp = this.comps.getCompFor(this.it);
6453
- if (comp == null) return this;
6454
- const { provide, provideType } = comp;
6455
- const dynObj = {};
6456
- let has = false;
6457
- for (const k in provide) {
6458
- dynObj[k] = provide[k].val.eval(this);
6459
- has = true;
6460
- }
6461
- for (const k in provideType) {
6462
- dynObj[k] = provideType[k];
6463
- has = true;
6464
- }
6465
- if (!has) return this;
6466
- const newDynBinds = [new ObjectFrame(dynObj), this.dynBinds];
6467
- const { comps, it, binds, views, viewsId, ctx } = this;
6468
- return new _Stack(comps, it, binds, newDynBinds, views, viewsId, ctx);
6469
- }
6470
- static root(comps, it, ctx) {
6471
- const binds = [new BindFrame(it, {}, true), null];
6472
- const dynBinds = [new ObjectFrame({}), null];
6473
- const views = ["main", null];
6474
- return new _Stack(comps, it, binds, dynBinds, views, "", ctx)._pushProvides();
6475
- }
6476
- enter(it, bindings = {}, isFrame = true) {
6477
- const { comps, binds, dynBinds, views, viewsId, ctx } = this;
6478
- const newBinds = [new BindFrame(it, bindings, isFrame), binds];
6479
- const stack = new _Stack(comps, it, newBinds, dynBinds, views, viewsId, ctx);
6480
- return isFrame ? stack._pushProvides() : stack;
6481
- }
6482
- pushViewName(name) {
6483
- const { comps, it, binds, dynBinds, views, ctx } = this;
6484
- const newViews = [name, views];
6485
- return new _Stack(comps, it, binds, dynBinds, newViews, computeViewsId(newViews), ctx);
6486
- }
6487
- // Published types are stable per scope and would only churn the render cache, so
6488
- // the cache key covers values alone.
6489
- _pushDynBindValuesToArray(arr, comp) {
6490
- for (const k in comp.provide) arr.push(this.lookupDynamic(k));
6491
- for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
6492
- }
6493
- // `*name`: the nearest binding above (including this component's own provides,
6494
- // pushed on entering it), else this component's declared default, else null.
6495
- lookupDynamic(name) {
6496
- const v = lookup(this.dynBinds, name);
6497
- if (v != null) return v;
6498
- const comp = this.comps.getCompFor(this.it);
6499
- return comp?.lookup[name]?.val?.eval(this) ?? null;
6500
- }
6501
- lookupBind(name) {
6502
- return lookup(this.binds, name);
6503
- }
6504
- lookupFieldRaw(name) {
6505
- return this.it[name] ?? null;
6506
- }
6507
- lookupMethod(name) {
6508
- const fn = this.it[name];
6509
- return fn instanceof Function ? fn.call(this.it) : null;
6510
- }
6511
- // The dispatched DOM event / drag info, read only by EventMemberVal's
6512
- // `e.<member>` handler args. Null outside a live event transaction.
6513
- lookupEvent() {
6514
- return this.ctx?.event ?? null;
6515
- }
6516
- lookupDragInfo() {
6517
- return this.ctx?.dragInfo ?? null;
6518
- }
6519
- getHandlerFor(name, key) {
6520
- return this.comps.getHandlerFor(this.it, name, key);
6521
- }
6522
- lookupBestView(views, defaultViewName) {
6523
- let n = this.views;
6524
- while (n !== null) {
6525
- const view = views[n[0]];
6526
- if (view !== void 0) return view;
6527
- n = n[1];
6528
- }
6529
- return views[defaultViewName];
6530
- }
6531
- };
6532
-
6533
6645
  // src/transactor.js
6534
6646
  var State2 = class {
6535
6647
  constructor(val) {
@@ -6605,9 +6717,9 @@ var Transactor = class {
6605
6717
  // `.a[.selId]` render target reconstructed from a DOM event) against the root the
6606
6718
  // handler read, so pathKeys carries concrete keys instead of dropping the dynamic
6607
6719
  // step. No-op when nobody is observing.
6608
- _emitRecord(root, { kind, name, args, path, targetPath, handler, handlerName, matched, before, after, parent }) {
6720
+ _emitRecord(root, { kind, name, args, path: path2, targetPath, handler, handlerName, matched, before, after, parent }) {
6609
6721
  if (this._observers.length === 0) return;
6610
- const pinned = path.pinKeys(root);
6722
+ const pinned = path2.pinKeys(root);
6611
6723
  this._emit({
6612
6724
  kind,
6613
6725
  name,
@@ -6658,18 +6770,20 @@ var Transactor = class {
6658
6770
  // intent's answerPath is: a reply must reach the sender that asked even if a key
6659
6771
  // moved while the message was in flight. Null when nobody is waiting — a host
6660
6772
  // sendAtRoot or a view's own `@on.*` — and ctx.sendReply refuses on that.
6661
- pushSend(path, name, args = [], parent = null, origin = null) {
6662
- const t = new SendEvent(path, this, name, args, parent);
6663
- t.origin = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
6773
+ pushSend(path2, name, args = [], parent = null, origin = null, txnPath = null) {
6774
+ const t = new SendEvent(path2, this, name, args, parent);
6775
+ t.txnPath = txnPath;
6776
+ t.origin = origin;
6777
+ t.originPinned = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
6664
6778
  this.pushTransaction(t);
6665
6779
  return this._link(t, parent);
6666
6780
  }
6667
6781
  // Raise an intent: a job the sender did not address, walked along a route until
6668
6782
  // something answers. `opts.route` is a list of legs (see DEFAULT_ROUTE) and
6669
6783
  // `opts.livePath` opts out of pinning the answer's destination keys.
6670
- pushIntent(path, name, args = [], opts = {}, parent = null) {
6784
+ pushIntent(path2, name, args = [], opts = {}, parent = null) {
6671
6785
  const release = parent ? parent.completion.track() : null;
6672
- const walk = new IntentWalk(this, path, name, args, opts, parent, release);
6786
+ const walk = new IntentWalk(this, path2, name, args, opts, parent, release);
6673
6787
  walk.advance();
6674
6788
  return walk;
6675
6789
  }
@@ -6703,16 +6817,17 @@ var Transactor = class {
6703
6817
  transaction._completion?.releaseSelf();
6704
6818
  }
6705
6819
  }
6706
- transactInputNow(path, event, eventHandler, dragInfo) {
6707
- this.transact(new InputEvent(path, event, eventHandler, this, dragInfo));
6820
+ transactInputNow(path2, event, eventHandler, dragInfo) {
6821
+ this.transact(new InputEvent(path2, event, eventHandler, this, dragInfo));
6708
6822
  }
6709
6823
  };
6710
6824
  function nullHandler() {
6711
6825
  return this;
6712
6826
  }
6713
6827
  var Transaction = class {
6714
- constructor(path, transactor, parentTransaction = null) {
6715
- this.path = path;
6828
+ constructor(path2, transactor, parentTransaction = null) {
6829
+ this.path = path2;
6830
+ this.txnPath = null;
6716
6831
  this.transactor = transactor;
6717
6832
  this.parentTransaction = parentTransaction;
6718
6833
  this._completion = null;
@@ -6761,9 +6876,11 @@ var Transaction = class {
6761
6876
  getHandlerAndArgs(_root, _instance, _comps) {
6762
6877
  return null;
6763
6878
  }
6764
- // The path used to apply the mutation. Teleports dynamic-var renders so it lands on
6765
- // the data's real location (the dispatch `this.path` keeps intermediates).
6879
+ // The path used to apply the mutation: the ACTIVE frame of the dispatch path, so
6880
+ // an event inside a `<x render="*name">` subtree updates the value where it
6881
+ // really lives (the dispatch `this.path` keeps the visual callers, for bubbling).
6766
6882
  getTransactionPath() {
6883
+ if (this.txnPath !== null) return this.txnPath;
6767
6884
  return this.path.toTransactionPath().compact();
6768
6885
  }
6769
6886
  run(curRoot, comps) {
@@ -6781,15 +6898,16 @@ var Transaction = class {
6781
6898
  }
6782
6899
  };
6783
6900
  var InputEvent = class extends Transaction {
6784
- constructor(path, e, handler, transactor, dragInfo) {
6785
- super(path, transactor);
6901
+ constructor(path2, e, handler, transactor, dragInfo) {
6902
+ super(path2, transactor);
6786
6903
  this.e = e;
6787
6904
  this.handler = handler;
6788
6905
  this.dragInfo = dragInfo;
6789
6906
  this._dispatchPath = null;
6790
6907
  }
6791
- // Frame steps removed, DynStep + one step per crossed component kept: bubbling
6792
- // it visits every component (including intermediates of a dynamic-var render).
6908
+ // Frame-only steps removed, one step per crossed component kept — inside every
6909
+ // continuation frame independently, so bubbling it visits every component and
6910
+ // then returns to the caller that wrote the `*name`.
6793
6911
  get dispatchPath() {
6794
6912
  this._dispatchPath ??= this.path.compact();
6795
6913
  return this._dispatchPath;
@@ -6824,11 +6942,11 @@ var InputEvent = class extends Transaction {
6824
6942
  this.transactor.pushIntent(this.dispatchPath, name, args, rest, this);
6825
6943
  }
6826
6944
  getHandlerAndArgs(root, _instance, comps) {
6827
- const stack = this.path.toTransactionPath().buildStack(Stack.root(comps, root, this));
6945
+ const stack = this.path.buildStack(Stack.root(comps, root, this));
6828
6946
  const [handler, args] = this.handler.getHandlerAndArgs(stack, this);
6829
6947
  this._handlerArgs = [...args];
6830
- const path = this.dispatchPath;
6831
- args.push(new EventContext(path, this.transactor, this));
6948
+ const path2 = this.dispatchPath;
6949
+ args.push(new EventContext(path2, this.transactor, this));
6832
6950
  return [handler, args];
6833
6951
  }
6834
6952
  // The dispatched DOM event, read only through `e.<member>` handler args
@@ -6839,11 +6957,11 @@ var InputEvent = class extends Transaction {
6839
6957
  }
6840
6958
  };
6841
6959
  var NameArgsTransaction = class extends Transaction {
6842
- constructor(path, transactor, name, args, parentTransaction) {
6843
- super(path, transactor, parentTransaction);
6960
+ constructor(path2, transactor, name, args, parentTransaction) {
6961
+ super(path2, transactor, parentTransaction);
6844
6962
  this.name = name;
6845
6963
  this.args = args;
6846
- this.targetPath = path;
6964
+ this.targetPath = path2;
6847
6965
  }
6848
6966
  handlerProp = null;
6849
6967
  // NameArgs verbs map their handler bucket straight to the observed kind:
@@ -6900,8 +7018,8 @@ var SendEvent = class extends NameArgsTransaction {
6900
7018
  };
6901
7019
  var IntentEvent = class extends NameArgsTransaction {
6902
7020
  handlerProp = "intent";
6903
- constructor(path, transactor, walk) {
6904
- super(path, transactor, walk.name, walk.args, walk.parent);
7021
+ constructor(path2, transactor, walk) {
7022
+ super(path2, transactor, walk.name, walk.args, walk.parent);
6905
7023
  this.walk = walk;
6906
7024
  this.targetPath = walk.origin;
6907
7025
  }
@@ -6928,17 +7046,17 @@ var PASS = /* @__PURE__ */ Symbol.for("tutuca.intent.pass");
6928
7046
  var REFUSAL_RING_CAP = 200;
6929
7047
  var INTENT_DEPTH = 64;
6930
7048
  var IntentWalk = class {
6931
- constructor(transactor, path, name, args, opts, parent, release) {
7049
+ constructor(transactor, path2, name, args, opts, parent, release) {
6932
7050
  this.transactor = transactor;
6933
7051
  this.name = name;
6934
7052
  this.args = args;
6935
7053
  this.route = opts?.route ?? DEFAULT_ROUTE;
6936
7054
  this.parent = parent;
6937
7055
  this.release = release;
6938
- this.origin = path;
6939
- this.answerPath = opts?.livePath ? null : path.toTransactionPath().pinKeys(transactor.state.val);
7056
+ this.origin = path2;
7057
+ this.answerPath = opts?.livePath ? null : path2.toTransactionPath().pinKeys(transactor.state.val);
6940
7058
  this.legIndex = 0;
6941
- this.dynAt = path;
7059
+ this.dynAt = path2;
6942
7060
  this.hops = 0;
6943
7061
  this.ended = false;
6944
7062
  }
@@ -6950,7 +7068,7 @@ var IntentWalk = class {
6950
7068
  while (this.legIndex < this.route.length) {
6951
7069
  const leg = this.route[this.legIndex];
6952
7070
  if (leg === "dyn") {
6953
- if (this.dynAt.steps.length === 0) {
7071
+ if (!this.dynAt.canPop()) {
6954
7072
  this.legIndex++;
6955
7073
  continue;
6956
7074
  }
@@ -7047,8 +7165,8 @@ var IntentWalk = class {
7047
7165
  if (this.ended) return;
7048
7166
  this.ended = true;
7049
7167
  if (name === null) return this.release?.();
7050
- const path = this.answerPath ?? this.origin;
7051
- const t = new SendEvent(path, this.transactor, name, args, this.parent);
7168
+ const t = new SendEvent(this.origin, this.transactor, name, args, this.parent);
7169
+ t.txnPath = this.answerPath;
7052
7170
  t._isAnswer = true;
7053
7171
  this.transactor.pushTransaction(t);
7054
7172
  if (this.release) t.completion.whenSubtreeSettled().then(this.release);
@@ -7114,8 +7232,8 @@ var Completion = class {
7114
7232
  }
7115
7233
  };
7116
7234
  var Dispatcher = class {
7117
- constructor(path, transactor, parentTransaction, root = transactor.state.val) {
7118
- this.path = path;
7235
+ constructor(path2, transactor, parentTransaction, root = transactor.state.val) {
7236
+ this.path = path2;
7119
7237
  this.transactor = transactor;
7120
7238
  this.parent = parentTransaction;
7121
7239
  this.root = root;
@@ -7143,7 +7261,9 @@ var Dispatcher = class {
7143
7261
  // replayed; provides are pushed on every component frame either way, so a provide
7144
7262
  // that reads a loop binding is the one case this cannot reproduce faithfully.
7145
7263
  _stack() {
7146
- this._stackMemo ??= this.path.toTransactionPath().buildStack(Stack.root(this.transactor.comps, this.root, this.parent));
7264
+ this._stackMemo ??= this.path.buildStack(
7265
+ Stack.root(this.transactor.comps, this.root, this.parent)
7266
+ );
7147
7267
  return this._stackMemo;
7148
7268
  }
7149
7269
  // Resolve a name the way the renderer would. `opts.route` takes the same legs in
@@ -7157,14 +7277,19 @@ var Dispatcher = class {
7157
7277
  );
7158
7278
  }
7159
7279
  // The `lex` leg without a Stack: the scope of the component whose handler is
7160
- // running, which is the leaf of this ctx's path.
7280
+ // running, which is the leaf of this ctx's path. A type name resolves to the
7281
+ // component registered under it; a value name to a path registered under it
7282
+ // (see ComponentStack.registerPaths), read against the current root.
7161
7283
  _lookupLex(name) {
7162
7284
  let Comp = null;
7163
7285
  this.walkPath((c) => {
7164
7286
  Comp = c;
7165
7287
  return false;
7166
7288
  });
7167
- return Comp?.scope?.lookupComponent(name) ?? null;
7289
+ const scope = Comp?.scope;
7290
+ if (scope == null) return null;
7291
+ if (isTypeName(name)) return scope.lookupComponent(name) ?? null;
7292
+ return scope.lookupPath(name)?.lookup(this.root) ?? null;
7168
7293
  }
7169
7294
  // A component lookup is a value lookup constrained to a component. The `lex` leg
7170
7295
  // can only ever answer with one; the `dyn` leg reads a binding an ancestor
@@ -7186,8 +7311,8 @@ var Dispatcher = class {
7186
7311
  send(name, args) {
7187
7312
  return this.sendAtPath(this.path, name, args);
7188
7313
  }
7189
- sendAtPath(path, name, args) {
7190
- return this.transactor.pushSend(path, name, args, this.parent, this.path);
7314
+ sendAtPath(path2, name, args) {
7315
+ return this.transactor.pushSend(path2, name, args, this.parent, this.path);
7191
7316
  }
7192
7317
  // An intent is ROUTED: it names a job and walks until something answers. The verb
7193
7318
  // does not decide which scope answers — `opts.route` does, written here at the call
@@ -7195,8 +7320,8 @@ var Dispatcher = class {
7195
7320
  intent(name, args, opts) {
7196
7321
  return this.intentAtPath(this.path, name, args, opts);
7197
7322
  }
7198
- intentAtPath(path, name, args, opts) {
7199
- return this.transactor.pushIntent(path, name, args, opts, this.parent);
7323
+ intentAtPath(path2, name, args, opts) {
7324
+ return this.transactor.pushIntent(path2, name, args, opts, this.parent);
7200
7325
  }
7201
7326
  };
7202
7327
  var EventContext = class extends Dispatcher {
@@ -7225,7 +7350,14 @@ var EventContext = class extends Dispatcher {
7225
7350
  this.transactor.refuse("NO_SENDER", { name });
7226
7351
  return null;
7227
7352
  }
7228
- return this.sendAtPath(origin, name, args);
7353
+ return this.transactor.pushSend(
7354
+ origin,
7355
+ name,
7356
+ args,
7357
+ this.parent,
7358
+ this.path,
7359
+ this.parent.originPinned
7360
+ );
7229
7361
  }
7230
7362
  // End the walk answering nothing — "served, and no answer".
7231
7363
  stop() {
@@ -7257,13 +7389,13 @@ var PathChanges = class extends PathBuilder {
7257
7389
  }
7258
7390
  };
7259
7391
  function rootDispatcher(transactor) {
7260
- return new Dispatcher(new Path([]), transactor, null);
7392
+ return new Dispatcher(new DispatchPath(), transactor, null);
7261
7393
  }
7262
7394
 
7263
7395
  // tools/core/results.js
7264
7396
  var ModuleInfo = class {
7265
- constructor({ path = null, present = /* @__PURE__ */ new Set(), counts = {}, warnings = [] }) {
7266
- this.path = path;
7397
+ constructor({ path: path2 = null, present = /* @__PURE__ */ new Set(), counts = {}, warnings = [] }) {
7398
+ this.path = path2;
7267
7399
  this.present = present;
7268
7400
  this.counts = counts;
7269
7401
  this.warnings = warnings;
@@ -7373,8 +7505,8 @@ var DescribeResult = class {
7373
7505
  }
7374
7506
  };
7375
7507
  var ModuleTestReport = class {
7376
- constructor({ path = null, suites = [], counts = { pass: 0, fail: 0, skip: 0, total: 0 } }) {
7377
- this.path = path;
7508
+ constructor({ path: path2 = null, suites = [], counts = { pass: 0, fail: 0, skip: 0, total: 0 } }) {
7509
+ this.path = path2;
7378
7510
  this.suites = suites;
7379
7511
  this.counts = counts;
7380
7512
  }
@@ -7417,8 +7549,8 @@ var Test = class {
7417
7549
  }
7418
7550
  };
7419
7551
  var ModuleTests = class {
7420
- constructor({ path = null, suites = [] } = {}) {
7421
- this.path = path;
7552
+ constructor({ path: path2 = null, suites = [] } = {}) {
7553
+ this.path = path2;
7422
7554
  this.suites = suites;
7423
7555
  }
7424
7556
  };
@@ -7440,8 +7572,8 @@ function titleFromArg(arg) {
7440
7572
  if (typeof arg === "function") return arg.name || "(anonymous)";
7441
7573
  return String(arg);
7442
7574
  }
7443
- function makeCollector({ path = null, components = [] } = {}) {
7444
- const moduleTests = new ModuleTests({ path, suites: [] });
7575
+ function makeCollector({ path: path2 = null, components = [] } = {}) {
7576
+ const moduleTests = new ModuleTests({ path: path2, suites: [] });
7445
7577
  const stack = [];
7446
7578
  function describe(...args) {
7447
7579
  let head;
@@ -7542,7 +7674,7 @@ async function driveStack(stack, value, phase, opts = {}) {
7542
7674
  val
7543
7675
  );
7544
7676
  });
7545
- dispatchPhase(rootDispatcher(transactor), new Path([]), phase, value);
7677
+ dispatchPhase(rootDispatcher(transactor), new DispatchPath(), phase, value);
7546
7678
  await transactor.settle();
7547
7679
  return transactor.state.val;
7548
7680
  }
@@ -7552,7 +7684,7 @@ async function drive(cfg, value, phase, opts = {}) {
7552
7684
  async function runTests({
7553
7685
  getTests,
7554
7686
  components = [],
7555
- path = null,
7687
+ path: path2 = null,
7556
7688
  expect: expect3,
7557
7689
  name = null,
7558
7690
  grep = null,
@@ -7563,13 +7695,13 @@ async function runTests({
7563
7695
  const counts = { pass: 0, fail: 0, skip: 0, total: 0 };
7564
7696
  if (typeof getTests !== "function") {
7565
7697
  return new TestReport({
7566
- modules: [new ModuleTestReport({ path, suites: [], counts })]
7698
+ modules: [new ModuleTestReport({ path: path2, suites: [], counts })]
7567
7699
  });
7568
7700
  }
7569
7701
  if (typeof expect3 !== "function") {
7570
7702
  throw new Error("runTests: expect must be provided (e.g. chai's expect)");
7571
7703
  }
7572
- const { describe, test: test2, moduleTests } = makeCollector({ path, components });
7704
+ const { describe, test: test2, moduleTests } = makeCollector({ path: path2, components });
7573
7705
  let _stack = null;
7574
7706
  const getStack = () => _stack ??= buildStack({ components, macros, intentHandlers });
7575
7707
  const driveLocal = (value, phase, opts = {}) => driveStack(getStack(), value, phase, opts);
@@ -7632,7 +7764,7 @@ async function runTests({
7632
7764
  if (r !== null) suiteResults.push(r);
7633
7765
  }
7634
7766
  return new TestReport({
7635
- modules: [new ModuleTestReport({ path, suites: suiteResults, counts })]
7767
+ modules: [new ModuleTestReport({ path: path2, suites: suiteResults, counts })]
7636
7768
  });
7637
7769
  }
7638
7770
 
@@ -7801,15 +7933,13 @@ function lintIdToMessage(id, info) {
7801
7933
  case "DYN_ALIAS_NOT_REFERENCED":
7802
7934
  return `Lookup '${info.name}' is defined but never used — remove it or reference it as '*${info.name}' in a view`;
7803
7935
  case "PROVIDE_NOT_ADDRESSABLE":
7804
- return `Provide '${info.name}' value '${info.value}' must be a field ('.f') or seq-access ('.s[.k]') — a method/constant can't be a render target`;
7936
+ return `Provide '${info.name}' value '${info.value}' must be a field ('.f') or seq-access ('.s[.k]') — a provide doubles as the path '<x render="*${info.name}">' resumes at, so this one is dropped and '*${info.name}' resolves to nothing`;
7805
7937
  case "LOOKUP_BAD_SHAPE":
7806
7938
  return `Lookup '${info.name}' has an invalid shape: ${info.problem}`;
7807
7939
  case "LOOKUP_NO_PROVIDER":
7808
- return info.hasDefault ? `Lookup '${info.name}' is provided by no component in scope, so it always resolves to its default` : `Lookup '${info.name}' is provided by no component in scope — add a 'provide' for it, or give this lookup a default`;
7940
+ return info.hasDefault ? `Lookup '${info.name}' is provided by no component in scope and matches no registered path, so it always resolves to its default` : `Lookup '${info.name}' is provided by no component in scope and matches no registered path — add a 'provide' for it, register a path under that name, or give this lookup a default`;
7809
7941
  case "PROVIDE_TYPE_BAD_SHAPE":
7810
7942
  return `Provide '${info.name}' starts uppercase, so it publishes a component type — its value must be 'self', not '${info.value}'`;
7811
- case "PROVIDE_NAME_COLLISION":
7812
- return `Provide '${info.name}' is also provided by '${info.other}' in the same scope — one name, one provider, so a lookup can find it`;
7813
7943
  case "UNKNOWN_MACRO_ARG":
7814
7944
  return `Argument '${info.name}' is not declared in macro '${info.macroName}'`;
7815
7945
  case "UNKNOWN_DIRECTIVE":
@@ -7975,7 +8105,7 @@ var App = class {
7975
8105
  const { type } = e;
7976
8106
  const isDrag = type === "dragover" || type === "dragstart" || type === "dragend" || type === "drop";
7977
8107
  const { rootNode: root, maxEventNodeDepth: maxDepth, comps, transactor } = this;
7978
- const [path, handlers] = Path.fromNodeAndEventName(
8108
+ const [path2, handlers] = DispatchPath.fromNodeAndEventName(
7979
8109
  e.target,
7980
8110
  type,
7981
8111
  root,
@@ -7983,9 +8113,9 @@ var App = class {
7983
8113
  comps,
7984
8114
  !isDrag
7985
8115
  );
7986
- if (isDrag) this._handleDragEvent(e, type, path);
7987
- if (path !== null && handlers !== null)
7988
- for (const handler of handlers) transactor.transactInputNow(path, e, handler, this.dragInfo);
8116
+ if (isDrag) this._handleDragEvent(e, type, path2);
8117
+ if (path2 !== null && handlers !== null)
8118
+ for (const handler of handlers) transactor.transactInputNow(path2, e, handler, this.dragInfo);
7989
8119
  }
7990
8120
  _handleTouchEvent(e) {
7991
8121
  const { type } = e;
@@ -8028,7 +8158,7 @@ var App = class {
8028
8158
  this._touch = null;
8029
8159
  }
8030
8160
  }
8031
- _handleDragEvent(e, type, path) {
8161
+ _handleDragEvent(e, type, path2) {
8032
8162
  if (type === "dragover") {
8033
8163
  const dropTarget = getClosestDropTarget(e.target, this.rootNode, Infinity);
8034
8164
  if (dropTarget !== null) {
@@ -8040,10 +8170,10 @@ var App = class {
8040
8170
  } else if (type === "dragstart") {
8041
8171
  e.target.dataset.dragging = 1;
8042
8172
  const rootValue = this.state.val;
8043
- const txnPath = path.compact().toTransactionPath();
8173
+ const txnPath = path2.compact().toTransactionPath();
8044
8174
  const value = txnPath.lookup(rootValue);
8045
8175
  const dragType = e.target.dataset.dragtype ?? "?";
8046
- const stack = path.toTransactionPath().buildStack(this.makeStack(rootValue));
8176
+ const stack = path2.buildStack(this.makeStack(rootValue));
8047
8177
  this.dragInfo = new DragInfo(stack, value, dragType, e.target);
8048
8178
  } else if (type === "drop") {
8049
8179
  e.preventDefault();
@@ -8118,7 +8248,7 @@ var App = class {
8118
8248
  this.rootNode.removeEventListener(name, this, listenerOpts(name));
8119
8249
  }
8120
8250
  sendAtRoot(name, args) {
8121
- this.transactor.pushSend(new Path([]), name, args);
8251
+ this.transactor.pushSend(new DispatchPath(), name, args);
8122
8252
  }
8123
8253
  registerComponents(comps, opts) {
8124
8254
  const scope = this.compStack.enter();
@@ -8258,7 +8388,20 @@ var WeakMapDomCache = class {
8258
8388
  };
8259
8389
 
8260
8390
  // src/renderer.js
8261
- var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk"];
8391
+ var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk", "rp"];
8392
+ function stampRenderBase(vdom, text) {
8393
+ if (vdom instanceof VNode)
8394
+ return new VNode(
8395
+ vdom.tag,
8396
+ { ...vdom.attrs, "data-rp": text },
8397
+ vdom.childs,
8398
+ vdom.key,
8399
+ vdom.namespace
8400
+ );
8401
+ if (vdom instanceof VFragment)
8402
+ return new VFragment(vdom.childs.map((c) => stampRenderBase(c, text)));
8403
+ return vdom;
8404
+ }
8262
8405
  var Renderer = class {
8263
8406
  constructor(comps) {
8264
8407
  this.comps = comps;
@@ -8295,17 +8438,17 @@ var Renderer = class {
8295
8438
  if (comp === null) return null;
8296
8439
  return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
8297
8440
  }
8298
- renderIt(stack, node, key, viewName) {
8441
+ renderIt(stack, node, key, viewName, base = null) {
8299
8442
  const comp = this.comps.getCompFor(stack.it);
8300
- return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
8443
+ return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName, base) : null;
8301
8444
  }
8302
8445
  // `node` is the parse node of the render site (`<x render>` / `render-it` /
8303
8446
  // `render-each`, or the view's root anode for the app root). It keys the
8304
8447
  // cache as a globally-unique object: node ids alone are unique only within a
8305
8448
  // single view, so the same value rendered by two components (e.g. through a
8306
8449
  // shared dynamic-var sequence) would otherwise collide in the cache.
8307
- _rValComp(stack, val, comp, node, key, viewName) {
8308
- const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}`;
8450
+ _rValComp(stack, val, comp, node, key, viewName, base = null) {
8451
+ const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}${stack.renderPath.addressKey}`;
8309
8452
  const cachePath = [node, val];
8310
8453
  stack._pushDynBindValuesToArray(cachePath, comp);
8311
8454
  const cachedNode = this.cache.get(cachePath, cacheKey);
@@ -8313,30 +8456,43 @@ var Renderer = class {
8313
8456
  const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
8314
8457
  const body = this.renderView(view, stack);
8315
8458
  if (body == null) return null;
8459
+ const baseJson = base === null ? null : pathToJson(base);
8316
8460
  const meta = this._renderMetadata({
8317
8461
  $: "Comp",
8318
8462
  nid: node?.nodeId ?? null,
8319
8463
  cid: comp.id,
8320
- vid: view.name
8464
+ vid: view.name,
8465
+ ...baseJson === null ? null : { base: baseJson }
8321
8466
  });
8322
- const dom = new VFragment([meta, body]);
8467
+ const dom = new VFragment([
8468
+ meta,
8469
+ baseJson === null ? body : stampRenderBase(body, JSON.stringify(baseJson))
8470
+ ]);
8323
8471
  this.cache.set(cachePath, cacheKey, dom);
8324
8472
  return dom;
8325
8473
  }
8326
8474
  pushEachEntry(r, nid, attrName, key, dom) {
8327
8475
  r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
8328
8476
  }
8329
- renderEachWhen(stack, iterInfo, view, nid) {
8477
+ renderEachWhen(stack, each) {
8478
+ const { iterInfo, node: view, nodeId: nid } = each;
8330
8479
  const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
8480
+ const seqPath = iterInfo.val instanceof DynVal ? stack.lookupDynamicLocated(iterInfo.val.name)?.path ?? null : null;
8331
8481
  const r = [];
8332
8482
  const it = stack.it;
8333
8483
  const renderOne = (key, value, attrName, binds) => {
8484
+ const itemBase = seqPath === null ? null : keyedPath(seqPath, key);
8485
+ const itemStep = itemBase === null ? each.itemStep(key) : null;
8486
+ const itemPath = itemBase !== null ? stack.renderPath.pushFrame(itemBase) : itemStep !== null ? stack.renderPath.pushItem(itemStep) : stack.renderPath;
8334
8487
  const cachePath = enricher ? [view, it, value] : [view, value];
8335
- const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
8488
+ const cacheKey = `${stack.viewsId ?? ""}${nid}${key}${itemPath.addressKey}`;
8336
8489
  const cachedNode = this.cache.get(cachePath, cacheKey);
8337
8490
  if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
8338
8491
  else {
8339
- const dom = this.renderView(view, stack.enter(value, binds, false));
8492
+ const dom = this.renderView(
8493
+ view,
8494
+ stack.enter(value, binds, false, itemPath, itemBase !== null)
8495
+ );
8340
8496
  if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
8341
8497
  this.cache.set(cachePath, cacheKey, dom);
8342
8498
  }
@@ -8593,7 +8749,6 @@ export {
8593
8749
  ModuleTests,
8594
8750
  PASS,
8595
8751
  PLACEHOLDERLESS_TEMPLATE_STRING,
8596
- PROVIDE_NAME_COLLISION,
8597
8752
  PROVIDE_NOT_ADDRESSABLE,
8598
8753
  PROVIDE_TYPE_BAD_SHAPE,
8599
8754
  ParseContext,
@@ -8634,6 +8789,7 @@ export {
8634
8789
  lintIdToMessage,
8635
8790
  macro,
8636
8791
  makeCollector,
8792
+ path,
8637
8793
  phaseOps,
8638
8794
  reportTestReportToConsole,
8639
8795
  resolveArgs,