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.
@@ -76,8 +76,11 @@ var Step = class {
76
76
  }
77
77
  setDraftValue(_root, _v) {
78
78
  }
79
- enterFrame(stack, next) {
80
- return stack.enter(next, {}, true);
79
+ // Re-enter this step while rebuilding a stack. `renderPath` is the dispatch
80
+ // position AFTER this step: a rebuilt frame must carry the same render path
81
+ // the renderer had there, or the provides it publishes would be located wrong.
82
+ enterFrame(stack, next, renderPath) {
83
+ return stack.enter(next, {}, true, renderPath);
81
84
  }
82
85
  toAbstractPathStep() {
83
86
  return this;
@@ -102,8 +105,8 @@ var BindStep = class _BindStep extends Step {
102
105
  lookup(v, _dval) {
103
106
  return v;
104
107
  }
105
- enterFrame(stack, next) {
106
- return stack.enter(next, { ...this.binds }, false);
108
+ enterFrame(stack, next, renderPath) {
109
+ return stack.enter(next, { ...this.binds }, false, renderPath);
107
110
  }
108
111
  withIndex(i) {
109
112
  return new _BindStep({ ...this.binds, key: i });
@@ -120,9 +123,9 @@ var ScopeBindStep = class _ScopeBindStep extends BindStep {
120
123
  super(binds);
121
124
  this.val = val;
122
125
  }
123
- enterFrame(stack, next) {
126
+ enterFrame(stack, next, renderPath) {
124
127
  const dyn = this.val.evalAsHandler(stack)?.call(stack.it) ?? {};
125
- return stack.enter(next, { ...this.binds, ...dyn }, false);
128
+ return stack.enter(next, { ...this.binds, ...dyn }, false, renderPath);
126
129
  }
127
130
  withIndex(i) {
128
131
  return new _ScopeBindStep(this.val, { ...this.binds, key: i });
@@ -165,8 +168,8 @@ var SeqStep = class extends Step {
165
168
  const seq = readKey(root, this.field, null);
166
169
  if (seq != null) writeSeqKey(seq, this.key, v);
167
170
  }
168
- enterFrame(stack, next) {
169
- return stack.enter(next, { key: this.key }, true);
171
+ enterFrame(stack, next, renderPath) {
172
+ return stack.enter(next, { key: this.key }, true, renderPath);
170
173
  }
171
174
  toKey() {
172
175
  return { field: this.field, key: this.key };
@@ -212,60 +215,52 @@ var EachBindStep = class extends Step {
212
215
  }
213
216
  // Replay the renderer's per-item binds (key, value + any @enrich-with binds)
214
217
  // so a rebuilt stack matches the one @each rendered with.
215
- enterFrame(stack, next) {
216
- return stack.enter(next, this.iterInfo.enrichBinds(stack, this.key), false);
218
+ enterFrame(stack, next, renderPath) {
219
+ return stack.enter(next, this.iterInfo.enrichBinds(stack, this.key), false, renderPath);
217
220
  }
218
221
  toAbstractPathStep() {
219
222
  return null;
220
223
  }
221
224
  };
222
225
  var EachRenderItStep = class extends SeqStep {
223
- enterFrame(stack, next) {
224
- return stack.enter(next, { key: this.key, value: next }, false).enter(next, {}, true);
226
+ enterFrame(stack, next, renderPath) {
227
+ return stack.enter(next, { key: this.key, value: next }, false, renderPath).enter(next, {}, true, renderPath);
225
228
  }
226
229
  toAbstractPathStep() {
227
230
  return new SeqStep(this.field, this.key);
228
231
  }
229
232
  };
230
- function warnRawDynStep(op, step) {
231
- console.warn(`Path.${op} reached a DynStep: call toTransactionPath() first`, step);
233
+ function stepToJson(step) {
234
+ if (step instanceof SeqStep) return { f: step.field, k: step.key };
235
+ if (step instanceof FieldStep) return { f: step.field };
236
+ if (step instanceof SeqAccessStep) return { f: step.seqField, a: step.keyField };
237
+ return null;
232
238
  }
233
- var DynStep = class extends Step {
234
- constructor(producerCompId, producerSteps) {
235
- super();
236
- this.producerCompId = producerCompId;
237
- this.producerSteps = producerSteps;
238
- this.interiorCids = /* @__PURE__ */ new Set();
239
- }
240
- // Steps spliced into the transaction path in place of this marker.
241
- teleportSteps() {
242
- return this.producerSteps;
243
- }
244
- lookup(_v, dval = null) {
245
- warnRawDynStep("lookup", this);
246
- return dval;
247
- }
248
- enterFrame(stack, _next) {
249
- warnRawDynStep("enterFrame", this);
250
- return stack;
251
- }
252
- };
253
- var DynEachStep = class extends DynStep {
254
- constructor(producerCompId, producerSteps, key) {
255
- super(producerCompId, producerSteps);
256
- this.key = key;
257
- }
258
- teleportSteps() {
259
- const { producerSteps, key } = this;
260
- if (producerSteps.length === 0) return producerSteps;
261
- const last = producerSteps[producerSteps.length - 1];
262
- if (!(last instanceof FieldStep)) {
263
- console.warn("DynEachStep: seq-access dynamic cannot be iterated", this);
264
- return producerSteps;
265
- }
266
- return producerSteps.slice(0, -1).concat(new SeqStep(last.field, key));
239
+ function stepFromJson(j) {
240
+ if (j == null || typeof j.f !== "string") return null;
241
+ if (j.k !== void 0) return new SeqStep(j.f, j.k);
242
+ if (j.a !== void 0) return new SeqAccessStep(j.f, j.a);
243
+ return new FieldStep(j.f);
244
+ }
245
+ function pathToJson(path2) {
246
+ const out = [];
247
+ for (const step of path2.steps) {
248
+ const j = stepToJson(step);
249
+ if (j === null) return null;
250
+ out.push(j);
267
251
  }
268
- };
252
+ return out;
253
+ }
254
+ function pathFromJson(j) {
255
+ if (!Array.isArray(j)) return null;
256
+ const steps = [];
257
+ for (const item of j) {
258
+ const step = stepFromJson(item);
259
+ if (step === null) return null;
260
+ steps.push(step);
261
+ }
262
+ return new Path(steps);
263
+ }
269
264
  var Path = class _Path {
270
265
  constructor(steps = []) {
271
266
  this.steps = steps;
@@ -276,40 +271,13 @@ var Path = class _Path {
276
271
  popStep() {
277
272
  return new _Path(this.steps.slice(0, -1));
278
273
  }
279
- // The dispatch path: frame-only steps removed, one step per crossed component
280
- // (DynStep included). `popStep` over it bubbles through every component.
274
+ // Frame-only steps removed, one step per crossed component: `popStep` over the
275
+ // result bubbles through every component.
281
276
  compact() {
282
277
  const out = [];
283
278
  for (const step of this.steps) {
284
279
  const s = step.toAbstractPathStep();
285
- if (s !== null) {
286
- if (s !== step) s._originCid = step._originCid;
287
- out.push(s);
288
- }
289
- }
290
- return new _Path(out);
291
- }
292
- // The abstract path used to apply a transaction: every DynStep is teleported —
293
- // the steps interior to its producer..consumer span are dropped and the
294
- // producer's own path spliced in — so a mutation lands on the data's real
295
- // location. A path with no DynStep is returned unchanged.
296
- toTransactionPath() {
297
- let hasDyn = false;
298
- for (const step of this.steps)
299
- if (step instanceof DynStep) {
300
- hasDyn = true;
301
- break;
302
- }
303
- if (!hasDyn) return this;
304
- const out = [];
305
- for (const step of this.steps) {
306
- if (step instanceof DynStep) {
307
- while (out.length > 0 && step.interiorCids.has(out[out.length - 1]._originCid)) out.pop();
308
- for (const ts of step.teleportSteps()) {
309
- ts._originCid = step.producerCompId;
310
- out.push(ts);
311
- }
312
- } else out.push(step);
280
+ if (s !== null) out.push(s);
313
281
  }
314
282
  return new _Path(out);
315
283
  }
@@ -317,7 +285,6 @@ var Path = class _Path {
317
285
  // key as it is *now* so a later lookup/setValue lands on the same item even if the
318
286
  // keyField changed meanwhile (e.g. the selected tab moved while an intent was in
319
287
  // flight). Returns a new Path with those steps replaced; `this` if nothing pinned.
320
- // Must be called on a transaction path (no DynSteps — call toTransactionPath first).
321
288
  pinKeys(root) {
322
289
  let curVal = root;
323
290
  let out = null;
@@ -340,8 +307,8 @@ var Path = class _Path {
340
307
  }
341
308
  // The values entered along the path, root→leaf (root included): index 0 is `root`,
342
309
  // the last entry is the leaf this path resolves to. Stops early at the first
343
- // unresolvable step. Call on a transaction path (no DynSteps). Used to walk the
344
- // component instances on a dispatch path (filter via Components.getCompFor).
310
+ // unresolvable step. Used to walk the component instances on a dispatch path
311
+ // (filter via Components.getCompFor).
345
312
  resolveChain(root) {
346
313
  const out = [root];
347
314
  let curVal = root;
@@ -355,7 +322,7 @@ var Path = class _Path {
355
322
  // A flat `[{ field, key? }]` list of the addressing steps, skipping frame-only
356
323
  // steps (binds). Generic path introspection so tooling (e.g. the storybook
357
324
  // activity log) can identify which subtree a transaction touched without
358
- // depending on Step internals. Call on a transaction path (no DynSteps).
325
+ // depending on Step internals.
359
326
  toKeys() {
360
327
  const out = [];
361
328
  for (const step of this.steps) {
@@ -376,62 +343,147 @@ var Path = class _Path {
376
343
  });
377
344
  }
378
345
  buildStack(stack) {
379
- let prev = stack.it;
380
- for (const step of this.steps) {
381
- const next = step.lookup(prev, NONE);
382
- if (next === NONE) {
383
- console.warn("bad PathItem", { root: stack.it, step, path: this });
384
- return null;
346
+ return walkItems(stack, this.steps, stack.renderPath ?? new DispatchPath(), this)?.[0] ?? null;
347
+ }
348
+ };
349
+ function walkItems(stack, items, renderPath, path2) {
350
+ let prev = stack.it;
351
+ for (const step of items) {
352
+ const next = step.lookup(prev, NONE);
353
+ if (next === NONE) {
354
+ console.warn("bad PathItem", { root: stack.it, step, path: path2 });
355
+ return null;
356
+ }
357
+ renderPath = renderPath.pushItem(step);
358
+ stack = step.enterFrame(stack, next, renderPath);
359
+ prev = next;
360
+ }
361
+ return [stack, renderPath];
362
+ }
363
+ var EMPTY_PATH = new Path([]);
364
+ var DispatchPath = class _DispatchPath {
365
+ constructor(frames = [{ base: EMPTY_PATH, items: [] }]) {
366
+ this.frames = frames;
367
+ }
368
+ // Plain addressing steps in one frame based at the root: what a caller means
369
+ // by "this position" when it has no continuation of its own.
370
+ static ofSteps(steps) {
371
+ return new _DispatchPath([{ base: EMPTY_PATH, items: steps.slice() }]);
372
+ }
373
+ get top() {
374
+ return this.frames[this.frames.length - 1];
375
+ }
376
+ _withTopItems(items) {
377
+ const frames = this.frames.slice();
378
+ frames[frames.length - 1] = { base: this.top.base, items };
379
+ return new _DispatchPath(frames);
380
+ }
381
+ concat(steps) {
382
+ if (this.frames.length === 0) return _DispatchPath.ofSteps(steps);
383
+ return this._withTopItems(this.top.items.concat(steps));
384
+ }
385
+ pushItem(step) {
386
+ return this.concat([step]);
387
+ }
388
+ pushFrame(base) {
389
+ return new _DispatchPath(this.frames.concat({ base, items: [] }));
390
+ }
391
+ // Whether bubbling has anywhere left to go: another step in this frame, or a
392
+ // visual caller underneath it.
393
+ canPop() {
394
+ const n = this.frames.length;
395
+ return n > 1 || n === 1 && this.frames[0].items.length > 0;
396
+ }
397
+ isRoot() {
398
+ return this.toTransactionPath().steps.length === 0;
399
+ }
400
+ // One component closer to the root. At the top of a frame that is popping back
401
+ // to the visual caller, not to the producer's own parent — the caller is where
402
+ // the `*name` was written, and where an unhandled message should keep going.
403
+ popStep() {
404
+ const n = this.frames.length;
405
+ if (n === 0) return this;
406
+ const top = this.frames[n - 1];
407
+ if (top.items.length > 0) return this._withTopItems(top.items.slice(0, -1));
408
+ if (n > 1) return new _DispatchPath(this.frames.slice(0, -1));
409
+ return this;
410
+ }
411
+ // Drop frame-only steps inside every frame independently; a frame's base is
412
+ // already addressing-only.
413
+ compact() {
414
+ return new _DispatchPath(
415
+ this.frames.map(({ base, items }) => ({ base, items: new Path(items).compact().steps }))
416
+ );
417
+ }
418
+ // A stable string for the ADDRESS this path denotes, for the render cache. The
419
+ // same immutable value can sit at two places in the tree, and a subtree rendered
420
+ // at one of them bakes that address in — the provides it publishes are located
421
+ // there. Frame-only steps address nothing, so they are compacted out: two sites
422
+ // that differ only in binds do render the same subtree.
423
+ get addressKey() {
424
+ this._addressKey ??= renderPathText(this.toTransactionPath().compact());
425
+ return this._addressKey;
426
+ }
427
+ // The active transaction address: the top frame's absolute base followed by
428
+ // its ordinary descendant steps. This is where a mutation lands.
429
+ toTransactionPath() {
430
+ const top = this.top;
431
+ return top === void 0 ? EMPTY_PATH : top.base.concat(top.items);
432
+ }
433
+ // Rebuild the render stack this path was dispatched from. A frame with a base
434
+ // re-enters at that absolute value first — replaying the resume a `*name`
435
+ // render performed — and then walks its ordinary items.
436
+ buildStack(stack) {
437
+ let renderPath = new _DispatchPath();
438
+ for (let i = 0; i < this.frames.length; i++) {
439
+ const { base, items } = this.frames[i];
440
+ if (i > 0 || base.steps.length > 0) {
441
+ const baseValue = base.lookup(stack.root, NONE);
442
+ if (baseValue === NONE) {
443
+ console.warn("bad frame base", { base, path: this });
444
+ return null;
445
+ }
446
+ renderPath = renderPath.pushFrame(base);
447
+ stack = stack.enter(baseValue, {}, true, renderPath);
385
448
  }
386
- stack = step.enterFrame(stack, next);
387
- prev = next;
449
+ const walked = walkItems(stack, items, renderPath, this);
450
+ if (walked === null) return null;
451
+ [stack, renderPath] = walked;
388
452
  }
389
453
  return stack;
390
454
  }
391
455
  static fromNodeAndEventName(node, eventName, rootNode, maxDepth, comps, stopOnNoEvent = true) {
392
- const pathSteps = [];
393
- const pendingDyns = [];
456
+ const parts = [];
394
457
  const bubbles = BUBBLING_EVENTS.has(eventName);
395
458
  let depth = 0;
396
459
  let eventIds = [];
397
460
  let handlers = null;
398
- let nodeIds = [];
461
+ let nodeRefs = [];
399
462
  let isLeafComponent = true;
400
463
  const crossComponent = (cidNum, vid) => {
401
464
  const comp = comps.getComponentForId(cidNum);
402
- let pushStep = true;
465
+ let pushPart = true;
403
466
  if (handlers === null && (isLeafComponent || bubbles)) {
404
467
  handlers = findHandlers(comp, eventIds, vid, eventName);
405
468
  if (handlers === null) {
406
469
  if (isLeafComponent && stopOnNoEvent && !bubbles) return false;
407
470
  } else if (!isLeafComponent) {
408
- pathSteps.length = 0;
409
- pendingDyns.length = 0;
410
- pushStep = false;
471
+ parts.length = 0;
472
+ pushPart = false;
411
473
  }
412
474
  }
413
475
  isLeafComponent = false;
414
- for (const dyn of pendingDyns) dyn.interiorCids.add(cidNum);
415
- if (pushStep) {
416
- const step = resolvePathStep(comp, nodeIds, vid);
417
- if (step) {
418
- step._originCid = cidNum;
419
- pathSteps.push(step);
420
- if (step instanceof DynStep) {
421
- step.interiorCids.add(cidNum);
422
- pendingDyns.push(step);
423
- }
424
- }
476
+ if (pushPart) {
477
+ const part = resolvePathPart(comp, nodeRefs, vid);
478
+ if (part) parts.push(part);
425
479
  }
426
- for (let i = pendingDyns.length - 1; i >= 0; i--)
427
- if (pendingDyns[i].producerCompId === cidNum) pendingDyns.splice(i, 1);
428
480
  eventIds = [];
429
- nodeIds = [];
481
+ nodeRefs = [];
430
482
  return true;
431
483
  };
432
484
  while (node && node !== rootNode && depth < maxDepth) {
433
485
  if (node?.dataset) {
434
- const { eid, cid, vid } = node.dataset;
486
+ const { eid, cid, vid, rp } = node.dataset;
435
487
  if (eid !== void 0) eventIds.push(eid);
436
488
  const metas = metaChain(node.previousSibling);
437
489
  let sawComp = false;
@@ -439,21 +491,47 @@ var Path = class _Path {
439
491
  if (m.$ === "Comp") {
440
492
  sawComp = true;
441
493
  if (!crossComponent(m.cid, m.vid)) return NO_EVENT_INFO;
442
- nodeIds.push({ nid: m.nid });
494
+ nodeRefs.push({ nid: m.nid, base: pathFromJson(m.base) });
443
495
  } else {
444
- nodeIds.push({ nid: m.nid, si: m.si, sk: m.sk });
496
+ nodeRefs.push({ nid: m.nid, si: m.si, sk: m.sk });
497
+ }
498
+ }
499
+ if (!sawComp && cid !== void 0) {
500
+ if (!crossComponent(+cid, vid)) return NO_EVENT_INFO;
501
+ if (rp !== void 0) {
502
+ const base = parseRenderPath(rp);
503
+ if (base !== null) nodeRefs.push({ base });
445
504
  }
446
505
  }
447
- if (!sawComp && cid !== void 0 && !crossComponent(+cid, vid)) return NO_EVENT_INFO;
448
506
  }
449
507
  depth += 1;
450
508
  node = node.parentNode;
451
509
  }
452
- if (pendingDyns.length > 0)
453
- console.warn("event reconstruction: dynamic-var producer not found", pendingDyns);
454
- return [new _Path(pathSteps.reverse()), handlers];
510
+ parts.reverse();
511
+ let path2 = new _DispatchPath();
512
+ for (const part of parts)
513
+ path2 = part.base !== void 0 ? path2.pushFrame(part.base) : path2.pushItem(part.step);
514
+ return [path2, handlers];
515
+ }
516
+ };
517
+ function keyedPath(path2, key) {
518
+ const steps = path2.steps;
519
+ const last = steps[steps.length - 1];
520
+ if (last instanceof SeqStep || last instanceof FieldStep)
521
+ return new Path(steps.slice(0, -1).concat(new SeqStep(last.field, key)));
522
+ return null;
523
+ }
524
+ function renderPathText(path2) {
525
+ return JSON.stringify(pathToJson(path2));
526
+ }
527
+ function parseRenderPath(text) {
528
+ try {
529
+ return pathFromJson(JSON.parse(text));
530
+ } catch (err) {
531
+ console.warn("bad render path", err, text);
532
+ return null;
455
533
  }
456
- };
534
+ }
457
535
  function metaChain(n) {
458
536
  const out = [];
459
537
  while (n?.nodeType === 8 && n.textContent[0] === "§") {
@@ -506,20 +584,27 @@ var StepCtx = class _StepCtx {
506
584
  return pi;
507
585
  }
508
586
  };
509
- function resolvePathStep(comp, nodeIds, vid) {
510
- for (let i = 0; i < nodeIds.length; i++) {
511
- const ctx = new StepCtx(comp, nodeIds, i, vid);
512
- const step = ctx.resolveNode().toPathStep(ctx);
513
- if (step !== null) return step;
587
+ function resolvePathPart(comp, nodeRefs, vid) {
588
+ for (let i = 0; i < nodeRefs.length; i++) {
589
+ const ref = nodeRefs[i];
590
+ if (ref.base != null) return { base: ref.base };
591
+ if (ref.nid === void 0 || ref.nid === null) continue;
592
+ const ctx = new StepCtx(comp, nodeRefs, i, vid);
593
+ const step = ctx.resolveNode()?.toPathStep(ctx) ?? null;
594
+ if (step !== null) return { step };
514
595
  }
515
596
  return null;
516
597
  }
517
598
  var NO_EVENT_INFO = [null, null];
518
599
  var BUBBLING_EVENTS = /* @__PURE__ */ new Set(["drop"]);
600
+ var path = () => new PathBuilder();
519
601
  var PathBuilder = class {
520
602
  constructor() {
521
603
  this.pathChanges = [];
522
604
  }
605
+ toPath() {
606
+ return new Path(this.pathChanges);
607
+ }
523
608
  add(pathChange) {
524
609
  this.pathChanges.push(pathChange);
525
610
  return this;
@@ -656,31 +741,14 @@ function parseBool(s, px) {
656
741
  const val = parseToken(tokens[0], px);
657
742
  return val !== null && kindOf(val) & G_BOOL ? val : null;
658
743
  }
659
- function parseText(s, px) {
660
- return _parseSingle(s, px, G_TEXT);
661
- }
662
- function parseComponent(s, px) {
663
- return _parseSingle(s, px, G_COMPONENT);
664
- }
665
- function parseSequence(s, px) {
666
- return _parseSingle(s, px, G_SEQUENCE);
667
- }
668
- function parseField(s, px) {
669
- return _parseSingle(s, px, G_FIELD);
670
- }
671
- function parseProvide(s, px) {
672
- return _parseSingle(s, px, G_PROVIDE);
673
- }
674
- function parseMacroAttr(s, px) {
675
- return _parseSingle(s, px, G_ALL);
676
- }
677
- function parseReceiveHandler(s, px) {
678
- return _parseHandler(s, px, "receive", true, true, false);
679
- }
680
- function parseAlterHandler(s, px) {
681
- const r = _parseHandler(s, px, "alter", false, false, true);
682
- return r === null ? null : r.handlerVal;
683
- }
744
+ var parseText = (s, px) => _parseSingle(s, px, G_TEXT);
745
+ var parseComponent = (s, px) => _parseSingle(s, px, G_COMPONENT);
746
+ var parseSequence = (s, px) => _parseSingle(s, px, G_SEQUENCE);
747
+ var parseField = (s, px) => _parseSingle(s, px, G_FIELD);
748
+ var parseProvide = (s, px) => _parseSingle(s, px, G_PROVIDE);
749
+ var parseMacroAttr = (s, px) => _parseSingle(s, px, G_ALL);
750
+ var parseReceiveHandler = (s, px) => _parseHandler(s, px, "receive", true, true, false);
751
+ var parseAlterHandler = (s, px) => _parseHandler(s, px, "alter", false, false, true)?.handlerVal ?? null;
684
752
  function _parseHandler(s, px, namespace, allowArgs, report, allowMethod) {
685
753
  const tokens = tokenizeValue(s.trim());
686
754
  const headTok = tokens[0] ?? "";
@@ -1369,19 +1437,9 @@ function childOpts(vnode, ns, opts) {
1369
1437
  const target = ns === SVG_NS && isForeignObject(vnode.tag) ? null : ns;
1370
1438
  return target === (opts.namespace ?? null) ? opts : { ...opts, namespace: target };
1371
1439
  }
1372
- var NEVER_ASSIGN = /* @__PURE__ */ new Set([
1373
- "width",
1374
- "height",
1375
- "href",
1376
- "list",
1377
- "form",
1378
- "tabIndex",
1379
- "download",
1380
- "rowSpan",
1381
- "colSpan",
1382
- "role",
1383
- "popover"
1384
- ]);
1440
+ var NEVER_ASSIGN = new Set(
1441
+ "width height href list form tabIndex download rowSpan colSpan role popover".split(" ")
1442
+ );
1385
1443
  var PROP_ATTR_NAME = { className: "class", htmlFor: "for" };
1386
1444
  function applyProperties(node, props) {
1387
1445
  const namespaced = isNamespaced(node);
@@ -1660,6 +1718,10 @@ function morphChildren(parentDom, oldChilds, newChilds, opts) {
1660
1718
  if (!used[i] && domNodes[i].parentNode === parentDom) parentDom.removeChild(domNodes[i]);
1661
1719
  }
1662
1720
  function render(vnode, container, options, prev) {
1721
+ if (vnode == null) {
1722
+ container.replaceChildren();
1723
+ return { vnode: null, dom: null };
1724
+ }
1663
1725
  const isFragment = vnode instanceof VFragment;
1664
1726
  if (prev && prev.vnode instanceof VFragment === isFragment) {
1665
1727
  const oldDom = isFragment ? container : prev.dom;
@@ -1699,13 +1761,16 @@ function h(tagName, properties, children, namespace) {
1699
1761
  }
1700
1762
 
1701
1763
  // src/anode.js
1702
- function resolveDynProducer(comp, name) {
1703
- const own = comp?.provide?.[name];
1704
- const producerComp = own != null ? comp : comp?.scope?.lookupProvider(name) ?? null;
1705
- const producerProvide = own ?? producerComp?.provide?.[name];
1706
- if (producerComp == null || producerProvide == null) return null;
1707
- const pi = producerProvide.val?.toPathItem?.() ?? null;
1708
- return { producerCompId: producerComp.id, producerSteps: pi ? [pi] : [] };
1764
+ function renderTarget(val, stack) {
1765
+ if (val instanceof DynVal) {
1766
+ const loc = stack.lookupDynamicLocated(val.name);
1767
+ if (loc?.path == null) return [loc?.value ?? null, stack.renderPath, null];
1768
+ return [loc.value, stack.renderPath.pushFrame(loc.path), loc.path];
1769
+ }
1770
+ const step = val.toPathItem?.() ?? null;
1771
+ if (step === null) return [val.eval(stack), stack.renderPath, null];
1772
+ const path2 = stack.renderPath.pushItem(step);
1773
+ return [val.eval(stack), path2, stack.pendingFrame ? path2.toTransactionPath() : null];
1709
1774
  }
1710
1775
  var BaseNode = class {
1711
1776
  render(_stack, _rx) {
@@ -2027,33 +2092,31 @@ var RenderViewId = class extends ANode {
2027
2092
  setDataAttr(_key, _val) {
2028
2093
  }
2029
2094
  };
2030
- function dynRenderStep(comp, name, key) {
2031
- const p = resolveDynProducer(comp, name);
2032
- if (!p) return null;
2033
- return key === void 0 ? new DynStep(p.producerCompId, p.producerSteps) : new DynEachStep(p.producerCompId, p.producerSteps, key);
2034
- }
2035
2095
  var RenderNode = class extends RenderViewId {
2036
2096
  render(stack, rx) {
2037
- const newStack = stack.enter(this.val.eval(stack), {}, true);
2038
- return rx.renderIt(newStack, this, "", this.evalViewName(stack));
2097
+ const [value, renderPath, base] = renderTarget(this.val, stack);
2098
+ const newStack = stack.enter(value, {}, true, renderPath, false);
2099
+ return rx.renderIt(newStack, this, "", this.evalViewName(stack), base);
2039
2100
  }
2101
+ // A `*name` target contributes no step: the site recorded the absolute base it
2102
+ // resumed at, and event reconstruction turns that into a continuation frame.
2040
2103
  toPathStep(ctx) {
2041
- if (this.val instanceof DynVal) return dynRenderStep(ctx.comp, this.val.name);
2104
+ if (this.val instanceof DynVal) return null;
2042
2105
  return super.toPathStep(ctx);
2043
2106
  }
2044
2107
  };
2045
2108
  var RenderItNode = class extends RenderViewId {
2046
2109
  render(stack, rx) {
2047
- const newStack = stack.enter(stack.it, {}, true);
2048
- return rx.renderIt(newStack, this, "", this.evalViewName(stack));
2110
+ const base = stack.pendingFrame ? stack.renderPath.toTransactionPath() : null;
2111
+ const newStack = stack.enter(stack.it, {}, true, stack.renderPath, false);
2112
+ return rx.renderIt(newStack, this, "", this.evalViewName(stack), base);
2049
2113
  }
2050
2114
  toPathStep(ctx) {
2051
2115
  const next = ctx.next();
2052
2116
  if (next === null) return null;
2053
2117
  const nextNode = next.resolveNode();
2054
2118
  if (nextNode instanceof EachNode && next.hasKey) {
2055
- if (nextNode.val instanceof DynVal)
2056
- return dynRenderStep(ctx.comp, nextNode.val.name, next.key);
2119
+ if (nextNode.val instanceof DynVal) return null;
2057
2120
  return new EachRenderItStep(nextNode.val.name, next.key);
2058
2121
  }
2059
2122
  return null;
@@ -2155,11 +2218,19 @@ var EachNode = class extends WrapperNode {
2155
2218
  this.iterInfo = new IterInfo(val, null, null, null);
2156
2219
  }
2157
2220
  render(stack, rx) {
2158
- return rx.renderEachWhen(stack, this.iterInfo, this.node, this.nodeId);
2221
+ return rx.renderEachWhen(stack, this);
2159
2222
  }
2160
2223
  toPathStep(ctx) {
2161
2224
  return ctx.hasKey ? new EachBindStep(this.iterInfo, ctx.key) : null;
2162
2225
  }
2226
+ // Where one item lives, for the render path. `@each` re-binds `it` to the item
2227
+ // whether or not the body is a component, so the position moves either way and
2228
+ // the step has to address it — `.rows` iterated at `key` IS `rows[key]`.
2229
+ // Null for a dynamic sequence: `*rows` carries its own absolute path and enters
2230
+ // a continuation frame instead (see Renderer.renderEachWhen).
2231
+ itemStep(key) {
2232
+ return this.val instanceof FieldVal ? new SeqStep(this.val.name, key) : null;
2233
+ }
2163
2234
  static register = true;
2164
2235
  };
2165
2236
  var IterInfo = class {
@@ -2420,6 +2491,215 @@ function compileModifiers(eventName, names) {
2420
2491
  };
2421
2492
  }
2422
2493
 
2494
+ // src/stack.js
2495
+ var STOP = /* @__PURE__ */ Symbol("STOP");
2496
+ var NEXT = /* @__PURE__ */ Symbol("NEXT");
2497
+ var DEFAULT_ROUTE = ["dyn", "lex"];
2498
+ function routeLookup(route, lex, dyn) {
2499
+ for (let i = 0; i < route.length; i++) {
2500
+ const leg = route[i];
2501
+ if (leg === "dyn") {
2502
+ const v = dyn();
2503
+ if (v != null) return v;
2504
+ } else if (leg === "lex") {
2505
+ const v = lex();
2506
+ if (v != null) return v;
2507
+ } else {
2508
+ console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
2509
+ }
2510
+ }
2511
+ return null;
2512
+ }
2513
+ var isTypeName = (s) => {
2514
+ const c = s.charCodeAt(0);
2515
+ return c >= 65 && c <= 90;
2516
+ };
2517
+ function lookup(chain, name, dv = null) {
2518
+ let n = chain;
2519
+ while (n !== null) {
2520
+ const r = n[0].lookup(name);
2521
+ if (r === STOP) return dv;
2522
+ if (r !== NEXT) return r;
2523
+ n = n[1];
2524
+ }
2525
+ return dv;
2526
+ }
2527
+ var BindFrame = class {
2528
+ constructor(it, binds, isFrame) {
2529
+ this.it = it;
2530
+ this.binds = binds;
2531
+ this.isFrame = isFrame;
2532
+ }
2533
+ lookup(name) {
2534
+ const v = this.binds[name];
2535
+ return v === void 0 ? this.isFrame ? STOP : NEXT : v;
2536
+ }
2537
+ };
2538
+ var DynFrame = class {
2539
+ constructor(binds, types) {
2540
+ this.binds = binds;
2541
+ this.types = types;
2542
+ }
2543
+ lookup(name) {
2544
+ const v = (isTypeName(name) ? this.types : this.binds)[name];
2545
+ return v === void 0 ? NEXT : v;
2546
+ }
2547
+ };
2548
+ function computeViewsId(views) {
2549
+ let s = "";
2550
+ let n = views;
2551
+ while (n !== null) {
2552
+ s += n[0];
2553
+ n = n[1];
2554
+ }
2555
+ return s === "main" ? "" : s;
2556
+ }
2557
+ var Stack = class _Stack {
2558
+ constructor(fields) {
2559
+ Object.assign(this, fields);
2560
+ }
2561
+ _with(patch) {
2562
+ return new _Stack({ ...this, ...patch });
2563
+ }
2564
+ // Evaluate every provide the entered component publishes and push them as one
2565
+ // dynBinds frame, keyed by NAME. A value is published together with the absolute
2566
+ // path it lives at: the same declaration is read as `*name` AND resumed at by
2567
+ // `<x render="*name">`, so a consumer needs both halves. Types go in the same
2568
+ // frame's other map. No-op with no provides.
2569
+ _pushProvides() {
2570
+ const comp = this.comps.getCompFor(this.it);
2571
+ if (comp == null) return this;
2572
+ const { provide, provideType } = comp;
2573
+ const binds = {};
2574
+ const types = {};
2575
+ let has = false;
2576
+ const base = this._publishBase();
2577
+ for (const k in provide) {
2578
+ const step = provide[k].val.toPathItem?.() ?? null;
2579
+ if (step === null) continue;
2580
+ const value = provide[k].val.eval(this);
2581
+ binds[k] = { value, path: base === null ? null : base.concat([step]) };
2582
+ has = true;
2583
+ }
2584
+ for (const k in provideType) {
2585
+ types[k] = provideType[k];
2586
+ has = true;
2587
+ }
2588
+ if (!has) return this;
2589
+ return this._with({ dynBinds: [new DynFrame(binds, types), this.dynBinds] });
2590
+ }
2591
+ // The absolute address of the component being entered, or null when this
2592
+ // position cannot be written down as one. Frame-only steps carry bindings and
2593
+ // address nothing, so they are compacted away first; what is left is checked
2594
+ // against the value actually being rendered, because a scope CAN move the
2595
+ // render position without contributing an addressing step (a plain `@each`
2596
+ // body re-binds `it` to the item while its rebuild step is an identity).
2597
+ //
2598
+ // A null base still publishes the VALUE — `*name` reads it fine — but there is
2599
+ // nowhere to resume, so `<x render="*name">` renders nothing rather than
2600
+ // editing whatever happens to live at the address we guessed.
2601
+ _publishBase() {
2602
+ const base = this.renderPath.toTransactionPath().compact();
2603
+ return base.lookup(this.root, NOT_FOUND) === this.it ? base : null;
2604
+ }
2605
+ static root(comps, it, ctx = null) {
2606
+ return new _Stack({
2607
+ comps,
2608
+ root: it,
2609
+ it,
2610
+ binds: [new BindFrame(it, {}, true), null],
2611
+ dynBinds: [new DynFrame({}, {}), null],
2612
+ views: ["main", null],
2613
+ viewsId: "",
2614
+ renderPath: new DispatchPath(),
2615
+ pendingFrame: false,
2616
+ ctx
2617
+ })._pushProvides();
2618
+ }
2619
+ // `renderPath` defaults to this stack's own: an ordinary scope does not move.
2620
+ // `pendingFrame` clears on a component frame (which emits the base) and is
2621
+ // inherited by transparent scopes, which have to carry it to the next boundary.
2622
+ enter(it, bindings = {}, isFrame = true, renderPath = this.renderPath, pendingFrame = null) {
2623
+ const stack = this._with({
2624
+ it,
2625
+ binds: [new BindFrame(it, bindings, isFrame), this.binds],
2626
+ renderPath,
2627
+ pendingFrame: pendingFrame ?? (isFrame ? false : this.pendingFrame)
2628
+ });
2629
+ return isFrame ? stack._pushProvides() : stack;
2630
+ }
2631
+ pushViewName(name) {
2632
+ const views = [name, this.views];
2633
+ return this._with({ views, viewsId: computeViewsId(views) });
2634
+ }
2635
+ // Published types are stable per scope and would only churn the render cache, so
2636
+ // the cache key covers values alone.
2637
+ _pushDynBindValuesToArray(arr, comp) {
2638
+ for (const k in comp.provide) arr.push(this.lookupDynamic(k));
2639
+ for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
2640
+ }
2641
+ // `*name`: the nearest binding above (including this component's own provides,
2642
+ // pushed on entering it), else a path registered in the component's lexical
2643
+ // scope, else this component's declared default, else null.
2644
+ //
2645
+ // One chain walk and no producer resolution: a lookup names what it WANTS, so the
2646
+ // frame it wants is keyed by that name. The default belongs to the CONSUMER's
2647
+ // declaration and is evaluated against the consumer's stack, which is why it is
2648
+ // consulted only after the whole chain has missed.
2649
+ lookupDynamicLocated(name) {
2650
+ if (isTypeName(name)) return null;
2651
+ const v = lookup(this.dynBinds, name);
2652
+ if (v != null) return v;
2653
+ const comp = this.comps.getCompFor(this.it);
2654
+ if (comp == null) return null;
2655
+ const path2 = comp.scope?.lookupPath?.(name) ?? null;
2656
+ if (path2 !== null) {
2657
+ const value2 = path2.lookup(this.root, NOT_FOUND);
2658
+ if (value2 !== NOT_FOUND) return { value: value2, path: path2 };
2659
+ }
2660
+ const dval = comp.lookup[name]?.val ?? null;
2661
+ if (dval === null) return null;
2662
+ const step = dval.toPathItem?.() ?? null;
2663
+ const value = dval.eval(this);
2664
+ return step === null ? { value, path: null } : { value, path: this.renderPath.toTransactionPath().concat([step]) };
2665
+ }
2666
+ lookupDynamic(name) {
2667
+ if (isTypeName(name)) return lookup(this.dynBinds, name);
2668
+ return this.lookupDynamicLocated(name)?.value ?? null;
2669
+ }
2670
+ lookupBind(name) {
2671
+ return lookup(this.binds, name);
2672
+ }
2673
+ lookupFieldRaw(name) {
2674
+ return this.it[name] ?? null;
2675
+ }
2676
+ lookupMethod(name) {
2677
+ const fn = this.it[name];
2678
+ return fn instanceof Function ? fn.call(this.it) : null;
2679
+ }
2680
+ // The dispatched DOM event / drag info, read only by EventMemberVal's
2681
+ // `e.<member>` handler args. Null outside a live event transaction.
2682
+ lookupEvent() {
2683
+ return this.ctx?.event ?? null;
2684
+ }
2685
+ lookupDragInfo() {
2686
+ return this.ctx?.dragInfo ?? null;
2687
+ }
2688
+ getHandlerFor(name, key) {
2689
+ return this.comps.getHandlerFor(this.it, name, key);
2690
+ }
2691
+ lookupBestView(views, defaultViewName) {
2692
+ let n = this.views;
2693
+ while (n !== null) {
2694
+ const view = views[n[0]];
2695
+ if (view !== void 0) return view;
2696
+ n = n[1];
2697
+ }
2698
+ return views[defaultViewName];
2699
+ }
2700
+ };
2701
+ var NOT_FOUND = /* @__PURE__ */ Symbol("NOT_FOUND");
2702
+
2423
2703
  // src/components.js
2424
2704
  var COMPONENT = /* @__PURE__ */ Symbol.for("tutuca.component");
2425
2705
  var Components = class {
@@ -2458,12 +2738,14 @@ var ComponentStack = class _ComponentStack {
2458
2738
  this.byName = {};
2459
2739
  this.intentsByName = {};
2460
2740
  this.macros = {};
2741
+ this.paths = {};
2461
2742
  }
2462
2743
  enter() {
2463
2744
  return new _ComponentStack(this.comps, this);
2464
2745
  }
2465
2746
  registerComponents(comps, opts) {
2466
- const { aliases = {} } = opts ?? {};
2747
+ const { aliases = {}, paths } = opts ?? {};
2748
+ if (paths) this.registerPaths(paths);
2467
2749
  for (let i = 0; i < comps.length; i++) {
2468
2750
  const Comp = comps[i];
2469
2751
  Comp[COMPONENT].scope = this.enter();
@@ -2477,6 +2759,27 @@ var ComponentStack = class _ComponentStack {
2477
2759
  else console.warn("alias", alias, "to inexistent component", aliases[alias]);
2478
2760
  }
2479
2761
  }
2762
+ // Register lowercase names as absolute paths from the app state root. A
2763
+ // descendant that declares one in its `lookup` reads and renders `*name` without
2764
+ // anything above it publishing one — which is what makes a session, a theme or a
2765
+ // host-owned value available in its natural registration scope, instead of forcing
2766
+ // an application root whose only job is to `provide` it. Register on a nested
2767
+ // scope to narrow a name; nearest registration wins.
2768
+ //
2769
+ // Uppercase names are ignored: a component TYPE is what `lookupComponent` already
2770
+ // answers, and a type has no path.
2771
+ registerPaths(paths) {
2772
+ for (const name in paths) {
2773
+ if (isTypeName(name)) {
2774
+ console.warn("registerPaths: a type name has no path", name);
2775
+ continue;
2776
+ }
2777
+ this.paths[name] = paths[name].toPath?.() ?? paths[name];
2778
+ }
2779
+ }
2780
+ lookupPath(name) {
2781
+ return this.paths[name] ?? this.parent?.lookupPath(name) ?? null;
2782
+ }
2480
2783
  registerMacros(macros) {
2481
2784
  for (const key in macros) {
2482
2785
  const lower = key.toLowerCase();
@@ -2508,17 +2811,16 @@ var ComponentStack = class _ComponentStack {
2508
2811
  lookupComponent(name) {
2509
2812
  return this.byName[name] ?? this.parent?.lookupComponent(name) ?? null;
2510
2813
  }
2511
- // The component in this scope chain that provides `name`, innermost first. A lookup
2512
- // names a value without naming its producer, so the render-target teleport
2513
- // (resolveDynProducer) recovers the producer here instead. Sound because
2514
- // PROVIDE_NAME_COLLISION keeps a provide name to one producer per chain; with two,
2515
- // this returns the innermost and the linter has already reported the ambiguity.
2516
- lookupProvider(name) {
2814
+ // Whether anything in this scope chain provides `name`. Existence only: a lookup
2815
+ // names what it WANTS and takes whoever is nearest above it at render time, so
2816
+ // there is no producer to identify — several components may publish one name and
2817
+ // the live render ancestry decides. Used by the linter to tell a lookup that can
2818
+ // be satisfied from one that never will be.
2819
+ hasProvider(name) {
2517
2820
  for (const compName in this.byName) {
2518
- const Comp = this.byName[compName];
2519
- if (Comp.provide?.[name] !== void 0) return Comp;
2821
+ if (this.byName[compName].provide?.[name] !== void 0) return true;
2520
2822
  }
2521
- return this.parent?.lookupProvider(name) ?? null;
2823
+ return this.parent?.hasProvider(name) ?? false;
2522
2824
  }
2523
2825
  lookupMacro(name) {
2524
2826
  return this.macros[name] ?? this.parent?.lookupMacro(name) ?? null;
@@ -2535,10 +2837,6 @@ var LookupInfo = class {
2535
2837
  }
2536
2838
  };
2537
2839
  var isString = (v) => typeof v === "string";
2538
- var isTypeName = (s) => {
2539
- const c = s.charCodeAt(0);
2540
- return c >= 65 && c <= 90;
2541
- };
2542
2840
  var _rawSpecKeys = "name view style commonStyle globalStyle receive intent alter views provide lookup fields methods statics";
2543
2841
  var KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
2544
2842
  var _compId = 0;
@@ -2611,160 +2909,6 @@ var Component = class {
2611
2909
  }
2612
2910
  };
2613
2911
 
2614
- // src/stack.js
2615
- var STOP = /* @__PURE__ */ Symbol("STOP");
2616
- var NEXT = /* @__PURE__ */ Symbol("NEXT");
2617
- var DEFAULT_ROUTE = ["dyn", "lex"];
2618
- function routeLookup(route, lex, dyn) {
2619
- for (let i = 0; i < route.length; i++) {
2620
- const leg = route[i];
2621
- if (leg === "dyn") {
2622
- const v = dyn();
2623
- if (v != null) return v;
2624
- } else if (leg === "lex") {
2625
- const v = lex();
2626
- if (v != null) return v;
2627
- } else {
2628
- console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
2629
- }
2630
- }
2631
- return null;
2632
- }
2633
- function lookup(chain, name, dv = null) {
2634
- let n = chain;
2635
- while (n !== null) {
2636
- const r = n[0].lookup(name);
2637
- if (r === STOP) return dv;
2638
- if (r !== NEXT) return r;
2639
- n = n[1];
2640
- }
2641
- return dv;
2642
- }
2643
- var BindFrame = class {
2644
- constructor(it, binds, isFrame) {
2645
- this.it = it;
2646
- this.binds = binds;
2647
- this.isFrame = isFrame;
2648
- }
2649
- lookup(name) {
2650
- const v = this.binds[name];
2651
- return v === void 0 ? this.isFrame ? STOP : NEXT : v;
2652
- }
2653
- };
2654
- var ObjectFrame = class {
2655
- constructor(binds) {
2656
- this.binds = binds;
2657
- }
2658
- lookup(key) {
2659
- const v = this.binds[key];
2660
- return v === void 0 ? NEXT : v;
2661
- }
2662
- };
2663
- function computeViewsId(views) {
2664
- let s = "";
2665
- let n = views;
2666
- while (n !== null) {
2667
- s += n[0];
2668
- n = n[1];
2669
- }
2670
- return s === "main" ? "" : s;
2671
- }
2672
- var Stack = class _Stack {
2673
- constructor(comps, it, binds, dynBinds, views, viewsId, ctx = null) {
2674
- this.comps = comps;
2675
- this.it = it;
2676
- this.binds = binds;
2677
- this.dynBinds = dynBinds;
2678
- this.views = views;
2679
- this.viewsId = viewsId;
2680
- this.ctx = ctx;
2681
- }
2682
- // Evaluate every provide the entered component publishes and push them as one
2683
- // dynBinds frame, keyed by NAME. Published types go in the same frame: a type name
2684
- // starts A-Z and a value name does not, so the two namespaces cannot collide and
2685
- // nearest-ancestor-wins falls out of frame order for both. No-op with no provides.
2686
- _pushProvides() {
2687
- const comp = this.comps.getCompFor(this.it);
2688
- if (comp == null) return this;
2689
- const { provide, provideType } = comp;
2690
- const dynObj = {};
2691
- let has = false;
2692
- for (const k in provide) {
2693
- dynObj[k] = provide[k].val.eval(this);
2694
- has = true;
2695
- }
2696
- for (const k in provideType) {
2697
- dynObj[k] = provideType[k];
2698
- has = true;
2699
- }
2700
- if (!has) return this;
2701
- const newDynBinds = [new ObjectFrame(dynObj), this.dynBinds];
2702
- const { comps, it, binds, views, viewsId, ctx } = this;
2703
- return new _Stack(comps, it, binds, newDynBinds, views, viewsId, ctx);
2704
- }
2705
- static root(comps, it, ctx) {
2706
- const binds = [new BindFrame(it, {}, true), null];
2707
- const dynBinds = [new ObjectFrame({}), null];
2708
- const views = ["main", null];
2709
- return new _Stack(comps, it, binds, dynBinds, views, "", ctx)._pushProvides();
2710
- }
2711
- enter(it, bindings = {}, isFrame = true) {
2712
- const { comps, binds, dynBinds, views, viewsId, ctx } = this;
2713
- const newBinds = [new BindFrame(it, bindings, isFrame), binds];
2714
- const stack = new _Stack(comps, it, newBinds, dynBinds, views, viewsId, ctx);
2715
- return isFrame ? stack._pushProvides() : stack;
2716
- }
2717
- pushViewName(name) {
2718
- const { comps, it, binds, dynBinds, views, ctx } = this;
2719
- const newViews = [name, views];
2720
- return new _Stack(comps, it, binds, dynBinds, newViews, computeViewsId(newViews), ctx);
2721
- }
2722
- // Published types are stable per scope and would only churn the render cache, so
2723
- // the cache key covers values alone.
2724
- _pushDynBindValuesToArray(arr, comp) {
2725
- for (const k in comp.provide) arr.push(this.lookupDynamic(k));
2726
- for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
2727
- }
2728
- // `*name`: the nearest binding above (including this component's own provides,
2729
- // pushed on entering it), else this component's declared default, else null.
2730
- lookupDynamic(name) {
2731
- const v = lookup(this.dynBinds, name);
2732
- if (v != null) return v;
2733
- const comp = this.comps.getCompFor(this.it);
2734
- return comp?.lookup[name]?.val?.eval(this) ?? null;
2735
- }
2736
- lookupBind(name) {
2737
- return lookup(this.binds, name);
2738
- }
2739
- lookupFieldRaw(name) {
2740
- return this.it[name] ?? null;
2741
- }
2742
- lookupMethod(name) {
2743
- const fn = this.it[name];
2744
- return fn instanceof Function ? fn.call(this.it) : null;
2745
- }
2746
- // The dispatched DOM event / drag info, read only by EventMemberVal's
2747
- // `e.<member>` handler args. Null outside a live event transaction.
2748
- lookupEvent() {
2749
- return this.ctx?.event ?? null;
2750
- }
2751
- lookupDragInfo() {
2752
- return this.ctx?.dragInfo ?? null;
2753
- }
2754
- getHandlerFor(name, key) {
2755
- return this.comps.getHandlerFor(this.it, name, key);
2756
- }
2757
- lookupBestView(views, defaultViewName) {
2758
- let n = this.views;
2759
- while (n !== null) {
2760
- const view = views[n[0]];
2761
- if (view !== void 0) return view;
2762
- n = n[1];
2763
- }
2764
- return views[defaultViewName];
2765
- }
2766
- };
2767
-
2768
2912
  // src/oo.js
2769
2913
  var BAD_VALUE = /* @__PURE__ */ Symbol("BadValue");
2770
2914
  var nullCoercer = (v) => v;
@@ -2797,9 +2941,22 @@ var CHECK_TYPE_LIST = Array.isArray;
2797
2941
  var CHECK_TYPE_OBJECT = isPlainObject;
2798
2942
  var CHECK_TYPE_MAP = (v) => v instanceof Map;
2799
2943
  var CHECK_TYPE_SET = (v) => v instanceof Set;
2944
+ var COERCE_NONE = (_v) => null;
2945
+ var COERCE_BOOL = (v) => !!v;
2946
+ var COERCE_STRING = (v) => v?.toString?.() ?? "";
2947
+ var COERCE_INT = (v) => Number.isFinite(v) ? Math.trunc(v) : null;
2948
+ var COERCE_LIST = (v) => Array.isArray(v) ? [...v] : null;
2949
+ var COERCE_OBJECT = (v) => isPlainObject(v) ? { ...v } : null;
2950
+ var COERCE_MAP = (v) => {
2951
+ if (v instanceof Map) return new Map(v);
2952
+ if (Array.isArray(v) || isPlainObject(v))
2953
+ return new Map(Array.isArray(v) ? v : Object.entries(v));
2954
+ return null;
2955
+ };
2956
+ var COERCE_SET = (v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null;
2800
2957
  var FieldBool = class extends Field {
2801
2958
  constructor(name, defaultValue = false) {
2802
- super("bool", name, CHECK_TYPE_BOOL, (v) => !!v, defaultValue);
2959
+ super("bool", name, CHECK_TYPE_BOOL, COERCE_BOOL, defaultValue);
2803
2960
  }
2804
2961
  };
2805
2962
  var FieldAny = class extends Field {
@@ -2809,23 +2966,17 @@ var FieldAny = class extends Field {
2809
2966
  };
2810
2967
  var FieldString = class extends Field {
2811
2968
  constructor(name, defaultValue = "") {
2812
- super("text", name, CHECK_TYPE_STRING, (v) => v?.toString?.() ?? "", defaultValue);
2969
+ super("text", name, CHECK_TYPE_STRING, COERCE_STRING, defaultValue);
2813
2970
  }
2814
2971
  };
2815
2972
  var FieldInt = class extends Field {
2816
2973
  constructor(name, defaultValue = 0) {
2817
- super(
2818
- "int",
2819
- name,
2820
- CHECK_TYPE_INT,
2821
- (v) => Number.isFinite(v) ? Math.trunc(v) : null,
2822
- defaultValue
2823
- );
2974
+ super("int", name, CHECK_TYPE_INT, COERCE_INT, defaultValue);
2824
2975
  }
2825
2976
  };
2826
2977
  var FieldFloat = class extends Field {
2827
2978
  constructor(name, defaultValue = 0) {
2828
- super("float", name, CHECK_TYPE_FLOAT, (_) => null, defaultValue);
2979
+ super("float", name, CHECK_TYPE_FLOAT, COERCE_NONE, defaultValue);
2829
2980
  }
2830
2981
  };
2831
2982
  var metaOf = (v) => v?.constructor?.[COMPONENT] ?? v?.constructor?.getMetaClass?.();
@@ -2838,45 +2989,22 @@ var FieldComp = class extends Field {
2838
2989
  };
2839
2990
  var FieldList = class extends Field {
2840
2991
  constructor(name, defaultValue = []) {
2841
- super("list", name, CHECK_TYPE_LIST, (v) => Array.isArray(v) ? [...v] : null, defaultValue);
2992
+ super("list", name, CHECK_TYPE_LIST, COERCE_LIST, defaultValue);
2842
2993
  }
2843
2994
  };
2844
2995
  var FieldObject = class extends Field {
2845
2996
  constructor(name, defaultValue = {}) {
2846
- super(
2847
- "object",
2848
- name,
2849
- CHECK_TYPE_OBJECT,
2850
- (v) => isPlainObject(v) ? { ...v } : null,
2851
- defaultValue
2852
- );
2997
+ super("object", name, CHECK_TYPE_OBJECT, COERCE_OBJECT, defaultValue);
2853
2998
  }
2854
2999
  };
2855
3000
  var FieldMap = class extends Field {
2856
3001
  constructor(name, defaultValue = /* @__PURE__ */ new Map()) {
2857
- super(
2858
- "map",
2859
- name,
2860
- CHECK_TYPE_MAP,
2861
- (v) => {
2862
- if (v instanceof Map) return new Map(v);
2863
- if (Array.isArray(v) || isPlainObject(v))
2864
- return new Map(Array.isArray(v) ? v : Object.entries(v));
2865
- return null;
2866
- },
2867
- defaultValue
2868
- );
3002
+ super("map", name, CHECK_TYPE_MAP, COERCE_MAP, defaultValue);
2869
3003
  }
2870
3004
  };
2871
3005
  var FieldSet = class extends Field {
2872
3006
  constructor(name, defaultValue = /* @__PURE__ */ new Set()) {
2873
- super(
2874
- "set",
2875
- name,
2876
- CHECK_TYPE_SET,
2877
- (v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null,
2878
- defaultValue
2879
- );
3007
+ super("set", name, CHECK_TYPE_SET, COERCE_SET, defaultValue);
2880
3008
  }
2881
3009
  };
2882
3010
  function mkCompField(field, scope, args) {
@@ -2974,8 +3102,7 @@ function classFromData(name, { fields = {}, methods, statics }) {
2974
3102
  const value = fields[field];
2975
3103
  const type = typeof value;
2976
3104
  if (type === "string") b.addField(field, value, FieldString);
2977
- else if (type === "number")
2978
- b.addField(field, value, Number.isInteger(value) ? FieldInt : FieldFloat);
3105
+ else if (type === "number") b.addField(field, value, FieldFloat);
2979
3106
  else if (type === "boolean") b.addField(field, value, FieldBool);
2980
3107
  else if (Array.isArray(value)) b.addField(field, [...value], FieldList);
2981
3108
  else if (value instanceof Set) b.addField(field, new Set(value), FieldSet);
@@ -3127,9 +3254,9 @@ var Transactor = class {
3127
3254
  // `.a[.selId]` render target reconstructed from a DOM event) against the root the
3128
3255
  // handler read, so pathKeys carries concrete keys instead of dropping the dynamic
3129
3256
  // step. No-op when nobody is observing.
3130
- _emitRecord(root, { kind, name, args, path, targetPath, handler, handlerName, matched, before, after, parent }) {
3257
+ _emitRecord(root, { kind, name, args, path: path2, targetPath, handler, handlerName, matched, before, after, parent }) {
3131
3258
  if (this._observers.length === 0) return;
3132
- const pinned = path.pinKeys(root);
3259
+ const pinned = path2.pinKeys(root);
3133
3260
  this._emit({
3134
3261
  kind,
3135
3262
  name,
@@ -3180,18 +3307,20 @@ var Transactor = class {
3180
3307
  // intent's answerPath is: a reply must reach the sender that asked even if a key
3181
3308
  // moved while the message was in flight. Null when nobody is waiting — a host
3182
3309
  // sendAtRoot or a view's own `@on.*` — and ctx.sendReply refuses on that.
3183
- pushSend(path, name, args = [], parent = null, origin = null) {
3184
- const t = new SendEvent(path, this, name, args, parent);
3185
- t.origin = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
3310
+ pushSend(path2, name, args = [], parent = null, origin = null, txnPath = null) {
3311
+ const t = new SendEvent(path2, this, name, args, parent);
3312
+ t.txnPath = txnPath;
3313
+ t.origin = origin;
3314
+ t.originPinned = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
3186
3315
  this.pushTransaction(t);
3187
3316
  return this._link(t, parent);
3188
3317
  }
3189
3318
  // Raise an intent: a job the sender did not address, walked along a route until
3190
3319
  // something answers. `opts.route` is a list of legs (see DEFAULT_ROUTE) and
3191
3320
  // `opts.livePath` opts out of pinning the answer's destination keys.
3192
- pushIntent(path, name, args = [], opts = {}, parent = null) {
3321
+ pushIntent(path2, name, args = [], opts = {}, parent = null) {
3193
3322
  const release = parent ? parent.completion.track() : null;
3194
- const walk = new IntentWalk(this, path, name, args, opts, parent, release);
3323
+ const walk = new IntentWalk(this, path2, name, args, opts, parent, release);
3195
3324
  walk.advance();
3196
3325
  return walk;
3197
3326
  }
@@ -3225,16 +3354,17 @@ var Transactor = class {
3225
3354
  transaction._completion?.releaseSelf();
3226
3355
  }
3227
3356
  }
3228
- transactInputNow(path, event, eventHandler, dragInfo) {
3229
- this.transact(new InputEvent(path, event, eventHandler, this, dragInfo));
3357
+ transactInputNow(path2, event, eventHandler, dragInfo) {
3358
+ this.transact(new InputEvent(path2, event, eventHandler, this, dragInfo));
3230
3359
  }
3231
3360
  };
3232
3361
  function nullHandler() {
3233
3362
  return this;
3234
3363
  }
3235
3364
  var Transaction = class {
3236
- constructor(path, transactor, parentTransaction = null) {
3237
- this.path = path;
3365
+ constructor(path2, transactor, parentTransaction = null) {
3366
+ this.path = path2;
3367
+ this.txnPath = null;
3238
3368
  this.transactor = transactor;
3239
3369
  this.parentTransaction = parentTransaction;
3240
3370
  this._completion = null;
@@ -3283,9 +3413,11 @@ var Transaction = class {
3283
3413
  getHandlerAndArgs(_root, _instance, _comps) {
3284
3414
  return null;
3285
3415
  }
3286
- // The path used to apply the mutation. Teleports dynamic-var renders so it lands on
3287
- // the data's real location (the dispatch `this.path` keeps intermediates).
3416
+ // The path used to apply the mutation: the ACTIVE frame of the dispatch path, so
3417
+ // an event inside a `<x render="*name">` subtree updates the value where it
3418
+ // really lives (the dispatch `this.path` keeps the visual callers, for bubbling).
3288
3419
  getTransactionPath() {
3420
+ if (this.txnPath !== null) return this.txnPath;
3289
3421
  return this.path.toTransactionPath().compact();
3290
3422
  }
3291
3423
  run(curRoot, comps) {
@@ -3303,15 +3435,16 @@ var Transaction = class {
3303
3435
  }
3304
3436
  };
3305
3437
  var InputEvent = class extends Transaction {
3306
- constructor(path, e, handler, transactor, dragInfo) {
3307
- super(path, transactor);
3438
+ constructor(path2, e, handler, transactor, dragInfo) {
3439
+ super(path2, transactor);
3308
3440
  this.e = e;
3309
3441
  this.handler = handler;
3310
3442
  this.dragInfo = dragInfo;
3311
3443
  this._dispatchPath = null;
3312
3444
  }
3313
- // Frame steps removed, DynStep + one step per crossed component kept: bubbling
3314
- // it visits every component (including intermediates of a dynamic-var render).
3445
+ // Frame-only steps removed, one step per crossed component kept — inside every
3446
+ // continuation frame independently, so bubbling it visits every component and
3447
+ // then returns to the caller that wrote the `*name`.
3315
3448
  get dispatchPath() {
3316
3449
  this._dispatchPath ??= this.path.compact();
3317
3450
  return this._dispatchPath;
@@ -3346,11 +3479,11 @@ var InputEvent = class extends Transaction {
3346
3479
  this.transactor.pushIntent(this.dispatchPath, name, args, rest, this);
3347
3480
  }
3348
3481
  getHandlerAndArgs(root, _instance, comps) {
3349
- const stack = this.path.toTransactionPath().buildStack(Stack.root(comps, root, this));
3482
+ const stack = this.path.buildStack(Stack.root(comps, root, this));
3350
3483
  const [handler, args] = this.handler.getHandlerAndArgs(stack, this);
3351
3484
  this._handlerArgs = [...args];
3352
- const path = this.dispatchPath;
3353
- args.push(new EventContext(path, this.transactor, this));
3485
+ const path2 = this.dispatchPath;
3486
+ args.push(new EventContext(path2, this.transactor, this));
3354
3487
  return [handler, args];
3355
3488
  }
3356
3489
  // The dispatched DOM event, read only through `e.<member>` handler args
@@ -3361,11 +3494,11 @@ var InputEvent = class extends Transaction {
3361
3494
  }
3362
3495
  };
3363
3496
  var NameArgsTransaction = class extends Transaction {
3364
- constructor(path, transactor, name, args, parentTransaction) {
3365
- super(path, transactor, parentTransaction);
3497
+ constructor(path2, transactor, name, args, parentTransaction) {
3498
+ super(path2, transactor, parentTransaction);
3366
3499
  this.name = name;
3367
3500
  this.args = args;
3368
- this.targetPath = path;
3501
+ this.targetPath = path2;
3369
3502
  }
3370
3503
  handlerProp = null;
3371
3504
  // NameArgs verbs map their handler bucket straight to the observed kind:
@@ -3422,8 +3555,8 @@ var SendEvent = class extends NameArgsTransaction {
3422
3555
  };
3423
3556
  var IntentEvent = class extends NameArgsTransaction {
3424
3557
  handlerProp = "intent";
3425
- constructor(path, transactor, walk) {
3426
- super(path, transactor, walk.name, walk.args, walk.parent);
3558
+ constructor(path2, transactor, walk) {
3559
+ super(path2, transactor, walk.name, walk.args, walk.parent);
3427
3560
  this.walk = walk;
3428
3561
  this.targetPath = walk.origin;
3429
3562
  }
@@ -3450,17 +3583,17 @@ var PASS = /* @__PURE__ */ Symbol.for("tutuca.intent.pass");
3450
3583
  var REFUSAL_RING_CAP = 200;
3451
3584
  var INTENT_DEPTH = 64;
3452
3585
  var IntentWalk = class {
3453
- constructor(transactor, path, name, args, opts, parent, release) {
3586
+ constructor(transactor, path2, name, args, opts, parent, release) {
3454
3587
  this.transactor = transactor;
3455
3588
  this.name = name;
3456
3589
  this.args = args;
3457
3590
  this.route = opts?.route ?? DEFAULT_ROUTE;
3458
3591
  this.parent = parent;
3459
3592
  this.release = release;
3460
- this.origin = path;
3461
- this.answerPath = opts?.livePath ? null : path.toTransactionPath().pinKeys(transactor.state.val);
3593
+ this.origin = path2;
3594
+ this.answerPath = opts?.livePath ? null : path2.toTransactionPath().pinKeys(transactor.state.val);
3462
3595
  this.legIndex = 0;
3463
- this.dynAt = path;
3596
+ this.dynAt = path2;
3464
3597
  this.hops = 0;
3465
3598
  this.ended = false;
3466
3599
  }
@@ -3472,7 +3605,7 @@ var IntentWalk = class {
3472
3605
  while (this.legIndex < this.route.length) {
3473
3606
  const leg = this.route[this.legIndex];
3474
3607
  if (leg === "dyn") {
3475
- if (this.dynAt.steps.length === 0) {
3608
+ if (!this.dynAt.canPop()) {
3476
3609
  this.legIndex++;
3477
3610
  continue;
3478
3611
  }
@@ -3569,8 +3702,8 @@ var IntentWalk = class {
3569
3702
  if (this.ended) return;
3570
3703
  this.ended = true;
3571
3704
  if (name === null) return this.release?.();
3572
- const path = this.answerPath ?? this.origin;
3573
- const t = new SendEvent(path, this.transactor, name, args, this.parent);
3705
+ const t = new SendEvent(this.origin, this.transactor, name, args, this.parent);
3706
+ t.txnPath = this.answerPath;
3574
3707
  t._isAnswer = true;
3575
3708
  this.transactor.pushTransaction(t);
3576
3709
  if (this.release) t.completion.whenSubtreeSettled().then(this.release);
@@ -3636,8 +3769,8 @@ var Completion = class {
3636
3769
  }
3637
3770
  };
3638
3771
  var Dispatcher = class {
3639
- constructor(path, transactor, parentTransaction, root = transactor.state.val) {
3640
- this.path = path;
3772
+ constructor(path2, transactor, parentTransaction, root = transactor.state.val) {
3773
+ this.path = path2;
3641
3774
  this.transactor = transactor;
3642
3775
  this.parent = parentTransaction;
3643
3776
  this.root = root;
@@ -3665,7 +3798,9 @@ var Dispatcher = class {
3665
3798
  // replayed; provides are pushed on every component frame either way, so a provide
3666
3799
  // that reads a loop binding is the one case this cannot reproduce faithfully.
3667
3800
  _stack() {
3668
- this._stackMemo ??= this.path.toTransactionPath().buildStack(Stack.root(this.transactor.comps, this.root, this.parent));
3801
+ this._stackMemo ??= this.path.buildStack(
3802
+ Stack.root(this.transactor.comps, this.root, this.parent)
3803
+ );
3669
3804
  return this._stackMemo;
3670
3805
  }
3671
3806
  // Resolve a name the way the renderer would. `opts.route` takes the same legs in
@@ -3679,14 +3814,19 @@ var Dispatcher = class {
3679
3814
  );
3680
3815
  }
3681
3816
  // The `lex` leg without a Stack: the scope of the component whose handler is
3682
- // running, which is the leaf of this ctx's path.
3817
+ // running, which is the leaf of this ctx's path. A type name resolves to the
3818
+ // component registered under it; a value name to a path registered under it
3819
+ // (see ComponentStack.registerPaths), read against the current root.
3683
3820
  _lookupLex(name) {
3684
3821
  let Comp = null;
3685
3822
  this.walkPath((c) => {
3686
3823
  Comp = c;
3687
3824
  return false;
3688
3825
  });
3689
- return Comp?.scope?.lookupComponent(name) ?? null;
3826
+ const scope = Comp?.scope;
3827
+ if (scope == null) return null;
3828
+ if (isTypeName(name)) return scope.lookupComponent(name) ?? null;
3829
+ return scope.lookupPath(name)?.lookup(this.root) ?? null;
3690
3830
  }
3691
3831
  // A component lookup is a value lookup constrained to a component. The `lex` leg
3692
3832
  // can only ever answer with one; the `dyn` leg reads a binding an ancestor
@@ -3708,8 +3848,8 @@ var Dispatcher = class {
3708
3848
  send(name, args) {
3709
3849
  return this.sendAtPath(this.path, name, args);
3710
3850
  }
3711
- sendAtPath(path, name, args) {
3712
- return this.transactor.pushSend(path, name, args, this.parent, this.path);
3851
+ sendAtPath(path2, name, args) {
3852
+ return this.transactor.pushSend(path2, name, args, this.parent, this.path);
3713
3853
  }
3714
3854
  // An intent is ROUTED: it names a job and walks until something answers. The verb
3715
3855
  // does not decide which scope answers — `opts.route` does, written here at the call
@@ -3717,8 +3857,8 @@ var Dispatcher = class {
3717
3857
  intent(name, args, opts) {
3718
3858
  return this.intentAtPath(this.path, name, args, opts);
3719
3859
  }
3720
- intentAtPath(path, name, args, opts) {
3721
- return this.transactor.pushIntent(path, name, args, opts, this.parent);
3860
+ intentAtPath(path2, name, args, opts) {
3861
+ return this.transactor.pushIntent(path2, name, args, opts, this.parent);
3722
3862
  }
3723
3863
  };
3724
3864
  var EventContext = class extends Dispatcher {
@@ -3747,7 +3887,14 @@ var EventContext = class extends Dispatcher {
3747
3887
  this.transactor.refuse("NO_SENDER", { name });
3748
3888
  return null;
3749
3889
  }
3750
- return this.sendAtPath(origin, name, args);
3890
+ return this.transactor.pushSend(
3891
+ origin,
3892
+ name,
3893
+ args,
3894
+ this.parent,
3895
+ this.path,
3896
+ this.parent.originPinned
3897
+ );
3751
3898
  }
3752
3899
  // End the walk answering nothing — "served, and no answer".
3753
3900
  stop() {
@@ -3779,7 +3926,7 @@ var PathChanges = class extends PathBuilder {
3779
3926
  }
3780
3927
  };
3781
3928
  function rootDispatcher(transactor) {
3782
- return new Dispatcher(new Path([]), transactor, null);
3929
+ return new Dispatcher(new DispatchPath(), transactor, null);
3783
3930
  }
3784
3931
 
3785
3932
  // src/app.js
@@ -3820,7 +3967,7 @@ var App = class {
3820
3967
  const { type } = e;
3821
3968
  const isDrag = type === "dragover" || type === "dragstart" || type === "dragend" || type === "drop";
3822
3969
  const { rootNode: root, maxEventNodeDepth: maxDepth, comps, transactor } = this;
3823
- const [path, handlers] = Path.fromNodeAndEventName(
3970
+ const [path2, handlers] = DispatchPath.fromNodeAndEventName(
3824
3971
  e.target,
3825
3972
  type,
3826
3973
  root,
@@ -3828,9 +3975,9 @@ var App = class {
3828
3975
  comps,
3829
3976
  !isDrag
3830
3977
  );
3831
- if (isDrag) this._handleDragEvent(e, type, path);
3832
- if (path !== null && handlers !== null)
3833
- for (const handler of handlers) transactor.transactInputNow(path, e, handler, this.dragInfo);
3978
+ if (isDrag) this._handleDragEvent(e, type, path2);
3979
+ if (path2 !== null && handlers !== null)
3980
+ for (const handler of handlers) transactor.transactInputNow(path2, e, handler, this.dragInfo);
3834
3981
  }
3835
3982
  _handleTouchEvent(e) {
3836
3983
  const { type } = e;
@@ -3873,7 +4020,7 @@ var App = class {
3873
4020
  this._touch = null;
3874
4021
  }
3875
4022
  }
3876
- _handleDragEvent(e, type, path) {
4023
+ _handleDragEvent(e, type, path2) {
3877
4024
  if (type === "dragover") {
3878
4025
  const dropTarget = getClosestDropTarget(e.target, this.rootNode, Infinity);
3879
4026
  if (dropTarget !== null) {
@@ -3885,10 +4032,10 @@ var App = class {
3885
4032
  } else if (type === "dragstart") {
3886
4033
  e.target.dataset.dragging = 1;
3887
4034
  const rootValue = this.state.val;
3888
- const txnPath = path.compact().toTransactionPath();
4035
+ const txnPath = path2.compact().toTransactionPath();
3889
4036
  const value = txnPath.lookup(rootValue);
3890
4037
  const dragType = e.target.dataset.dragtype ?? "?";
3891
- const stack = path.toTransactionPath().buildStack(this.makeStack(rootValue));
4038
+ const stack = path2.buildStack(this.makeStack(rootValue));
3892
4039
  this.dragInfo = new DragInfo(stack, value, dragType, e.target);
3893
4040
  } else if (type === "drop") {
3894
4041
  e.preventDefault();
@@ -3963,7 +4110,7 @@ var App = class {
3963
4110
  this.rootNode.removeEventListener(name, this, listenerOpts(name));
3964
4111
  }
3965
4112
  sendAtRoot(name, args) {
3966
- this.transactor.pushSend(new Path([]), name, args);
4113
+ this.transactor.pushSend(new DispatchPath(), name, args);
3967
4114
  }
3968
4115
  registerComponents(comps, opts) {
3969
4116
  const scope = this.compStack.enter();
@@ -4181,7 +4328,20 @@ var WeakMapDomCache = class {
4181
4328
  };
4182
4329
 
4183
4330
  // src/renderer.js
4184
- var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk"];
4331
+ var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk", "rp"];
4332
+ function stampRenderBase(vdom, text) {
4333
+ if (vdom instanceof VNode)
4334
+ return new VNode(
4335
+ vdom.tag,
4336
+ { ...vdom.attrs, "data-rp": text },
4337
+ vdom.childs,
4338
+ vdom.key,
4339
+ vdom.namespace
4340
+ );
4341
+ if (vdom instanceof VFragment)
4342
+ return new VFragment(vdom.childs.map((c) => stampRenderBase(c, text)));
4343
+ return vdom;
4344
+ }
4185
4345
  var Renderer = class {
4186
4346
  constructor(comps) {
4187
4347
  this.comps = comps;
@@ -4218,17 +4378,17 @@ var Renderer = class {
4218
4378
  if (comp === null) return null;
4219
4379
  return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
4220
4380
  }
4221
- renderIt(stack, node, key, viewName) {
4381
+ renderIt(stack, node, key, viewName, base = null) {
4222
4382
  const comp = this.comps.getCompFor(stack.it);
4223
- return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
4383
+ return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName, base) : null;
4224
4384
  }
4225
4385
  // `node` is the parse node of the render site (`<x render>` / `render-it` /
4226
4386
  // `render-each`, or the view's root anode for the app root). It keys the
4227
4387
  // cache as a globally-unique object: node ids alone are unique only within a
4228
4388
  // single view, so the same value rendered by two components (e.g. through a
4229
4389
  // shared dynamic-var sequence) would otherwise collide in the cache.
4230
- _rValComp(stack, val, comp, node, key, viewName) {
4231
- const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}`;
4390
+ _rValComp(stack, val, comp, node, key, viewName, base = null) {
4391
+ const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}${stack.renderPath.addressKey}`;
4232
4392
  const cachePath = [node, val];
4233
4393
  stack._pushDynBindValuesToArray(cachePath, comp);
4234
4394
  const cachedNode = this.cache.get(cachePath, cacheKey);
@@ -4236,30 +4396,43 @@ var Renderer = class {
4236
4396
  const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
4237
4397
  const body = this.renderView(view, stack);
4238
4398
  if (body == null) return null;
4399
+ const baseJson = base === null ? null : pathToJson(base);
4239
4400
  const meta = this._renderMetadata({
4240
4401
  $: "Comp",
4241
4402
  nid: node?.nodeId ?? null,
4242
4403
  cid: comp.id,
4243
- vid: view.name
4404
+ vid: view.name,
4405
+ ...baseJson === null ? null : { base: baseJson }
4244
4406
  });
4245
- const dom = new VFragment([meta, body]);
4407
+ const dom = new VFragment([
4408
+ meta,
4409
+ baseJson === null ? body : stampRenderBase(body, JSON.stringify(baseJson))
4410
+ ]);
4246
4411
  this.cache.set(cachePath, cacheKey, dom);
4247
4412
  return dom;
4248
4413
  }
4249
4414
  pushEachEntry(r, nid, attrName, key, dom) {
4250
4415
  r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
4251
4416
  }
4252
- renderEachWhen(stack, iterInfo, view, nid) {
4417
+ renderEachWhen(stack, each) {
4418
+ const { iterInfo, node: view, nodeId: nid } = each;
4253
4419
  const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
4420
+ const seqPath = iterInfo.val instanceof DynVal ? stack.lookupDynamicLocated(iterInfo.val.name)?.path ?? null : null;
4254
4421
  const r = [];
4255
4422
  const it = stack.it;
4256
4423
  const renderOne = (key, value, attrName, binds) => {
4424
+ const itemBase = seqPath === null ? null : keyedPath(seqPath, key);
4425
+ const itemStep = itemBase === null ? each.itemStep(key) : null;
4426
+ const itemPath = itemBase !== null ? stack.renderPath.pushFrame(itemBase) : itemStep !== null ? stack.renderPath.pushItem(itemStep) : stack.renderPath;
4257
4427
  const cachePath = enricher ? [view, it, value] : [view, value];
4258
- const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
4428
+ const cacheKey = `${stack.viewsId ?? ""}${nid}${key}${itemPath.addressKey}`;
4259
4429
  const cachedNode = this.cache.get(cachePath, cacheKey);
4260
4430
  if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
4261
4431
  else {
4262
- const dom = this.renderView(view, stack.enter(value, binds, false));
4432
+ const dom = this.renderView(
4433
+ view,
4434
+ stack.enter(value, binds, false, itemPath, itemBase !== null)
4435
+ );
4263
4436
  if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
4264
4437
  this.cache.set(cachePath, cacheKey, dom);
4265
4438
  }
@@ -4371,6 +4544,7 @@ export {
4371
4544
  html,
4372
4545
  injectCss,
4373
4546
  macro,
4547
+ path,
4374
4548
  phaseOps,
4375
4549
  resolveArgs,
4376
4550
  rootDispatcher,