tutuca 0.13.3 → 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;
@@ -1834,13 +1919,16 @@ function h(tagName, properties, children, namespace) {
1834
1919
  }
1835
1920
 
1836
1921
  // src/anode.js
1837
- function resolveDynProducer(comp, name) {
1838
- const own = comp?.provide?.[name];
1839
- const producerComp = own != null ? comp : comp?.scope?.lookupProvider(name) ?? null;
1840
- const producerProvide = own ?? producerComp?.provide?.[name];
1841
- if (producerComp == null || producerProvide == null) return null;
1842
- const pi = producerProvide.val?.toPathItem?.() ?? null;
1843
- 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];
1844
1932
  }
1845
1933
  var BaseNode = class {
1846
1934
  render(_stack, _rx) {
@@ -2162,33 +2250,31 @@ var RenderViewId = class extends ANode {
2162
2250
  setDataAttr(_key, _val) {
2163
2251
  }
2164
2252
  };
2165
- function dynRenderStep(comp, name, key) {
2166
- const p = resolveDynProducer(comp, name);
2167
- if (!p) return null;
2168
- return key === void 0 ? new DynStep(p.producerCompId, p.producerSteps) : new DynEachStep(p.producerCompId, p.producerSteps, key);
2169
- }
2170
2253
  var RenderNode = class extends RenderViewId {
2171
2254
  render(stack, rx) {
2172
- const newStack = stack.enter(this.val.eval(stack), {}, true);
2173
- 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);
2174
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.
2175
2261
  toPathStep(ctx) {
2176
- if (this.val instanceof DynVal) return dynRenderStep(ctx.comp, this.val.name);
2262
+ if (this.val instanceof DynVal) return null;
2177
2263
  return super.toPathStep(ctx);
2178
2264
  }
2179
2265
  };
2180
2266
  var RenderItNode = class extends RenderViewId {
2181
2267
  render(stack, rx) {
2182
- const newStack = stack.enter(stack.it, {}, true);
2183
- 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);
2184
2271
  }
2185
2272
  toPathStep(ctx) {
2186
2273
  const next = ctx.next();
2187
2274
  if (next === null) return null;
2188
2275
  const nextNode = next.resolveNode();
2189
2276
  if (nextNode instanceof EachNode && next.hasKey) {
2190
- if (nextNode.val instanceof DynVal)
2191
- return dynRenderStep(ctx.comp, nextNode.val.name, next.key);
2277
+ if (nextNode.val instanceof DynVal) return null;
2192
2278
  return new EachRenderItStep(nextNode.val.name, next.key);
2193
2279
  }
2194
2280
  return null;
@@ -2290,11 +2376,19 @@ var EachNode = class extends WrapperNode {
2290
2376
  this.iterInfo = new IterInfo(val, null, null, null);
2291
2377
  }
2292
2378
  render(stack, rx) {
2293
- return rx.renderEachWhen(stack, this.iterInfo, this.node, this.nodeId);
2379
+ return rx.renderEachWhen(stack, this);
2294
2380
  }
2295
2381
  toPathStep(ctx) {
2296
2382
  return ctx.hasKey ? new EachBindStep(this.iterInfo, ctx.key) : null;
2297
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
+ }
2298
2392
  static register = true;
2299
2393
  };
2300
2394
  var IterInfo = class {
@@ -2633,6 +2727,215 @@ function collectAppClassesInSet(app) {
2633
2727
  return classes;
2634
2728
  }
2635
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
+
2636
2939
  // src/components.js
2637
2940
  var COMPONENT = /* @__PURE__ */ Symbol.for("tutuca.component");
2638
2941
  var Components = class {
@@ -2671,12 +2974,14 @@ var ComponentStack = class _ComponentStack {
2671
2974
  this.byName = {};
2672
2975
  this.intentsByName = {};
2673
2976
  this.macros = {};
2977
+ this.paths = {};
2674
2978
  }
2675
2979
  enter() {
2676
2980
  return new _ComponentStack(this.comps, this);
2677
2981
  }
2678
2982
  registerComponents(comps, opts) {
2679
- const { aliases = {} } = opts ?? {};
2983
+ const { aliases = {}, paths } = opts ?? {};
2984
+ if (paths) this.registerPaths(paths);
2680
2985
  for (let i = 0; i < comps.length; i++) {
2681
2986
  const Comp = comps[i];
2682
2987
  Comp[COMPONENT].scope = this.enter();
@@ -2690,6 +2995,27 @@ var ComponentStack = class _ComponentStack {
2690
2995
  else console.warn("alias", alias, "to inexistent component", aliases[alias]);
2691
2996
  }
2692
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
+ }
2693
3019
  registerMacros(macros) {
2694
3020
  for (const key in macros) {
2695
3021
  const lower = key.toLowerCase();
@@ -2721,17 +3047,16 @@ var ComponentStack = class _ComponentStack {
2721
3047
  lookupComponent(name) {
2722
3048
  return this.byName[name] ?? this.parent?.lookupComponent(name) ?? null;
2723
3049
  }
2724
- // The component in this scope chain that provides `name`, innermost first. A lookup
2725
- // names a value without naming its producer, so the render-target teleport
2726
- // (resolveDynProducer) recovers the producer here instead. Sound because
2727
- // PROVIDE_NAME_COLLISION keeps a provide name to one producer per chain; with two,
2728
- // this returns the innermost and the linter has already reported the ambiguity.
2729
- 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) {
2730
3056
  for (const compName in this.byName) {
2731
- const Comp = this.byName[compName];
2732
- if (Comp.provide?.[name] !== void 0) return Comp;
3057
+ if (this.byName[compName].provide?.[name] !== void 0) return true;
2733
3058
  }
2734
- return this.parent?.lookupProvider(name) ?? null;
3059
+ return this.parent?.hasProvider(name) ?? false;
2735
3060
  }
2736
3061
  lookupMacro(name) {
2737
3062
  return this.macros[name] ?? this.parent?.lookupMacro(name) ?? null;
@@ -2748,10 +3073,6 @@ var LookupInfo = class {
2748
3073
  }
2749
3074
  };
2750
3075
  var isString = (v) => typeof v === "string";
2751
- var isTypeName = (s) => {
2752
- const c = s.charCodeAt(0);
2753
- return c >= 65 && c <= 90;
2754
- };
2755
3076
  var _rawSpecKeys = "name view style commonStyle globalStyle receive intent alter views provide lookup fields methods statics";
2756
3077
  var KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
2757
3078
  var _compId = 0;
@@ -5115,7 +5436,6 @@ var DYN_VAL_NOT_DEFINED = "DYN_VAL_NOT_DEFINED";
5115
5436
  var DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED";
5116
5437
  var PROVIDE_NOT_ADDRESSABLE = "PROVIDE_NOT_ADDRESSABLE";
5117
5438
  var PROVIDE_TYPE_BAD_SHAPE = "PROVIDE_TYPE_BAD_SHAPE";
5118
- var PROVIDE_NAME_COLLISION = "PROVIDE_NAME_COLLISION";
5119
5439
  var LOOKUP_BAD_SHAPE = "LOOKUP_BAD_SHAPE";
5120
5440
  var LOOKUP_NO_PROVIDER = "LOOKUP_NO_PROVIDER";
5121
5441
  var RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP";
@@ -5238,7 +5558,6 @@ function checkComponent(Comp, lx = new LintContext(), { wellKnownExtras = EMPTY_
5238
5558
  checkFieldMethodNameCollisions(lx, Comp);
5239
5559
  checkProvidesAreAddressable(lx, Comp);
5240
5560
  checkProvidedTypes(lx, Comp);
5241
- checkProvideNameCollisions(lx, Comp);
5242
5561
  checkLookupShapes(lx, Comp);
5243
5562
  checkLookupTypesResolve(lx, Comp);
5244
5563
  checkLookupsHaveProviders(lx, Comp);
@@ -5922,19 +6241,6 @@ function checkProvidedTypes(lx, Comp) {
5922
6241
  if (raw !== "self") lx.error(PROVIDE_TYPE_BAD_SHAPE, { name, value: raw });
5923
6242
  }
5924
6243
  }
5925
- function checkProvideNameCollisions(lx, Comp) {
5926
- const scope = Comp.scope;
5927
- if (!scope) return;
5928
- for (const name in Comp.provide) {
5929
- for (let s = scope; s; s = s.parent) {
5930
- for (const otherName in s.byName) {
5931
- const Other = s.byName[otherName];
5932
- if (Other !== Comp && Other.provide?.[name] !== void 0)
5933
- lx.error(PROVIDE_NAME_COLLISION, { name, other: Other.name });
5934
- }
5935
- }
5936
- }
5937
- }
5938
6244
  var KNOWN_LOOKUP_KEYS = /* @__PURE__ */ new Set(["name", "default"]);
5939
6245
  function checkLookupShapes(lx, Comp) {
5940
6246
  const raw = Comp._rawLookup;
@@ -5976,7 +6282,7 @@ function checkLookupsHaveProviders(lx, Comp) {
5976
6282
  if (!scope) return;
5977
6283
  for (const name in Comp.lookup) {
5978
6284
  if (isTypeName2(name)) continue;
5979
- if (scope.lookupProvider?.(name)) continue;
6285
+ if (scope.hasProvider?.(name) || scope.lookupPath?.(name)) continue;
5980
6286
  const info = { name, hasDefault: Comp.lookup[name].val != null };
5981
6287
  if (info.hasDefault) lx.hint(LOOKUP_NO_PROVIDER, info);
5982
6288
  else lx.error(LOOKUP_NO_PROVIDER, info);
@@ -6336,160 +6642,6 @@ Component.fromSpec = (opts) => {
6336
6642
  };
6337
6643
  var component = (opts) => Component.fromSpec(opts);
6338
6644
 
6339
- // src/stack.js
6340
- var STOP = /* @__PURE__ */ Symbol("STOP");
6341
- var NEXT = /* @__PURE__ */ Symbol("NEXT");
6342
- var DEFAULT_ROUTE = ["dyn", "lex"];
6343
- function routeLookup(route, lex, dyn) {
6344
- for (let i = 0; i < route.length; i++) {
6345
- const leg = route[i];
6346
- if (leg === "dyn") {
6347
- const v = dyn();
6348
- if (v != null) return v;
6349
- } else if (leg === "lex") {
6350
- const v = lex();
6351
- if (v != null) return v;
6352
- } else {
6353
- console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
6354
- }
6355
- }
6356
- return null;
6357
- }
6358
- function lookup(chain, name, dv = null) {
6359
- let n = chain;
6360
- while (n !== null) {
6361
- const r = n[0].lookup(name);
6362
- if (r === STOP) return dv;
6363
- if (r !== NEXT) return r;
6364
- n = n[1];
6365
- }
6366
- return dv;
6367
- }
6368
- var BindFrame = class {
6369
- constructor(it, binds, isFrame) {
6370
- this.it = it;
6371
- this.binds = binds;
6372
- this.isFrame = isFrame;
6373
- }
6374
- lookup(name) {
6375
- const v = this.binds[name];
6376
- return v === void 0 ? this.isFrame ? STOP : NEXT : v;
6377
- }
6378
- };
6379
- var ObjectFrame = class {
6380
- constructor(binds) {
6381
- this.binds = binds;
6382
- }
6383
- lookup(key) {
6384
- const v = this.binds[key];
6385
- return v === void 0 ? NEXT : v;
6386
- }
6387
- };
6388
- function computeViewsId(views) {
6389
- let s = "";
6390
- let n = views;
6391
- while (n !== null) {
6392
- s += n[0];
6393
- n = n[1];
6394
- }
6395
- return s === "main" ? "" : s;
6396
- }
6397
- var Stack = class _Stack {
6398
- constructor(comps, it, binds, dynBinds, views, viewsId, ctx = null) {
6399
- this.comps = comps;
6400
- this.it = it;
6401
- this.binds = binds;
6402
- this.dynBinds = dynBinds;
6403
- this.views = views;
6404
- this.viewsId = viewsId;
6405
- this.ctx = ctx;
6406
- }
6407
- // Evaluate every provide the entered component publishes and push them as one
6408
- // dynBinds frame, keyed by NAME. Published types go in the same frame: a type name
6409
- // starts A-Z and a value name does not, so the two namespaces cannot collide and
6410
- // nearest-ancestor-wins falls out of frame order for both. No-op with no provides.
6411
- _pushProvides() {
6412
- const comp = this.comps.getCompFor(this.it);
6413
- if (comp == null) return this;
6414
- const { provide, provideType } = comp;
6415
- const dynObj = {};
6416
- let has = false;
6417
- for (const k in provide) {
6418
- dynObj[k] = provide[k].val.eval(this);
6419
- has = true;
6420
- }
6421
- for (const k in provideType) {
6422
- dynObj[k] = provideType[k];
6423
- has = true;
6424
- }
6425
- if (!has) return this;
6426
- const newDynBinds = [new ObjectFrame(dynObj), this.dynBinds];
6427
- const { comps, it, binds, views, viewsId, ctx } = this;
6428
- return new _Stack(comps, it, binds, newDynBinds, views, viewsId, ctx);
6429
- }
6430
- static root(comps, it, ctx) {
6431
- const binds = [new BindFrame(it, {}, true), null];
6432
- const dynBinds = [new ObjectFrame({}), null];
6433
- const views = ["main", null];
6434
- return new _Stack(comps, it, binds, dynBinds, views, "", ctx)._pushProvides();
6435
- }
6436
- enter(it, bindings = {}, isFrame = true) {
6437
- const { comps, binds, dynBinds, views, viewsId, ctx } = this;
6438
- const newBinds = [new BindFrame(it, bindings, isFrame), binds];
6439
- const stack = new _Stack(comps, it, newBinds, dynBinds, views, viewsId, ctx);
6440
- return isFrame ? stack._pushProvides() : stack;
6441
- }
6442
- pushViewName(name) {
6443
- const { comps, it, binds, dynBinds, views, ctx } = this;
6444
- const newViews = [name, views];
6445
- return new _Stack(comps, it, binds, dynBinds, newViews, computeViewsId(newViews), ctx);
6446
- }
6447
- // Published types are stable per scope and would only churn the render cache, so
6448
- // the cache key covers values alone.
6449
- _pushDynBindValuesToArray(arr, comp) {
6450
- for (const k in comp.provide) arr.push(this.lookupDynamic(k));
6451
- for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
6452
- }
6453
- // `*name`: the nearest binding above (including this component's own provides,
6454
- // pushed on entering it), else this component's declared default, else null.
6455
- lookupDynamic(name) {
6456
- const v = lookup(this.dynBinds, name);
6457
- if (v != null) return v;
6458
- const comp = this.comps.getCompFor(this.it);
6459
- return comp?.lookup[name]?.val?.eval(this) ?? null;
6460
- }
6461
- lookupBind(name) {
6462
- return lookup(this.binds, name);
6463
- }
6464
- lookupFieldRaw(name) {
6465
- return this.it[name] ?? null;
6466
- }
6467
- lookupMethod(name) {
6468
- const fn = this.it[name];
6469
- return fn instanceof Function ? fn.call(this.it) : null;
6470
- }
6471
- // The dispatched DOM event / drag info, read only by EventMemberVal's
6472
- // `e.<member>` handler args. Null outside a live event transaction.
6473
- lookupEvent() {
6474
- return this.ctx?.event ?? null;
6475
- }
6476
- lookupDragInfo() {
6477
- return this.ctx?.dragInfo ?? null;
6478
- }
6479
- getHandlerFor(name, key) {
6480
- return this.comps.getHandlerFor(this.it, name, key);
6481
- }
6482
- lookupBestView(views, defaultViewName) {
6483
- let n = this.views;
6484
- while (n !== null) {
6485
- const view = views[n[0]];
6486
- if (view !== void 0) return view;
6487
- n = n[1];
6488
- }
6489
- return views[defaultViewName];
6490
- }
6491
- };
6492
-
6493
6645
  // src/transactor.js
6494
6646
  var State2 = class {
6495
6647
  constructor(val) {
@@ -6565,9 +6717,9 @@ var Transactor = class {
6565
6717
  // `.a[.selId]` render target reconstructed from a DOM event) against the root the
6566
6718
  // handler read, so pathKeys carries concrete keys instead of dropping the dynamic
6567
6719
  // step. No-op when nobody is observing.
6568
- _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 }) {
6569
6721
  if (this._observers.length === 0) return;
6570
- const pinned = path.pinKeys(root);
6722
+ const pinned = path2.pinKeys(root);
6571
6723
  this._emit({
6572
6724
  kind,
6573
6725
  name,
@@ -6618,18 +6770,20 @@ var Transactor = class {
6618
6770
  // intent's answerPath is: a reply must reach the sender that asked even if a key
6619
6771
  // moved while the message was in flight. Null when nobody is waiting — a host
6620
6772
  // sendAtRoot or a view's own `@on.*` — and ctx.sendReply refuses on that.
6621
- pushSend(path, name, args = [], parent = null, origin = null) {
6622
- const t = new SendEvent(path, this, name, args, parent);
6623
- 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);
6624
6778
  this.pushTransaction(t);
6625
6779
  return this._link(t, parent);
6626
6780
  }
6627
6781
  // Raise an intent: a job the sender did not address, walked along a route until
6628
6782
  // something answers. `opts.route` is a list of legs (see DEFAULT_ROUTE) and
6629
6783
  // `opts.livePath` opts out of pinning the answer's destination keys.
6630
- pushIntent(path, name, args = [], opts = {}, parent = null) {
6784
+ pushIntent(path2, name, args = [], opts = {}, parent = null) {
6631
6785
  const release = parent ? parent.completion.track() : null;
6632
- const walk = new IntentWalk(this, path, name, args, opts, parent, release);
6786
+ const walk = new IntentWalk(this, path2, name, args, opts, parent, release);
6633
6787
  walk.advance();
6634
6788
  return walk;
6635
6789
  }
@@ -6663,16 +6817,17 @@ var Transactor = class {
6663
6817
  transaction._completion?.releaseSelf();
6664
6818
  }
6665
6819
  }
6666
- transactInputNow(path, event, eventHandler, dragInfo) {
6667
- 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));
6668
6822
  }
6669
6823
  };
6670
6824
  function nullHandler() {
6671
6825
  return this;
6672
6826
  }
6673
6827
  var Transaction = class {
6674
- constructor(path, transactor, parentTransaction = null) {
6675
- this.path = path;
6828
+ constructor(path2, transactor, parentTransaction = null) {
6829
+ this.path = path2;
6830
+ this.txnPath = null;
6676
6831
  this.transactor = transactor;
6677
6832
  this.parentTransaction = parentTransaction;
6678
6833
  this._completion = null;
@@ -6721,9 +6876,11 @@ var Transaction = class {
6721
6876
  getHandlerAndArgs(_root, _instance, _comps) {
6722
6877
  return null;
6723
6878
  }
6724
- // The path used to apply the mutation. Teleports dynamic-var renders so it lands on
6725
- // 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).
6726
6882
  getTransactionPath() {
6883
+ if (this.txnPath !== null) return this.txnPath;
6727
6884
  return this.path.toTransactionPath().compact();
6728
6885
  }
6729
6886
  run(curRoot, comps) {
@@ -6741,15 +6898,16 @@ var Transaction = class {
6741
6898
  }
6742
6899
  };
6743
6900
  var InputEvent = class extends Transaction {
6744
- constructor(path, e, handler, transactor, dragInfo) {
6745
- super(path, transactor);
6901
+ constructor(path2, e, handler, transactor, dragInfo) {
6902
+ super(path2, transactor);
6746
6903
  this.e = e;
6747
6904
  this.handler = handler;
6748
6905
  this.dragInfo = dragInfo;
6749
6906
  this._dispatchPath = null;
6750
6907
  }
6751
- // Frame steps removed, DynStep + one step per crossed component kept: bubbling
6752
- // 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`.
6753
6911
  get dispatchPath() {
6754
6912
  this._dispatchPath ??= this.path.compact();
6755
6913
  return this._dispatchPath;
@@ -6784,11 +6942,11 @@ var InputEvent = class extends Transaction {
6784
6942
  this.transactor.pushIntent(this.dispatchPath, name, args, rest, this);
6785
6943
  }
6786
6944
  getHandlerAndArgs(root, _instance, comps) {
6787
- const stack = this.path.toTransactionPath().buildStack(Stack.root(comps, root, this));
6945
+ const stack = this.path.buildStack(Stack.root(comps, root, this));
6788
6946
  const [handler, args] = this.handler.getHandlerAndArgs(stack, this);
6789
6947
  this._handlerArgs = [...args];
6790
- const path = this.dispatchPath;
6791
- args.push(new EventContext(path, this.transactor, this));
6948
+ const path2 = this.dispatchPath;
6949
+ args.push(new EventContext(path2, this.transactor, this));
6792
6950
  return [handler, args];
6793
6951
  }
6794
6952
  // The dispatched DOM event, read only through `e.<member>` handler args
@@ -6799,11 +6957,11 @@ var InputEvent = class extends Transaction {
6799
6957
  }
6800
6958
  };
6801
6959
  var NameArgsTransaction = class extends Transaction {
6802
- constructor(path, transactor, name, args, parentTransaction) {
6803
- super(path, transactor, parentTransaction);
6960
+ constructor(path2, transactor, name, args, parentTransaction) {
6961
+ super(path2, transactor, parentTransaction);
6804
6962
  this.name = name;
6805
6963
  this.args = args;
6806
- this.targetPath = path;
6964
+ this.targetPath = path2;
6807
6965
  }
6808
6966
  handlerProp = null;
6809
6967
  // NameArgs verbs map their handler bucket straight to the observed kind:
@@ -6860,8 +7018,8 @@ var SendEvent = class extends NameArgsTransaction {
6860
7018
  };
6861
7019
  var IntentEvent = class extends NameArgsTransaction {
6862
7020
  handlerProp = "intent";
6863
- constructor(path, transactor, walk) {
6864
- super(path, transactor, walk.name, walk.args, walk.parent);
7021
+ constructor(path2, transactor, walk) {
7022
+ super(path2, transactor, walk.name, walk.args, walk.parent);
6865
7023
  this.walk = walk;
6866
7024
  this.targetPath = walk.origin;
6867
7025
  }
@@ -6888,17 +7046,17 @@ var PASS = /* @__PURE__ */ Symbol.for("tutuca.intent.pass");
6888
7046
  var REFUSAL_RING_CAP = 200;
6889
7047
  var INTENT_DEPTH = 64;
6890
7048
  var IntentWalk = class {
6891
- constructor(transactor, path, name, args, opts, parent, release) {
7049
+ constructor(transactor, path2, name, args, opts, parent, release) {
6892
7050
  this.transactor = transactor;
6893
7051
  this.name = name;
6894
7052
  this.args = args;
6895
7053
  this.route = opts?.route ?? DEFAULT_ROUTE;
6896
7054
  this.parent = parent;
6897
7055
  this.release = release;
6898
- this.origin = path;
6899
- 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);
6900
7058
  this.legIndex = 0;
6901
- this.dynAt = path;
7059
+ this.dynAt = path2;
6902
7060
  this.hops = 0;
6903
7061
  this.ended = false;
6904
7062
  }
@@ -6910,7 +7068,7 @@ var IntentWalk = class {
6910
7068
  while (this.legIndex < this.route.length) {
6911
7069
  const leg = this.route[this.legIndex];
6912
7070
  if (leg === "dyn") {
6913
- if (this.dynAt.steps.length === 0) {
7071
+ if (!this.dynAt.canPop()) {
6914
7072
  this.legIndex++;
6915
7073
  continue;
6916
7074
  }
@@ -7007,8 +7165,8 @@ var IntentWalk = class {
7007
7165
  if (this.ended) return;
7008
7166
  this.ended = true;
7009
7167
  if (name === null) return this.release?.();
7010
- const path = this.answerPath ?? this.origin;
7011
- 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;
7012
7170
  t._isAnswer = true;
7013
7171
  this.transactor.pushTransaction(t);
7014
7172
  if (this.release) t.completion.whenSubtreeSettled().then(this.release);
@@ -7074,8 +7232,8 @@ var Completion = class {
7074
7232
  }
7075
7233
  };
7076
7234
  var Dispatcher = class {
7077
- constructor(path, transactor, parentTransaction, root = transactor.state.val) {
7078
- this.path = path;
7235
+ constructor(path2, transactor, parentTransaction, root = transactor.state.val) {
7236
+ this.path = path2;
7079
7237
  this.transactor = transactor;
7080
7238
  this.parent = parentTransaction;
7081
7239
  this.root = root;
@@ -7103,7 +7261,9 @@ var Dispatcher = class {
7103
7261
  // replayed; provides are pushed on every component frame either way, so a provide
7104
7262
  // that reads a loop binding is the one case this cannot reproduce faithfully.
7105
7263
  _stack() {
7106
- 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
+ );
7107
7267
  return this._stackMemo;
7108
7268
  }
7109
7269
  // Resolve a name the way the renderer would. `opts.route` takes the same legs in
@@ -7117,14 +7277,19 @@ var Dispatcher = class {
7117
7277
  );
7118
7278
  }
7119
7279
  // The `lex` leg without a Stack: the scope of the component whose handler is
7120
- // 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.
7121
7283
  _lookupLex(name) {
7122
7284
  let Comp = null;
7123
7285
  this.walkPath((c) => {
7124
7286
  Comp = c;
7125
7287
  return false;
7126
7288
  });
7127
- 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;
7128
7293
  }
7129
7294
  // A component lookup is a value lookup constrained to a component. The `lex` leg
7130
7295
  // can only ever answer with one; the `dyn` leg reads a binding an ancestor
@@ -7146,8 +7311,8 @@ var Dispatcher = class {
7146
7311
  send(name, args) {
7147
7312
  return this.sendAtPath(this.path, name, args);
7148
7313
  }
7149
- sendAtPath(path, name, args) {
7150
- 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);
7151
7316
  }
7152
7317
  // An intent is ROUTED: it names a job and walks until something answers. The verb
7153
7318
  // does not decide which scope answers — `opts.route` does, written here at the call
@@ -7155,8 +7320,8 @@ var Dispatcher = class {
7155
7320
  intent(name, args, opts) {
7156
7321
  return this.intentAtPath(this.path, name, args, opts);
7157
7322
  }
7158
- intentAtPath(path, name, args, opts) {
7159
- 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);
7160
7325
  }
7161
7326
  };
7162
7327
  var EventContext = class extends Dispatcher {
@@ -7185,7 +7350,14 @@ var EventContext = class extends Dispatcher {
7185
7350
  this.transactor.refuse("NO_SENDER", { name });
7186
7351
  return null;
7187
7352
  }
7188
- 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
+ );
7189
7361
  }
7190
7362
  // End the walk answering nothing — "served, and no answer".
7191
7363
  stop() {
@@ -7217,13 +7389,13 @@ var PathChanges = class extends PathBuilder {
7217
7389
  }
7218
7390
  };
7219
7391
  function rootDispatcher(transactor) {
7220
- return new Dispatcher(new Path([]), transactor, null);
7392
+ return new Dispatcher(new DispatchPath(), transactor, null);
7221
7393
  }
7222
7394
 
7223
7395
  // tools/core/results.js
7224
7396
  var ModuleInfo = class {
7225
- constructor({ path = null, present = /* @__PURE__ */ new Set(), counts = {}, warnings = [] }) {
7226
- this.path = path;
7397
+ constructor({ path: path2 = null, present = /* @__PURE__ */ new Set(), counts = {}, warnings = [] }) {
7398
+ this.path = path2;
7227
7399
  this.present = present;
7228
7400
  this.counts = counts;
7229
7401
  this.warnings = warnings;
@@ -7333,8 +7505,8 @@ var DescribeResult = class {
7333
7505
  }
7334
7506
  };
7335
7507
  var ModuleTestReport = class {
7336
- constructor({ path = null, suites = [], counts = { pass: 0, fail: 0, skip: 0, total: 0 } }) {
7337
- this.path = path;
7508
+ constructor({ path: path2 = null, suites = [], counts = { pass: 0, fail: 0, skip: 0, total: 0 } }) {
7509
+ this.path = path2;
7338
7510
  this.suites = suites;
7339
7511
  this.counts = counts;
7340
7512
  }
@@ -7377,8 +7549,8 @@ var Test = class {
7377
7549
  }
7378
7550
  };
7379
7551
  var ModuleTests = class {
7380
- constructor({ path = null, suites = [] } = {}) {
7381
- this.path = path;
7552
+ constructor({ path: path2 = null, suites = [] } = {}) {
7553
+ this.path = path2;
7382
7554
  this.suites = suites;
7383
7555
  }
7384
7556
  };
@@ -7400,8 +7572,8 @@ function titleFromArg(arg) {
7400
7572
  if (typeof arg === "function") return arg.name || "(anonymous)";
7401
7573
  return String(arg);
7402
7574
  }
7403
- function makeCollector({ path = null, components = [] } = {}) {
7404
- const moduleTests = new ModuleTests({ path, suites: [] });
7575
+ function makeCollector({ path: path2 = null, components = [] } = {}) {
7576
+ const moduleTests = new ModuleTests({ path: path2, suites: [] });
7405
7577
  const stack = [];
7406
7578
  function describe(...args) {
7407
7579
  let head;
@@ -7502,7 +7674,7 @@ async function driveStack(stack, value, phase, opts = {}) {
7502
7674
  val
7503
7675
  );
7504
7676
  });
7505
- dispatchPhase(rootDispatcher(transactor), new Path([]), phase, value);
7677
+ dispatchPhase(rootDispatcher(transactor), new DispatchPath(), phase, value);
7506
7678
  await transactor.settle();
7507
7679
  return transactor.state.val;
7508
7680
  }
@@ -7512,7 +7684,7 @@ async function drive(cfg, value, phase, opts = {}) {
7512
7684
  async function runTests({
7513
7685
  getTests,
7514
7686
  components = [],
7515
- path = null,
7687
+ path: path2 = null,
7516
7688
  expect: expect3,
7517
7689
  name = null,
7518
7690
  grep = null,
@@ -7523,13 +7695,13 @@ async function runTests({
7523
7695
  const counts = { pass: 0, fail: 0, skip: 0, total: 0 };
7524
7696
  if (typeof getTests !== "function") {
7525
7697
  return new TestReport({
7526
- modules: [new ModuleTestReport({ path, suites: [], counts })]
7698
+ modules: [new ModuleTestReport({ path: path2, suites: [], counts })]
7527
7699
  });
7528
7700
  }
7529
7701
  if (typeof expect3 !== "function") {
7530
7702
  throw new Error("runTests: expect must be provided (e.g. chai's expect)");
7531
7703
  }
7532
- const { describe, test: test2, moduleTests } = makeCollector({ path, components });
7704
+ const { describe, test: test2, moduleTests } = makeCollector({ path: path2, components });
7533
7705
  let _stack = null;
7534
7706
  const getStack = () => _stack ??= buildStack({ components, macros, intentHandlers });
7535
7707
  const driveLocal = (value, phase, opts = {}) => driveStack(getStack(), value, phase, opts);
@@ -7592,7 +7764,7 @@ async function runTests({
7592
7764
  if (r !== null) suiteResults.push(r);
7593
7765
  }
7594
7766
  return new TestReport({
7595
- modules: [new ModuleTestReport({ path, suites: suiteResults, counts })]
7767
+ modules: [new ModuleTestReport({ path: path2, suites: suiteResults, counts })]
7596
7768
  });
7597
7769
  }
7598
7770
 
@@ -7761,15 +7933,13 @@ function lintIdToMessage(id, info) {
7761
7933
  case "DYN_ALIAS_NOT_REFERENCED":
7762
7934
  return `Lookup '${info.name}' is defined but never used — remove it or reference it as '*${info.name}' in a view`;
7763
7935
  case "PROVIDE_NOT_ADDRESSABLE":
7764
- 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`;
7765
7937
  case "LOOKUP_BAD_SHAPE":
7766
7938
  return `Lookup '${info.name}' has an invalid shape: ${info.problem}`;
7767
7939
  case "LOOKUP_NO_PROVIDER":
7768
- 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`;
7769
7941
  case "PROVIDE_TYPE_BAD_SHAPE":
7770
7942
  return `Provide '${info.name}' starts uppercase, so it publishes a component type — its value must be 'self', not '${info.value}'`;
7771
- case "PROVIDE_NAME_COLLISION":
7772
- return `Provide '${info.name}' is also provided by '${info.other}' in the same scope — one name, one provider, so a lookup can find it`;
7773
7943
  case "UNKNOWN_MACRO_ARG":
7774
7944
  return `Argument '${info.name}' is not declared in macro '${info.macroName}'`;
7775
7945
  case "UNKNOWN_DIRECTIVE":
@@ -7935,7 +8105,7 @@ var App = class {
7935
8105
  const { type } = e;
7936
8106
  const isDrag = type === "dragover" || type === "dragstart" || type === "dragend" || type === "drop";
7937
8107
  const { rootNode: root, maxEventNodeDepth: maxDepth, comps, transactor } = this;
7938
- const [path, handlers] = Path.fromNodeAndEventName(
8108
+ const [path2, handlers] = DispatchPath.fromNodeAndEventName(
7939
8109
  e.target,
7940
8110
  type,
7941
8111
  root,
@@ -7943,9 +8113,9 @@ var App = class {
7943
8113
  comps,
7944
8114
  !isDrag
7945
8115
  );
7946
- if (isDrag) this._handleDragEvent(e, type, path);
7947
- if (path !== null && handlers !== null)
7948
- 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);
7949
8119
  }
7950
8120
  _handleTouchEvent(e) {
7951
8121
  const { type } = e;
@@ -7988,7 +8158,7 @@ var App = class {
7988
8158
  this._touch = null;
7989
8159
  }
7990
8160
  }
7991
- _handleDragEvent(e, type, path) {
8161
+ _handleDragEvent(e, type, path2) {
7992
8162
  if (type === "dragover") {
7993
8163
  const dropTarget = getClosestDropTarget(e.target, this.rootNode, Infinity);
7994
8164
  if (dropTarget !== null) {
@@ -8000,10 +8170,10 @@ var App = class {
8000
8170
  } else if (type === "dragstart") {
8001
8171
  e.target.dataset.dragging = 1;
8002
8172
  const rootValue = this.state.val;
8003
- const txnPath = path.compact().toTransactionPath();
8173
+ const txnPath = path2.compact().toTransactionPath();
8004
8174
  const value = txnPath.lookup(rootValue);
8005
8175
  const dragType = e.target.dataset.dragtype ?? "?";
8006
- const stack = path.toTransactionPath().buildStack(this.makeStack(rootValue));
8176
+ const stack = path2.buildStack(this.makeStack(rootValue));
8007
8177
  this.dragInfo = new DragInfo(stack, value, dragType, e.target);
8008
8178
  } else if (type === "drop") {
8009
8179
  e.preventDefault();
@@ -8078,7 +8248,7 @@ var App = class {
8078
8248
  this.rootNode.removeEventListener(name, this, listenerOpts(name));
8079
8249
  }
8080
8250
  sendAtRoot(name, args) {
8081
- this.transactor.pushSend(new Path([]), name, args);
8251
+ this.transactor.pushSend(new DispatchPath(), name, args);
8082
8252
  }
8083
8253
  registerComponents(comps, opts) {
8084
8254
  const scope = this.compStack.enter();
@@ -8218,7 +8388,20 @@ var WeakMapDomCache = class {
8218
8388
  };
8219
8389
 
8220
8390
  // src/renderer.js
8221
- 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
+ }
8222
8405
  var Renderer = class {
8223
8406
  constructor(comps) {
8224
8407
  this.comps = comps;
@@ -8255,17 +8438,17 @@ var Renderer = class {
8255
8438
  if (comp === null) return null;
8256
8439
  return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
8257
8440
  }
8258
- renderIt(stack, node, key, viewName) {
8441
+ renderIt(stack, node, key, viewName, base = null) {
8259
8442
  const comp = this.comps.getCompFor(stack.it);
8260
- 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;
8261
8444
  }
8262
8445
  // `node` is the parse node of the render site (`<x render>` / `render-it` /
8263
8446
  // `render-each`, or the view's root anode for the app root). It keys the
8264
8447
  // cache as a globally-unique object: node ids alone are unique only within a
8265
8448
  // single view, so the same value rendered by two components (e.g. through a
8266
8449
  // shared dynamic-var sequence) would otherwise collide in the cache.
8267
- _rValComp(stack, val, comp, node, key, viewName) {
8268
- 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}`;
8269
8452
  const cachePath = [node, val];
8270
8453
  stack._pushDynBindValuesToArray(cachePath, comp);
8271
8454
  const cachedNode = this.cache.get(cachePath, cacheKey);
@@ -8273,30 +8456,43 @@ var Renderer = class {
8273
8456
  const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
8274
8457
  const body = this.renderView(view, stack);
8275
8458
  if (body == null) return null;
8459
+ const baseJson = base === null ? null : pathToJson(base);
8276
8460
  const meta = this._renderMetadata({
8277
8461
  $: "Comp",
8278
8462
  nid: node?.nodeId ?? null,
8279
8463
  cid: comp.id,
8280
- vid: view.name
8464
+ vid: view.name,
8465
+ ...baseJson === null ? null : { base: baseJson }
8281
8466
  });
8282
- const dom = new VFragment([meta, body]);
8467
+ const dom = new VFragment([
8468
+ meta,
8469
+ baseJson === null ? body : stampRenderBase(body, JSON.stringify(baseJson))
8470
+ ]);
8283
8471
  this.cache.set(cachePath, cacheKey, dom);
8284
8472
  return dom;
8285
8473
  }
8286
8474
  pushEachEntry(r, nid, attrName, key, dom) {
8287
8475
  r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
8288
8476
  }
8289
- renderEachWhen(stack, iterInfo, view, nid) {
8477
+ renderEachWhen(stack, each) {
8478
+ const { iterInfo, node: view, nodeId: nid } = each;
8290
8479
  const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
8480
+ const seqPath = iterInfo.val instanceof DynVal ? stack.lookupDynamicLocated(iterInfo.val.name)?.path ?? null : null;
8291
8481
  const r = [];
8292
8482
  const it = stack.it;
8293
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;
8294
8487
  const cachePath = enricher ? [view, it, value] : [view, value];
8295
- const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
8488
+ const cacheKey = `${stack.viewsId ?? ""}${nid}${key}${itemPath.addressKey}`;
8296
8489
  const cachedNode = this.cache.get(cachePath, cacheKey);
8297
8490
  if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
8298
8491
  else {
8299
- 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
+ );
8300
8496
  if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
8301
8497
  this.cache.set(cachePath, cacheKey, dom);
8302
8498
  }
@@ -8553,7 +8749,6 @@ export {
8553
8749
  ModuleTests,
8554
8750
  PASS,
8555
8751
  PLACEHOLDERLESS_TEMPLATE_STRING,
8556
- PROVIDE_NAME_COLLISION,
8557
8752
  PROVIDE_NOT_ADDRESSABLE,
8558
8753
  PROVIDE_TYPE_BAD_SHAPE,
8559
8754
  ParseContext,
@@ -8594,6 +8789,7 @@ export {
8594
8789
  lintIdToMessage,
8595
8790
  macro,
8596
8791
  makeCollector,
8792
+ path,
8597
8793
  phaseOps,
8598
8794
  reportTestReportToConsole,
8599
8795
  resolveArgs,