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.
@@ -1039,8 +1039,68 @@ var init_immer2 = __esm({
1039
1039
  });
1040
1040
 
1041
1041
  // src/path.js
1042
- function warnRawDynStep(op, step) {
1043
- console.warn(`Path.${op} reached a DynStep: call toTransactionPath() first`, step);
1042
+ function stepToJson(step) {
1043
+ if (step instanceof SeqStep) return { f: step.field, k: step.key };
1044
+ if (step instanceof FieldStep) return { f: step.field };
1045
+ if (step instanceof SeqAccessStep) return { f: step.seqField, a: step.keyField };
1046
+ return null;
1047
+ }
1048
+ function stepFromJson(j) {
1049
+ if (j == null || typeof j.f !== "string") return null;
1050
+ if (j.k !== void 0) return new SeqStep(j.f, j.k);
1051
+ if (j.a !== void 0) return new SeqAccessStep(j.f, j.a);
1052
+ return new FieldStep(j.f);
1053
+ }
1054
+ function pathToJson(path) {
1055
+ const out = [];
1056
+ for (const step of path.steps) {
1057
+ const j = stepToJson(step);
1058
+ if (j === null) return null;
1059
+ out.push(j);
1060
+ }
1061
+ return out;
1062
+ }
1063
+ function pathFromJson(j) {
1064
+ if (!Array.isArray(j)) return null;
1065
+ const steps = [];
1066
+ for (const item of j) {
1067
+ const step = stepFromJson(item);
1068
+ if (step === null) return null;
1069
+ steps.push(step);
1070
+ }
1071
+ return new Path(steps);
1072
+ }
1073
+ function walkItems(stack, items, renderPath, path) {
1074
+ let prev = stack.it;
1075
+ for (const step of items) {
1076
+ const next = step.lookup(prev, NONE);
1077
+ if (next === NONE) {
1078
+ console.warn("bad PathItem", { root: stack.it, step, path });
1079
+ return null;
1080
+ }
1081
+ renderPath = renderPath.pushItem(step);
1082
+ stack = step.enterFrame(stack, next, renderPath);
1083
+ prev = next;
1084
+ }
1085
+ return [stack, renderPath];
1086
+ }
1087
+ function keyedPath(path, key) {
1088
+ const steps = path.steps;
1089
+ const last = steps[steps.length - 1];
1090
+ if (last instanceof SeqStep || last instanceof FieldStep)
1091
+ return new Path(steps.slice(0, -1).concat(new SeqStep(last.field, key)));
1092
+ return null;
1093
+ }
1094
+ function renderPathText(path) {
1095
+ return JSON.stringify(pathToJson(path));
1096
+ }
1097
+ function parseRenderPath(text) {
1098
+ try {
1099
+ return pathFromJson(JSON.parse(text));
1100
+ } catch (err) {
1101
+ console.warn("bad render path", err, text);
1102
+ return null;
1103
+ }
1044
1104
  }
1045
1105
  function metaChain(n) {
1046
1106
  const out = [];
@@ -1061,15 +1121,18 @@ function findHandlers(comp, eventIds, vid, eventName) {
1061
1121
  }
1062
1122
  return null;
1063
1123
  }
1064
- function resolvePathStep(comp, nodeIds, vid) {
1065
- for (let i = 0; i < nodeIds.length; i++) {
1066
- const ctx = new StepCtx(comp, nodeIds, i, vid);
1067
- const step = ctx.resolveNode().toPathStep(ctx);
1068
- if (step !== null) return step;
1124
+ function resolvePathPart(comp, nodeRefs, vid) {
1125
+ for (let i = 0; i < nodeRefs.length; i++) {
1126
+ const ref = nodeRefs[i];
1127
+ if (ref.base != null) return { base: ref.base };
1128
+ if (ref.nid === void 0 || ref.nid === null) continue;
1129
+ const ctx = new StepCtx(comp, nodeRefs, i, vid);
1130
+ const step = ctx.resolveNode()?.toPathStep(ctx) ?? null;
1131
+ if (step !== null) return { step };
1069
1132
  }
1070
1133
  return null;
1071
1134
  }
1072
- var NONE, readKey, writeKey, writeSeqKey, Step, BindStep, ScopeBindStep, FieldStep, SeqStep, SeqAccessStep, EachBindStep, EachRenderItStep, DynStep, DynEachStep, Path, StepCtx, NO_EVENT_INFO, BUBBLING_EVENTS, PathBuilder;
1135
+ var NONE, readKey, writeKey, writeSeqKey, Step, BindStep, ScopeBindStep, FieldStep, SeqStep, SeqAccessStep, EachBindStep, EachRenderItStep, Path, EMPTY_PATH, DispatchPath, StepCtx, NO_EVENT_INFO, BUBBLING_EVENTS, PathBuilder;
1073
1136
  var init_path = __esm({
1074
1137
  "src/path.js"() {
1075
1138
  init_collection();
@@ -1100,8 +1163,11 @@ var init_path = __esm({
1100
1163
  }
1101
1164
  setDraftValue(_root, _v) {
1102
1165
  }
1103
- enterFrame(stack, next) {
1104
- return stack.enter(next, {}, true);
1166
+ // Re-enter this step while rebuilding a stack. `renderPath` is the dispatch
1167
+ // position AFTER this step: a rebuilt frame must carry the same render path
1168
+ // the renderer had there, or the provides it publishes would be located wrong.
1169
+ enterFrame(stack, next, renderPath) {
1170
+ return stack.enter(next, {}, true, renderPath);
1105
1171
  }
1106
1172
  toAbstractPathStep() {
1107
1173
  return this;
@@ -1126,8 +1192,8 @@ var init_path = __esm({
1126
1192
  lookup(v, _dval) {
1127
1193
  return v;
1128
1194
  }
1129
- enterFrame(stack, next) {
1130
- return stack.enter(next, { ...this.binds }, false);
1195
+ enterFrame(stack, next, renderPath) {
1196
+ return stack.enter(next, { ...this.binds }, false, renderPath);
1131
1197
  }
1132
1198
  withIndex(i) {
1133
1199
  return new _BindStep({ ...this.binds, key: i });
@@ -1144,9 +1210,9 @@ var init_path = __esm({
1144
1210
  super(binds);
1145
1211
  this.val = val;
1146
1212
  }
1147
- enterFrame(stack, next) {
1213
+ enterFrame(stack, next, renderPath) {
1148
1214
  const dyn = this.val.evalAsHandler(stack)?.call(stack.it) ?? {};
1149
- return stack.enter(next, { ...this.binds, ...dyn }, false);
1215
+ return stack.enter(next, { ...this.binds, ...dyn }, false, renderPath);
1150
1216
  }
1151
1217
  withIndex(i) {
1152
1218
  return new _ScopeBindStep(this.val, { ...this.binds, key: i });
@@ -1189,8 +1255,8 @@ var init_path = __esm({
1189
1255
  const seq = readKey(root, this.field, null);
1190
1256
  if (seq != null) writeSeqKey(seq, this.key, v);
1191
1257
  }
1192
- enterFrame(stack, next) {
1193
- return stack.enter(next, { key: this.key }, true);
1258
+ enterFrame(stack, next, renderPath) {
1259
+ return stack.enter(next, { key: this.key }, true, renderPath);
1194
1260
  }
1195
1261
  toKey() {
1196
1262
  return { field: this.field, key: this.key };
@@ -1236,57 +1302,21 @@ var init_path = __esm({
1236
1302
  }
1237
1303
  // Replay the renderer's per-item binds (key, value + any @enrich-with binds)
1238
1304
  // so a rebuilt stack matches the one @each rendered with.
1239
- enterFrame(stack, next) {
1240
- return stack.enter(next, this.iterInfo.enrichBinds(stack, this.key), false);
1305
+ enterFrame(stack, next, renderPath) {
1306
+ return stack.enter(next, this.iterInfo.enrichBinds(stack, this.key), false, renderPath);
1241
1307
  }
1242
1308
  toAbstractPathStep() {
1243
1309
  return null;
1244
1310
  }
1245
1311
  };
1246
1312
  EachRenderItStep = class extends SeqStep {
1247
- enterFrame(stack, next) {
1248
- return stack.enter(next, { key: this.key, value: next }, false).enter(next, {}, true);
1313
+ enterFrame(stack, next, renderPath) {
1314
+ return stack.enter(next, { key: this.key, value: next }, false, renderPath).enter(next, {}, true, renderPath);
1249
1315
  }
1250
1316
  toAbstractPathStep() {
1251
1317
  return new SeqStep(this.field, this.key);
1252
1318
  }
1253
1319
  };
1254
- DynStep = class extends Step {
1255
- constructor(producerCompId, producerSteps) {
1256
- super();
1257
- this.producerCompId = producerCompId;
1258
- this.producerSteps = producerSteps;
1259
- this.interiorCids = /* @__PURE__ */ new Set();
1260
- }
1261
- // Steps spliced into the transaction path in place of this marker.
1262
- teleportSteps() {
1263
- return this.producerSteps;
1264
- }
1265
- lookup(_v, dval = null) {
1266
- warnRawDynStep("lookup", this);
1267
- return dval;
1268
- }
1269
- enterFrame(stack, _next) {
1270
- warnRawDynStep("enterFrame", this);
1271
- return stack;
1272
- }
1273
- };
1274
- DynEachStep = class extends DynStep {
1275
- constructor(producerCompId, producerSteps, key) {
1276
- super(producerCompId, producerSteps);
1277
- this.key = key;
1278
- }
1279
- teleportSteps() {
1280
- const { producerSteps, key } = this;
1281
- if (producerSteps.length === 0) return producerSteps;
1282
- const last = producerSteps[producerSteps.length - 1];
1283
- if (!(last instanceof FieldStep)) {
1284
- console.warn("DynEachStep: seq-access dynamic cannot be iterated", this);
1285
- return producerSteps;
1286
- }
1287
- return producerSteps.slice(0, -1).concat(new SeqStep(last.field, key));
1288
- }
1289
- };
1290
1320
  Path = class _Path {
1291
1321
  constructor(steps = []) {
1292
1322
  this.steps = steps;
@@ -1297,40 +1327,13 @@ var init_path = __esm({
1297
1327
  popStep() {
1298
1328
  return new _Path(this.steps.slice(0, -1));
1299
1329
  }
1300
- // The dispatch path: frame-only steps removed, one step per crossed component
1301
- // (DynStep included). `popStep` over it bubbles through every component.
1330
+ // Frame-only steps removed, one step per crossed component: `popStep` over the
1331
+ // result bubbles through every component.
1302
1332
  compact() {
1303
1333
  const out = [];
1304
1334
  for (const step of this.steps) {
1305
1335
  const s = step.toAbstractPathStep();
1306
- if (s !== null) {
1307
- if (s !== step) s._originCid = step._originCid;
1308
- out.push(s);
1309
- }
1310
- }
1311
- return new _Path(out);
1312
- }
1313
- // The abstract path used to apply a transaction: every DynStep is teleported —
1314
- // the steps interior to its producer..consumer span are dropped and the
1315
- // producer's own path spliced in — so a mutation lands on the data's real
1316
- // location. A path with no DynStep is returned unchanged.
1317
- toTransactionPath() {
1318
- let hasDyn = false;
1319
- for (const step of this.steps)
1320
- if (step instanceof DynStep) {
1321
- hasDyn = true;
1322
- break;
1323
- }
1324
- if (!hasDyn) return this;
1325
- const out = [];
1326
- for (const step of this.steps) {
1327
- if (step instanceof DynStep) {
1328
- while (out.length > 0 && step.interiorCids.has(out[out.length - 1]._originCid)) out.pop();
1329
- for (const ts of step.teleportSteps()) {
1330
- ts._originCid = step.producerCompId;
1331
- out.push(ts);
1332
- }
1333
- } else out.push(step);
1336
+ if (s !== null) out.push(s);
1334
1337
  }
1335
1338
  return new _Path(out);
1336
1339
  }
@@ -1338,7 +1341,6 @@ var init_path = __esm({
1338
1341
  // key as it is *now* so a later lookup/setValue lands on the same item even if the
1339
1342
  // keyField changed meanwhile (e.g. the selected tab moved while an intent was in
1340
1343
  // flight). Returns a new Path with those steps replaced; `this` if nothing pinned.
1341
- // Must be called on a transaction path (no DynSteps — call toTransactionPath first).
1342
1344
  pinKeys(root) {
1343
1345
  let curVal = root;
1344
1346
  let out = null;
@@ -1361,8 +1363,8 @@ var init_path = __esm({
1361
1363
  }
1362
1364
  // The values entered along the path, root→leaf (root included): index 0 is `root`,
1363
1365
  // the last entry is the leaf this path resolves to. Stops early at the first
1364
- // unresolvable step. Call on a transaction path (no DynSteps). Used to walk the
1365
- // component instances on a dispatch path (filter via Components.getCompFor).
1366
+ // unresolvable step. Used to walk the component instances on a dispatch path
1367
+ // (filter via Components.getCompFor).
1366
1368
  resolveChain(root) {
1367
1369
  const out = [root];
1368
1370
  let curVal = root;
@@ -1376,7 +1378,7 @@ var init_path = __esm({
1376
1378
  // A flat `[{ field, key? }]` list of the addressing steps, skipping frame-only
1377
1379
  // steps (binds). Generic path introspection so tooling (e.g. the storybook
1378
1380
  // activity log) can identify which subtree a transaction touched without
1379
- // depending on Step internals. Call on a transaction path (no DynSteps).
1381
+ // depending on Step internals.
1380
1382
  toKeys() {
1381
1383
  const out = [];
1382
1384
  for (const step of this.steps) {
@@ -1397,62 +1399,133 @@ var init_path = __esm({
1397
1399
  });
1398
1400
  }
1399
1401
  buildStack(stack) {
1400
- let prev = stack.it;
1401
- for (const step of this.steps) {
1402
- const next = step.lookup(prev, NONE);
1403
- if (next === NONE) {
1404
- console.warn("bad PathItem", { root: stack.it, step, path: this });
1405
- return null;
1402
+ return walkItems(stack, this.steps, stack.renderPath ?? new DispatchPath(), this)?.[0] ?? null;
1403
+ }
1404
+ };
1405
+ EMPTY_PATH = new Path([]);
1406
+ DispatchPath = class _DispatchPath {
1407
+ constructor(frames = [{ base: EMPTY_PATH, items: [] }]) {
1408
+ this.frames = frames;
1409
+ }
1410
+ // Plain addressing steps in one frame based at the root: what a caller means
1411
+ // by "this position" when it has no continuation of its own.
1412
+ static ofSteps(steps) {
1413
+ return new _DispatchPath([{ base: EMPTY_PATH, items: steps.slice() }]);
1414
+ }
1415
+ get top() {
1416
+ return this.frames[this.frames.length - 1];
1417
+ }
1418
+ _withTopItems(items) {
1419
+ const frames = this.frames.slice();
1420
+ frames[frames.length - 1] = { base: this.top.base, items };
1421
+ return new _DispatchPath(frames);
1422
+ }
1423
+ concat(steps) {
1424
+ if (this.frames.length === 0) return _DispatchPath.ofSteps(steps);
1425
+ return this._withTopItems(this.top.items.concat(steps));
1426
+ }
1427
+ pushItem(step) {
1428
+ return this.concat([step]);
1429
+ }
1430
+ pushFrame(base) {
1431
+ return new _DispatchPath(this.frames.concat({ base, items: [] }));
1432
+ }
1433
+ // Whether bubbling has anywhere left to go: another step in this frame, or a
1434
+ // visual caller underneath it.
1435
+ canPop() {
1436
+ const n = this.frames.length;
1437
+ return n > 1 || n === 1 && this.frames[0].items.length > 0;
1438
+ }
1439
+ isRoot() {
1440
+ return this.toTransactionPath().steps.length === 0;
1441
+ }
1442
+ // One component closer to the root. At the top of a frame that is popping back
1443
+ // to the visual caller, not to the producer's own parent — the caller is where
1444
+ // the `*name` was written, and where an unhandled message should keep going.
1445
+ popStep() {
1446
+ const n = this.frames.length;
1447
+ if (n === 0) return this;
1448
+ const top = this.frames[n - 1];
1449
+ if (top.items.length > 0) return this._withTopItems(top.items.slice(0, -1));
1450
+ if (n > 1) return new _DispatchPath(this.frames.slice(0, -1));
1451
+ return this;
1452
+ }
1453
+ // Drop frame-only steps inside every frame independently; a frame's base is
1454
+ // already addressing-only.
1455
+ compact() {
1456
+ return new _DispatchPath(
1457
+ this.frames.map(({ base, items }) => ({ base, items: new Path(items).compact().steps }))
1458
+ );
1459
+ }
1460
+ // A stable string for the ADDRESS this path denotes, for the render cache. The
1461
+ // same immutable value can sit at two places in the tree, and a subtree rendered
1462
+ // at one of them bakes that address in — the provides it publishes are located
1463
+ // there. Frame-only steps address nothing, so they are compacted out: two sites
1464
+ // that differ only in binds do render the same subtree.
1465
+ get addressKey() {
1466
+ this._addressKey ??= renderPathText(this.toTransactionPath().compact());
1467
+ return this._addressKey;
1468
+ }
1469
+ // The active transaction address: the top frame's absolute base followed by
1470
+ // its ordinary descendant steps. This is where a mutation lands.
1471
+ toTransactionPath() {
1472
+ const top = this.top;
1473
+ return top === void 0 ? EMPTY_PATH : top.base.concat(top.items);
1474
+ }
1475
+ // Rebuild the render stack this path was dispatched from. A frame with a base
1476
+ // re-enters at that absolute value first — replaying the resume a `*name`
1477
+ // render performed — and then walks its ordinary items.
1478
+ buildStack(stack) {
1479
+ let renderPath = new _DispatchPath();
1480
+ for (let i = 0; i < this.frames.length; i++) {
1481
+ const { base, items } = this.frames[i];
1482
+ if (i > 0 || base.steps.length > 0) {
1483
+ const baseValue = base.lookup(stack.root, NONE);
1484
+ if (baseValue === NONE) {
1485
+ console.warn("bad frame base", { base, path: this });
1486
+ return null;
1487
+ }
1488
+ renderPath = renderPath.pushFrame(base);
1489
+ stack = stack.enter(baseValue, {}, true, renderPath);
1406
1490
  }
1407
- stack = step.enterFrame(stack, next);
1408
- prev = next;
1491
+ const walked = walkItems(stack, items, renderPath, this);
1492
+ if (walked === null) return null;
1493
+ [stack, renderPath] = walked;
1409
1494
  }
1410
1495
  return stack;
1411
1496
  }
1412
1497
  static fromNodeAndEventName(node, eventName, rootNode, maxDepth, comps, stopOnNoEvent = true) {
1413
- const pathSteps = [];
1414
- const pendingDyns = [];
1498
+ const parts = [];
1415
1499
  const bubbles = BUBBLING_EVENTS.has(eventName);
1416
1500
  let depth = 0;
1417
1501
  let eventIds = [];
1418
1502
  let handlers = null;
1419
- let nodeIds = [];
1503
+ let nodeRefs = [];
1420
1504
  let isLeafComponent = true;
1421
1505
  const crossComponent = (cidNum, vid) => {
1422
1506
  const comp = comps.getComponentForId(cidNum);
1423
- let pushStep = true;
1507
+ let pushPart = true;
1424
1508
  if (handlers === null && (isLeafComponent || bubbles)) {
1425
1509
  handlers = findHandlers(comp, eventIds, vid, eventName);
1426
1510
  if (handlers === null) {
1427
1511
  if (isLeafComponent && stopOnNoEvent && !bubbles) return false;
1428
1512
  } else if (!isLeafComponent) {
1429
- pathSteps.length = 0;
1430
- pendingDyns.length = 0;
1431
- pushStep = false;
1513
+ parts.length = 0;
1514
+ pushPart = false;
1432
1515
  }
1433
1516
  }
1434
1517
  isLeafComponent = false;
1435
- for (const dyn of pendingDyns) dyn.interiorCids.add(cidNum);
1436
- if (pushStep) {
1437
- const step = resolvePathStep(comp, nodeIds, vid);
1438
- if (step) {
1439
- step._originCid = cidNum;
1440
- pathSteps.push(step);
1441
- if (step instanceof DynStep) {
1442
- step.interiorCids.add(cidNum);
1443
- pendingDyns.push(step);
1444
- }
1445
- }
1518
+ if (pushPart) {
1519
+ const part = resolvePathPart(comp, nodeRefs, vid);
1520
+ if (part) parts.push(part);
1446
1521
  }
1447
- for (let i = pendingDyns.length - 1; i >= 0; i--)
1448
- if (pendingDyns[i].producerCompId === cidNum) pendingDyns.splice(i, 1);
1449
1522
  eventIds = [];
1450
- nodeIds = [];
1523
+ nodeRefs = [];
1451
1524
  return true;
1452
1525
  };
1453
1526
  while (node && node !== rootNode && depth < maxDepth) {
1454
1527
  if (node?.dataset) {
1455
- const { eid, cid, vid } = node.dataset;
1528
+ const { eid, cid, vid, rp } = node.dataset;
1456
1529
  if (eid !== void 0) eventIds.push(eid);
1457
1530
  const metas = metaChain(node.previousSibling);
1458
1531
  let sawComp = false;
@@ -1460,19 +1533,27 @@ var init_path = __esm({
1460
1533
  if (m.$ === "Comp") {
1461
1534
  sawComp = true;
1462
1535
  if (!crossComponent(m.cid, m.vid)) return NO_EVENT_INFO;
1463
- nodeIds.push({ nid: m.nid });
1536
+ nodeRefs.push({ nid: m.nid, base: pathFromJson(m.base) });
1464
1537
  } else {
1465
- nodeIds.push({ nid: m.nid, si: m.si, sk: m.sk });
1538
+ nodeRefs.push({ nid: m.nid, si: m.si, sk: m.sk });
1539
+ }
1540
+ }
1541
+ if (!sawComp && cid !== void 0) {
1542
+ if (!crossComponent(+cid, vid)) return NO_EVENT_INFO;
1543
+ if (rp !== void 0) {
1544
+ const base = parseRenderPath(rp);
1545
+ if (base !== null) nodeRefs.push({ base });
1466
1546
  }
1467
1547
  }
1468
- if (!sawComp && cid !== void 0 && !crossComponent(+cid, vid)) return NO_EVENT_INFO;
1469
1548
  }
1470
1549
  depth += 1;
1471
1550
  node = node.parentNode;
1472
1551
  }
1473
- if (pendingDyns.length > 0)
1474
- console.warn("event reconstruction: dynamic-var producer not found", pendingDyns);
1475
- return [new _Path(pathSteps.reverse()), handlers];
1552
+ parts.reverse();
1553
+ let path = new _DispatchPath();
1554
+ for (const part of parts)
1555
+ path = part.base !== void 0 ? path.pushFrame(part.base) : path.pushItem(part.step);
1556
+ return [path, handlers];
1476
1557
  }
1477
1558
  };
1478
1559
  StepCtx = class _StepCtx {
@@ -1514,6 +1595,9 @@ var init_path = __esm({
1514
1595
  constructor() {
1515
1596
  this.pathChanges = [];
1516
1597
  }
1598
+ toPath() {
1599
+ return new Path(this.pathChanges);
1600
+ }
1517
1601
  add(pathChange) {
1518
1602
  this.pathChanges.push(pathChange);
1519
1603
  return this;
@@ -1619,31 +1703,6 @@ function parseBool(s, px) {
1619
1703
  const val = parseToken(tokens[0], px);
1620
1704
  return val !== null && kindOf(val) & G_BOOL ? val : null;
1621
1705
  }
1622
- function parseText(s, px) {
1623
- return _parseSingle(s, px, G_TEXT);
1624
- }
1625
- function parseComponent(s, px) {
1626
- return _parseSingle(s, px, G_COMPONENT);
1627
- }
1628
- function parseSequence(s, px) {
1629
- return _parseSingle(s, px, G_SEQUENCE);
1630
- }
1631
- function parseField(s, px) {
1632
- return _parseSingle(s, px, G_FIELD);
1633
- }
1634
- function parseProvide(s, px) {
1635
- return _parseSingle(s, px, G_PROVIDE);
1636
- }
1637
- function parseMacroAttr(s, px) {
1638
- return _parseSingle(s, px, G_ALL);
1639
- }
1640
- function parseReceiveHandler(s, px) {
1641
- return _parseHandler(s, px, "receive", true, true, false);
1642
- }
1643
- function parseAlterHandler(s, px) {
1644
- const r = _parseHandler(s, px, "alter", false, false, true);
1645
- return r === null ? null : r.handlerVal;
1646
- }
1647
1706
  function _parseHandler(s, px, namespace, allowArgs, report, allowMethod) {
1648
1707
  const tokens = tokenizeValue(s.trim());
1649
1708
  const headTok = tokens[0] ?? "";
@@ -1713,7 +1772,7 @@ function kindOf(val) {
1713
1772
  if (val instanceof EventMemberVal) return K_EVENT;
1714
1773
  return 0;
1715
1774
  }
1716
- var VALID_VAL_ID_RE, isValidValId, VALID_FLOAT_RE, STR_TPL_SPLIT_RE, mkVal, VAL_TOKEN_RE, tokenizeValue, unescapeStr, K_CONST, K_STRTPL, K_FIELD, K_BIND, K_DYN, K_NAME, K_SEQ, K_METHOD, K_EVENT, G_BOOL, G_TEXT, G_COMPONENT, G_SEQUENCE, G_PROVIDE, G_FIELD, G_VALUE, G_HANDLER_ARG, G_ALL, toNullIfNaN, predTruthy, PREDICATES, BaseVal, ConstVal, NULL_CONST_VAL, PredicateVal, VarVal, StrTplVal, NameVal, HandlerNameVal, mk404Handler, keyIs, macCtrl, nullSafe, EVENT_CONVENIENCES, EventMemberVal, RenderVal, RenderNameVal, BindVal, BindMemberVal, DynVal, FieldVal, MethodVal, SeqAccessVal;
1775
+ var VALID_VAL_ID_RE, isValidValId, VALID_FLOAT_RE, STR_TPL_SPLIT_RE, mkVal, VAL_TOKEN_RE, tokenizeValue, unescapeStr, K_CONST, K_STRTPL, K_FIELD, K_BIND, K_DYN, K_NAME, K_SEQ, K_METHOD, K_EVENT, G_BOOL, G_TEXT, G_COMPONENT, G_SEQUENCE, G_PROVIDE, G_FIELD, G_VALUE, G_HANDLER_ARG, G_ALL, toNullIfNaN, predTruthy, PREDICATES, parseText, parseComponent, parseSequence, parseField, parseProvide, parseMacroAttr, parseReceiveHandler, parseAlterHandler, BaseVal, ConstVal, NULL_CONST_VAL, PredicateVal, VarVal, StrTplVal, NameVal, HandlerNameVal, mk404Handler, keyIs, macCtrl, nullSafe, EVENT_CONVENIENCES, EventMemberVal, RenderVal, RenderNameVal, BindVal, BindMemberVal, DynVal, FieldVal, MethodVal, SeqAccessVal;
1717
1776
  var init_value = __esm({
1718
1777
  "src/value.js"() {
1719
1778
  init_collection();
@@ -1757,6 +1816,14 @@ var init_value = __esm({
1757
1816
  "null?": { name: "null?", arity: 1, fn: (v) => v == null },
1758
1817
  "equals?": { name: "equals?", arity: 2, fn: (a, b) => Object.is(a, b) }
1759
1818
  };
1819
+ parseText = (s, px) => _parseSingle(s, px, G_TEXT);
1820
+ parseComponent = (s, px) => _parseSingle(s, px, G_COMPONENT);
1821
+ parseSequence = (s, px) => _parseSingle(s, px, G_SEQUENCE);
1822
+ parseField = (s, px) => _parseSingle(s, px, G_FIELD);
1823
+ parseProvide = (s, px) => _parseSingle(s, px, G_PROVIDE);
1824
+ parseMacroAttr = (s, px) => _parseSingle(s, px, G_ALL);
1825
+ parseReceiveHandler = (s, px) => _parseHandler(s, px, "receive", true, true, false);
1826
+ parseAlterHandler = (s, px) => _parseHandler(s, px, "alter", false, false, true)?.handlerVal ?? null;
1760
1827
  BaseVal = class {
1761
1828
  render(_stack, _rx) {
1762
1829
  }
@@ -2558,6 +2625,10 @@ function morphChildren(parentDom, oldChilds, newChilds, opts) {
2558
2625
  if (!used2[i] && domNodes[i].parentNode === parentDom) parentDom.removeChild(domNodes[i]);
2559
2626
  }
2560
2627
  function render(vnode, container, options, prev) {
2628
+ if (vnode == null) {
2629
+ container.replaceChildren();
2630
+ return { vnode: null, dom: null };
2631
+ }
2561
2632
  const isFragment = vnode instanceof VFragment;
2562
2633
  if (prev && prev.vnode instanceof VFragment === isFragment) {
2563
2634
  const oldDom = isFragment ? container : prev.dom;
@@ -2607,19 +2678,9 @@ var init_vdom = __esm({
2607
2678
  };
2608
2679
  isForeignObject = (tag) => tag.length === 13 && tag.toLowerCase() === "foreignobject";
2609
2680
  effectiveNs = (vnode, opts) => vnode.namespace ?? opts.namespace ?? null;
2610
- NEVER_ASSIGN = /* @__PURE__ */ new Set([
2611
- "width",
2612
- "height",
2613
- "href",
2614
- "list",
2615
- "form",
2616
- "tabIndex",
2617
- "download",
2618
- "rowSpan",
2619
- "colSpan",
2620
- "role",
2621
- "popover"
2622
- ]);
2681
+ NEVER_ASSIGN = new Set(
2682
+ "width height href list form tabIndex download rowSpan colSpan role popover".split(" ")
2683
+ );
2623
2684
  PROP_ATTR_NAME = { className: "class", htmlFor: "for" };
2624
2685
  VBase = class {
2625
2686
  };
@@ -2725,13 +2786,16 @@ var init_vdom = __esm({
2725
2786
  });
2726
2787
 
2727
2788
  // src/anode.js
2728
- function resolveDynProducer(comp, name) {
2729
- const own = comp?.provide?.[name];
2730
- const producerComp = own != null ? comp : comp?.scope?.lookupProvider(name) ?? null;
2731
- const producerProvide = own ?? producerComp?.provide?.[name];
2732
- if (producerComp == null || producerProvide == null) return null;
2733
- const pi = producerProvide.val?.toPathItem?.() ?? null;
2734
- return { producerCompId: producerComp.id, producerSteps: pi ? [pi] : [] };
2789
+ function renderTarget(val, stack) {
2790
+ if (val instanceof DynVal) {
2791
+ const loc = stack.lookupDynamicLocated(val.name);
2792
+ if (loc?.path == null) return [loc?.value ?? null, stack.renderPath, null];
2793
+ return [loc.value, stack.renderPath.pushFrame(loc.path), loc.path];
2794
+ }
2795
+ const step = val.toPathItem?.() ?? null;
2796
+ if (step === null) return [val.eval(stack), stack.renderPath, null];
2797
+ const path = stack.renderPath.pushItem(step);
2798
+ return [val.eval(stack), path, stack.pendingFrame ? path.toTransactionPath() : null];
2735
2799
  }
2736
2800
  function optimizeChilds(childs) {
2737
2801
  for (let i = 0; i < childs.length; i++) {
@@ -2839,11 +2903,6 @@ function makeWrapperNode(data, px) {
2839
2903
  }
2840
2904
  return node;
2841
2905
  }
2842
- function dynRenderStep(comp, name, key) {
2843
- const p = resolveDynProducer(comp, name);
2844
- if (!p) return null;
2845
- return key === void 0 ? new DynStep(p.producerCompId, p.producerSteps) : new DynEachStep(p.producerCompId, p.producerSteps, key);
2846
- }
2847
2906
  function parseRenderEach(px, value, as, attrs) {
2848
2907
  const seqVal = parseXOpVal("render-each", value, px, parseSequence);
2849
2908
  if (seqVal === null) return null;
@@ -3113,26 +3172,29 @@ var init_anode = __esm({
3113
3172
  };
3114
3173
  RenderNode = class extends RenderViewId {
3115
3174
  render(stack, rx) {
3116
- const newStack = stack.enter(this.val.eval(stack), {}, true);
3117
- return rx.renderIt(newStack, this, "", this.evalViewName(stack));
3175
+ const [value, renderPath, base] = renderTarget(this.val, stack);
3176
+ const newStack = stack.enter(value, {}, true, renderPath, false);
3177
+ return rx.renderIt(newStack, this, "", this.evalViewName(stack), base);
3118
3178
  }
3179
+ // A `*name` target contributes no step: the site recorded the absolute base it
3180
+ // resumed at, and event reconstruction turns that into a continuation frame.
3119
3181
  toPathStep(ctx) {
3120
- if (this.val instanceof DynVal) return dynRenderStep(ctx.comp, this.val.name);
3182
+ if (this.val instanceof DynVal) return null;
3121
3183
  return super.toPathStep(ctx);
3122
3184
  }
3123
3185
  };
3124
3186
  RenderItNode = class extends RenderViewId {
3125
3187
  render(stack, rx) {
3126
- const newStack = stack.enter(stack.it, {}, true);
3127
- return rx.renderIt(newStack, this, "", this.evalViewName(stack));
3188
+ const base = stack.pendingFrame ? stack.renderPath.toTransactionPath() : null;
3189
+ const newStack = stack.enter(stack.it, {}, true, stack.renderPath, false);
3190
+ return rx.renderIt(newStack, this, "", this.evalViewName(stack), base);
3128
3191
  }
3129
3192
  toPathStep(ctx) {
3130
3193
  const next = ctx.next();
3131
3194
  if (next === null) return null;
3132
3195
  const nextNode = next.resolveNode();
3133
3196
  if (nextNode instanceof EachNode && next.hasKey) {
3134
- if (nextNode.val instanceof DynVal)
3135
- return dynRenderStep(ctx.comp, nextNode.val.name, next.key);
3197
+ if (nextNode.val instanceof DynVal) return null;
3136
3198
  return new EachRenderItStep(nextNode.val.name, next.key);
3137
3199
  }
3138
3200
  return null;
@@ -3222,11 +3284,19 @@ var init_anode = __esm({
3222
3284
  this.iterInfo = new IterInfo(val, null, null, null);
3223
3285
  }
3224
3286
  render(stack, rx) {
3225
- return rx.renderEachWhen(stack, this.iterInfo, this.node, this.nodeId);
3287
+ return rx.renderEachWhen(stack, this);
3226
3288
  }
3227
3289
  toPathStep(ctx) {
3228
3290
  return ctx.hasKey ? new EachBindStep(this.iterInfo, ctx.key) : null;
3229
3291
  }
3292
+ // Where one item lives, for the render path. `@each` re-binds `it` to the item
3293
+ // whether or not the body is a component, so the position moves either way and
3294
+ // the step has to address it — `.rows` iterated at `key` IS `rows[key]`.
3295
+ // Null for a dynamic sequence: `*rows` carries its own absolute path and enters
3296
+ // a continuation frame instead (see Renderer.renderEachWhen).
3297
+ itemStep(key) {
3298
+ return this.val instanceof FieldVal ? new SeqStep(this.val.name, key) : null;
3299
+ }
3230
3300
  static register = true;
3231
3301
  };
3232
3302
  IterInfo = class {
@@ -3447,11 +3517,227 @@ var init_anode = __esm({
3447
3517
  }
3448
3518
  });
3449
3519
 
3520
+ // src/stack.js
3521
+ function routeLookup(route, lex, dyn) {
3522
+ for (let i = 0; i < route.length; i++) {
3523
+ const leg = route[i];
3524
+ if (leg === "dyn") {
3525
+ const v = dyn();
3526
+ if (v != null) return v;
3527
+ } else if (leg === "lex") {
3528
+ const v = lex();
3529
+ if (v != null) return v;
3530
+ } else {
3531
+ console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
3532
+ }
3533
+ }
3534
+ return null;
3535
+ }
3536
+ function lookup(chain, name, dv = null) {
3537
+ let n = chain;
3538
+ while (n !== null) {
3539
+ const r = n[0].lookup(name);
3540
+ if (r === STOP) return dv;
3541
+ if (r !== NEXT) return r;
3542
+ n = n[1];
3543
+ }
3544
+ return dv;
3545
+ }
3546
+ function computeViewsId(views) {
3547
+ let s = "";
3548
+ let n = views;
3549
+ while (n !== null) {
3550
+ s += n[0];
3551
+ n = n[1];
3552
+ }
3553
+ return s === "main" ? "" : s;
3554
+ }
3555
+ var STOP, NEXT, DEFAULT_ROUTE, isTypeName, BindFrame, DynFrame, Stack, NOT_FOUND;
3556
+ var init_stack = __esm({
3557
+ "src/stack.js"() {
3558
+ init_path();
3559
+ STOP = /* @__PURE__ */ Symbol("STOP");
3560
+ NEXT = /* @__PURE__ */ Symbol("NEXT");
3561
+ DEFAULT_ROUTE = ["dyn", "lex"];
3562
+ isTypeName = (s) => {
3563
+ const c = s.charCodeAt(0);
3564
+ return c >= 65 && c <= 90;
3565
+ };
3566
+ BindFrame = class {
3567
+ constructor(it, binds, isFrame) {
3568
+ this.it = it;
3569
+ this.binds = binds;
3570
+ this.isFrame = isFrame;
3571
+ }
3572
+ lookup(name) {
3573
+ const v = this.binds[name];
3574
+ return v === void 0 ? this.isFrame ? STOP : NEXT : v;
3575
+ }
3576
+ };
3577
+ DynFrame = class {
3578
+ constructor(binds, types) {
3579
+ this.binds = binds;
3580
+ this.types = types;
3581
+ }
3582
+ lookup(name) {
3583
+ const v = (isTypeName(name) ? this.types : this.binds)[name];
3584
+ return v === void 0 ? NEXT : v;
3585
+ }
3586
+ };
3587
+ Stack = class _Stack {
3588
+ constructor(fields) {
3589
+ Object.assign(this, fields);
3590
+ }
3591
+ _with(patch) {
3592
+ return new _Stack({ ...this, ...patch });
3593
+ }
3594
+ // Evaluate every provide the entered component publishes and push them as one
3595
+ // dynBinds frame, keyed by NAME. A value is published together with the absolute
3596
+ // path it lives at: the same declaration is read as `*name` AND resumed at by
3597
+ // `<x render="*name">`, so a consumer needs both halves. Types go in the same
3598
+ // frame's other map. No-op with no provides.
3599
+ _pushProvides() {
3600
+ const comp = this.comps.getCompFor(this.it);
3601
+ if (comp == null) return this;
3602
+ const { provide, provideType } = comp;
3603
+ const binds = {};
3604
+ const types = {};
3605
+ let has2 = false;
3606
+ const base = this._publishBase();
3607
+ for (const k in provide) {
3608
+ const step = provide[k].val.toPathItem?.() ?? null;
3609
+ if (step === null) continue;
3610
+ const value = provide[k].val.eval(this);
3611
+ binds[k] = { value, path: base === null ? null : base.concat([step]) };
3612
+ has2 = true;
3613
+ }
3614
+ for (const k in provideType) {
3615
+ types[k] = provideType[k];
3616
+ has2 = true;
3617
+ }
3618
+ if (!has2) return this;
3619
+ return this._with({ dynBinds: [new DynFrame(binds, types), this.dynBinds] });
3620
+ }
3621
+ // The absolute address of the component being entered, or null when this
3622
+ // position cannot be written down as one. Frame-only steps carry bindings and
3623
+ // address nothing, so they are compacted away first; what is left is checked
3624
+ // against the value actually being rendered, because a scope CAN move the
3625
+ // render position without contributing an addressing step (a plain `@each`
3626
+ // body re-binds `it` to the item while its rebuild step is an identity).
3627
+ //
3628
+ // A null base still publishes the VALUE — `*name` reads it fine — but there is
3629
+ // nowhere to resume, so `<x render="*name">` renders nothing rather than
3630
+ // editing whatever happens to live at the address we guessed.
3631
+ _publishBase() {
3632
+ const base = this.renderPath.toTransactionPath().compact();
3633
+ return base.lookup(this.root, NOT_FOUND) === this.it ? base : null;
3634
+ }
3635
+ static root(comps, it, ctx = null) {
3636
+ return new _Stack({
3637
+ comps,
3638
+ root: it,
3639
+ it,
3640
+ binds: [new BindFrame(it, {}, true), null],
3641
+ dynBinds: [new DynFrame({}, {}), null],
3642
+ views: ["main", null],
3643
+ viewsId: "",
3644
+ renderPath: new DispatchPath(),
3645
+ pendingFrame: false,
3646
+ ctx
3647
+ })._pushProvides();
3648
+ }
3649
+ // `renderPath` defaults to this stack's own: an ordinary scope does not move.
3650
+ // `pendingFrame` clears on a component frame (which emits the base) and is
3651
+ // inherited by transparent scopes, which have to carry it to the next boundary.
3652
+ enter(it, bindings = {}, isFrame = true, renderPath = this.renderPath, pendingFrame = null) {
3653
+ const stack = this._with({
3654
+ it,
3655
+ binds: [new BindFrame(it, bindings, isFrame), this.binds],
3656
+ renderPath,
3657
+ pendingFrame: pendingFrame ?? (isFrame ? false : this.pendingFrame)
3658
+ });
3659
+ return isFrame ? stack._pushProvides() : stack;
3660
+ }
3661
+ pushViewName(name) {
3662
+ const views = [name, this.views];
3663
+ return this._with({ views, viewsId: computeViewsId(views) });
3664
+ }
3665
+ // Published types are stable per scope and would only churn the render cache, so
3666
+ // the cache key covers values alone.
3667
+ _pushDynBindValuesToArray(arr, comp) {
3668
+ for (const k in comp.provide) arr.push(this.lookupDynamic(k));
3669
+ for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
3670
+ }
3671
+ // `*name`: the nearest binding above (including this component's own provides,
3672
+ // pushed on entering it), else a path registered in the component's lexical
3673
+ // scope, else this component's declared default, else null.
3674
+ //
3675
+ // One chain walk and no producer resolution: a lookup names what it WANTS, so the
3676
+ // frame it wants is keyed by that name. The default belongs to the CONSUMER's
3677
+ // declaration and is evaluated against the consumer's stack, which is why it is
3678
+ // consulted only after the whole chain has missed.
3679
+ lookupDynamicLocated(name) {
3680
+ if (isTypeName(name)) return null;
3681
+ const v = lookup(this.dynBinds, name);
3682
+ if (v != null) return v;
3683
+ const comp = this.comps.getCompFor(this.it);
3684
+ if (comp == null) return null;
3685
+ const path = comp.scope?.lookupPath?.(name) ?? null;
3686
+ if (path !== null) {
3687
+ const value2 = path.lookup(this.root, NOT_FOUND);
3688
+ if (value2 !== NOT_FOUND) return { value: value2, path };
3689
+ }
3690
+ const dval = comp.lookup[name]?.val ?? null;
3691
+ if (dval === null) return null;
3692
+ const step = dval.toPathItem?.() ?? null;
3693
+ const value = dval.eval(this);
3694
+ return step === null ? { value, path: null } : { value, path: this.renderPath.toTransactionPath().concat([step]) };
3695
+ }
3696
+ lookupDynamic(name) {
3697
+ if (isTypeName(name)) return lookup(this.dynBinds, name);
3698
+ return this.lookupDynamicLocated(name)?.value ?? null;
3699
+ }
3700
+ lookupBind(name) {
3701
+ return lookup(this.binds, name);
3702
+ }
3703
+ lookupFieldRaw(name) {
3704
+ return this.it[name] ?? null;
3705
+ }
3706
+ lookupMethod(name) {
3707
+ const fn = this.it[name];
3708
+ return fn instanceof Function ? fn.call(this.it) : null;
3709
+ }
3710
+ // The dispatched DOM event / drag info, read only by EventMemberVal's
3711
+ // `e.<member>` handler args. Null outside a live event transaction.
3712
+ lookupEvent() {
3713
+ return this.ctx?.event ?? null;
3714
+ }
3715
+ lookupDragInfo() {
3716
+ return this.ctx?.dragInfo ?? null;
3717
+ }
3718
+ getHandlerFor(name, key) {
3719
+ return this.comps.getHandlerFor(this.it, name, key);
3720
+ }
3721
+ lookupBestView(views, defaultViewName) {
3722
+ let n = this.views;
3723
+ while (n !== null) {
3724
+ const view = views[n[0]];
3725
+ if (view !== void 0) return view;
3726
+ n = n[1];
3727
+ }
3728
+ return views[defaultViewName];
3729
+ }
3730
+ };
3731
+ NOT_FOUND = /* @__PURE__ */ Symbol("NOT_FOUND");
3732
+ }
3733
+ });
3734
+
3450
3735
  // src/components.js
3451
- var COMPONENT, Components, ComponentStack, ProvideInfo, LookupInfo, isString, isTypeName, _rawSpecKeys, KNOWN_SPEC_KEYS, _compId, Component;
3736
+ var COMPONENT, Components, ComponentStack, ProvideInfo, LookupInfo, isString, _rawSpecKeys, KNOWN_SPEC_KEYS, _compId, Component;
3452
3737
  var init_components = __esm({
3453
3738
  "src/components.js"() {
3454
3739
  init_anode();
3740
+ init_stack();
3455
3741
  init_value();
3456
3742
  COMPONENT = /* @__PURE__ */ Symbol.for("tutuca.component");
3457
3743
  Components = class {
@@ -3490,12 +3776,14 @@ var init_components = __esm({
3490
3776
  this.byName = {};
3491
3777
  this.intentsByName = {};
3492
3778
  this.macros = {};
3779
+ this.paths = {};
3493
3780
  }
3494
3781
  enter() {
3495
3782
  return new _ComponentStack(this.comps, this);
3496
3783
  }
3497
3784
  registerComponents(comps, opts) {
3498
- const { aliases: aliases2 = {} } = opts ?? {};
3785
+ const { aliases: aliases2 = {}, paths } = opts ?? {};
3786
+ if (paths) this.registerPaths(paths);
3499
3787
  for (let i = 0; i < comps.length; i++) {
3500
3788
  const Comp = comps[i];
3501
3789
  Comp[COMPONENT].scope = this.enter();
@@ -3509,6 +3797,27 @@ var init_components = __esm({
3509
3797
  else console.warn("alias", alias, "to inexistent component", aliases2[alias]);
3510
3798
  }
3511
3799
  }
3800
+ // Register lowercase names as absolute paths from the app state root. A
3801
+ // descendant that declares one in its `lookup` reads and renders `*name` without
3802
+ // anything above it publishing one — which is what makes a session, a theme or a
3803
+ // host-owned value available in its natural registration scope, instead of forcing
3804
+ // an application root whose only job is to `provide` it. Register on a nested
3805
+ // scope to narrow a name; nearest registration wins.
3806
+ //
3807
+ // Uppercase names are ignored: a component TYPE is what `lookupComponent` already
3808
+ // answers, and a type has no path.
3809
+ registerPaths(paths) {
3810
+ for (const name in paths) {
3811
+ if (isTypeName(name)) {
3812
+ console.warn("registerPaths: a type name has no path", name);
3813
+ continue;
3814
+ }
3815
+ this.paths[name] = paths[name].toPath?.() ?? paths[name];
3816
+ }
3817
+ }
3818
+ lookupPath(name) {
3819
+ return this.paths[name] ?? this.parent?.lookupPath(name) ?? null;
3820
+ }
3512
3821
  registerMacros(macros) {
3513
3822
  for (const key in macros) {
3514
3823
  const lower = key.toLowerCase();
@@ -3540,17 +3849,16 @@ var init_components = __esm({
3540
3849
  lookupComponent(name) {
3541
3850
  return this.byName[name] ?? this.parent?.lookupComponent(name) ?? null;
3542
3851
  }
3543
- // The component in this scope chain that provides `name`, innermost first. A lookup
3544
- // names a value without naming its producer, so the render-target teleport
3545
- // (resolveDynProducer) recovers the producer here instead. Sound because
3546
- // PROVIDE_NAME_COLLISION keeps a provide name to one producer per chain; with two,
3547
- // this returns the innermost and the linter has already reported the ambiguity.
3548
- lookupProvider(name) {
3852
+ // Whether anything in this scope chain provides `name`. Existence only: a lookup
3853
+ // names what it WANTS and takes whoever is nearest above it at render time, so
3854
+ // there is no producer to identify several components may publish one name and
3855
+ // the live render ancestry decides. Used by the linter to tell a lookup that can
3856
+ // be satisfied from one that never will be.
3857
+ hasProvider(name) {
3549
3858
  for (const compName in this.byName) {
3550
- const Comp = this.byName[compName];
3551
- if (Comp.provide?.[name] !== void 0) return Comp;
3859
+ if (this.byName[compName].provide?.[name] !== void 0) return true;
3552
3860
  }
3553
- return this.parent?.lookupProvider(name) ?? null;
3861
+ return this.parent?.hasProvider(name) ?? false;
3554
3862
  }
3555
3863
  lookupMacro(name) {
3556
3864
  return this.macros[name] ?? this.parent?.lookupMacro(name) ?? null;
@@ -3567,10 +3875,6 @@ var init_components = __esm({
3567
3875
  }
3568
3876
  };
3569
3877
  isString = (v) => typeof v === "string";
3570
- isTypeName = (s) => {
3571
- const c = s.charCodeAt(0);
3572
- return c >= 65 && c <= 90;
3573
- };
3574
3878
  _rawSpecKeys = "name view style commonStyle globalStyle receive intent alter views provide lookup fields methods statics";
3575
3879
  KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
3576
3880
  _compId = 0;
@@ -5981,7 +6285,6 @@ function checkComponent(Comp, lx = new LintContext(), { wellKnownExtras = EMPTY_
5981
6285
  checkFieldMethodNameCollisions(lx, Comp);
5982
6286
  checkProvidesAreAddressable(lx, Comp);
5983
6287
  checkProvidedTypes(lx, Comp);
5984
- checkProvideNameCollisions(lx, Comp);
5985
6288
  checkLookupShapes(lx, Comp);
5986
6289
  checkLookupTypesResolve(lx, Comp);
5987
6290
  checkLookupsHaveProviders(lx, Comp);
@@ -6556,19 +6859,6 @@ function checkProvidedTypes(lx, Comp) {
6556
6859
  if (raw !== "self") lx.error(PROVIDE_TYPE_BAD_SHAPE, { name, value: raw });
6557
6860
  }
6558
6861
  }
6559
- function checkProvideNameCollisions(lx, Comp) {
6560
- const scope = Comp.scope;
6561
- if (!scope) return;
6562
- for (const name in Comp.provide) {
6563
- for (let s = scope; s; s = s.parent) {
6564
- for (const otherName in s.byName) {
6565
- const Other = s.byName[otherName];
6566
- if (Other !== Comp && Other.provide?.[name] !== void 0)
6567
- lx.error(PROVIDE_NAME_COLLISION, { name, other: Other.name });
6568
- }
6569
- }
6570
- }
6571
- }
6572
6862
  function checkLookupShapes(lx, Comp) {
6573
6863
  const raw = Comp._rawLookup;
6574
6864
  if (!Array.isArray(raw)) {
@@ -6609,7 +6899,7 @@ function checkLookupsHaveProviders(lx, Comp) {
6609
6899
  if (!scope) return;
6610
6900
  for (const name in Comp.lookup) {
6611
6901
  if (isTypeName2(name)) continue;
6612
- if (scope.lookupProvider?.(name)) continue;
6902
+ if (scope.hasProvider?.(name) || scope.lookupPath?.(name)) continue;
6613
6903
  const info = { name, hasDefault: Comp.lookup[name].val != null };
6614
6904
  if (info.hasDefault) lx.hint(LOOKUP_NO_PROVIDER, info);
6615
6905
  else lx.error(LOOKUP_NO_PROVIDER, info);
@@ -6631,7 +6921,7 @@ function checkUnreferencedDynamics(lx, Comp, referencedDynamics) {
6631
6921
  }
6632
6922
  }
6633
6923
  }
6634
- var KNOWN_COMPONENT_SPEC_KEYS, EMPTY_SET, FRAMEWORK_WELL_KNOWN_EXTRAS, KNOWN_DIRECTIVE_NAMES, isTypeName2, ALT_HANDLER_NOT_DEFINED, ALT_HANDLER_NOT_REFERENCED, DYN_VAL_NOT_DEFINED, DYN_ALIAS_NOT_REFERENCED, PROVIDE_NOT_ADDRESSABLE, PROVIDE_TYPE_BAD_SHAPE, PROVIDE_NAME_COLLISION, LOOKUP_BAD_SHAPE, LOOKUP_NO_PROVIDER, RENDER_IT_OUTSIDE_OF_LOOP, UNKNOWN_EVENT_MODIFIER, RECEIVE_HANDLER_NOT_IMPLEMENTED, EVENT_HANDLER_METHOD_NOT_ALLOWED, HANDLER_NAME_COLLISION, FIELD_VAL_NOT_DEFINED, FIELD_VAL_IS_METHOD, METHOD_VAL_NOT_DEFINED, METHOD_VAL_IS_FIELD, DUPLICATE_ATTR_DEFINITION, IF_NO_BRANCH_SET, UNKNOWN_COMPONENT_NAME, UNKNOWN_MACRO_ARG, UNKNOWN_DIRECTIVE, UNKNOWN_X_OP, UNKNOWN_X_ATTR, X_OP_IGNORES_CHILDREN, MAYBE_DROP_AT_PREFIX, MAYBE_ADD_AT_PREFIX, BAD_VALUE, UNSUPPORTED_EXPR_SYNTAX, BINDING_MEMBER_TOO_DEEP, SUGGEST_BINDING_MEMBER, REDUNDANT_TEMPLATE_STRING, PLACEHOLDERLESS_TEMPLATE_STRING, CONSTANT_CONDITION, UNKNOWN_COMPONENT_SPEC_KEY, COMP_FIELD_BAD_SHAPE, ASYNC_HANDLER, TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE, GLOBAL_SELECTOR_IN_SCOPED_STYLE, FIELD_METHOD_NAME_COLLISION, X_KNOWN_OP_NAMES, X_KNOWN_ATTR_NAMES, HOST_DIRECTIVE_ONLY_NAMES, LEVEL_WARN2, LEVEL_ERROR2, LEVEL_HINT, PARSE_ISSUES, BINDING_MEMBER_TOO_DEEP_RE, UNSUPPORTED_EXPR_GUIDANCE, HTML_LINT_OPTS, NO_WRAPPERS2, ANY_EVENT_MODIFIERS, fixTo, BOOL_CONDITION_ORIGINS, isBoolConditionCtx, ATTR_VAL_CHECKERS, NODE_KIND_TO_CTX, HANDLER_CHANNELS, ASYNC_HANDLER_HELP, NON_NESTABLE_AT_RULE, GLOBAL_LEADING_SELECTOR, IGNORE_DIRECTIVE, blankRun, STYLE_TO_GLOBAL_HELP, KNOWN_LOOKUP_KEYS, LintContext, LintParseContext;
6924
+ var KNOWN_COMPONENT_SPEC_KEYS, EMPTY_SET, FRAMEWORK_WELL_KNOWN_EXTRAS, KNOWN_DIRECTIVE_NAMES, isTypeName2, ALT_HANDLER_NOT_DEFINED, ALT_HANDLER_NOT_REFERENCED, DYN_VAL_NOT_DEFINED, DYN_ALIAS_NOT_REFERENCED, PROVIDE_NOT_ADDRESSABLE, PROVIDE_TYPE_BAD_SHAPE, LOOKUP_BAD_SHAPE, LOOKUP_NO_PROVIDER, RENDER_IT_OUTSIDE_OF_LOOP, UNKNOWN_EVENT_MODIFIER, RECEIVE_HANDLER_NOT_IMPLEMENTED, EVENT_HANDLER_METHOD_NOT_ALLOWED, HANDLER_NAME_COLLISION, FIELD_VAL_NOT_DEFINED, FIELD_VAL_IS_METHOD, METHOD_VAL_NOT_DEFINED, METHOD_VAL_IS_FIELD, DUPLICATE_ATTR_DEFINITION, IF_NO_BRANCH_SET, UNKNOWN_COMPONENT_NAME, UNKNOWN_MACRO_ARG, UNKNOWN_DIRECTIVE, UNKNOWN_X_OP, UNKNOWN_X_ATTR, X_OP_IGNORES_CHILDREN, MAYBE_DROP_AT_PREFIX, MAYBE_ADD_AT_PREFIX, BAD_VALUE, UNSUPPORTED_EXPR_SYNTAX, BINDING_MEMBER_TOO_DEEP, SUGGEST_BINDING_MEMBER, REDUNDANT_TEMPLATE_STRING, PLACEHOLDERLESS_TEMPLATE_STRING, CONSTANT_CONDITION, UNKNOWN_COMPONENT_SPEC_KEY, COMP_FIELD_BAD_SHAPE, ASYNC_HANDLER, TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE, GLOBAL_SELECTOR_IN_SCOPED_STYLE, FIELD_METHOD_NAME_COLLISION, X_KNOWN_OP_NAMES, X_KNOWN_ATTR_NAMES, HOST_DIRECTIVE_ONLY_NAMES, LEVEL_WARN2, LEVEL_ERROR2, LEVEL_HINT, PARSE_ISSUES, BINDING_MEMBER_TOO_DEEP_RE, UNSUPPORTED_EXPR_GUIDANCE, HTML_LINT_OPTS, NO_WRAPPERS2, ANY_EVENT_MODIFIERS, fixTo, BOOL_CONDITION_ORIGINS, isBoolConditionCtx, ATTR_VAL_CHECKERS, NODE_KIND_TO_CTX, HANDLER_CHANNELS, ASYNC_HANDLER_HELP, NON_NESTABLE_AT_RULE, GLOBAL_LEADING_SELECTOR, IGNORE_DIRECTIVE, blankRun, STYLE_TO_GLOBAL_HELP, KNOWN_LOOKUP_KEYS, LintContext, LintParseContext;
6635
6925
  var init_lint_check = __esm({
6636
6926
  "tools/core/lint-check.js"() {
6637
6927
  init_anode();
@@ -6669,7 +6959,6 @@ var init_lint_check = __esm({
6669
6959
  DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED";
6670
6960
  PROVIDE_NOT_ADDRESSABLE = "PROVIDE_NOT_ADDRESSABLE";
6671
6961
  PROVIDE_TYPE_BAD_SHAPE = "PROVIDE_TYPE_BAD_SHAPE";
6672
- PROVIDE_NAME_COLLISION = "PROVIDE_NAME_COLLISION";
6673
6962
  LOOKUP_BAD_SHAPE = "LOOKUP_BAD_SHAPE";
6674
6963
  LOOKUP_NO_PROVIDER = "LOOKUP_NO_PROVIDER";
6675
6964
  RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP";
@@ -6997,7 +7286,7 @@ var init_lint_rules = __esm({
6997
7286
  code: LOOKUP_NO_PROVIDER,
6998
7287
  level: "error",
6999
7288
  group: "Dynamic bindings",
7000
- summary: "`lookup` name is provided by no component in scope."
7289
+ summary: "`lookup` name is neither provided in scope nor a registered path."
7001
7290
  },
7002
7291
  {
7003
7292
  code: PROVIDE_TYPE_BAD_SHAPE,
@@ -7005,12 +7294,6 @@ var init_lint_rules = __esm({
7005
7294
  group: "Dynamic bindings",
7006
7295
  summary: 'A PascalCase `provide` publishes a component type; its value must be `"self"`.'
7007
7296
  },
7008
- {
7009
- code: PROVIDE_NAME_COLLISION,
7010
- level: "error",
7011
- group: "Dynamic bindings",
7012
- summary: "Two components in one scope chain `provide` the same name."
7013
- },
7014
7297
  // Templates / events
7015
7298
  {
7016
7299
  code: RENDER_IT_OUTSIDE_OF_LOOP,
@@ -7299,15 +7582,13 @@ function lintIdToMessage(id, info) {
7299
7582
  case "DYN_ALIAS_NOT_REFERENCED":
7300
7583
  return `Lookup '${info.name}' is defined but never used — remove it or reference it as '*${info.name}' in a view`;
7301
7584
  case "PROVIDE_NOT_ADDRESSABLE":
7302
- 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`;
7585
+ 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`;
7303
7586
  case "LOOKUP_BAD_SHAPE":
7304
7587
  return `Lookup '${info.name}' has an invalid shape: ${info.problem}`;
7305
7588
  case "LOOKUP_NO_PROVIDER":
7306
- 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`;
7589
+ 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`;
7307
7590
  case "PROVIDE_TYPE_BAD_SHAPE":
7308
7591
  return `Provide '${info.name}' starts uppercase, so it publishes a component type — its value must be 'self', not '${info.value}'`;
7309
- case "PROVIDE_NAME_COLLISION":
7310
- return `Provide '${info.name}' is also provided by '${info.other}' in the same scope — one name, one provider, so a lookup can find it`;
7311
7592
  case "UNKNOWN_MACRO_ARG":
7312
7593
  return `Argument '${info.name}' is not declared in macro '${info.macroName}'`;
7313
7594
  case "UNKNOWN_DIRECTIVE":
@@ -12782,165 +13063,6 @@ var init_list = __esm({
12782
13063
  }
12783
13064
  });
12784
13065
 
12785
- // src/stack.js
12786
- function routeLookup(route, lex, dyn) {
12787
- for (let i = 0; i < route.length; i++) {
12788
- const leg = route[i];
12789
- if (leg === "dyn") {
12790
- const v = dyn();
12791
- if (v != null) return v;
12792
- } else if (leg === "lex") {
12793
- const v = lex();
12794
- if (v != null) return v;
12795
- } else {
12796
- console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
12797
- }
12798
- }
12799
- return null;
12800
- }
12801
- function lookup(chain, name, dv = null) {
12802
- let n = chain;
12803
- while (n !== null) {
12804
- const r = n[0].lookup(name);
12805
- if (r === STOP) return dv;
12806
- if (r !== NEXT) return r;
12807
- n = n[1];
12808
- }
12809
- return dv;
12810
- }
12811
- function computeViewsId(views) {
12812
- let s = "";
12813
- let n = views;
12814
- while (n !== null) {
12815
- s += n[0];
12816
- n = n[1];
12817
- }
12818
- return s === "main" ? "" : s;
12819
- }
12820
- var STOP, NEXT, DEFAULT_ROUTE, BindFrame, ObjectFrame, Stack;
12821
- var init_stack = __esm({
12822
- "src/stack.js"() {
12823
- STOP = /* @__PURE__ */ Symbol("STOP");
12824
- NEXT = /* @__PURE__ */ Symbol("NEXT");
12825
- DEFAULT_ROUTE = ["dyn", "lex"];
12826
- BindFrame = class {
12827
- constructor(it, binds, isFrame) {
12828
- this.it = it;
12829
- this.binds = binds;
12830
- this.isFrame = isFrame;
12831
- }
12832
- lookup(name) {
12833
- const v = this.binds[name];
12834
- return v === void 0 ? this.isFrame ? STOP : NEXT : v;
12835
- }
12836
- };
12837
- ObjectFrame = class {
12838
- constructor(binds) {
12839
- this.binds = binds;
12840
- }
12841
- lookup(key) {
12842
- const v = this.binds[key];
12843
- return v === void 0 ? NEXT : v;
12844
- }
12845
- };
12846
- Stack = class _Stack {
12847
- constructor(comps, it, binds, dynBinds, views, viewsId, ctx = null) {
12848
- this.comps = comps;
12849
- this.it = it;
12850
- this.binds = binds;
12851
- this.dynBinds = dynBinds;
12852
- this.views = views;
12853
- this.viewsId = viewsId;
12854
- this.ctx = ctx;
12855
- }
12856
- // Evaluate every provide the entered component publishes and push them as one
12857
- // dynBinds frame, keyed by NAME. Published types go in the same frame: a type name
12858
- // starts A-Z and a value name does not, so the two namespaces cannot collide and
12859
- // nearest-ancestor-wins falls out of frame order for both. No-op with no provides.
12860
- _pushProvides() {
12861
- const comp = this.comps.getCompFor(this.it);
12862
- if (comp == null) return this;
12863
- const { provide, provideType } = comp;
12864
- const dynObj = {};
12865
- let has2 = false;
12866
- for (const k in provide) {
12867
- dynObj[k] = provide[k].val.eval(this);
12868
- has2 = true;
12869
- }
12870
- for (const k in provideType) {
12871
- dynObj[k] = provideType[k];
12872
- has2 = true;
12873
- }
12874
- if (!has2) return this;
12875
- const newDynBinds = [new ObjectFrame(dynObj), this.dynBinds];
12876
- const { comps, it, binds, views, viewsId, ctx } = this;
12877
- return new _Stack(comps, it, binds, newDynBinds, views, viewsId, ctx);
12878
- }
12879
- static root(comps, it, ctx) {
12880
- const binds = [new BindFrame(it, {}, true), null];
12881
- const dynBinds = [new ObjectFrame({}), null];
12882
- const views = ["main", null];
12883
- return new _Stack(comps, it, binds, dynBinds, views, "", ctx)._pushProvides();
12884
- }
12885
- enter(it, bindings = {}, isFrame = true) {
12886
- const { comps, binds, dynBinds, views, viewsId, ctx } = this;
12887
- const newBinds = [new BindFrame(it, bindings, isFrame), binds];
12888
- const stack = new _Stack(comps, it, newBinds, dynBinds, views, viewsId, ctx);
12889
- return isFrame ? stack._pushProvides() : stack;
12890
- }
12891
- pushViewName(name) {
12892
- const { comps, it, binds, dynBinds, views, ctx } = this;
12893
- const newViews = [name, views];
12894
- return new _Stack(comps, it, binds, dynBinds, newViews, computeViewsId(newViews), ctx);
12895
- }
12896
- // Published types are stable per scope and would only churn the render cache, so
12897
- // the cache key covers values alone.
12898
- _pushDynBindValuesToArray(arr, comp) {
12899
- for (const k in comp.provide) arr.push(this.lookupDynamic(k));
12900
- for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
12901
- }
12902
- // `*name`: the nearest binding above (including this component's own provides,
12903
- // pushed on entering it), else this component's declared default, else null.
12904
- lookupDynamic(name) {
12905
- const v = lookup(this.dynBinds, name);
12906
- if (v != null) return v;
12907
- const comp = this.comps.getCompFor(this.it);
12908
- return comp?.lookup[name]?.val?.eval(this) ?? null;
12909
- }
12910
- lookupBind(name) {
12911
- return lookup(this.binds, name);
12912
- }
12913
- lookupFieldRaw(name) {
12914
- return this.it[name] ?? null;
12915
- }
12916
- lookupMethod(name) {
12917
- const fn = this.it[name];
12918
- return fn instanceof Function ? fn.call(this.it) : null;
12919
- }
12920
- // The dispatched DOM event / drag info, read only by EventMemberVal's
12921
- // `e.<member>` handler args. Null outside a live event transaction.
12922
- lookupEvent() {
12923
- return this.ctx?.event ?? null;
12924
- }
12925
- lookupDragInfo() {
12926
- return this.ctx?.dragInfo ?? null;
12927
- }
12928
- getHandlerFor(name, key) {
12929
- return this.comps.getHandlerFor(this.it, name, key);
12930
- }
12931
- lookupBestView(views, defaultViewName) {
12932
- let n = this.views;
12933
- while (n !== null) {
12934
- const view = views[n[0]];
12935
- if (view !== void 0) return view;
12936
- n = n[1];
12937
- }
12938
- return views[defaultViewName];
12939
- }
12940
- };
12941
- }
12942
- });
12943
-
12944
13066
  // src/oo.js
12945
13067
  function mkCompField(field, scope, args) {
12946
13068
  const Comp = scope?.lookupComponent(field.type) ?? null;
@@ -12961,8 +13083,7 @@ function classFromData(name, { fields = {}, methods, statics }) {
12961
13083
  const value = fields[field];
12962
13084
  const type3 = typeof value;
12963
13085
  if (type3 === "string") b.addField(field, value, FieldString);
12964
- else if (type3 === "number")
12965
- b.addField(field, value, Number.isInteger(value) ? FieldInt : FieldFloat);
13086
+ else if (type3 === "number") b.addField(field, value, FieldFloat);
12966
13087
  else if (type3 === "boolean") b.addField(field, value, FieldBool);
12967
13088
  else if (Array.isArray(value)) b.addField(field, [...value], FieldList);
12968
13089
  else if (value instanceof Set) b.addField(field, new Set(value), FieldSet);
@@ -13003,7 +13124,7 @@ function assertNoReservedComponentStatics(statics = {}) {
13003
13124
  }
13004
13125
  }
13005
13126
  }
13006
- var BAD_VALUE2, nullCoercer, Field, CHECK_TYPE_ANY, CHECK_TYPE_INT, CHECK_TYPE_FLOAT, CHECK_TYPE_BOOL, CHECK_TYPE_STRING, CHECK_TYPE_LIST, CHECK_TYPE_OBJECT, CHECK_TYPE_MAP, CHECK_TYPE_SET, FieldBool, FieldAny, FieldString, FieldInt, FieldFloat, metaOf, getTypeName, FieldComp, FieldList, FieldObject, FieldMap, FieldSet, ClassBuilder, FIELD_CLASS, fieldsByTypeName, META_KEYS, RESERVED_COMPONENT_STATICS;
13127
+ var BAD_VALUE2, nullCoercer, Field, CHECK_TYPE_ANY, CHECK_TYPE_INT, CHECK_TYPE_FLOAT, CHECK_TYPE_BOOL, CHECK_TYPE_STRING, CHECK_TYPE_LIST, CHECK_TYPE_OBJECT, CHECK_TYPE_MAP, CHECK_TYPE_SET, COERCE_NONE, COERCE_BOOL, COERCE_STRING, COERCE_INT, COERCE_LIST, COERCE_OBJECT, COERCE_MAP, COERCE_SET, FieldBool, FieldAny, FieldString, FieldInt, FieldFloat, metaOf, getTypeName, FieldComp, FieldList, FieldObject, FieldMap, FieldSet, ClassBuilder, FIELD_CLASS, fieldsByTypeName, META_KEYS, RESERVED_COMPONENT_STATICS;
13007
13128
  var init_oo = __esm({
13008
13129
  "src/oo.js"() {
13009
13130
  init_collection();
@@ -13040,9 +13161,22 @@ var init_oo = __esm({
13040
13161
  CHECK_TYPE_OBJECT = isPlainObject;
13041
13162
  CHECK_TYPE_MAP = (v) => v instanceof Map;
13042
13163
  CHECK_TYPE_SET = (v) => v instanceof Set;
13164
+ COERCE_NONE = (_v) => null;
13165
+ COERCE_BOOL = (v) => !!v;
13166
+ COERCE_STRING = (v) => v?.toString?.() ?? "";
13167
+ COERCE_INT = (v) => Number.isFinite(v) ? Math.trunc(v) : null;
13168
+ COERCE_LIST = (v) => Array.isArray(v) ? [...v] : null;
13169
+ COERCE_OBJECT = (v) => isPlainObject(v) ? { ...v } : null;
13170
+ COERCE_MAP = (v) => {
13171
+ if (v instanceof Map) return new Map(v);
13172
+ if (Array.isArray(v) || isPlainObject(v))
13173
+ return new Map(Array.isArray(v) ? v : Object.entries(v));
13174
+ return null;
13175
+ };
13176
+ COERCE_SET = (v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null;
13043
13177
  FieldBool = class extends Field {
13044
13178
  constructor(name, defaultValue = false) {
13045
- super("bool", name, CHECK_TYPE_BOOL, (v) => !!v, defaultValue);
13179
+ super("bool", name, CHECK_TYPE_BOOL, COERCE_BOOL, defaultValue);
13046
13180
  }
13047
13181
  };
13048
13182
  FieldAny = class extends Field {
@@ -13052,23 +13186,17 @@ var init_oo = __esm({
13052
13186
  };
13053
13187
  FieldString = class extends Field {
13054
13188
  constructor(name, defaultValue = "") {
13055
- super("text", name, CHECK_TYPE_STRING, (v) => v?.toString?.() ?? "", defaultValue);
13189
+ super("text", name, CHECK_TYPE_STRING, COERCE_STRING, defaultValue);
13056
13190
  }
13057
13191
  };
13058
13192
  FieldInt = class extends Field {
13059
13193
  constructor(name, defaultValue = 0) {
13060
- super(
13061
- "int",
13062
- name,
13063
- CHECK_TYPE_INT,
13064
- (v) => Number.isFinite(v) ? Math.trunc(v) : null,
13065
- defaultValue
13066
- );
13194
+ super("int", name, CHECK_TYPE_INT, COERCE_INT, defaultValue);
13067
13195
  }
13068
13196
  };
13069
13197
  FieldFloat = class extends Field {
13070
13198
  constructor(name, defaultValue = 0) {
13071
- super("float", name, CHECK_TYPE_FLOAT, (_) => null, defaultValue);
13199
+ super("float", name, CHECK_TYPE_FLOAT, COERCE_NONE, defaultValue);
13072
13200
  }
13073
13201
  };
13074
13202
  metaOf = (v) => v?.constructor?.[COMPONENT] ?? v?.constructor?.getMetaClass?.();
@@ -13081,45 +13209,22 @@ var init_oo = __esm({
13081
13209
  };
13082
13210
  FieldList = class extends Field {
13083
13211
  constructor(name, defaultValue = []) {
13084
- super("list", name, CHECK_TYPE_LIST, (v) => Array.isArray(v) ? [...v] : null, defaultValue);
13212
+ super("list", name, CHECK_TYPE_LIST, COERCE_LIST, defaultValue);
13085
13213
  }
13086
13214
  };
13087
13215
  FieldObject = class extends Field {
13088
13216
  constructor(name, defaultValue = {}) {
13089
- super(
13090
- "object",
13091
- name,
13092
- CHECK_TYPE_OBJECT,
13093
- (v) => isPlainObject(v) ? { ...v } : null,
13094
- defaultValue
13095
- );
13217
+ super("object", name, CHECK_TYPE_OBJECT, COERCE_OBJECT, defaultValue);
13096
13218
  }
13097
13219
  };
13098
13220
  FieldMap = class extends Field {
13099
13221
  constructor(name, defaultValue = /* @__PURE__ */ new Map()) {
13100
- super(
13101
- "map",
13102
- name,
13103
- CHECK_TYPE_MAP,
13104
- (v) => {
13105
- if (v instanceof Map) return new Map(v);
13106
- if (Array.isArray(v) || isPlainObject(v))
13107
- return new Map(Array.isArray(v) ? v : Object.entries(v));
13108
- return null;
13109
- },
13110
- defaultValue
13111
- );
13222
+ super("map", name, CHECK_TYPE_MAP, COERCE_MAP, defaultValue);
13112
13223
  }
13113
13224
  };
13114
13225
  FieldSet = class extends Field {
13115
13226
  constructor(name, defaultValue = /* @__PURE__ */ new Set()) {
13116
- super(
13117
- "set",
13118
- name,
13119
- CHECK_TYPE_SET,
13120
- (v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null,
13121
- defaultValue
13122
- );
13227
+ super("set", name, CHECK_TYPE_SET, COERCE_SET, defaultValue);
13123
13228
  }
13124
13229
  };
13125
13230
  ClassBuilder = class {
@@ -13243,7 +13348,7 @@ function warnNotIntent(verb) {
13243
13348
  console.warn(`ctx.${verb}() is only meaningful in an "intent" handler - ignored`);
13244
13349
  }
13245
13350
  function rootDispatcher(transactor) {
13246
- return new Dispatcher(new Path([]), transactor, null);
13351
+ return new Dispatcher(new DispatchPath(), transactor, null);
13247
13352
  }
13248
13353
  var State2, Transactor, Transaction, InputEvent, NameArgsTransaction, SendEvent, IntentEvent, PASS, REFUSAL_RING_CAP, INTENT_DEPTH, IntentWalk, Completion, Dispatcher, EventContext, PathChanges;
13249
13354
  var init_transactor = __esm({
@@ -13380,9 +13485,11 @@ var init_transactor = __esm({
13380
13485
  // intent's answerPath is: a reply must reach the sender that asked even if a key
13381
13486
  // moved while the message was in flight. Null when nobody is waiting — a host
13382
13487
  // sendAtRoot or a view's own `@on.*` — and ctx.sendReply refuses on that.
13383
- pushSend(path, name, args = [], parent = null, origin = null) {
13488
+ pushSend(path, name, args = [], parent = null, origin = null, txnPath = null) {
13384
13489
  const t = new SendEvent(path, this, name, args, parent);
13385
- t.origin = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
13490
+ t.txnPath = txnPath;
13491
+ t.origin = origin;
13492
+ t.originPinned = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
13386
13493
  this.pushTransaction(t);
13387
13494
  return this._link(t, parent);
13388
13495
  }
@@ -13432,6 +13539,7 @@ var init_transactor = __esm({
13432
13539
  Transaction = class {
13433
13540
  constructor(path, transactor, parentTransaction = null) {
13434
13541
  this.path = path;
13542
+ this.txnPath = null;
13435
13543
  this.transactor = transactor;
13436
13544
  this.parentTransaction = parentTransaction;
13437
13545
  this._completion = null;
@@ -13480,9 +13588,11 @@ var init_transactor = __esm({
13480
13588
  getHandlerAndArgs(_root, _instance, _comps) {
13481
13589
  return null;
13482
13590
  }
13483
- // The path used to apply the mutation. Teleports dynamic-var renders so it lands on
13484
- // the data's real location (the dispatch `this.path` keeps intermediates).
13591
+ // The path used to apply the mutation: the ACTIVE frame of the dispatch path, so
13592
+ // an event inside a `<x render="*name">` subtree updates the value where it
13593
+ // really lives (the dispatch `this.path` keeps the visual callers, for bubbling).
13485
13594
  getTransactionPath() {
13595
+ if (this.txnPath !== null) return this.txnPath;
13486
13596
  return this.path.toTransactionPath().compact();
13487
13597
  }
13488
13598
  run(curRoot, comps) {
@@ -13507,8 +13617,9 @@ var init_transactor = __esm({
13507
13617
  this.dragInfo = dragInfo;
13508
13618
  this._dispatchPath = null;
13509
13619
  }
13510
- // Frame steps removed, DynStep + one step per crossed component kept: bubbling
13511
- // it visits every component (including intermediates of a dynamic-var render).
13620
+ // Frame-only steps removed, one step per crossed component kept — inside every
13621
+ // continuation frame independently, so bubbling it visits every component and
13622
+ // then returns to the caller that wrote the `*name`.
13512
13623
  get dispatchPath() {
13513
13624
  this._dispatchPath ??= this.path.compact();
13514
13625
  return this._dispatchPath;
@@ -13543,7 +13654,7 @@ var init_transactor = __esm({
13543
13654
  this.transactor.pushIntent(this.dispatchPath, name, args, rest, this);
13544
13655
  }
13545
13656
  getHandlerAndArgs(root, _instance, comps) {
13546
- const stack = this.path.toTransactionPath().buildStack(Stack.root(comps, root, this));
13657
+ const stack = this.path.buildStack(Stack.root(comps, root, this));
13547
13658
  const [handler, args] = this.handler.getHandlerAndArgs(stack, this);
13548
13659
  this._handlerArgs = [...args];
13549
13660
  const path = this.dispatchPath;
@@ -13669,7 +13780,7 @@ var init_transactor = __esm({
13669
13780
  while (this.legIndex < this.route.length) {
13670
13781
  const leg = this.route[this.legIndex];
13671
13782
  if (leg === "dyn") {
13672
- if (this.dynAt.steps.length === 0) {
13783
+ if (!this.dynAt.canPop()) {
13673
13784
  this.legIndex++;
13674
13785
  continue;
13675
13786
  }
@@ -13766,8 +13877,8 @@ var init_transactor = __esm({
13766
13877
  if (this.ended) return;
13767
13878
  this.ended = true;
13768
13879
  if (name === null) return this.release?.();
13769
- const path = this.answerPath ?? this.origin;
13770
- const t = new SendEvent(path, this.transactor, name, args, this.parent);
13880
+ const t = new SendEvent(this.origin, this.transactor, name, args, this.parent);
13881
+ t.txnPath = this.answerPath;
13771
13882
  t._isAnswer = true;
13772
13883
  this.transactor.pushTransaction(t);
13773
13884
  if (this.release) t.completion.whenSubtreeSettled().then(this.release);
@@ -13862,7 +13973,9 @@ var init_transactor = __esm({
13862
13973
  // replayed; provides are pushed on every component frame either way, so a provide
13863
13974
  // that reads a loop binding is the one case this cannot reproduce faithfully.
13864
13975
  _stack() {
13865
- this._stackMemo ??= this.path.toTransactionPath().buildStack(Stack.root(this.transactor.comps, this.root, this.parent));
13976
+ this._stackMemo ??= this.path.buildStack(
13977
+ Stack.root(this.transactor.comps, this.root, this.parent)
13978
+ );
13866
13979
  return this._stackMemo;
13867
13980
  }
13868
13981
  // Resolve a name the way the renderer would. `opts.route` takes the same legs in
@@ -13876,14 +13989,19 @@ var init_transactor = __esm({
13876
13989
  );
13877
13990
  }
13878
13991
  // The `lex` leg without a Stack: the scope of the component whose handler is
13879
- // running, which is the leaf of this ctx's path.
13992
+ // running, which is the leaf of this ctx's path. A type name resolves to the
13993
+ // component registered under it; a value name to a path registered under it
13994
+ // (see ComponentStack.registerPaths), read against the current root.
13880
13995
  _lookupLex(name) {
13881
13996
  let Comp = null;
13882
13997
  this.walkPath((c) => {
13883
13998
  Comp = c;
13884
13999
  return false;
13885
14000
  });
13886
- return Comp?.scope?.lookupComponent(name) ?? null;
14001
+ const scope = Comp?.scope;
14002
+ if (scope == null) return null;
14003
+ if (isTypeName(name)) return scope.lookupComponent(name) ?? null;
14004
+ return scope.lookupPath(name)?.lookup(this.root) ?? null;
13887
14005
  }
13888
14006
  // A component lookup is a value lookup constrained to a component. The `lex` leg
13889
14007
  // can only ever answer with one; the `dyn` leg reads a binding an ancestor
@@ -13944,7 +14062,14 @@ var init_transactor = __esm({
13944
14062
  this.transactor.refuse("NO_SENDER", { name });
13945
14063
  return null;
13946
14064
  }
13947
- return this.sendAtPath(origin, name, args);
14065
+ return this.transactor.pushSend(
14066
+ origin,
14067
+ name,
14068
+ args,
14069
+ this.parent,
14070
+ this.path,
14071
+ this.parent.originPinned
14072
+ );
13948
14073
  }
13949
14074
  // End the walk answering nothing — "served, and no answer".
13950
14075
  stop() {
@@ -14055,7 +14180,7 @@ var init_app = __esm({
14055
14180
  const { type: type3 } = e;
14056
14181
  const isDrag = type3 === "dragover" || type3 === "dragstart" || type3 === "dragend" || type3 === "drop";
14057
14182
  const { rootNode: root, maxEventNodeDepth: maxDepth, comps, transactor } = this;
14058
- const [path, handlers] = Path.fromNodeAndEventName(
14183
+ const [path, handlers] = DispatchPath.fromNodeAndEventName(
14059
14184
  e.target,
14060
14185
  type3,
14061
14186
  root,
@@ -14123,7 +14248,7 @@ var init_app = __esm({
14123
14248
  const txnPath = path.compact().toTransactionPath();
14124
14249
  const value = txnPath.lookup(rootValue);
14125
14250
  const dragType = e.target.dataset.dragtype ?? "?";
14126
- const stack = path.toTransactionPath().buildStack(this.makeStack(rootValue));
14251
+ const stack = path.buildStack(this.makeStack(rootValue));
14127
14252
  this.dragInfo = new DragInfo(stack, value, dragType, e.target);
14128
14253
  } else if (type3 === "drop") {
14129
14254
  e.preventDefault();
@@ -14198,7 +14323,7 @@ var init_app = __esm({
14198
14323
  this.rootNode.removeEventListener(name, this, listenerOpts(name));
14199
14324
  }
14200
14325
  sendAtRoot(name, args) {
14201
- this.transactor.pushSend(new Path([]), name, args);
14326
+ this.transactor.pushSend(new DispatchPath(), name, args);
14202
14327
  }
14203
14328
  registerComponents(comps, opts) {
14204
14329
  const scope = this.compStack.enter();
@@ -14342,13 +14467,28 @@ var init_cache = __esm({
14342
14467
  });
14343
14468
 
14344
14469
  // src/renderer.js
14470
+ function stampRenderBase(vdom, text) {
14471
+ if (vdom instanceof VNode)
14472
+ return new VNode(
14473
+ vdom.tag,
14474
+ { ...vdom.attrs, "data-rp": text },
14475
+ vdom.childs,
14476
+ vdom.key,
14477
+ vdom.namespace
14478
+ );
14479
+ if (vdom instanceof VFragment)
14480
+ return new VFragment(vdom.childs.map((c) => stampRenderBase(c, text)));
14481
+ return vdom;
14482
+ }
14345
14483
  var DATASET_ATTRS, Renderer;
14346
14484
  var init_renderer = __esm({
14347
14485
  "src/renderer.js"() {
14348
14486
  init_cache();
14349
14487
  init_iteration();
14488
+ init_path();
14489
+ init_value();
14350
14490
  init_vdom();
14351
- DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk"];
14491
+ DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk", "rp"];
14352
14492
  Renderer = class {
14353
14493
  constructor(comps) {
14354
14494
  this.comps = comps;
@@ -14385,17 +14525,17 @@ var init_renderer = __esm({
14385
14525
  if (comp === null) return null;
14386
14526
  return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
14387
14527
  }
14388
- renderIt(stack, node, key, viewName) {
14528
+ renderIt(stack, node, key, viewName, base = null) {
14389
14529
  const comp = this.comps.getCompFor(stack.it);
14390
- return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
14530
+ return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName, base) : null;
14391
14531
  }
14392
14532
  // `node` is the parse node of the render site (`<x render>` / `render-it` /
14393
14533
  // `render-each`, or the view's root anode for the app root). It keys the
14394
14534
  // cache as a globally-unique object: node ids alone are unique only within a
14395
14535
  // single view, so the same value rendered by two components (e.g. through a
14396
14536
  // shared dynamic-var sequence) would otherwise collide in the cache.
14397
- _rValComp(stack, val, comp, node, key, viewName) {
14398
- const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}`;
14537
+ _rValComp(stack, val, comp, node, key, viewName, base = null) {
14538
+ const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}${stack.renderPath.addressKey}`;
14399
14539
  const cachePath = [node, val];
14400
14540
  stack._pushDynBindValuesToArray(cachePath, comp);
14401
14541
  const cachedNode = this.cache.get(cachePath, cacheKey);
@@ -14403,30 +14543,43 @@ var init_renderer = __esm({
14403
14543
  const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
14404
14544
  const body = this.renderView(view, stack);
14405
14545
  if (body == null) return null;
14546
+ const baseJson = base === null ? null : pathToJson(base);
14406
14547
  const meta = this._renderMetadata({
14407
14548
  $: "Comp",
14408
14549
  nid: node?.nodeId ?? null,
14409
14550
  cid: comp.id,
14410
- vid: view.name
14551
+ vid: view.name,
14552
+ ...baseJson === null ? null : { base: baseJson }
14411
14553
  });
14412
- const dom = new VFragment([meta, body]);
14554
+ const dom = new VFragment([
14555
+ meta,
14556
+ baseJson === null ? body : stampRenderBase(body, JSON.stringify(baseJson))
14557
+ ]);
14413
14558
  this.cache.set(cachePath, cacheKey, dom);
14414
14559
  return dom;
14415
14560
  }
14416
14561
  pushEachEntry(r, nid, attrName, key, dom) {
14417
14562
  r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
14418
14563
  }
14419
- renderEachWhen(stack, iterInfo, view, nid) {
14564
+ renderEachWhen(stack, each2) {
14565
+ const { iterInfo, node: view, nodeId: nid } = each2;
14420
14566
  const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
14567
+ const seqPath = iterInfo.val instanceof DynVal ? stack.lookupDynamicLocated(iterInfo.val.name)?.path ?? null : null;
14421
14568
  const r = [];
14422
14569
  const it = stack.it;
14423
14570
  const renderOne = (key, value, attrName, binds) => {
14571
+ const itemBase = seqPath === null ? null : keyedPath(seqPath, key);
14572
+ const itemStep = itemBase === null ? each2.itemStep(key) : null;
14573
+ const itemPath = itemBase !== null ? stack.renderPath.pushFrame(itemBase) : itemStep !== null ? stack.renderPath.pushItem(itemStep) : stack.renderPath;
14424
14574
  const cachePath = enricher ? [view, it, value] : [view, value];
14425
- const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
14575
+ const cacheKey = `${stack.viewsId ?? ""}${nid}${key}${itemPath.addressKey}`;
14426
14576
  const cachedNode = this.cache.get(cachePath, cacheKey);
14427
14577
  if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
14428
14578
  else {
14429
- const dom = this.renderView(view, stack.enter(value, binds, false));
14579
+ const dom = this.renderView(
14580
+ view,
14581
+ stack.enter(value, binds, false, itemPath, itemBase !== null)
14582
+ );
14430
14583
  if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
14431
14584
  this.cache.set(cachePath, cacheKey, dom);
14432
14585
  }
@@ -14482,7 +14635,7 @@ function renderToHTMLNode(document2, components, macros, rootState, ParseContext
14482
14635
  const comps = new Components();
14483
14636
  const renderer = new Renderer(comps);
14484
14637
  const app = new App(container, comps, renderer, ParseContext2);
14485
- const scope = app.registerComponents(components);
14638
+ const scope = app.registerComponents(components, { paths: opts.paths });
14486
14639
  if (macros) scope.registerMacros(macros);
14487
14640
  if (opts.intentHandlers) scope.registerIntentHandlers(opts.intentHandlers);
14488
14641
  app.rootViewName = opts.view ?? null;
@@ -14508,7 +14661,7 @@ async function renderToHTMLDriven(document2, components, macros, rootState, Pars
14508
14661
  );
14509
14662
  try {
14510
14663
  if (phase) {
14511
- dispatchPhase(rootDispatcher(app.transactor), new Path([]), phase, app.state.val);
14664
+ dispatchPhase(rootDispatcher(app.transactor), new DispatchPath(), phase, app.state.val);
14512
14665
  await app.transactor.settle();
14513
14666
  }
14514
14667
  return serializeContainer(container);
@@ -14746,7 +14899,7 @@ async function driveStack(stack, value, phase, opts = {}) {
14746
14899
  val
14747
14900
  );
14748
14901
  });
14749
- dispatchPhase(rootDispatcher(transactor), new Path([]), phase, value);
14902
+ dispatchPhase(rootDispatcher(transactor), new DispatchPath(), phase, value);
14750
14903
  await transactor.settle();
14751
14904
  return transactor.state.val;
14752
14905
  }