tutuca 0.13.3 → 0.15.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);
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,26 +1121,23 @@ 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, 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();
1076
1139
  init_immer2();
1077
1140
  NONE = /* @__PURE__ */ Symbol("NONE");
1078
- readKey = (value, key, dval = null) => {
1079
- if (value == null) return dval;
1080
- if (value instanceof Map) return value.has(key) ? value.get(key) : dval;
1081
- if (value instanceof Set) return value.has(key) ? key : dval;
1082
- return Object.hasOwn(value, key) ? value[key] : dval;
1083
- };
1084
1141
  writeKey = (value, key, next) => {
1085
1142
  if (value instanceof Map) value.set(key, next);
1086
1143
  else if (value instanceof Set) {
@@ -1088,20 +1145,15 @@ var init_path = __esm({
1088
1145
  value.add(next);
1089
1146
  } else value[key] = next;
1090
1147
  };
1091
- writeSeqKey = (value, key, next) => {
1092
- if (value instanceof Map || value instanceof Set || Array.isArray(value) || Object.hasOwn(value, key))
1093
- writeKey(value, key, next);
1094
- else if (typeof value?.set === "function") value.set(key, next);
1095
- else writeKey(value, key, next);
1096
- };
1148
+ writeSeqKey = (value, key, next) => !(value instanceof Map || value instanceof Set || Array.isArray(value)) && !Object.hasOwn(value, key) && typeof value?.set === "function" ? value.set(key, next) : writeKey(value, key, next);
1097
1149
  Step = class {
1098
- lookup(_v, dval = null) {
1099
- return dval;
1100
- }
1101
1150
  setDraftValue(_root, _v) {
1102
1151
  }
1103
- enterFrame(stack, next) {
1104
- return stack.enter(next, {}, true);
1152
+ // Re-enter this step while rebuilding a stack. `renderPath` is the dispatch
1153
+ // position AFTER this step: a rebuilt frame must carry the same render path
1154
+ // the renderer had there, or the provides it publishes would be located wrong.
1155
+ enterFrame(stack, next, renderPath) {
1156
+ return stack.enter(next, {}, true, renderPath);
1105
1157
  }
1106
1158
  toAbstractPathStep() {
1107
1159
  return this;
@@ -1118,41 +1170,29 @@ var init_path = __esm({
1118
1170
  return null;
1119
1171
  }
1120
1172
  };
1121
- BindStep = class _BindStep extends Step {
1173
+ BindStep = class extends Step {
1122
1174
  constructor(binds) {
1123
1175
  super();
1124
1176
  this.binds = binds;
1125
1177
  }
1126
- lookup(v, _dval) {
1178
+ lookup(v) {
1127
1179
  return v;
1128
1180
  }
1129
- enterFrame(stack, next) {
1130
- return stack.enter(next, { ...this.binds }, false);
1131
- }
1132
- withIndex(i) {
1133
- return new _BindStep({ ...this.binds, key: i });
1134
- }
1135
- withKey(key) {
1136
- return new _BindStep({ ...this.binds, key });
1181
+ enterFrame(stack, next, renderPath) {
1182
+ return stack.enter(next, { ...this.binds }, false, renderPath);
1137
1183
  }
1138
1184
  toAbstractPathStep() {
1139
1185
  return null;
1140
1186
  }
1141
1187
  };
1142
- ScopeBindStep = class _ScopeBindStep extends BindStep {
1188
+ ScopeBindStep = class extends BindStep {
1143
1189
  constructor(val, binds = {}) {
1144
1190
  super(binds);
1145
1191
  this.val = val;
1146
1192
  }
1147
- enterFrame(stack, next) {
1193
+ enterFrame(stack, next, renderPath) {
1148
1194
  const dyn = this.val.evalAsHandler(stack)?.call(stack.it) ?? {};
1149
- return stack.enter(next, { ...this.binds, ...dyn }, false);
1150
- }
1151
- withIndex(i) {
1152
- return new _ScopeBindStep(this.val, { ...this.binds, key: i });
1153
- }
1154
- withKey(key) {
1155
- return new _ScopeBindStep(this.val, { ...this.binds, key });
1195
+ return stack.enter(next, { ...this.binds, ...dyn }, false, renderPath);
1156
1196
  }
1157
1197
  };
1158
1198
  FieldStep = class extends Step {
@@ -1160,18 +1200,12 @@ var init_path = __esm({
1160
1200
  super();
1161
1201
  this.field = field;
1162
1202
  }
1163
- lookup(v, dval = null) {
1164
- return readKey(v, this.field, dval);
1203
+ lookup(v) {
1204
+ return seqGet(v, this.field, NONE);
1165
1205
  }
1166
1206
  setDraftValue(root, v) {
1167
1207
  writeKey(root, this.field, v);
1168
1208
  }
1169
- withIndex(i) {
1170
- return new SeqStep(this.field, i);
1171
- }
1172
- withKey(k) {
1173
- return new SeqStep(this.field, k);
1174
- }
1175
1209
  toKey() {
1176
1210
  return { field: this.field };
1177
1211
  }
@@ -1182,15 +1216,15 @@ var init_path = __esm({
1182
1216
  this.field = field;
1183
1217
  this.key = key;
1184
1218
  }
1185
- lookup(v, dval = null) {
1186
- return seqGet(readKey(v, this.field, null), this.key, dval);
1219
+ lookup(v) {
1220
+ return seqGet(seqGet(v, this.field, null), this.key, NONE);
1187
1221
  }
1188
1222
  setDraftValue(root, v) {
1189
- const seq = readKey(root, this.field, null);
1223
+ const seq = seqGet(root, this.field, null);
1190
1224
  if (seq != null) writeSeqKey(seq, this.key, v);
1191
1225
  }
1192
- enterFrame(stack, next) {
1193
- return stack.enter(next, { key: this.key }, true);
1226
+ enterFrame(stack, next, renderPath) {
1227
+ return stack.enter(next, { key: this.key }, true, renderPath);
1194
1228
  }
1195
1229
  toKey() {
1196
1230
  return { field: this.field, key: this.key };
@@ -1202,20 +1236,20 @@ var init_path = __esm({
1202
1236
  this.seqField = seqField;
1203
1237
  this.keyField = keyField;
1204
1238
  }
1205
- lookup(v, dval = null) {
1206
- const seq = readKey(v, this.seqField, NONE);
1207
- const key = readKey(v, this.keyField, NONE);
1208
- return key !== NONE && seq !== NONE ? seqGet(seq, key, dval) : dval;
1239
+ lookup(v) {
1240
+ const seq = seqGet(v, this.seqField, NONE);
1241
+ const key = seqGet(v, this.keyField, NONE);
1242
+ return key !== NONE && seq !== NONE ? seqGet(seq, key, NONE) : NONE;
1209
1243
  }
1210
1244
  setDraftValue(root, v) {
1211
- const seq = readKey(root, this.seqField, NONE);
1212
- const key = readKey(root, this.keyField, NONE);
1245
+ const seq = seqGet(root, this.seqField, NONE);
1246
+ const key = seqGet(root, this.keyField, NONE);
1213
1247
  if (seq !== NONE && key !== NONE) writeSeqKey(seq, key, v);
1214
1248
  }
1215
1249
  // Resolve `keyField` against `v` now and freeze it as a literal-key `SeqStep`, so a
1216
1250
  // later lookup/setValue lands on this same item even if `keyField` changes meanwhile.
1217
1251
  pinKey(v) {
1218
- const key = readKey(v, this.keyField, NONE);
1252
+ const key = seqGet(v, this.keyField, NONE);
1219
1253
  return key === NONE ? this : new SeqStep(this.seqField, key);
1220
1254
  }
1221
1255
  // The key is a *field reference* resolved live, so it is unknown without a value;
@@ -1226,67 +1260,31 @@ var init_path = __esm({
1226
1260
  }
1227
1261
  };
1228
1262
  EachBindStep = class extends Step {
1229
- constructor(iterInfo, key) {
1263
+ constructor(eachNode, key) {
1230
1264
  super();
1231
- this.iterInfo = iterInfo;
1265
+ this.eachNode = eachNode;
1232
1266
  this.key = key;
1233
1267
  }
1234
- lookup(v, _dval) {
1268
+ lookup(v) {
1235
1269
  return v;
1236
1270
  }
1237
1271
  // Replay the renderer's per-item binds (key, value + any @enrich-with binds)
1238
1272
  // 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);
1273
+ enterFrame(stack, next, renderPath) {
1274
+ return stack.enter(next, this.eachNode.enrichBinds(stack, this.key), false, renderPath);
1241
1275
  }
1242
1276
  toAbstractPathStep() {
1243
1277
  return null;
1244
1278
  }
1245
1279
  };
1246
1280
  EachRenderItStep = class extends SeqStep {
1247
- enterFrame(stack, next) {
1248
- return stack.enter(next, { key: this.key, value: next }, false).enter(next, {}, true);
1281
+ enterFrame(stack, next, renderPath) {
1282
+ return stack.enter(next, { key: this.key, value: next }, false, renderPath).enter(next, {}, true, renderPath);
1249
1283
  }
1250
1284
  toAbstractPathStep() {
1251
1285
  return new SeqStep(this.field, this.key);
1252
1286
  }
1253
1287
  };
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
1288
  Path = class _Path {
1291
1289
  constructor(steps = []) {
1292
1290
  this.steps = steps;
@@ -1294,43 +1292,13 @@ var init_path = __esm({
1294
1292
  concat(steps) {
1295
1293
  return new _Path(this.steps.concat(steps));
1296
1294
  }
1297
- popStep() {
1298
- return new _Path(this.steps.slice(0, -1));
1299
- }
1300
- // The dispatch path: frame-only steps removed, one step per crossed component
1301
- // (DynStep included). `popStep` over it bubbles through every component.
1295
+ // Frame-only steps removed, one step per crossed component: `popStep` over the
1296
+ // result bubbles through every component.
1302
1297
  compact() {
1303
1298
  const out = [];
1304
1299
  for (const step of this.steps) {
1305
1300
  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);
1301
+ if (s !== null) out.push(s);
1334
1302
  }
1335
1303
  return new _Path(out);
1336
1304
  }
@@ -1338,7 +1306,6 @@ var init_path = __esm({
1338
1306
  // key as it is *now* so a later lookup/setValue lands on the same item even if the
1339
1307
  // keyField changed meanwhile (e.g. the selected tab moved while an intent was in
1340
1308
  // 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
1309
  pinKeys(root) {
1343
1310
  let curVal = root;
1344
1311
  let out = null;
@@ -1346,7 +1313,7 @@ var init_path = __esm({
1346
1313
  const step = this.steps[i];
1347
1314
  const pinned = step.pinKey(curVal);
1348
1315
  if (pinned !== step) (out ??= this.steps.slice())[i] = pinned;
1349
- curVal = step.lookup(curVal, NONE);
1316
+ curVal = step.lookup(curVal);
1350
1317
  if (curVal === NONE) break;
1351
1318
  }
1352
1319
  return out ? new _Path(out) : this;
@@ -1354,20 +1321,20 @@ var init_path = __esm({
1354
1321
  lookup(v, dval = null) {
1355
1322
  let curVal = v;
1356
1323
  for (const step of this.steps) {
1357
- curVal = step.lookup(curVal, NONE);
1324
+ curVal = step.lookup(curVal);
1358
1325
  if (curVal === NONE) return dval;
1359
1326
  }
1360
1327
  return curVal;
1361
1328
  }
1362
1329
  // The values entered along the path, root→leaf (root included): index 0 is `root`,
1363
1330
  // 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).
1331
+ // unresolvable step. Used to walk the component instances on a dispatch path
1332
+ // (filter via Components.getCompFor).
1366
1333
  resolveChain(root) {
1367
1334
  const out = [root];
1368
1335
  let curVal = root;
1369
1336
  for (const step of this.steps) {
1370
- curVal = step.lookup(curVal, NONE);
1337
+ curVal = step.lookup(curVal);
1371
1338
  if (curVal === NONE) break;
1372
1339
  out.push(curVal);
1373
1340
  }
@@ -1376,7 +1343,7 @@ var init_path = __esm({
1376
1343
  // A flat `[{ field, key? }]` list of the addressing steps, skipping frame-only
1377
1344
  // steps (binds). Generic path introspection so tooling (e.g. the storybook
1378
1345
  // activity log) can identify which subtree a transaction touched without
1379
- // depending on Step internals. Call on a transaction path (no DynSteps).
1346
+ // depending on Step internals.
1380
1347
  toKeys() {
1381
1348
  const out = [];
1382
1349
  for (const step of this.steps) {
@@ -1390,69 +1357,132 @@ var init_path = __esm({
1390
1357
  return produce(root, (draft) => {
1391
1358
  let parent = draft;
1392
1359
  for (let i = 0; i < this.steps.length - 1; i++) {
1393
- parent = this.steps[i].lookup(parent, NONE);
1360
+ parent = this.steps[i].lookup(parent);
1394
1361
  if (parent === NONE) return;
1395
1362
  }
1396
1363
  this.steps.at(-1).setDraftValue(parent, v);
1397
1364
  });
1398
1365
  }
1366
+ };
1367
+ EMPTY_PATH = new Path([]);
1368
+ DispatchPath = class _DispatchPath {
1369
+ constructor(frames = [{ base: EMPTY_PATH, items: [] }]) {
1370
+ this.frames = frames;
1371
+ }
1372
+ // Plain addressing steps in one frame based at the root: what a caller means
1373
+ // by "this position" when it has no continuation of its own.
1374
+ static ofSteps(steps) {
1375
+ return new _DispatchPath([{ base: EMPTY_PATH, items: steps.slice() }]);
1376
+ }
1377
+ get top() {
1378
+ return this.frames[this.frames.length - 1];
1379
+ }
1380
+ _withTopItems(items) {
1381
+ const frames = this.frames.slice();
1382
+ frames[frames.length - 1] = { base: this.top.base, items };
1383
+ return new _DispatchPath(frames);
1384
+ }
1385
+ concat(steps) {
1386
+ return this._withTopItems(this.top.items.concat(steps));
1387
+ }
1388
+ pushItem(step) {
1389
+ return this.concat([step]);
1390
+ }
1391
+ pushFrame(base) {
1392
+ return new _DispatchPath(this.frames.concat({ base, items: [] }));
1393
+ }
1394
+ // Whether bubbling has anywhere left to go: another step in this frame, or a
1395
+ // visual caller underneath it. (A path always has at least one frame.)
1396
+ canPop() {
1397
+ return this.frames.length > 1 || this.frames[0].items.length > 0;
1398
+ }
1399
+ // One component closer to the root. At the top of a frame that is popping back
1400
+ // to the visual caller, not to the producer's own parent — the caller is where
1401
+ // the `*name` was written, and where an unhandled message should keep going.
1402
+ popStep() {
1403
+ const { top } = this;
1404
+ if (top.items.length > 0) return this._withTopItems(top.items.slice(0, -1));
1405
+ if (this.frames.length > 1) return new _DispatchPath(this.frames.slice(0, -1));
1406
+ return this;
1407
+ }
1408
+ // Drop frame-only steps inside every frame independently; a frame's base is
1409
+ // already addressing-only.
1410
+ compact() {
1411
+ return new _DispatchPath(
1412
+ this.frames.map(({ base, items }) => ({ base, items: new Path(items).compact().steps }))
1413
+ );
1414
+ }
1415
+ // A stable string for the ADDRESS this path denotes, for the render cache. The
1416
+ // same immutable value can sit at two places in the tree, and a subtree rendered
1417
+ // at one of them bakes that address in — the provides it publishes are located
1418
+ // there. Frame-only steps address nothing, so they are compacted out: two sites
1419
+ // that differ only in binds do render the same subtree.
1420
+ get addressKey() {
1421
+ this._addressKey ??= renderPathText(this.toTransactionPath().compact());
1422
+ return this._addressKey;
1423
+ }
1424
+ // The active transaction address: the top frame's absolute base followed by
1425
+ // its ordinary descendant steps. This is where a mutation lands.
1426
+ toTransactionPath() {
1427
+ const { base, items } = this.top;
1428
+ return base.concat(items);
1429
+ }
1430
+ // Rebuild the render stack this path was dispatched from. A frame with a base
1431
+ // re-enters at that absolute value first — replaying the resume a `*name`
1432
+ // render performed — and then walks its ordinary items.
1399
1433
  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;
1434
+ let renderPath = new _DispatchPath();
1435
+ for (let i = 0; i < this.frames.length; i++) {
1436
+ const { base, items } = this.frames[i];
1437
+ if (i > 0 || base.steps.length > 0) {
1438
+ const baseValue = base.lookup(stack.root, NONE);
1439
+ if (baseValue === NONE) {
1440
+ console.warn("bad frame base", { base, path: this });
1441
+ return null;
1442
+ }
1443
+ renderPath = renderPath.pushFrame(base);
1444
+ stack = stack.enter(baseValue, {}, true, renderPath);
1406
1445
  }
1407
- stack = step.enterFrame(stack, next);
1408
- prev = next;
1446
+ const walked = walkItems(stack, items, renderPath, this);
1447
+ if (walked === null) return null;
1448
+ [stack, renderPath] = walked;
1409
1449
  }
1410
1450
  return stack;
1411
1451
  }
1412
- static fromNodeAndEventName(node, eventName, rootNode, maxDepth, comps, stopOnNoEvent = true) {
1413
- const pathSteps = [];
1414
- const pendingDyns = [];
1452
+ // Reconstruct the dispatch position of a DOM event from the `data-*` stamps and
1453
+ // `§…§` meta comments the renderer left on the way down. `stopOnNoEvent` is off for
1454
+ // drag events, whose path is needed even where no handler is registered.
1455
+ static fromNodeAndEventName(node, eventName, rootNode, comps, stopOnNoEvent = true) {
1456
+ const parts = [];
1415
1457
  const bubbles = BUBBLING_EVENTS.has(eventName);
1416
- let depth = 0;
1417
1458
  let eventIds = [];
1418
1459
  let handlers = null;
1419
- let nodeIds = [];
1460
+ let nodeRefs = [];
1420
1461
  let isLeafComponent = true;
1421
1462
  const crossComponent = (cidNum, vid) => {
1422
1463
  const comp = comps.getComponentForId(cidNum);
1423
- let pushStep = true;
1464
+ let pushPart = true;
1424
1465
  if (handlers === null && (isLeafComponent || bubbles)) {
1425
1466
  handlers = findHandlers(comp, eventIds, vid, eventName);
1426
1467
  if (handlers === null) {
1427
1468
  if (isLeafComponent && stopOnNoEvent && !bubbles) return false;
1428
1469
  } else if (!isLeafComponent) {
1429
- pathSteps.length = 0;
1430
- pendingDyns.length = 0;
1431
- pushStep = false;
1470
+ parts.length = 0;
1471
+ pushPart = false;
1432
1472
  }
1433
1473
  }
1434
1474
  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
- }
1475
+ if (pushPart) {
1476
+ const part = resolvePathPart(comp, nodeRefs, vid);
1477
+ if (part) parts.push(part);
1446
1478
  }
1447
- for (let i = pendingDyns.length - 1; i >= 0; i--)
1448
- if (pendingDyns[i].producerCompId === cidNum) pendingDyns.splice(i, 1);
1449
1479
  eventIds = [];
1450
- nodeIds = [];
1480
+ nodeRefs = [];
1451
1481
  return true;
1452
1482
  };
1453
- while (node && node !== rootNode && depth < maxDepth) {
1483
+ while (node && node !== rootNode) {
1454
1484
  if (node?.dataset) {
1455
- const { eid, cid, vid } = node.dataset;
1485
+ const { eid, cid, vid, rp } = node.dataset;
1456
1486
  if (eid !== void 0) eventIds.push(eid);
1457
1487
  const metas = metaChain(node.previousSibling);
1458
1488
  let sawComp = false;
@@ -1460,19 +1490,26 @@ var init_path = __esm({
1460
1490
  if (m.$ === "Comp") {
1461
1491
  sawComp = true;
1462
1492
  if (!crossComponent(m.cid, m.vid)) return NO_EVENT_INFO;
1463
- nodeIds.push({ nid: m.nid });
1493
+ nodeRefs.push({ nid: m.nid, base: pathFromJson(m.base) });
1464
1494
  } else {
1465
- nodeIds.push({ nid: m.nid, si: m.si, sk: m.sk });
1495
+ nodeRefs.push({ nid: m.nid, si: m.si, sk: m.sk });
1496
+ }
1497
+ }
1498
+ if (!sawComp && cid !== void 0) {
1499
+ if (!crossComponent(+cid, vid)) return NO_EVENT_INFO;
1500
+ if (rp !== void 0) {
1501
+ const base = parseRenderPath(rp);
1502
+ if (base !== null) nodeRefs.push({ base });
1466
1503
  }
1467
1504
  }
1468
- if (!sawComp && cid !== void 0 && !crossComponent(+cid, vid)) return NO_EVENT_INFO;
1469
1505
  }
1470
- depth += 1;
1471
1506
  node = node.parentNode;
1472
1507
  }
1473
- if (pendingDyns.length > 0)
1474
- console.warn("event reconstruction: dynamic-var producer not found", pendingDyns);
1475
- return [new _Path(pathSteps.reverse()), handlers];
1508
+ parts.reverse();
1509
+ let path = new _DispatchPath();
1510
+ for (const part of parts)
1511
+ path = part.base !== void 0 ? path.pushFrame(part.base) : path.pushItem(part.step);
1512
+ return [path, handlers];
1476
1513
  }
1477
1514
  };
1478
1515
  StepCtx = class _StepCtx {
@@ -1500,13 +1537,6 @@ var init_path = __esm({
1500
1537
  resolveNode() {
1501
1538
  return this.comp.getNodeForId(+this.meta.nid, this.vid);
1502
1539
  }
1503
- applyKey(pi) {
1504
- if (pi === null) return null;
1505
- const m = this.meta;
1506
- if (m.si !== void 0) return pi.withIndex(+m.si);
1507
- if (m.sk !== void 0) return pi.withKey(m.sk);
1508
- return pi;
1509
- }
1510
1540
  };
1511
1541
  NO_EVENT_INFO = [null, null];
1512
1542
  BUBBLING_EVENTS = /* @__PURE__ */ new Set(["drop"]);
@@ -1514,6 +1544,9 @@ var init_path = __esm({
1514
1544
  constructor() {
1515
1545
  this.pathChanges = [];
1516
1546
  }
1547
+ toPath() {
1548
+ return new Path(this.pathChanges);
1549
+ }
1517
1550
  add(pathChange) {
1518
1551
  this.pathChanges.push(pathChange);
1519
1552
  return this;
@@ -1531,14 +1564,6 @@ var init_path = __esm({
1531
1564
  }
1532
1565
  });
1533
1566
 
1534
- // src/util/env.js
1535
- var isMac;
1536
- var init_env = __esm({
1537
- "src/util/env.js"() {
1538
- isMac = (globalThis.navigator?.userAgent ?? "").toLowerCase().includes("mac");
1539
- }
1540
- });
1541
-
1542
1567
  // src/value.js
1543
1568
  function sizeOf(v) {
1544
1569
  if (v == null) return null;
@@ -1675,25 +1700,12 @@ function _parsePredicate(s, tokens, px) {
1675
1700
  }
1676
1701
  return new PredicateVal(pred, args);
1677
1702
  }
1678
- function kindOf(val) {
1679
- if (val === null) return 0;
1680
- if (val instanceof ConstVal) return val.kind;
1681
- if (val instanceof StrTplVal) return val.kind;
1682
- if (val instanceof SeqAccessVal) return K_SEQ;
1683
- if (val instanceof FieldVal) return K_FIELD;
1684
- if (val instanceof MethodVal) return K_METHOD;
1685
- if (val instanceof BindVal) return K_BIND;
1686
- if (val instanceof DynVal) return K_DYN;
1687
- if (val instanceof NameVal) return K_NAME;
1688
- if (val instanceof EventMemberVal) return K_EVENT;
1689
- return 0;
1690
- }
1691
- 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;
1703
+ var isMac, 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, kindOf, BaseVal, ConstVal, NULL_CONST_VAL, PredicateVal, StrTplVal, NameVal, HandlerNameVal, mk404Handler, keyIs, macCtrl, nullSafe, EVENT_CONVENIENCES, EventMemberVal, SigilNameVal, BindVal, BindMemberVal, DynVal, FieldVal, MethodVal, SeqAccessVal;
1692
1704
  var init_value = __esm({
1693
1705
  "src/value.js"() {
1694
1706
  init_collection();
1695
1707
  init_path();
1696
- init_env();
1708
+ isMac = (globalThis.navigator?.userAgent ?? "").toLowerCase().includes("mac");
1697
1709
  VALID_VAL_ID_RE = /^[a-zA-Z][a-zA-Z0-9_]*\??$/;
1698
1710
  isValidValId = (name) => VALID_VAL_ID_RE.test(name);
1699
1711
  VALID_FLOAT_RE = /^-?[0-9]+(\.[0-9]+)?$/;
@@ -1740,9 +1752,10 @@ var init_value = __esm({
1740
1752
  parseMacroAttr = (s, px) => _parseSingle(s, px, G_ALL);
1741
1753
  parseReceiveHandler = (s, px) => _parseHandler(s, px, "receive", true, true, false);
1742
1754
  parseAlterHandler = (s, px) => _parseHandler(s, px, "alter", false, false, true)?.handlerVal ?? null;
1755
+ kindOf = (val) => val?.kind ?? 0;
1743
1756
  BaseVal = class {
1744
- render(_stack, _rx) {
1745
- }
1757
+ // No kind: never accepted by a group (PredicateVal, which only parseBool builds).
1758
+ kind = 0;
1746
1759
  eval(_stack) {
1747
1760
  }
1748
1761
  toPathItem() {
@@ -1762,9 +1775,6 @@ var init_value = __esm({
1762
1775
  this.val = val;
1763
1776
  this.kind = kind;
1764
1777
  }
1765
- render(_stack, _rx) {
1766
- return this.val;
1767
- }
1768
1778
  eval(_stack) {
1769
1779
  return this.val;
1770
1780
  }
@@ -1790,9 +1800,7 @@ var init_value = __esm({
1790
1800
  return `${this.pred.name} ${this.args.map(String).join(" ")}`;
1791
1801
  }
1792
1802
  };
1793
- VarVal = class extends BaseVal {
1794
- };
1795
- StrTplVal = class _StrTplVal extends VarVal {
1803
+ StrTplVal = class _StrTplVal extends BaseVal {
1796
1804
  constructor(vals) {
1797
1805
  super();
1798
1806
  this.vals = vals;
@@ -1805,9 +1813,6 @@ var init_value = __esm({
1805
1813
  for (const v of this.vals) if (!(v instanceof ConstVal) || v.fromMacroVar) return false;
1806
1814
  return true;
1807
1815
  }
1808
- render(stack, _rx) {
1809
- return this.eval(stack);
1810
- }
1811
1816
  eval(stack) {
1812
1817
  const strs = new Array(this.vals.length);
1813
1818
  for (let i = 0; i < this.vals.length; i++) strs[i] = this.vals[i]?.eval(stack, "");
@@ -1844,7 +1849,8 @@ var init_value = __esm({
1844
1849
  return new _StrTplVal(lo === 0 && hi === vals.length ? vals : vals.slice(lo, hi));
1845
1850
  }
1846
1851
  };
1847
- NameVal = class extends VarVal {
1852
+ NameVal = class extends BaseVal {
1853
+ kind = K_NAME;
1848
1854
  constructor(name) {
1849
1855
  super();
1850
1856
  this.name = name;
@@ -1894,6 +1900,7 @@ var init_value = __esm({
1894
1900
  dragKey: nullSafe((info) => info.lookupBind("key"))
1895
1901
  };
1896
1902
  EventMemberVal = class extends BaseVal {
1903
+ kind = K_EVENT;
1897
1904
  constructor(members) {
1898
1905
  super();
1899
1906
  this.members = members;
@@ -1916,24 +1923,21 @@ var init_value = __esm({
1916
1923
  return `e.${this.members.join(".")}`;
1917
1924
  }
1918
1925
  };
1919
- RenderVal = class extends BaseVal {
1920
- render(stack, _rx) {
1921
- return this.eval(stack);
1922
- }
1923
- };
1924
- RenderNameVal = class extends RenderVal {
1926
+ SigilNameVal = class extends BaseVal {
1925
1927
  constructor(name) {
1926
1928
  super();
1927
1929
  this.name = name;
1928
1930
  }
1931
+ toString() {
1932
+ return this.sigil + this.name;
1933
+ }
1929
1934
  };
1930
- BindVal = class extends RenderNameVal {
1935
+ BindVal = class extends SigilNameVal {
1936
+ kind = K_BIND;
1937
+ sigil = "@";
1931
1938
  eval(stack) {
1932
1939
  return stack.lookupBind(this.name);
1933
1940
  }
1934
- toString() {
1935
- return `@${this.name}`;
1936
- }
1937
1941
  };
1938
1942
  BindMemberVal = class extends BindVal {
1939
1943
  constructor(name, member) {
@@ -1948,37 +1952,35 @@ var init_value = __esm({
1948
1952
  return `@${this.name}.${this.member}`;
1949
1953
  }
1950
1954
  };
1951
- DynVal = class extends RenderNameVal {
1955
+ DynVal = class extends SigilNameVal {
1956
+ kind = K_DYN;
1957
+ sigil = "*";
1952
1958
  eval(stack) {
1953
1959
  return stack.lookupDynamic(this.name);
1954
1960
  }
1955
- toString() {
1956
- return `*${this.name}`;
1957
- }
1958
1961
  };
1959
- FieldVal = class extends RenderNameVal {
1962
+ FieldVal = class extends SigilNameVal {
1963
+ kind = K_FIELD;
1964
+ sigil = ".";
1960
1965
  eval(stack) {
1961
1966
  return stack.lookupFieldRaw(this.name);
1962
1967
  }
1963
1968
  toPathItem() {
1964
1969
  return new FieldStep(this.name);
1965
1970
  }
1966
- toString() {
1967
- return `.${this.name}`;
1968
- }
1969
1971
  };
1970
- MethodVal = class extends RenderNameVal {
1972
+ MethodVal = class extends SigilNameVal {
1973
+ kind = K_METHOD;
1974
+ sigil = "$";
1971
1975
  eval(stack) {
1972
1976
  return stack.lookupMethod(this.name);
1973
1977
  }
1974
1978
  evalAsHandler(stack) {
1975
1979
  return stack.lookupFieldRaw(this.name);
1976
1980
  }
1977
- toString() {
1978
- return `$${this.name}`;
1979
- }
1980
1981
  };
1981
- SeqAccessVal = class extends RenderVal {
1982
+ SeqAccessVal = class extends BaseVal {
1983
+ kind = K_SEQ;
1982
1984
  constructor(seqVal, keyVal) {
1983
1985
  super();
1984
1986
  this.seqVal = seqVal;
@@ -2013,11 +2015,11 @@ function parseDirectiveValue(px, directiveName, source, parser) {
2013
2015
  function parseIterationDirectives(attributes, px) {
2014
2016
  const parseNamed = (name) => {
2015
2017
  const attr = attributes.getNamedItem(`@${name}`);
2016
- return attr ? parseDirectiveValue(px, name, attr.value, parseAlterHandler) : null;
2018
+ return attr ? parseIterDirective(px, name, attr.value) : null;
2017
2019
  };
2018
2020
  return { whenVal: parseNamed("when"), loopWithVal: parseNamed("loop-with") };
2019
2021
  }
2020
- var Attributes, booleanAttrsRaw, booleanAttrs, AttrParser, ConstAttrs, DynAttrs, BaseAttr, Attr, ConstAttr, RawHtmlAttr, NOT_SET_VAL, IfAttr, EventHandler;
2022
+ var Attributes, booleanAttrsRaw, booleanAttrs, ITER_DIRECTIVES, parseIterDirective, AttrParser, ConstAttrs, DynAttrs, BaseAttr, Attr, ConstAttr, RawHtmlAttr, NOT_SET_VAL, IfAttr, EventHandler;
2021
2023
  var init_attribute = __esm({
2022
2024
  "src/attribute.js"() {
2023
2025
  init_value();
@@ -2025,15 +2027,18 @@ var init_attribute = __esm({
2025
2027
  constructor(items) {
2026
2028
  this.items = items;
2027
2029
  }
2028
- static parse(attributes, px, parseAll = false) {
2029
- return new AttrParser(px).parse(attributes, parseAll);
2030
- }
2031
2030
  isConstant() {
2032
2031
  return false;
2033
2032
  }
2034
2033
  };
2035
2034
  booleanAttrsRaw = "itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly,async,autofocus,autoplay,controls,default,defer,disabled,hidden,inert,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected";
2036
2035
  booleanAttrs = new Set(booleanAttrsRaw.split(","));
2036
+ ITER_DIRECTIVES = {
2037
+ when: "whenVal",
2038
+ "loop-with": "loopWithVal",
2039
+ "enrich-with": "enrichWithVal"
2040
+ };
2041
+ parseIterDirective = (px, name, s) => parseDirectiveValue(px, name, s, parseAlterHandler);
2037
2042
  AttrParser = class {
2038
2043
  constructor(px) {
2039
2044
  this.px = px;
@@ -2071,11 +2076,22 @@ var init_attribute = __esm({
2071
2076
  this.px.onParseIssue("bad-value", info);
2072
2077
  }
2073
2078
  }
2074
- parseThen(s) {
2075
- if (this.ifAttr) this.ifAttr.thenVal = parseText(s, this.px) ?? NOT_SET_VAL;
2079
+ // `@then` / `@else` fill the branches of the `@if.<attr>` on the same element.
2080
+ parseBranch(slot, name, s) {
2081
+ if (this.ifAttr === null) return this._orphan(name, s, "if");
2082
+ this.ifAttr[slot] = parseText(s, this.px) ?? NOT_SET_VAL;
2083
+ }
2084
+ // `@when` / `@loop-with` / `@enrich-with` amend the `@each` on the same element. A
2085
+ // loop-less `@enrich-with` is a scope of its own; the other two need the loop.
2086
+ parseIter(name, s) {
2087
+ const val = parseIterDirective(this.px, name, s);
2088
+ if (this.eachAttr !== null) this.eachAttr[ITER_DIRECTIVES[name]] = val;
2089
+ else if (name === "enrich-with") this.pushWrapper("scope", s, val);
2090
+ else this._orphan(name, s, "each");
2076
2091
  }
2077
- parseElse(value) {
2078
- if (this.ifAttr) this.ifAttr.elseVal = parseText(value, this.px) ?? NOT_SET_VAL;
2092
+ // A directive that only means something next to another one, written alone.
2093
+ _orphan(name, value, needs) {
2094
+ this.px.onParseIssue("orphan-directive", { name, value, needs });
2079
2095
  }
2080
2096
  parseEvent(directiveName, value) {
2081
2097
  const [eventName, ...modifiers] = directiveName.slice(3).split("+");
@@ -2089,77 +2105,51 @@ var init_attribute = __esm({
2089
2105
  this.events.add(eventName, handler, modifiers);
2090
2106
  }
2091
2107
  }
2092
- _parseDirectiveValue(directiveName, s, parserFn) {
2093
- return parseDirectiveValue(this.px, directiveName, s, parserFn);
2094
- }
2095
2108
  parseDirective(s, directiveName) {
2096
2109
  switch (directiveName) {
2097
2110
  case "dangerouslysetinnerhtml":
2098
2111
  this.attrs ??= [];
2099
- this.attrs.push(new RawHtmlAttr(this._parseDirectiveValue(directiveName, s, parseText)));
2112
+ this.attrs.push(new RawHtmlAttr(parseDirectiveValue(this.px, directiveName, s, parseText)));
2100
2113
  this.hasDynamic = true;
2101
2114
  return;
2102
2115
  case "push-view":
2103
- this.pushWrapper("push-view", s, this._parseDirectiveValue(directiveName, s, parseText));
2116
+ this.pushWrapper("push-view", s, parseDirectiveValue(this.px, directiveName, s, parseText));
2104
2117
  return;
2105
2118
  case "text":
2106
- this.textChild = this._parseDirectiveValue(directiveName, s, parseText);
2119
+ this.textChild = parseDirectiveValue(this.px, directiveName, s, parseText);
2107
2120
  return;
2108
2121
  case "show":
2109
- this.pushWrapper("show", s, this._parseDirectiveValue(directiveName, s, parseBool));
2122
+ this.pushWrapper("show", s, parseDirectiveValue(this.px, directiveName, s, parseBool));
2110
2123
  return;
2111
2124
  case "hide":
2112
- this.pushWrapper("hide", s, this._parseDirectiveValue(directiveName, s, parseBool));
2125
+ this.pushWrapper("hide", s, parseDirectiveValue(this.px, directiveName, s, parseBool));
2113
2126
  return;
2114
2127
  case "each": {
2115
- const val = this._parseDirectiveValue(directiveName, s, parseSequence);
2128
+ const val = parseDirectiveValue(this.px, directiveName, s, parseSequence);
2116
2129
  this.eachAttr = this.pushWrapper("each", s, val);
2117
2130
  return;
2118
2131
  }
2119
- case "enrich-with":
2120
- if (this.eachAttr !== null)
2121
- this.eachAttr.enrichWithVal = this._parseDirectiveValue(
2122
- directiveName,
2123
- s,
2124
- parseAlterHandler
2125
- );
2126
- else
2127
- this.pushWrapper(
2128
- "scope",
2129
- s,
2130
- this._parseDirectiveValue(directiveName, s, parseAlterHandler)
2131
- );
2132
- return;
2133
2132
  case "when":
2134
- this._parseWhen(s);
2135
- return;
2136
2133
  case "loop-with":
2137
- this._parseLoopWith(s);
2134
+ case "enrich-with":
2135
+ this.parseIter(directiveName, s);
2138
2136
  return;
2139
2137
  case "then":
2140
- this.parseThen(s);
2138
+ this.parseBranch("thenVal", directiveName, s);
2141
2139
  return;
2142
2140
  case "else":
2143
- this.parseElse(s);
2141
+ this.parseBranch("elseVal", directiveName, s);
2144
2142
  return;
2145
2143
  }
2146
2144
  if (directiveName.startsWith("on.")) this.parseEvent(directiveName, s);
2147
2145
  else if (directiveName.startsWith("if.")) this.parseIf(directiveName, s);
2148
- else if (directiveName.startsWith("then.")) this.parseThen(s);
2149
- else if (directiveName.startsWith("else.")) this.parseElse(s);
2146
+ else if (directiveName.startsWith("then.")) this.parseBranch("thenVal", directiveName, s);
2147
+ else if (directiveName.startsWith("else.")) this.parseBranch("elseVal", directiveName, s);
2150
2148
  else {
2151
2149
  const info = { name: directiveName, value: s };
2152
2150
  this.px.onParseIssue("unknown-directive", info);
2153
2151
  }
2154
2152
  }
2155
- _parseWhen(s) {
2156
- if (this.eachAttr !== null)
2157
- this.eachAttr.whenVal = this._parseDirectiveValue("when", s, parseAlterHandler);
2158
- }
2159
- _parseLoopWith(s) {
2160
- if (this.eachAttr !== null)
2161
- this.eachAttr.loopWithVal = this._parseDirectiveValue("loop-with", s, parseAlterHandler);
2162
- }
2163
2153
  parse(attributes, parseAll = false) {
2164
2154
  for (const { name, value } of attributes) {
2165
2155
  const charCode = name.charCodeAt(0);
@@ -2420,6 +2410,17 @@ function addChild(normalizedChildren, child) {
2420
2410
  else normalizedChildren.push(child);
2421
2411
  } else normalizedChildren.push(new VText(child));
2422
2412
  }
2413
+ function applyAttrs(node, attrs, placeChildren) {
2414
+ const hasValue = "value" in attrs;
2415
+ const hasChecked = "checked" in attrs;
2416
+ if (hasValue || hasChecked) {
2417
+ const { value: _v, checked: _c, ...rest } = attrs;
2418
+ applyProperties(node, rest);
2419
+ } else applyProperties(node, attrs);
2420
+ placeChildren();
2421
+ if (hasValue) applyValueLast(node, attrs.value);
2422
+ if (hasChecked) setProp(node, "checked", attrs.checked, isNamespaced(node));
2423
+ }
2423
2424
  function diffProps(a, b) {
2424
2425
  if (a === b) return null;
2425
2426
  let diff = null;
@@ -2450,22 +2451,16 @@ function morphNode(domNode, source, target, opts) {
2450
2451
  }
2451
2452
  if (type3 === 1 && source.isSameKind(target)) {
2452
2453
  const propsDiff = diffProps(source.attrs, target.attrs);
2453
- const hasValue = propsDiff != null && "value" in propsDiff;
2454
- const hasChecked = propsDiff != null && "checked" in propsDiff;
2455
- if (propsDiff) {
2456
- if (hasValue || hasChecked) {
2457
- const { value: _v, checked: _c, ...rest } = propsDiff;
2458
- applyProperties(domNode, rest);
2459
- } else applyProperties(domNode, propsDiff);
2460
- }
2461
- if (!target.attrs.dangerouslySetInnerHTML) {
2454
+ const placeChildren = () => {
2455
+ if (target.attrs.dangerouslySetInnerHTML) return;
2462
2456
  const ns = effectiveNs(target, opts);
2463
2457
  morphChildren(domNode, source.childs, target.childs, childOpts(target, ns, opts));
2458
+ };
2459
+ if (propsDiff) applyAttrs(domNode, propsDiff, placeChildren);
2460
+ else placeChildren();
2461
+ if (!(propsDiff && "value" in propsDiff) && source.tag === "SELECT") {
2462
+ if (target.attrs.value !== void 0) applyValueLast(domNode, target.attrs.value);
2464
2463
  }
2465
- if (hasValue) applyValueLast(domNode, propsDiff.value);
2466
- else if (source.tag === "SELECT" && target.attrs.value !== void 0)
2467
- applyValueLast(domNode, target.attrs.value);
2468
- if (hasChecked) setProp(domNode, "checked", propsDiff.checked, false);
2469
2464
  return domNode;
2470
2465
  }
2471
2466
  if (type3 === 11) {
@@ -2685,16 +2680,7 @@ var init_vdom = __esm({
2685
2680
  const createOpts = attrs.is != null ? { is: attrs.is } : void 0;
2686
2681
  const node = ns === null ? doc.createElement(tag, createOpts) : doc.createElementNS(ns, tag, createOpts);
2687
2682
  const cOpts = childOpts(this, ns, opts);
2688
- if ("value" in attrs || "checked" in attrs) {
2689
- const { value, checked, ...rest } = attrs;
2690
- applyProperties(node, rest);
2691
- appendChildNodes(node, this.childs, cOpts);
2692
- if (value !== void 0) applyValueLast(node, value);
2693
- if (checked !== void 0) setProp(node, "checked", checked, false);
2694
- } else {
2695
- applyProperties(node, attrs);
2696
- appendChildNodes(node, this.childs, cOpts);
2697
- }
2683
+ applyAttrs(node, attrs, () => appendChildNodes(node, this.childs, cOpts));
2698
2684
  return node;
2699
2685
  }
2700
2686
  };
@@ -2702,20 +2688,16 @@ var init_vdom = __esm({
2702
2688
  });
2703
2689
 
2704
2690
  // src/anode.js
2705
- function resolveDynProducer(comp, name) {
2706
- const own = comp?.provide?.[name];
2707
- const producerComp = own != null ? comp : comp?.scope?.lookupProvider(name) ?? null;
2708
- const producerProvide = own ?? producerComp?.provide?.[name];
2709
- if (producerComp == null || producerProvide == null) return null;
2710
- const pi = producerProvide.val?.toPathItem?.() ?? null;
2711
- return { producerCompId: producerComp.id, producerSteps: pi ? [pi] : [] };
2712
- }
2713
- function optimizeChilds(childs) {
2714
- for (let i = 0; i < childs.length; i++) {
2715
- const child = childs[i];
2716
- if (child.isConstant()) childs[i] = new RenderOnceNode(child);
2717
- else child.optimize();
2691
+ function renderTarget(val, stack) {
2692
+ if (val instanceof DynVal) {
2693
+ const loc = stack.lookupDynamicLocated(val.name);
2694
+ if (loc?.path == null) return [loc?.value ?? null, stack.renderPath, null];
2695
+ return [loc.value, stack.renderPath.pushFrame(loc.path), loc.path];
2718
2696
  }
2697
+ const step = val.toPathItem?.() ?? null;
2698
+ if (step === null) return [val.eval(stack), stack.renderPath, null];
2699
+ const path = stack.renderPath.pushItem(step);
2700
+ return [val.eval(stack), path, stack.pendingFrame ? path.toTransactionPath() : null];
2719
2701
  }
2720
2702
  function optimizeNode(node) {
2721
2703
  if (node.isConstant()) return new RenderOnceNode(node);
@@ -2816,11 +2798,6 @@ function makeWrapperNode(data, px) {
2816
2798
  }
2817
2799
  return node;
2818
2800
  }
2819
- function dynRenderStep(comp, name, key) {
2820
- const p = resolveDynProducer(comp, name);
2821
- if (!p) return null;
2822
- return key === void 0 ? new DynStep(p.producerCompId, p.producerSteps) : new DynEachStep(p.producerCompId, p.producerSteps, key);
2823
- }
2824
2801
  function parseRenderEach(px, value, as, attrs) {
2825
2802
  const seqVal = parseXOpVal("render-each", value, px, parseSequence);
2826
2803
  if (seqVal === null) return null;
@@ -2837,7 +2814,7 @@ function xOp(consumed = [], { wrappable = false, wrapper = null, ignoresChildren
2837
2814
  return { consumed: new Set(consumed), wrappable, wrapper, ignoresChildren };
2838
2815
  }
2839
2816
  function trimEdgeWhite(node) {
2840
- if (!node.isWhiteSpace?.()) return false;
2817
+ if (!node.isWhiteSpace()) return false;
2841
2818
  node.condenseWhiteSpace();
2842
2819
  return true;
2843
2820
  }
@@ -2848,7 +2825,7 @@ function condenseChildsWhites(childs) {
2848
2825
  if (last > 0 && trimEdgeWhite(childs[last])) emptied = true;
2849
2826
  for (let i = 1; i < last; i++) {
2850
2827
  const cur = childs[i];
2851
- if (!(cur.isWhiteSpace?.() && cur.hasNewLine())) continue;
2828
+ if (!(cur.isWhiteSpace() && cur.hasNewLine())) continue;
2852
2829
  const bothBlock = isBlockDomNode(childs[i - 1]) && isBlockDomNode(childs[i + 1]);
2853
2830
  cur.condenseWhiteSpace(bothBlock ? "" : " ");
2854
2831
  if (bothBlock) emptied = true;
@@ -2875,7 +2852,7 @@ function compileModifiers(eventName, names) {
2875
2852
  return w(this, f, args, ctx);
2876
2853
  };
2877
2854
  }
2878
- var BaseNode, TextNode, CommentNode, ChildsNode, DomNode, FragmentNode, maybeFragment, VALID_NODE_RE, ANode, MacroNode, RenderViewId, RenderNode, RenderItNode, RenderTextNode, RenderOnceNode, WrapperNode, ShowNode, HideNode, PushViewNameNode, SlotNode, ScopeNode, EachNode, IterInfo, X_OPS, WRAPPER_NODES, ParseContext, _htmlBlockTags, HTML_BLOCK_TAGS, isBlockDomNode, isEmptyText, isIgnorableXChild, hasMeaningfulChilds, View, NodeEvents, NodeEvent, fwdIfCtxPred, fwdIfEventPred, fwdIfKey, fwdCtrl, fwdMeta, fwdAlt, MOD_WRAPPERS_FOR_ANY_EVENT, MOD_WRAPPERS_BY_EVENT, MOD_EFFECTS, NO_WRAPPERS, identityModifierWrapper;
2855
+ var BaseNode, TextNode, CommentNode, ChildsNode, DomNode, FragmentNode, maybeFragment, VALID_NODE_RE, ANode, MacroNode, RenderViewId, RenderNode, RenderItNode, RenderTextNode, RenderOnceNode, WrapperNode, ShowNode, HideNode, PushViewNameNode, SlotNode, ScopeNode, EachNode, IterInfo, X_OPS, WRAPPER_NODES, ParseContext, _htmlBlockTags, HTML_BLOCK_TAGS, isBlockDomNode, isEmptyText, isIgnorableXChild, hasMeaningfulChilds, NodeEvents, NodeEvent, fwdIfCtxPred, fwdIfEventPred, fwdIfKey, fwdCtrl, fwdMeta, fwdAlt, MOD_WRAPPERS_FOR_ANY_EVENT, MOD_WRAPPERS_BY_EVENT, MOD_EFFECTS, NO_WRAPPERS, identityModifierWrapper;
2879
2856
  var init_anode = __esm({
2880
2857
  "src/anode.js"() {
2881
2858
  init_attribute();
@@ -2887,8 +2864,11 @@ var init_anode = __esm({
2887
2864
  render(_stack, _rx) {
2888
2865
  return null;
2889
2866
  }
2890
- setDataAttr(key, val) {
2891
- console.warn("setDataAttr not implemented for", this, { key, val });
2867
+ // Stamp a `data-*` attribute on the element(s) this node renders. A no-op for
2868
+ // nodes that produce no element of their own (text, `<x render*>`): a view whose
2869
+ // root is one of those records its component boundary in the `§Comp§` meta
2870
+ // comment instead (see Renderer._rValComp).
2871
+ setDataAttr(_key, _val) {
2892
2872
  }
2893
2873
  isConstant() {
2894
2874
  return false;
@@ -2927,8 +2907,6 @@ var init_anode = __esm({
2927
2907
  isConstant() {
2928
2908
  return true;
2929
2909
  }
2930
- setDataAttr(_key, _val) {
2931
- }
2932
2910
  };
2933
2911
  CommentNode = class extends TextNode {
2934
2912
  render(_stack, rx) {
@@ -2944,7 +2922,8 @@ var init_anode = __esm({
2944
2922
  return this.childs.every((v) => v.isConstant());
2945
2923
  }
2946
2924
  optimize() {
2947
- optimizeChilds(this.childs);
2925
+ const { childs } = this;
2926
+ for (let i = 0; i < childs.length; i++) childs[i] = optimizeNode(childs[i]);
2948
2927
  }
2949
2928
  };
2950
2929
  DomNode = class extends ChildsNode {
@@ -2956,8 +2935,7 @@ var init_anode = __esm({
2956
2935
  }
2957
2936
  render(stack, rx) {
2958
2937
  const childNodes = new Array(this.childs.length);
2959
- for (let i = 0; i < childNodes.length; i++)
2960
- childNodes[i] = this.childs[i]?.render?.(stack, rx) ?? null;
2938
+ for (let i = 0; i < childNodes.length; i++) childNodes[i] = this.childs[i].render(stack, rx);
2961
2939
  return rx.renderTag(this.tagName, this.attrs.eval(stack), childNodes, this.namespace);
2962
2940
  }
2963
2941
  setDataAttr(key, val) {
@@ -2969,7 +2947,7 @@ var init_anode = __esm({
2969
2947
  };
2970
2948
  FragmentNode = class extends ChildsNode {
2971
2949
  render(stack, rx) {
2972
- return rx.renderFragment(this.childs.map((c) => c?.render(stack, rx)));
2950
+ return rx.renderFragment(this.childs.map((c) => c.render(stack, rx)));
2973
2951
  }
2974
2952
  setDataAttr(key, val) {
2975
2953
  for (const child of this.childs) child.setDataAttr(key, val);
@@ -2983,8 +2961,10 @@ var init_anode = __esm({
2983
2961
  this.nodeId = nodeId;
2984
2962
  this.val = val;
2985
2963
  }
2986
- toPathStep(ctx) {
2987
- return ctx.applyKey(this.val?.toPathItem?.() ?? null);
2964
+ // The addressing step this node contributes to an event's dispatch path, given
2965
+ // the `StepCtx` of the meta that named it; null when it addresses nothing.
2966
+ toPathStep(_ctx) {
2967
+ return this.val?.toPathItem?.() ?? null;
2988
2968
  }
2989
2969
  static parse(html, px) {
2990
2970
  const nodes = px.parseHTML(html);
@@ -3000,8 +2980,8 @@ var init_anode = __esm({
3000
2980
  return maybeFragment(trimmed);
3001
2981
  }
3002
2982
  static fromDOM(node, px) {
3003
- if (node instanceof px.Text) return new TextNode(node.textContent);
3004
- else if (node instanceof px.Comment) return new CommentNode(node.textContent);
2983
+ if (node.nodeType === 3) return new TextNode(node.textContent);
2984
+ if (node.nodeType === 8) return new CommentNode(node.textContent);
3005
2985
  const { childNodes, attributes: attrs, tagName: tag } = node;
3006
2986
  const childs = [];
3007
2987
  for (let i = 0; i < childNodes.length; i++) {
@@ -3019,11 +2999,11 @@ var init_anode = __esm({
3019
2999
  const slotName = attrs.getNamedItem("name")?.value ?? "_";
3020
3000
  return px.frame.macroSlots[slotName] ?? maybeFragment(childs);
3021
3001
  }
3022
- const [nAttrs, wrappers] = Attributes.parse(attrs, px, true);
3002
+ const [nAttrs, wrappers] = new AttrParser(px).parse(attrs, true);
3023
3003
  px.onAttributes(nAttrs, wrappers, null, true, tag);
3024
3004
  return wrap(px.newMacroNode(macroName, nAttrs.toMacroVars(), childs), px, wrappers);
3025
3005
  } else if (VALID_NODE_RE.test(tag)) {
3026
- const [nAttrs, wrappers, textChild] = Attributes.parse(attrs, px);
3006
+ const [nAttrs, wrappers, textChild] = new AttrParser(px).parse(attrs);
3027
3007
  px.onAttributes(nAttrs, wrappers, textChild, false, tag);
3028
3008
  if (textChild) childs.unshift(new RenderTextNode(null, textChild));
3029
3009
  const domChilds = tag !== "PRE" ? condenseChildsWhites(childs) : childs;
@@ -3082,34 +3062,32 @@ var init_anode = __esm({
3082
3062
  evalViewName(stack) {
3083
3063
  return this.viewVal ? this.viewVal.eval(stack) : null;
3084
3064
  }
3085
- // A `<x render*>` produces no DOM element of its own to carry `data-cid`;
3086
- // when it is a view's root the component boundary is recorded in the `Comp`
3087
- // meta comment instead (see Renderer._rValComp), so this is a no-op.
3088
- setDataAttr(_key, _val) {
3089
- }
3090
3065
  };
3091
3066
  RenderNode = class extends RenderViewId {
3092
3067
  render(stack, rx) {
3093
- const newStack = stack.enter(this.val.eval(stack), {}, true);
3094
- return rx.renderIt(newStack, this, "", this.evalViewName(stack));
3068
+ const [value, renderPath, base] = renderTarget(this.val, stack);
3069
+ const newStack = stack.enter(value, {}, true, renderPath, false);
3070
+ return rx.renderIt(newStack, this, this.evalViewName(stack), base);
3095
3071
  }
3072
+ // A `*name` target contributes no step: the site recorded the absolute base it
3073
+ // resumed at, and event reconstruction turns that into a continuation frame.
3096
3074
  toPathStep(ctx) {
3097
- if (this.val instanceof DynVal) return dynRenderStep(ctx.comp, this.val.name);
3075
+ if (this.val instanceof DynVal) return null;
3098
3076
  return super.toPathStep(ctx);
3099
3077
  }
3100
3078
  };
3101
3079
  RenderItNode = class extends RenderViewId {
3102
3080
  render(stack, rx) {
3103
- const newStack = stack.enter(stack.it, {}, true);
3104
- return rx.renderIt(newStack, this, "", this.evalViewName(stack));
3081
+ const base = stack.pendingFrame ? stack.renderPath.toTransactionPath() : null;
3082
+ const newStack = stack.enter(stack.it, {}, true, stack.renderPath, false);
3083
+ return rx.renderIt(newStack, this, this.evalViewName(stack), base);
3105
3084
  }
3106
3085
  toPathStep(ctx) {
3107
3086
  const next = ctx.next();
3108
3087
  if (next === null) return null;
3109
3088
  const nextNode = next.resolveNode();
3110
3089
  if (nextNode instanceof EachNode && next.hasKey) {
3111
- if (nextNode.val instanceof DynVal)
3112
- return dynRenderStep(ctx.comp, nextNode.val.name, next.key);
3090
+ if (nextNode.val instanceof DynVal) return null;
3113
3091
  return new EachRenderItStep(nextNode.val.name, next.key);
3114
3092
  }
3115
3093
  return null;
@@ -3119,9 +3097,6 @@ var init_anode = __esm({
3119
3097
  render(stack, _rx) {
3120
3098
  return this.val.eval(stack);
3121
3099
  }
3122
- // Renders to a text node, which can't carry `data-cid`.
3123
- setDataAttr(_key, _val) {
3124
- }
3125
3100
  };
3126
3101
  RenderOnceNode = class extends BaseNode {
3127
3102
  constructor(node) {
@@ -3196,36 +3171,46 @@ var init_anode = __esm({
3196
3171
  EachNode = class extends WrapperNode {
3197
3172
  constructor(nodeId, val) {
3198
3173
  super(nodeId, val);
3199
- this.iterInfo = new IterInfo(val, null, null, null);
3174
+ this.iterInfo = new IterInfo();
3200
3175
  }
3201
3176
  render(stack, rx) {
3202
- return rx.renderEachWhen(stack, this.iterInfo, this.node, this.nodeId);
3203
- }
3204
- toPathStep(ctx) {
3205
- return ctx.hasKey ? new EachBindStep(this.iterInfo, ctx.key) : null;
3177
+ return rx.renderEachWhen(stack, this);
3206
3178
  }
3207
- static register = true;
3208
- };
3209
- IterInfo = class {
3210
- constructor(val, whenVal, loopWithVal, enrichWithVal) {
3211
- this.val = val;
3212
- this.whenVal = whenVal;
3213
- this.loopWithVal = loopWithVal;
3214
- this.enrichWithVal = enrichWithVal;
3215
- }
3216
- eval(stack) {
3217
- const seq = this.val.eval(stack) ?? [];
3218
- const filter = this.whenVal?.evalAsHandler(stack) ?? filterAlwaysTrue;
3219
- const loopWith = this.loopWithVal?.evalAsHandler(stack) ?? nullLoopWith;
3220
- const enricher = this.enrichWithVal?.evalAsHandler(stack) ?? null;
3221
- return { seq, filter, loopWith, enricher };
3179
+ // The sequence and the three loop handlers, resolved against `stack`.
3180
+ evalIter(stack) {
3181
+ const { whenVal, loopWithVal, enrichWithVal } = this.iterInfo;
3182
+ return {
3183
+ seq: this.val.eval(stack) ?? [],
3184
+ filter: whenVal?.evalAsHandler(stack) ?? filterAlwaysTrue,
3185
+ loopWith: loopWithVal?.evalAsHandler(stack) ?? nullLoopWith,
3186
+ enricher: enrichWithVal?.evalAsHandler(stack) ?? null
3187
+ };
3222
3188
  }
3223
3189
  // Rebuild the per-item binds for `key` (see iteration.js bindsForKey).
3224
3190
  enrichBinds(stack, key) {
3225
- const { seq, filter, loopWith, enricher } = this.eval(stack);
3191
+ const { seq, filter, loopWith, enricher } = this.evalIter(stack);
3226
3192
  const ctx = makeLoopCtx(stack, filter);
3227
3193
  return bindsForKey({ seq, it: stack.it, loopWith, enricher, ctx }, key);
3228
3194
  }
3195
+ toPathStep(ctx) {
3196
+ return ctx.hasKey ? new EachBindStep(this, ctx.key) : null;
3197
+ }
3198
+ // Where one item lives, for the render path. `@each` re-binds `it` to the item
3199
+ // whether or not the body is a component, so the position moves either way and
3200
+ // the step has to address it — `.rows` iterated at `key` IS `rows[key]`.
3201
+ // Null for a dynamic sequence: `*rows` carries its own absolute path and enters
3202
+ // a continuation frame instead (see Renderer.renderEachWhen).
3203
+ itemStep(key) {
3204
+ return this.val instanceof FieldVal ? new SeqStep(this.val.name, key) : null;
3205
+ }
3206
+ static register = true;
3207
+ };
3208
+ IterInfo = class {
3209
+ constructor() {
3210
+ this.whenVal = null;
3211
+ this.loopWithVal = null;
3212
+ this.enrichWithVal = null;
3213
+ }
3229
3214
  };
3230
3215
  X_OPS = {
3231
3216
  slot: xOp(),
@@ -3249,26 +3234,31 @@ var init_anode = __esm({
3249
3234
  scope: ScopeNode,
3250
3235
  "push-view": PushViewNameNode
3251
3236
  };
3252
- ParseContext = class _ParseContext {
3253
- constructor(document2, Text, Comment, nodes, events2, macroNodes, frame, parent) {
3254
- this.nodes = nodes ?? [];
3255
- this.events = events2 ?? [];
3256
- this.macroNodes = macroNodes ?? [];
3257
- this.parent = parent ?? null;
3258
- this.frame = frame ?? {};
3259
- this.document = document2 ?? globalThis.document;
3260
- this.Text = Text ?? globalThis.Text;
3261
- this.Comment = Comment ?? globalThis.Comment;
3262
- this.cacheConstNodes = true;
3237
+ ParseContext = class {
3238
+ constructor(document2 = globalThis.document) {
3239
+ this.document = document2;
3240
+ this.nodes = [];
3241
+ this.events = [];
3242
+ this.macroNodes = [];
3243
+ this.parent = null;
3244
+ this.frame = {};
3263
3245
  this.currentTag = null;
3264
3246
  }
3265
3247
  isInsideMacro(name) {
3266
3248
  return this.frame.macroName === name || this.parent?.isInsideMacro(name);
3267
3249
  }
3250
+ // A macro body parses in a child context that shares every accumulator with
3251
+ // its parent (nodes, events, macroNodes, and whatever a subclass collects) and
3252
+ // differs only in its frame. Cloned rather than constructed so a subclass
3253
+ // keeps collecting inside macro bodies without re-implementing this — the
3254
+ // lint and class-set contexts used to lose (or had to copy) exactly that.
3268
3255
  enterMacro(macroName, macroVars, macroSlots) {
3269
- const { document: document2, Text, Comment, nodes, events: events2, macroNodes } = this;
3270
- const frame = { macroName, macroVars, macroSlots };
3271
- return new _ParseContext(document2, Text, Comment, nodes, events2, macroNodes, frame, this);
3256
+ const child = Object.create(Object.getPrototypeOf(this));
3257
+ return Object.assign(child, this, {
3258
+ frame: { macroName, macroVars, macroSlots },
3259
+ parent: this,
3260
+ currentTag: null
3261
+ });
3272
3262
  }
3273
3263
  parseHTML(html) {
3274
3264
  const t = this.document.createElement("template");
@@ -3333,33 +3323,8 @@ var init_anode = __esm({
3333
3323
  return node instanceof DomNode && HTML_BLOCK_TAGS.has(node.tagName);
3334
3324
  };
3335
3325
  isEmptyText = (c) => c instanceof TextNode && c.val === "";
3336
- isIgnorableXChild = (c) => c instanceof CommentNode || (c.isWhiteSpace?.() ?? false);
3326
+ isIgnorableXChild = (c) => c instanceof CommentNode || c.isWhiteSpace();
3337
3327
  hasMeaningfulChilds = (childs) => childs.some((c) => !isIgnorableXChild(c));
3338
- View = class {
3339
- constructor(name, rawView = "No View Defined", style = "", anode = null, ctx = null) {
3340
- this.name = name;
3341
- this.anode = anode;
3342
- this.style = style;
3343
- this.ctx = ctx;
3344
- this.rawView = rawView;
3345
- }
3346
- compile(ctx, scope, cid) {
3347
- this.ctx = ctx;
3348
- this.anode = ANode.parse(this.rawView, ctx);
3349
- this.anode.setDataAttr("data-cid", cid);
3350
- this.anode.setDataAttr("data-vid", this.name);
3351
- this.ctx.compile(scope);
3352
- if (ctx.cacheConstNodes) this.anode = optimizeNode(this.anode);
3353
- }
3354
- render(stack, rx) {
3355
- if (this.anode === null) {
3356
- throw new Error(
3357
- `tutuca: view "${this.name}" was rendered before it was compiled — its component is not registered in this app/scope. Source: ${String(this.rawView).slice(0, 80).replace(/\s+/g, " ")}…`
3358
- );
3359
- }
3360
- return this.anode.render(stack, rx);
3361
- }
3362
- };
3363
3328
  NodeEvents = class {
3364
3329
  constructor(id) {
3365
3330
  this.id = id;
@@ -3374,7 +3339,7 @@ var init_anode = __esm({
3374
3339
  getHandlersFor(eventName) {
3375
3340
  let r = null;
3376
3341
  for (const handler of this.handlers)
3377
- if (handler.handlesEventName(eventName)) {
3342
+ if (handler.name === eventName) {
3378
3343
  r ??= [];
3379
3344
  r.push(handler);
3380
3345
  }
@@ -3388,9 +3353,6 @@ var init_anode = __esm({
3388
3353
  this.modifierWrapper = compileModifiers(name, modifiers);
3389
3354
  this.modifiers = modifiers;
3390
3355
  }
3391
- handlesEventName(name) {
3392
- return this.name === name;
3393
- }
3394
3356
  getHandlerAndArgs(stack, event) {
3395
3357
  const r = this.handlerCall.getHandlerAndArgs(stack, event);
3396
3358
  r[0] = this.modifierWrapper(r[0], event);
@@ -3424,11 +3386,212 @@ var init_anode = __esm({
3424
3386
  }
3425
3387
  });
3426
3388
 
3389
+ // src/stack.js
3390
+ function lookup(chain, name, dv = null) {
3391
+ let n = chain;
3392
+ while (n !== null) {
3393
+ const r = n[0].lookup(name);
3394
+ if (r === STOP) return dv;
3395
+ if (r !== NEXT) return r;
3396
+ n = n[1];
3397
+ }
3398
+ return dv;
3399
+ }
3400
+ function computeViewsId(views) {
3401
+ let s = "";
3402
+ let n = views;
3403
+ while (n !== null) {
3404
+ s += n[0];
3405
+ n = n[1];
3406
+ }
3407
+ return s === "main" ? "" : s;
3408
+ }
3409
+ var STOP, NEXT, DEFAULT_ROUTE, isTypeName, BindFrame, DynFrame, Stack, NOT_FOUND;
3410
+ var init_stack = __esm({
3411
+ "src/stack.js"() {
3412
+ init_path();
3413
+ STOP = /* @__PURE__ */ Symbol("STOP");
3414
+ NEXT = /* @__PURE__ */ Symbol("NEXT");
3415
+ DEFAULT_ROUTE = ["dyn", "lex"];
3416
+ isTypeName = (s) => {
3417
+ const c = s.charCodeAt(0);
3418
+ return c >= 65 && c <= 90;
3419
+ };
3420
+ BindFrame = class {
3421
+ constructor(it, binds, isFrame) {
3422
+ this.it = it;
3423
+ this.binds = binds;
3424
+ this.isFrame = isFrame;
3425
+ }
3426
+ lookup(name) {
3427
+ const v = this.binds[name];
3428
+ return v === void 0 ? this.isFrame ? STOP : NEXT : v;
3429
+ }
3430
+ };
3431
+ DynFrame = class {
3432
+ constructor(binds, types) {
3433
+ this.binds = binds;
3434
+ this.types = types;
3435
+ }
3436
+ lookup(name) {
3437
+ const v = (isTypeName(name) ? this.types : this.binds)[name];
3438
+ return v === void 0 ? NEXT : v;
3439
+ }
3440
+ };
3441
+ Stack = class _Stack {
3442
+ constructor(fields) {
3443
+ Object.assign(this, fields);
3444
+ }
3445
+ _with(patch) {
3446
+ return new _Stack({ ...this, ...patch });
3447
+ }
3448
+ // Evaluate every provide the entered component publishes and push them as one
3449
+ // dynBinds frame, keyed by NAME. A value is published together with the absolute
3450
+ // path it lives at: the same declaration is read as `*name` AND resumed at by
3451
+ // `<x render="*name">`, so a consumer needs both halves. Types go in the same
3452
+ // frame's other map. No-op with no provides.
3453
+ _pushProvides() {
3454
+ const comp = this.comps.getCompFor(this.it);
3455
+ if (comp == null) return this;
3456
+ const { provide, provideType } = comp;
3457
+ const binds = {};
3458
+ const types = {};
3459
+ let has2 = false;
3460
+ const base = this._publishBase();
3461
+ for (const k in provide) {
3462
+ const step = provide[k].toPathItem?.() ?? null;
3463
+ if (step === null) continue;
3464
+ const value = provide[k].eval(this);
3465
+ binds[k] = { value, path: base === null ? null : base.concat([step]) };
3466
+ has2 = true;
3467
+ }
3468
+ for (const k in provideType) {
3469
+ types[k] = provideType[k];
3470
+ has2 = true;
3471
+ }
3472
+ if (!has2) return this;
3473
+ return this._with({ dynBinds: [new DynFrame(binds, types), this.dynBinds] });
3474
+ }
3475
+ // The absolute address of the component being entered, or null when this
3476
+ // position cannot be written down as one. Frame-only steps carry bindings and
3477
+ // address nothing, so they are compacted away first; what is left is checked
3478
+ // against the value actually being rendered, because a scope CAN move the
3479
+ // render position without contributing an addressing step (a plain `@each`
3480
+ // body re-binds `it` to the item while its rebuild step is an identity).
3481
+ //
3482
+ // A null base still publishes the VALUE — `*name` reads it fine — but there is
3483
+ // nowhere to resume, so `<x render="*name">` renders nothing rather than
3484
+ // editing whatever happens to live at the address we guessed.
3485
+ _publishBase() {
3486
+ const base = this.renderPath.toTransactionPath().compact();
3487
+ return base.lookup(this.root, NOT_FOUND) === this.it ? base : null;
3488
+ }
3489
+ static root(comps, it, ctx = null) {
3490
+ return new _Stack({
3491
+ comps,
3492
+ root: it,
3493
+ it,
3494
+ binds: [new BindFrame(it, {}, true), null],
3495
+ dynBinds: [new DynFrame({}, {}), null],
3496
+ views: ["main", null],
3497
+ viewsId: "",
3498
+ renderPath: new DispatchPath(),
3499
+ pendingFrame: false,
3500
+ ctx
3501
+ })._pushProvides();
3502
+ }
3503
+ // `renderPath` defaults to this stack's own: an ordinary scope does not move.
3504
+ // `pendingFrame` clears on a component frame (which emits the base) and is
3505
+ // inherited by transparent scopes, which have to carry it to the next boundary.
3506
+ enter(it, bindings = {}, isFrame = true, renderPath = this.renderPath, pendingFrame = null) {
3507
+ const stack = this._with({
3508
+ it,
3509
+ binds: [new BindFrame(it, bindings, isFrame), this.binds],
3510
+ renderPath,
3511
+ pendingFrame: pendingFrame ?? (isFrame ? false : this.pendingFrame)
3512
+ });
3513
+ return isFrame ? stack._pushProvides() : stack;
3514
+ }
3515
+ pushViewName(name) {
3516
+ const views = [name, this.views];
3517
+ return this._with({ views, viewsId: computeViewsId(views) });
3518
+ }
3519
+ // Published types are stable per scope and would only churn the render cache, so
3520
+ // the cache key covers values alone.
3521
+ _pushDynBindValuesToArray(arr, comp) {
3522
+ for (const k in comp.provide) arr.push(this.lookupDynamic(k));
3523
+ for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
3524
+ }
3525
+ // `*name`: the nearest binding above (including this component's own provides,
3526
+ // pushed on entering it), else a path registered in the component's lexical
3527
+ // scope, else this component's declared default, else null.
3528
+ //
3529
+ // One chain walk and no producer resolution: a lookup names what it WANTS, so the
3530
+ // frame it wants is keyed by that name. The default belongs to the CONSUMER's
3531
+ // declaration and is evaluated against the consumer's stack, which is why it is
3532
+ // consulted only after the whole chain has missed.
3533
+ lookupDynamicLocated(name) {
3534
+ if (isTypeName(name)) return null;
3535
+ const v = lookup(this.dynBinds, name);
3536
+ if (v != null) return v;
3537
+ const comp = this.comps.getCompFor(this.it);
3538
+ if (comp == null) return null;
3539
+ const path = comp.scope?.lookupPath?.(name) ?? null;
3540
+ if (path !== null) {
3541
+ const value2 = path.lookup(this.root, NOT_FOUND);
3542
+ if (value2 !== NOT_FOUND) return { value: value2, path };
3543
+ }
3544
+ const dval = comp.lookup[name] ?? null;
3545
+ if (dval === null) return null;
3546
+ const step = dval.toPathItem?.() ?? null;
3547
+ const value = dval.eval(this);
3548
+ return step === null ? { value, path: null } : { value, path: this.renderPath.toTransactionPath().concat([step]) };
3549
+ }
3550
+ lookupDynamic(name) {
3551
+ if (isTypeName(name)) return lookup(this.dynBinds, name);
3552
+ return this.lookupDynamicLocated(name)?.value ?? null;
3553
+ }
3554
+ lookupBind(name) {
3555
+ return lookup(this.binds, name);
3556
+ }
3557
+ lookupFieldRaw(name) {
3558
+ return this.it[name] ?? null;
3559
+ }
3560
+ lookupMethod(name) {
3561
+ const fn = this.it[name];
3562
+ return fn instanceof Function ? fn.call(this.it) : null;
3563
+ }
3564
+ // The dispatched DOM event / drag info, read only by EventMemberVal's
3565
+ // `e.<member>` handler args. Null outside a live event transaction (`ctx` is
3566
+ // then a send/intent transaction, which carries no `e`).
3567
+ lookupEvent() {
3568
+ return this.ctx?.e ?? null;
3569
+ }
3570
+ lookupDragInfo() {
3571
+ return this.ctx?.dragInfo ?? null;
3572
+ }
3573
+ getHandlerFor(name, key) {
3574
+ return this.comps.getHandlerFor(this.it, name, key);
3575
+ }
3576
+ lookupBestView(views, defaultViewName) {
3577
+ let n = this.views;
3578
+ while (n !== null) {
3579
+ const view = views[n[0]];
3580
+ if (view !== void 0) return view;
3581
+ n = n[1];
3582
+ }
3583
+ return views[defaultViewName];
3584
+ }
3585
+ };
3586
+ NOT_FOUND = /* @__PURE__ */ Symbol("NOT_FOUND");
3587
+ }
3588
+ });
3589
+
3427
3590
  // src/components.js
3428
- var COMPONENT, Components, ComponentStack, ProvideInfo, LookupInfo, isString, isTypeName, _rawSpecKeys, KNOWN_SPEC_KEYS, _compId, Component;
3591
+ var COMPONENT, Components, ComponentStack, isString, _rawSpecKeys, KNOWN_SPEC_KEYS, COMPONENT_METHODS;
3429
3592
  var init_components = __esm({
3430
3593
  "src/components.js"() {
3431
- init_anode();
3594
+ init_stack();
3432
3595
  init_value();
3433
3596
  COMPONENT = /* @__PURE__ */ Symbol.for("tutuca.component");
3434
3597
  Components = class {
@@ -3436,7 +3599,7 @@ var init_components = __esm({
3436
3599
  this.byId = /* @__PURE__ */ new Map();
3437
3600
  }
3438
3601
  registerComponent(Comp) {
3439
- this.byId.set(Comp[COMPONENT].id, Comp);
3602
+ this.byId.set(Comp.id, Comp);
3440
3603
  }
3441
3604
  getComponentForId(id) {
3442
3605
  return this.byId.get(id) ?? null;
@@ -3456,7 +3619,7 @@ var init_components = __esm({
3456
3619
  }
3457
3620
  compileStyles() {
3458
3621
  const styles2 = [];
3459
- for (const Comp of this.byId.values()) styles2.push(Comp[COMPONENT].compileStyle());
3622
+ for (const Comp of this.byId.values()) styles2.push(Comp.compileStyle());
3460
3623
  return styles2.join("\n");
3461
3624
  }
3462
3625
  };
@@ -3467,15 +3630,17 @@ var init_components = __esm({
3467
3630
  this.byName = {};
3468
3631
  this.intentsByName = {};
3469
3632
  this.macros = {};
3633
+ this.paths = {};
3470
3634
  }
3471
3635
  enter() {
3472
3636
  return new _ComponentStack(this.comps, this);
3473
3637
  }
3474
3638
  registerComponents(comps, opts) {
3475
- const { aliases: aliases2 = {} } = opts ?? {};
3639
+ const { aliases: aliases2 = {}, paths } = opts ?? {};
3640
+ if (paths) this.registerPaths(paths);
3476
3641
  for (let i = 0; i < comps.length; i++) {
3477
3642
  const Comp = comps[i];
3478
- Comp[COMPONENT].scope = this.enter();
3643
+ Comp.scope = this.enter();
3479
3644
  this.comps.registerComponent(Comp);
3480
3645
  this.byName[Comp.name] = Comp;
3481
3646
  }
@@ -3486,6 +3651,27 @@ var init_components = __esm({
3486
3651
  else console.warn("alias", alias, "to inexistent component", aliases2[alias]);
3487
3652
  }
3488
3653
  }
3654
+ // Register lowercase names as absolute paths from the app state root. A
3655
+ // descendant that declares one in its `lookup` reads and renders `*name` without
3656
+ // anything above it publishing one — which is what makes a session, a theme or a
3657
+ // host-owned value available in its natural registration scope, instead of forcing
3658
+ // an application root whose only job is to `provide` it. Register on a nested
3659
+ // scope to narrow a name; nearest registration wins.
3660
+ //
3661
+ // Uppercase names are ignored: a component TYPE is what `lookupComponent` already
3662
+ // answers, and a type has no path.
3663
+ registerPaths(paths) {
3664
+ for (const name in paths) {
3665
+ if (isTypeName(name)) {
3666
+ console.warn("registerPaths: a type name has no path", name);
3667
+ continue;
3668
+ }
3669
+ this.paths[name] = paths[name].toPath();
3670
+ }
3671
+ }
3672
+ lookupPath(name) {
3673
+ return this.paths[name] ?? this.parent?.lookupPath(name) ?? null;
3674
+ }
3489
3675
  registerMacros(macros) {
3490
3676
  for (const key in macros) {
3491
3677
  const lower = key.toLowerCase();
@@ -3517,97 +3703,56 @@ var init_components = __esm({
3517
3703
  lookupComponent(name) {
3518
3704
  return this.byName[name] ?? this.parent?.lookupComponent(name) ?? null;
3519
3705
  }
3520
- // The component in this scope chain that provides `name`, innermost first. A lookup
3521
- // names a value without naming its producer, so the render-target teleport
3522
- // (resolveDynProducer) recovers the producer here instead. Sound because
3523
- // PROVIDE_NAME_COLLISION keeps a provide name to one producer per chain; with two,
3524
- // this returns the innermost and the linter has already reported the ambiguity.
3525
- lookupProvider(name) {
3706
+ // Whether anything in this scope chain provides `name`. Existence only: a lookup
3707
+ // names what it WANTS and takes whoever is nearest above it at render time, so
3708
+ // there is no producer to identify several components may publish one name and
3709
+ // the live render ancestry decides. Used by the linter to tell a lookup that can
3710
+ // be satisfied from one that never will be.
3711
+ hasProvider(name) {
3526
3712
  for (const compName in this.byName) {
3527
- const Comp = this.byName[compName];
3528
- if (Comp.provide?.[name] !== void 0) return Comp;
3713
+ if (this.byName[compName].provide?.[name] !== void 0) return true;
3529
3714
  }
3530
- return this.parent?.lookupProvider(name) ?? null;
3715
+ return this.parent?.hasProvider(name) ?? false;
3531
3716
  }
3532
3717
  lookupMacro(name) {
3533
3718
  return this.macros[name] ?? this.parent?.lookupMacro(name) ?? null;
3534
3719
  }
3535
3720
  };
3536
- ProvideInfo = class {
3537
- constructor(val) {
3538
- this.val = val;
3539
- }
3540
- };
3541
- LookupInfo = class {
3542
- constructor(val) {
3543
- this.val = val;
3544
- }
3545
- };
3546
3721
  isString = (v) => typeof v === "string";
3547
- isTypeName = (s) => {
3548
- const c = s.charCodeAt(0);
3549
- return c >= 65 && c <= 90;
3550
- };
3551
3722
  _rawSpecKeys = "name view style commonStyle globalStyle receive intent alter views provide lookup fields methods statics";
3552
3723
  KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
3553
- _compId = 0;
3554
- Component = class {
3555
- constructor(Class, o) {
3556
- this.id = _compId++;
3557
- this.name = o.name ?? "UnkComp";
3558
- this.Class = Class;
3559
- this.views = { main: new View("main", o.view, o.style) };
3560
- this.commonStyle = o.commonStyle ?? "";
3561
- this.globalStyle = o.globalStyle ?? "";
3562
- this.receive = o.receive ?? {};
3563
- this.intent = o.intent ?? {};
3564
- this.alter = o.alter ?? {};
3565
- for (const name in o.views ?? {}) {
3566
- const v = o.views[name];
3567
- const { view, style } = isString(v) ? { view: v } : v;
3568
- this.views[name] = new View(name, view, style);
3569
- }
3570
- this._rawProvide = o.provide ?? {};
3571
- this._rawLookup = o.lookup ?? [];
3572
- this.provide = {};
3573
- this.provideType = {};
3574
- this.lookup = {};
3575
- this.scope = null;
3576
- this.spec = o;
3577
- this.extra = {};
3578
- for (const key of Object.keys(o)) if (!KNOWN_SPEC_KEYS.has(key)) this.extra[key] = o[key];
3579
- }
3724
+ COMPONENT_METHODS = {
3580
3725
  compile(ParseContext2) {
3581
3726
  for (const name in this.views)
3582
3727
  this.views[name].compile(new ParseContext2(), this.scope, this.id);
3583
3728
  const ctx = this.views.main.ctx;
3584
3729
  for (const key in this._rawProvide) {
3585
3730
  if (isTypeName(key)) {
3586
- if (this._rawProvide[key] === "self") this.provideType[key] = this.Class;
3731
+ if (this._rawProvide[key] === "self") this.provideType[key] = this;
3587
3732
  continue;
3588
3733
  }
3589
3734
  const val = parseProvide(this._rawProvide[key], ctx);
3590
- if (val) this.provide[key] = new ProvideInfo(val);
3735
+ if (val) this.provide[key] = val;
3591
3736
  }
3592
3737
  for (const entry of this._rawLookup) {
3593
3738
  const name = isString(entry) ? entry : isString(entry?.name) ? entry.name : null;
3594
3739
  if (name === null) continue;
3595
3740
  const defStr = isString(entry?.default) ? entry.default : null;
3596
- this.lookup[name] = new LookupInfo(defStr === null ? null : parseField(defStr, ctx));
3741
+ this.lookup[name] = defStr === null ? null : parseField(defStr, ctx);
3597
3742
  }
3598
3743
  for (const key in this.lookup)
3599
3744
  if (this.provide[key] !== void 0)
3600
3745
  console.warn("name declared in both provide and lookup", this.name, key);
3601
- }
3746
+ },
3602
3747
  getView(name) {
3603
3748
  return this.views[name] ?? this.views.main;
3604
- }
3605
- getEventForId(id, name = "main") {
3606
- return this.getView(name).ctx.getEventForId(id);
3607
- }
3608
- getNodeForId(id, name = "main") {
3609
- return this.getView(name).ctx.getNodeForId(id);
3610
- }
3749
+ },
3750
+ getEventForId(id, viewName) {
3751
+ return this.getView(viewName).ctx.getEventForId(id);
3752
+ },
3753
+ getNodeForId(id, viewName) {
3754
+ return this.getView(viewName).ctx.getNodeForId(id);
3755
+ },
3611
3756
  compileStyle() {
3612
3757
  const { id, commonStyle, globalStyle, views } = this;
3613
3758
  const styles2 = commonStyle ? [`[data-cid="${id}"]{${commonStyle}}`] : [];
@@ -3622,6 +3767,63 @@ var init_components = __esm({
3622
3767
  }
3623
3768
  });
3624
3769
 
3770
+ // src/util/parsectx.js
3771
+ var ParseCtxClassSetCollector;
3772
+ var init_parsectx = __esm({
3773
+ "src/util/parsectx.js"() {
3774
+ init_anode();
3775
+ init_value();
3776
+ ParseCtxClassSetCollector = class extends ParseContext {
3777
+ constructor(...args) {
3778
+ super(...args);
3779
+ this.classes = /* @__PURE__ */ new Set();
3780
+ }
3781
+ _addClasses(s) {
3782
+ for (const v of s.split(/\s+/)) {
3783
+ this.classes.add(v);
3784
+ }
3785
+ }
3786
+ onAttributes(attrs, _wrapperAttrs, _textChild, _isMacroCall, _tag) {
3787
+ if (Array.isArray(attrs.items)) {
3788
+ for (const attr of attrs.items) {
3789
+ if (attr.name !== "class") {
3790
+ continue;
3791
+ }
3792
+ const { val, thenVal, elseVal } = attr;
3793
+ if (thenVal !== void 0) {
3794
+ this._maybeAddVal(thenVal);
3795
+ this._maybeAddVal(elseVal);
3796
+ } else {
3797
+ this._maybeAddVal(val);
3798
+ }
3799
+ }
3800
+ } else {
3801
+ const attr = attrs.items.class;
3802
+ if (attr) {
3803
+ this._addClasses(attr);
3804
+ }
3805
+ }
3806
+ }
3807
+ _maybeAddVal(value) {
3808
+ if (!this._maybeAddStrTpl(value) && typeof value?.val === "string") {
3809
+ this._addClasses(value.val);
3810
+ }
3811
+ }
3812
+ _maybeAddStrTpl(value) {
3813
+ if (value?.vals !== void 0) {
3814
+ for (const val of value.vals) {
3815
+ if (val instanceof ConstVal && val.val !== "") {
3816
+ this._addClasses(val.val);
3817
+ }
3818
+ }
3819
+ return true;
3820
+ }
3821
+ return false;
3822
+ }
3823
+ };
3824
+ }
3825
+ });
3826
+
3625
3827
  // tools/core/html-tokenizer.js
3626
3828
  function isWhitespace(c) {
3627
3829
  return c === CharCodes.Space || c === CharCodes.NewLine || c === CharCodes.Tab || c === CharCodes.FormFeed || c === CharCodes.CarriageReturn;
@@ -5958,7 +6160,6 @@ function checkComponent(Comp, lx = new LintContext(), { wellKnownExtras = EMPTY_
5958
6160
  checkFieldMethodNameCollisions(lx, Comp);
5959
6161
  checkProvidesAreAddressable(lx, Comp);
5960
6162
  checkProvidedTypes(lx, Comp);
5961
- checkProvideNameCollisions(lx, Comp);
5962
6163
  checkLookupShapes(lx, Comp);
5963
6164
  checkLookupTypesResolve(lx, Comp);
5964
6165
  checkLookupsHaveProviders(lx, Comp);
@@ -6533,19 +6734,6 @@ function checkProvidedTypes(lx, Comp) {
6533
6734
  if (raw !== "self") lx.error(PROVIDE_TYPE_BAD_SHAPE, { name, value: raw });
6534
6735
  }
6535
6736
  }
6536
- function checkProvideNameCollisions(lx, Comp) {
6537
- const scope = Comp.scope;
6538
- if (!scope) return;
6539
- for (const name in Comp.provide) {
6540
- for (let s = scope; s; s = s.parent) {
6541
- for (const otherName in s.byName) {
6542
- const Other = s.byName[otherName];
6543
- if (Other !== Comp && Other.provide?.[name] !== void 0)
6544
- lx.error(PROVIDE_NAME_COLLISION, { name, other: Other.name });
6545
- }
6546
- }
6547
- }
6548
- }
6549
6737
  function checkLookupShapes(lx, Comp) {
6550
6738
  const raw = Comp._rawLookup;
6551
6739
  if (!Array.isArray(raw)) {
@@ -6586,8 +6774,8 @@ function checkLookupsHaveProviders(lx, Comp) {
6586
6774
  if (!scope) return;
6587
6775
  for (const name in Comp.lookup) {
6588
6776
  if (isTypeName2(name)) continue;
6589
- if (scope.lookupProvider?.(name)) continue;
6590
- const info = { name, hasDefault: Comp.lookup[name].val != null };
6777
+ if (scope.hasProvider?.(name) || scope.lookupPath?.(name)) continue;
6778
+ const info = { name, hasDefault: Comp.lookup[name] != null };
6591
6779
  if (info.hasDefault) lx.hint(LOOKUP_NO_PROVIDER, info);
6592
6780
  else lx.error(LOOKUP_NO_PROVIDER, info);
6593
6781
  }
@@ -6608,11 +6796,12 @@ function checkUnreferencedDynamics(lx, Comp, referencedDynamics) {
6608
6796
  }
6609
6797
  }
6610
6798
  }
6611
- 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;
6799
+ 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, ORPHAN_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;
6612
6800
  var init_lint_check = __esm({
6613
6801
  "tools/core/lint-check.js"() {
6614
6802
  init_anode();
6615
6803
  init_components();
6804
+ init_parsectx();
6616
6805
  init_htmllinter();
6617
6806
  init_closest_name();
6618
6807
  KNOWN_COMPONENT_SPEC_KEYS = new Set(
@@ -6646,7 +6835,6 @@ var init_lint_check = __esm({
6646
6835
  DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED";
6647
6836
  PROVIDE_NOT_ADDRESSABLE = "PROVIDE_NOT_ADDRESSABLE";
6648
6837
  PROVIDE_TYPE_BAD_SHAPE = "PROVIDE_TYPE_BAD_SHAPE";
6649
- PROVIDE_NAME_COLLISION = "PROVIDE_NAME_COLLISION";
6650
6838
  LOOKUP_BAD_SHAPE = "LOOKUP_BAD_SHAPE";
6651
6839
  LOOKUP_NO_PROVIDER = "LOOKUP_NO_PROVIDER";
6652
6840
  RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP";
@@ -6663,6 +6851,7 @@ var init_lint_check = __esm({
6663
6851
  UNKNOWN_COMPONENT_NAME = "UNKNOWN_COMPONENT_NAME";
6664
6852
  UNKNOWN_MACRO_ARG = "UNKNOWN_MACRO_ARG";
6665
6853
  UNKNOWN_DIRECTIVE = "UNKNOWN_DIRECTIVE";
6854
+ ORPHAN_DIRECTIVE = "ORPHAN_DIRECTIVE";
6666
6855
  UNKNOWN_X_OP = "UNKNOWN_X_OP";
6667
6856
  UNKNOWN_X_ATTR = "UNKNOWN_X_ATTR";
6668
6857
  X_OP_IGNORES_CHILDREN = "X_OP_IGNORES_CHILDREN";
@@ -6697,6 +6886,7 @@ var init_lint_check = __esm({
6697
6886
  LEVEL_HINT = "hint";
6698
6887
  PARSE_ISSUES = {
6699
6888
  "unknown-directive": { id: UNKNOWN_DIRECTIVE, candidates: KNOWN_DIRECTIVE_NAMES },
6889
+ "orphan-directive": { id: ORPHAN_DIRECTIVE },
6700
6890
  "unknown-x-op": { id: UNKNOWN_X_OP, candidates: X_KNOWN_OP_NAMES, atPrefix: X_KNOWN_OP_NAMES },
6701
6891
  "unknown-x-attr": {
6702
6892
  id: UNKNOWN_X_ATTR,
@@ -6851,13 +7041,14 @@ var init_lint_check = __esm({
6851
7041
  this.reports.push({ id, info, level, context: { ...this.frame }, suggestion });
6852
7042
  }
6853
7043
  };
6854
- LintParseContext = class extends ParseContext {
6855
- constructor(document2, Text, Comment) {
6856
- super(document2, Text, Comment);
7044
+ LintParseContext = class extends ParseCtxClassSetCollector {
7045
+ constructor(document2) {
7046
+ super(document2);
6857
7047
  this.attrs = [];
6858
7048
  this.parseIssues = [];
6859
7049
  }
6860
7050
  onAttributes(attrs, wrapperAttrs, textChild, isMacroCall = false, tag = null) {
7051
+ super.onAttributes(attrs, wrapperAttrs, textChild, isMacroCall, tag);
6861
7052
  this.attrs.push({ attrs, wrapperAttrs, textChild, isMacroCall, tag });
6862
7053
  }
6863
7054
  onParseIssue(kind, info) {
@@ -6974,7 +7165,7 @@ var init_lint_rules = __esm({
6974
7165
  code: LOOKUP_NO_PROVIDER,
6975
7166
  level: "error",
6976
7167
  group: "Dynamic bindings",
6977
- summary: "`lookup` name is provided by no component in scope."
7168
+ summary: "`lookup` name is neither provided in scope nor a registered path."
6978
7169
  },
6979
7170
  {
6980
7171
  code: PROVIDE_TYPE_BAD_SHAPE,
@@ -6982,12 +7173,6 @@ var init_lint_rules = __esm({
6982
7173
  group: "Dynamic bindings",
6983
7174
  summary: 'A PascalCase `provide` publishes a component type; its value must be `"self"`.'
6984
7175
  },
6985
- {
6986
- code: PROVIDE_NAME_COLLISION,
6987
- level: "error",
6988
- group: "Dynamic bindings",
6989
- summary: "Two components in one scope chain `provide` the same name."
6990
- },
6991
7176
  // Templates / events
6992
7177
  {
6993
7178
  code: RENDER_IT_OUTSIDE_OF_LOOP,
@@ -7019,6 +7204,12 @@ var init_lint_rules = __esm({
7019
7204
  group: "Templates / events",
7020
7205
  summary: "`@directive` name is not recognized (typo or unsupported)."
7021
7206
  },
7207
+ {
7208
+ code: ORPHAN_DIRECTIVE,
7209
+ level: "error",
7210
+ group: "Templates / events",
7211
+ summary: "`@when` / `@loop-with` without an `@each`, or `@then` / `@else` without an `@if`, on the same element — the directive is ignored."
7212
+ },
7022
7213
  {
7023
7214
  code: UNKNOWN_X_OP,
7024
7215
  level: "error",
@@ -7276,19 +7467,19 @@ function lintIdToMessage(id, info) {
7276
7467
  case "DYN_ALIAS_NOT_REFERENCED":
7277
7468
  return `Lookup '${info.name}' is defined but never used — remove it or reference it as '*${info.name}' in a view`;
7278
7469
  case "PROVIDE_NOT_ADDRESSABLE":
7279
- 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`;
7470
+ 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`;
7280
7471
  case "LOOKUP_BAD_SHAPE":
7281
7472
  return `Lookup '${info.name}' has an invalid shape: ${info.problem}`;
7282
7473
  case "LOOKUP_NO_PROVIDER":
7283
- 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`;
7474
+ 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`;
7284
7475
  case "PROVIDE_TYPE_BAD_SHAPE":
7285
7476
  return `Provide '${info.name}' starts uppercase, so it publishes a component type — its value must be 'self', not '${info.value}'`;
7286
- case "PROVIDE_NAME_COLLISION":
7287
- return `Provide '${info.name}' is also provided by '${info.other}' in the same scope — one name, one provider, so a lookup can find it`;
7288
7477
  case "UNKNOWN_MACRO_ARG":
7289
7478
  return `Argument '${info.name}' is not declared in macro '${info.macroName}'`;
7290
7479
  case "UNKNOWN_DIRECTIVE":
7291
7480
  return `Unknown directive '@${info.name}=${JSON.stringify(info.value)}'${fmtTagSuffix(info)}`;
7481
+ case "ORPHAN_DIRECTIVE":
7482
+ return `'@${info.name}=${JSON.stringify(info.value)}' needs an '@${info.needs}' on the same element — it was ignored${fmtTagSuffix(info)}`;
7292
7483
  case "UNKNOWN_X_OP":
7293
7484
  return `Unknown <x> op '${info.name}=${JSON.stringify(info.value)}'${fmtTagSuffix(info)}`;
7294
7485
  case "UNKNOWN_X_ATTR":
@@ -12661,303 +12852,105 @@ function getComponentDoc(comp) {
12661
12852
  });
12662
12853
  }
12663
12854
  return {
12664
- name,
12665
- methods: userMethods,
12666
- receive: receiveHandlers,
12667
- intent: intentHandlers,
12668
- fields: fieldDocs
12669
- };
12670
- }
12671
- function docComponents(normalized, { name = null } = {}) {
12672
- const comps = normalized.components;
12673
- const picked = name === null ? comps : comps.filter((c) => c.name === name);
12674
- return new ComponentDocs({ items: picked.map((comp) => getComponentDoc(comp)) });
12675
- }
12676
- var init_docs = __esm({
12677
- "tools/core/docs.js"() {
12678
- init_results();
12679
- }
12680
- });
12681
-
12682
- // tools/core/lint.js
12683
- function lintComponents(normalized, { name = null, LintParseContextClass, wellKnownExtras = /* @__PURE__ */ new Set() }) {
12684
- const comps = normalized.components;
12685
- const picked = name === null ? comps : comps.filter((c) => c.name === name);
12686
- const stack = new ComponentStack();
12687
- stack.registerComponents(comps);
12688
- if (normalized.macros) stack.registerMacros(normalized.macros);
12689
- if (normalized.intentHandlers) stack.registerIntentHandlers(normalized.intentHandlers);
12690
- const results = [];
12691
- for (const comp of picked) {
12692
- comp.compile(LintParseContextClass);
12693
- const lx = checkComponent(comp, void 0, { wellKnownExtras });
12694
- results.push(
12695
- new LintComponentResult({
12696
- componentName: comp.name,
12697
- findings: lx.reports.map((r) => new LintFinding(r))
12698
- })
12699
- );
12700
- }
12701
- return new LintReport({ components: results });
12702
- }
12703
- var init_lint2 = __esm({
12704
- "tools/core/lint.js"() {
12705
- init_components();
12706
- init_lint_check();
12707
- init_results();
12708
- }
12709
- });
12710
-
12711
- // tools/core/list.js
12712
- function summarize(comp) {
12713
- const { fields: fieldMap, name } = comp;
12714
- const fields = [];
12715
- for (const fieldName in fieldMap) {
12716
- const f = fieldMap[fieldName];
12717
- fields.push({ name: fieldName, type: f.type });
12718
- }
12719
- return new ComponentSummary({
12720
- name,
12721
- views: Object.keys(comp.views ?? {}),
12722
- fields
12723
- });
12724
- }
12725
- function listComponents(normalized, { name = null, limit = 0 } = {}) {
12726
- const comps = normalized.components;
12727
- const picked = name === null ? comps : comps.filter((c) => c.name === name);
12728
- const total = picked.length;
12729
- const capped = limit > 0 ? picked.slice(0, limit) : picked;
12730
- return new ComponentList({
12731
- items: capped.map(summarize),
12732
- total,
12733
- truncated: capped.length < total
12734
- });
12735
- }
12736
- function listExamples(normalized, { limit = 0 } = {}) {
12737
- const sections = normalized.sections;
12738
- const total = sections.reduce((n, s) => n + (s.items?.length ?? 0), 0);
12739
- if (limit <= 0) {
12740
- return new ExampleIndex({ sections, total, truncated: false });
12741
- }
12742
- const capped = [];
12743
- let remaining = limit;
12744
- for (const s of sections) {
12745
- if (remaining <= 0) break;
12746
- const items = s.items.slice(0, remaining);
12747
- capped.push({ ...s, items });
12748
- remaining -= items.length;
12749
- }
12750
- return new ExampleIndex({
12751
- sections: capped,
12752
- total,
12753
- truncated: capped.reduce((n, s) => n + s.items.length, 0) < total
12754
- });
12755
- }
12756
- var init_list = __esm({
12757
- "tools/core/list.js"() {
12758
- init_results();
12759
- }
12760
- });
12761
-
12762
- // src/stack.js
12763
- function routeLookup(route, lex, dyn) {
12764
- for (let i = 0; i < route.length; i++) {
12765
- const leg = route[i];
12766
- if (leg === "dyn") {
12767
- const v = dyn();
12768
- if (v != null) return v;
12769
- } else if (leg === "lex") {
12770
- const v = lex();
12771
- if (v != null) return v;
12772
- } else {
12773
- console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
12774
- }
12775
- }
12776
- return null;
12777
- }
12778
- function lookup(chain, name, dv = null) {
12779
- let n = chain;
12780
- while (n !== null) {
12781
- const r = n[0].lookup(name);
12782
- if (r === STOP) return dv;
12783
- if (r !== NEXT) return r;
12784
- n = n[1];
12785
- }
12786
- return dv;
12787
- }
12788
- function computeViewsId(views) {
12789
- let s = "";
12790
- let n = views;
12791
- while (n !== null) {
12792
- s += n[0];
12793
- n = n[1];
12794
- }
12795
- return s === "main" ? "" : s;
12796
- }
12797
- var STOP, NEXT, DEFAULT_ROUTE, BindFrame, ObjectFrame, Stack;
12798
- var init_stack = __esm({
12799
- "src/stack.js"() {
12800
- STOP = /* @__PURE__ */ Symbol("STOP");
12801
- NEXT = /* @__PURE__ */ Symbol("NEXT");
12802
- DEFAULT_ROUTE = ["dyn", "lex"];
12803
- BindFrame = class {
12804
- constructor(it, binds, isFrame) {
12805
- this.it = it;
12806
- this.binds = binds;
12807
- this.isFrame = isFrame;
12808
- }
12809
- lookup(name) {
12810
- const v = this.binds[name];
12811
- return v === void 0 ? this.isFrame ? STOP : NEXT : v;
12812
- }
12813
- };
12814
- ObjectFrame = class {
12815
- constructor(binds) {
12816
- this.binds = binds;
12817
- }
12818
- lookup(key) {
12819
- const v = this.binds[key];
12820
- return v === void 0 ? NEXT : v;
12821
- }
12822
- };
12823
- Stack = class _Stack {
12824
- constructor(comps, it, binds, dynBinds, views, viewsId, ctx = null) {
12825
- this.comps = comps;
12826
- this.it = it;
12827
- this.binds = binds;
12828
- this.dynBinds = dynBinds;
12829
- this.views = views;
12830
- this.viewsId = viewsId;
12831
- this.ctx = ctx;
12832
- }
12833
- // Evaluate every provide the entered component publishes and push them as one
12834
- // dynBinds frame, keyed by NAME. Published types go in the same frame: a type name
12835
- // starts A-Z and a value name does not, so the two namespaces cannot collide and
12836
- // nearest-ancestor-wins falls out of frame order for both. No-op with no provides.
12837
- _pushProvides() {
12838
- const comp = this.comps.getCompFor(this.it);
12839
- if (comp == null) return this;
12840
- const { provide, provideType } = comp;
12841
- const dynObj = {};
12842
- let has2 = false;
12843
- for (const k in provide) {
12844
- dynObj[k] = provide[k].val.eval(this);
12845
- has2 = true;
12846
- }
12847
- for (const k in provideType) {
12848
- dynObj[k] = provideType[k];
12849
- has2 = true;
12850
- }
12851
- if (!has2) return this;
12852
- const newDynBinds = [new ObjectFrame(dynObj), this.dynBinds];
12853
- const { comps, it, binds, views, viewsId, ctx } = this;
12854
- return new _Stack(comps, it, binds, newDynBinds, views, viewsId, ctx);
12855
- }
12856
- static root(comps, it, ctx) {
12857
- const binds = [new BindFrame(it, {}, true), null];
12858
- const dynBinds = [new ObjectFrame({}), null];
12859
- const views = ["main", null];
12860
- return new _Stack(comps, it, binds, dynBinds, views, "", ctx)._pushProvides();
12861
- }
12862
- enter(it, bindings = {}, isFrame = true) {
12863
- const { comps, binds, dynBinds, views, viewsId, ctx } = this;
12864
- const newBinds = [new BindFrame(it, bindings, isFrame), binds];
12865
- const stack = new _Stack(comps, it, newBinds, dynBinds, views, viewsId, ctx);
12866
- return isFrame ? stack._pushProvides() : stack;
12867
- }
12868
- pushViewName(name) {
12869
- const { comps, it, binds, dynBinds, views, ctx } = this;
12870
- const newViews = [name, views];
12871
- return new _Stack(comps, it, binds, dynBinds, newViews, computeViewsId(newViews), ctx);
12872
- }
12873
- // Published types are stable per scope and would only churn the render cache, so
12874
- // the cache key covers values alone.
12875
- _pushDynBindValuesToArray(arr, comp) {
12876
- for (const k in comp.provide) arr.push(this.lookupDynamic(k));
12877
- for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
12878
- }
12879
- // `*name`: the nearest binding above (including this component's own provides,
12880
- // pushed on entering it), else this component's declared default, else null.
12881
- lookupDynamic(name) {
12882
- const v = lookup(this.dynBinds, name);
12883
- if (v != null) return v;
12884
- const comp = this.comps.getCompFor(this.it);
12885
- return comp?.lookup[name]?.val?.eval(this) ?? null;
12886
- }
12887
- lookupBind(name) {
12888
- return lookup(this.binds, name);
12889
- }
12890
- lookupFieldRaw(name) {
12891
- return this.it[name] ?? null;
12892
- }
12893
- lookupMethod(name) {
12894
- const fn = this.it[name];
12895
- return fn instanceof Function ? fn.call(this.it) : null;
12896
- }
12897
- // The dispatched DOM event / drag info, read only by EventMemberVal's
12898
- // `e.<member>` handler args. Null outside a live event transaction.
12899
- lookupEvent() {
12900
- return this.ctx?.event ?? null;
12901
- }
12902
- lookupDragInfo() {
12903
- return this.ctx?.dragInfo ?? null;
12904
- }
12905
- getHandlerFor(name, key) {
12906
- return this.comps.getHandlerFor(this.it, name, key);
12907
- }
12908
- lookupBestView(views, defaultViewName) {
12909
- let n = this.views;
12910
- while (n !== null) {
12911
- const view = views[n[0]];
12912
- if (view !== void 0) return view;
12913
- n = n[1];
12914
- }
12915
- return views[defaultViewName];
12916
- }
12917
- };
12855
+ name,
12856
+ methods: userMethods,
12857
+ receive: receiveHandlers,
12858
+ intent: intentHandlers,
12859
+ fields: fieldDocs
12860
+ };
12861
+ }
12862
+ function docComponents(normalized, { name = null } = {}) {
12863
+ const comps = normalized.components;
12864
+ const picked = name === null ? comps : comps.filter((c) => c.name === name);
12865
+ return new ComponentDocs({ items: picked.map((comp) => getComponentDoc(comp)) });
12866
+ }
12867
+ var init_docs = __esm({
12868
+ "tools/core/docs.js"() {
12869
+ init_results();
12918
12870
  }
12919
12871
  });
12920
12872
 
12921
- // src/oo.js
12922
- function mkCompField(field, scope, args) {
12923
- const Comp = scope?.lookupComponent(field.type) ?? null;
12924
- if (Comp === null)
12925
- console.warn(
12926
- scope ? `component field "${field.name}": component "${field.type}" not found in scope` : `component field "${field.name}": cannot resolve component "${field.type}" — built without a registered scope (use ${field.type}.make({}) as the default, or build via a registered component)`
12873
+ // tools/core/lint.js
12874
+ function lintComponents(normalized, { name = null, LintParseContextClass, wellKnownExtras = /* @__PURE__ */ new Set() }) {
12875
+ const comps = normalized.components;
12876
+ const picked = name === null ? comps : comps.filter((c) => c.name === name);
12877
+ const stack = new ComponentStack();
12878
+ stack.registerComponents(comps);
12879
+ if (normalized.macros) stack.registerMacros(normalized.macros);
12880
+ if (normalized.intentHandlers) stack.registerIntentHandlers(normalized.intentHandlers);
12881
+ const results = [];
12882
+ for (const comp of picked) {
12883
+ comp.compile(LintParseContextClass);
12884
+ const lx = checkComponent(comp, void 0, { wellKnownExtras });
12885
+ results.push(
12886
+ new LintComponentResult({
12887
+ componentName: comp.name,
12888
+ findings: lx.reports.map((r) => new LintFinding(r))
12889
+ })
12927
12890
  );
12928
- return Comp?.make({ ...field.args, ...args }, { scope }) ?? null;
12929
- }
12930
- function fieldFromDescriptor(name, value) {
12931
- const FieldCls = fieldsByTypeName[value.type] ?? FieldAny;
12932
- const probe = new FieldCls(name);
12933
- return [FieldCls, probe.coerceOr(value.defaultValue, probe.defaultValue)];
12934
- }
12935
- function classFromData(name, { fields = {}, methods, statics }) {
12936
- const b = new ClassBuilder(name);
12937
- for (const field in fields) {
12938
- const value = fields[field];
12939
- const type3 = typeof value;
12940
- if (type3 === "string") b.addField(field, value, FieldString);
12941
- else if (type3 === "number") b.addField(field, value, FieldFloat);
12942
- else if (type3 === "boolean") b.addField(field, value, FieldBool);
12943
- else if (Array.isArray(value)) b.addField(field, [...value], FieldList);
12944
- else if (value instanceof Set) b.addField(field, new Set(value), FieldSet);
12945
- else if (value instanceof Map) b.addField(field, new Map(value), FieldMap);
12946
- else if (value?.type && Object.hasOwn(value, "defaultValue")) {
12947
- const [FieldCls, dval] = fieldFromDescriptor(field, value);
12948
- b.addField(field, dval, FieldCls);
12949
- } else if (value?.component && value?.args !== void 0)
12950
- b.addCompField(field, value.component, value.args);
12951
- else if (isPlainObject(value)) b.addField(field, { ...value }, FieldObject);
12952
- else {
12953
- const FieldCls = value?.[FIELD_CLASS] ?? FieldAny;
12954
- b.addField(field, value, FieldCls);
12955
- }
12956
12891
  }
12957
- if (methods) b.methods(methods);
12958
- if (statics) b.statics(statics);
12959
- return b.build();
12892
+ return new LintReport({ components: results });
12893
+ }
12894
+ var init_lint2 = __esm({
12895
+ "tools/core/lint.js"() {
12896
+ init_components();
12897
+ init_lint_check();
12898
+ init_results();
12899
+ }
12900
+ });
12901
+
12902
+ // tools/core/list.js
12903
+ function summarize(comp) {
12904
+ const { fields: fieldMap, name } = comp;
12905
+ const fields = [];
12906
+ for (const fieldName in fieldMap) {
12907
+ const f = fieldMap[fieldName];
12908
+ fields.push({ name: fieldName, type: f.type });
12909
+ }
12910
+ return new ComponentSummary({
12911
+ name,
12912
+ views: Object.keys(comp.views ?? {}),
12913
+ fields
12914
+ });
12915
+ }
12916
+ function listComponents(normalized, { name = null, limit = 0 } = {}) {
12917
+ const comps = normalized.components;
12918
+ const picked = name === null ? comps : comps.filter((c) => c.name === name);
12919
+ const total = picked.length;
12920
+ const capped = limit > 0 ? picked.slice(0, limit) : picked;
12921
+ return new ComponentList({
12922
+ items: capped.map(summarize),
12923
+ total,
12924
+ truncated: capped.length < total
12925
+ });
12926
+ }
12927
+ function listExamples(normalized, { limit = 0 } = {}) {
12928
+ const sections = normalized.sections;
12929
+ const total = sections.reduce((n, s) => n + (s.items?.length ?? 0), 0);
12930
+ if (limit <= 0) {
12931
+ return new ExampleIndex({ sections, total, truncated: false });
12932
+ }
12933
+ const capped = [];
12934
+ let remaining = limit;
12935
+ for (const s of sections) {
12936
+ if (remaining <= 0) break;
12937
+ const items = s.items.slice(0, remaining);
12938
+ capped.push({ ...s, items });
12939
+ remaining -= items.length;
12940
+ }
12941
+ return new ExampleIndex({
12942
+ sections: capped,
12943
+ total,
12944
+ truncated: capped.reduce((n, s) => n + s.items.length, 0) < total
12945
+ });
12960
12946
  }
12947
+ var init_list = __esm({
12948
+ "tools/core/list.js"() {
12949
+ init_results();
12950
+ }
12951
+ });
12952
+
12953
+ // src/oo.js
12961
12954
  function validateDraftFields(current2, draft) {
12962
12955
  const meta = metaOf(current2);
12963
12956
  if (!meta) return;
@@ -12972,226 +12965,41 @@ function validateDraftFields(current2, draft) {
12972
12965
  }
12973
12966
  }
12974
12967
  }
12975
- function assertNoReservedComponentStatics(statics = {}) {
12976
- for (const name in statics) {
12977
- if (RESERVED_COMPONENT_STATICS.has(name)) {
12978
- throw new TypeError(`component static "${name}" is reserved by the framework`);
12979
- }
12980
- }
12981
- }
12982
- 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;
12968
+ var BAD_VALUE2, COERCE_NONE, COERCE_MAP, FIELD_TYPES, metaOf, RESERVED_COMPONENT_STATICS;
12983
12969
  var init_oo = __esm({
12984
12970
  "src/oo.js"() {
12985
12971
  init_collection();
12986
12972
  init_components();
12987
- init_immer2();
12988
12973
  BAD_VALUE2 = /* @__PURE__ */ Symbol("BadValue");
12989
- nullCoercer = (v) => v;
12990
- Field = class {
12991
- constructor(type3, name, typeCheck, coercer, defaultValue = null) {
12992
- this.type = type3;
12993
- this.name = name;
12994
- this.typeCheck = typeCheck;
12995
- this.coercer = coercer;
12996
- this.defaultValue = defaultValue;
12997
- }
12998
- isValid(v) {
12999
- return this.typeCheck(v);
13000
- }
13001
- coerceOr(v, defaultValue = null) {
13002
- if (this.isValid(v)) return v;
13003
- const v1 = this.coercer(v);
13004
- return this.isValid(v1) ? v1 : defaultValue;
13005
- }
13006
- coerceOrDefault(v) {
13007
- return this.coerceOr(v, this.defaultValue);
13008
- }
13009
- };
13010
- CHECK_TYPE_ANY = (_v) => true;
13011
- CHECK_TYPE_INT = Number.isInteger;
13012
- CHECK_TYPE_FLOAT = Number.isFinite;
13013
- CHECK_TYPE_BOOL = (v) => typeof v === "boolean";
13014
- CHECK_TYPE_STRING = (v) => typeof v === "string";
13015
- CHECK_TYPE_LIST = Array.isArray;
13016
- CHECK_TYPE_OBJECT = isPlainObject;
13017
- CHECK_TYPE_MAP = (v) => v instanceof Map;
13018
- CHECK_TYPE_SET = (v) => v instanceof Set;
13019
12974
  COERCE_NONE = (_v) => null;
13020
- COERCE_BOOL = (v) => !!v;
13021
- COERCE_STRING = (v) => v?.toString?.() ?? "";
13022
- COERCE_INT = (v) => Number.isFinite(v) ? Math.trunc(v) : null;
13023
- COERCE_LIST = (v) => Array.isArray(v) ? [...v] : null;
13024
- COERCE_OBJECT = (v) => isPlainObject(v) ? { ...v } : null;
13025
12975
  COERCE_MAP = (v) => {
13026
12976
  if (v instanceof Map) return new Map(v);
13027
12977
  if (Array.isArray(v) || isPlainObject(v))
13028
12978
  return new Map(Array.isArray(v) ? v : Object.entries(v));
13029
12979
  return null;
13030
12980
  };
13031
- COERCE_SET = (v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null;
13032
- FieldBool = class extends Field {
13033
- constructor(name, defaultValue = false) {
13034
- super("bool", name, CHECK_TYPE_BOOL, COERCE_BOOL, defaultValue);
13035
- }
13036
- };
13037
- FieldAny = class extends Field {
13038
- constructor(name, defaultValue = null) {
13039
- super("any", name, CHECK_TYPE_ANY, nullCoercer, defaultValue);
13040
- }
13041
- };
13042
- FieldString = class extends Field {
13043
- constructor(name, defaultValue = "") {
13044
- super("text", name, CHECK_TYPE_STRING, COERCE_STRING, defaultValue);
13045
- }
13046
- };
13047
- FieldInt = class extends Field {
13048
- constructor(name, defaultValue = 0) {
13049
- super("int", name, CHECK_TYPE_INT, COERCE_INT, defaultValue);
13050
- }
13051
- };
13052
- FieldFloat = class extends Field {
13053
- constructor(name, defaultValue = 0) {
13054
- super("float", name, CHECK_TYPE_FLOAT, COERCE_NONE, defaultValue);
13055
- }
13056
- };
13057
- metaOf = (v) => v?.constructor?.[COMPONENT] ?? v?.constructor?.getMetaClass?.();
13058
- getTypeName = (v) => metaOf(v)?.name ?? null;
13059
- FieldComp = class extends Field {
13060
- constructor(type3, name, args) {
13061
- super(type3, name, (v) => getTypeName(v) === type3, nullCoercer, null);
13062
- this.args = args;
13063
- }
13064
- };
13065
- FieldList = class extends Field {
13066
- constructor(name, defaultValue = []) {
13067
- super("list", name, CHECK_TYPE_LIST, COERCE_LIST, defaultValue);
13068
- }
13069
- };
13070
- FieldObject = class extends Field {
13071
- constructor(name, defaultValue = {}) {
13072
- super("object", name, CHECK_TYPE_OBJECT, COERCE_OBJECT, defaultValue);
13073
- }
13074
- };
13075
- FieldMap = class extends Field {
13076
- constructor(name, defaultValue = /* @__PURE__ */ new Map()) {
13077
- super("map", name, CHECK_TYPE_MAP, COERCE_MAP, defaultValue);
13078
- }
13079
- };
13080
- FieldSet = class extends Field {
13081
- constructor(name, defaultValue = /* @__PURE__ */ new Set()) {
13082
- super("set", name, CHECK_TYPE_SET, COERCE_SET, defaultValue);
13083
- }
12981
+ FIELD_TYPES = {
12982
+ any: [(_v) => true, COERCE_NONE, () => null],
12983
+ text: [(v) => typeof v === "string", (v) => v?.toString?.() ?? "", () => ""],
12984
+ int: [Number.isInteger, (v) => Number.isFinite(v) ? Math.trunc(v) : null, () => 0],
12985
+ float: [Number.isFinite, COERCE_NONE, () => 0],
12986
+ bool: [(v) => typeof v === "boolean", (v) => !!v, () => false],
12987
+ list: [Array.isArray, (v) => Array.isArray(v) ? [...v] : null, () => []],
12988
+ object: [isPlainObject, (v) => isPlainObject(v) ? { ...v } : null, () => ({})],
12989
+ map: [(v) => v instanceof Map, COERCE_MAP, () => /* @__PURE__ */ new Map()],
12990
+ set: [
12991
+ (v) => v instanceof Set,
12992
+ (v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null,
12993
+ () => /* @__PURE__ */ new Set()
12994
+ ]
13084
12995
  };
13085
- ClassBuilder = class {
13086
- constructor(name) {
13087
- this.name = name;
13088
- this.fields = {};
13089
- this.compFields = /* @__PURE__ */ new Set();
13090
- this._methods = {};
13091
- this._statics = {};
13092
- }
13093
- build() {
13094
- const { name, fields, compFields, _methods } = this;
13095
- const defaults = Object.fromEntries(
13096
- Object.entries(fields).map(([fieldName, field]) => [fieldName, field.defaultValue])
13097
- );
13098
- const Class = {
13099
- [name]: class {
13100
- constructor(values = {}) {
13101
- Object.assign(this, defaults, values);
13102
- }
13103
- }
13104
- }[name];
13105
- Class[DRAFTABLE] = true;
13106
- Object.assign(Class.prototype, _methods);
13107
- const metaClass = { fields, name, methods: _methods };
13108
- Object.assign(
13109
- Class,
13110
- {
13111
- getMetaClass: () => metaClass,
13112
- make(inArgs = {}, opts = {}) {
13113
- const args = {};
13114
- const scope = opts.scope ?? this[COMPONENT]?.scope ?? this.scope;
13115
- for (const key in inArgs) {
13116
- const field = fields[key];
13117
- if (compFields.has(key)) args[key] = mkCompField(field, scope, inArgs[key]);
13118
- else if (field === void 0)
13119
- console.warn("extra argument to constructor:", name, key, inArgs);
13120
- else args[key] = field.coerceOrDefault(inArgs[key]);
13121
- }
13122
- for (const key of compFields)
13123
- if (args[key] === void 0) args[key] = mkCompField(fields[key], scope, inArgs[key]);
13124
- return freeze(new this(args), true);
13125
- }
13126
- },
13127
- this._statics
13128
- );
13129
- return Class;
13130
- }
13131
- methods(proto) {
13132
- for (const k in proto) this._methods[k] = proto[k];
13133
- }
13134
- statics(proto) {
13135
- for (const k in proto) this._statics[k] = proto[k];
13136
- }
13137
- addField(name, dval, FieldCls) {
13138
- const field = new FieldCls(name, dval);
13139
- this.fields[name] = field;
13140
- return field;
13141
- }
13142
- addCompField(name, type3, args) {
13143
- const field = new FieldComp(type3, name, args);
13144
- this.compFields.add(name);
13145
- this.fields[name] = field;
13146
- return field;
13147
- }
13148
- };
13149
- FIELD_CLASS = /* @__PURE__ */ Symbol.for("tutuca.fieldClass");
13150
- fieldsByTypeName = {
13151
- text: FieldString,
13152
- int: FieldInt,
13153
- float: FieldFloat,
13154
- bool: FieldBool,
13155
- list: FieldList,
13156
- object: FieldObject,
13157
- map: FieldMap,
13158
- set: FieldSet,
13159
- any: FieldAny
13160
- };
13161
- META_KEYS = "name id fields methods views receive intent alter provide provideType lookup spec extra commonStyle globalStyle scope _rawProvide _rawLookup".split(
13162
- " "
13163
- );
12996
+ metaOf = (v) => v?.constructor?.getMetaClass?.();
13164
12997
  RESERVED_COMPONENT_STATICS = /* @__PURE__ */ new Set([
13165
- ...META_KEYS,
13166
- "make",
13167
- "getMetaClass",
13168
- ...Object.getOwnPropertyNames(Component.prototype)
12998
+ ..."name id fields methods views receive intent alter provide provideType lookup spec extra commonStyle globalStyle scope _rawProvide _rawLookup make getMetaClass".split(
12999
+ " "
13000
+ ),
13001
+ ...Object.keys(COMPONENT_METHODS)
13169
13002
  ]);
13170
- Component.fromSpec = (opts) => {
13171
- assertNoReservedComponentStatics(opts.statics);
13172
- const Class = classFromData(opts.name, opts);
13173
- const comp = new Component(Class, opts);
13174
- const metaClass = Class.getMetaClass();
13175
- comp.fields = metaClass.fields;
13176
- comp.methods = metaClass.methods;
13177
- Class.getMetaClass = () => comp;
13178
- Class[COMPONENT] = comp;
13179
- for (const key of META_KEYS)
13180
- if (!Object.hasOwn(Class, key))
13181
- Object.defineProperty(Class, key, {
13182
- get() {
13183
- return comp[key];
13184
- },
13185
- set(v) {
13186
- comp[key] = v;
13187
- },
13188
- configurable: true
13189
- });
13190
- for (const key of Object.getOwnPropertyNames(Component.prototype))
13191
- if (key !== "constructor" && !Object.hasOwn(Class, key))
13192
- Class[key] = (...args) => comp[key](...args);
13193
- return Class;
13194
- };
13195
13003
  }
13196
13004
  });
13197
13005
 
@@ -13199,11 +13007,22 @@ var init_oo = __esm({
13199
13007
  function nullHandler() {
13200
13008
  return this;
13201
13009
  }
13010
+ function subscribe(list, cb) {
13011
+ list.push(cb);
13012
+ return () => {
13013
+ const i = list.indexOf(cb);
13014
+ if (i !== -1) list.splice(i, 1);
13015
+ };
13016
+ }
13017
+ function warnUnknownLeg(what, leg) {
13018
+ console.warn(`unknown ${what} route leg`, leg, '- expected "dyn" or "lex"');
13019
+ return null;
13020
+ }
13202
13021
  function warnNotIntent(verb) {
13203
13022
  console.warn(`ctx.${verb}() is only meaningful in an "intent" handler - ignored`);
13204
13023
  }
13205
13024
  function rootDispatcher(transactor) {
13206
- return new Dispatcher(new Path([]), transactor, null);
13025
+ return new Dispatcher(new DispatchPath(), transactor, null);
13207
13026
  }
13208
13027
  var State2, Transactor, Transaction, InputEvent, NameArgsTransaction, SendEvent, IntentEvent, PASS, REFUSAL_RING_CAP, INTENT_DEPTH, IntentWalk, Completion, Dispatcher, EventContext, PathChanges;
13209
13028
  var init_transactor = __esm({
@@ -13251,14 +13070,7 @@ var init_transactor = __esm({
13251
13070
  // carry: kind, name, args, path, pathKeys, targetPath, handler, handlerName,
13252
13071
  // matched, before, after, parent, timestamp. Purely observational.
13253
13072
  observe(cb) {
13254
- this._observers.push(cb);
13255
- return () => {
13256
- const i = this._observers.indexOf(cb);
13257
- if (i !== -1) this._observers.splice(i, 1);
13258
- };
13259
- }
13260
- _emit(record) {
13261
- for (const cb of this._observers) cb(record);
13073
+ return subscribe(this._observers, cb);
13262
13074
  }
13263
13075
  // Record a refusal: a dispatch the runtime could not carry out. Kinds are
13264
13076
  // open strings; today the runtime raises NO_HANDLER (a receive name with no
@@ -13277,11 +13089,7 @@ var init_transactor = __esm({
13277
13089
  // Subscribe to refusal records as they happen. Returns an unsubscribe fn,
13278
13090
  // like observe().
13279
13091
  observeRefusals(cb) {
13280
- this._refusalObservers.push(cb);
13281
- return () => {
13282
- const i = this._refusalObservers.indexOf(cb);
13283
- if (i !== -1) this._refusalObservers.splice(i, 1);
13284
- };
13092
+ return subscribe(this._refusalObservers, cb);
13285
13093
  }
13286
13094
  // Build and dispatch one observer record. Pins field-resolved keys (e.g. a
13287
13095
  // `.a[.selId]` render target reconstructed from a DOM event) against the root the
@@ -13290,7 +13098,7 @@ var init_transactor = __esm({
13290
13098
  _emitRecord(root, { kind, name, args, path, targetPath, handler, handlerName, matched, before, after, parent }) {
13291
13099
  if (this._observers.length === 0) return;
13292
13100
  const pinned = path.pinKeys(root);
13293
- this._emit({
13101
+ const record = {
13294
13102
  kind,
13295
13103
  name,
13296
13104
  args: args ?? null,
@@ -13304,14 +13112,14 @@ var init_transactor = __esm({
13304
13112
  after,
13305
13113
  parent,
13306
13114
  timestamp: Date.now()
13307
- });
13115
+ };
13116
+ for (const cb of this._observers) cb(record);
13308
13117
  }
13309
13118
  // The observer record for a settled transaction. The resolved handler
13310
13119
  // (`_resolvedHandler`/`_matched`) and per-leaf before/after (`_before`/`_after`)
13311
13120
  // were captured while the handler ran (see callHandler / Transaction.run).
13312
13121
  _emitTransaction(transaction, root) {
13313
13122
  if (this._observers.length === 0) return;
13314
- if (transaction._resolvedHandler === void 0) return;
13315
13123
  this._emitRecord(root, {
13316
13124
  kind: transaction.observeKind,
13317
13125
  name: transaction.observeName,
@@ -13325,26 +13133,22 @@ var init_transactor = __esm({
13325
13133
  parent: transaction.parentTransaction
13326
13134
  });
13327
13135
  }
13328
- // Make `child` a tracked unit of `parent`'s subtree: the parent's completion stays open
13329
- // until the child's *whole* subtree settles. Tracking happens at dispatch time — during
13330
- // the parent's handler or afterTransaction — while the parent's self-unit is still held,
13331
- // so the parent counter can't reach zero before the child is registered. Returns `child`.
13332
- _link(child, parent) {
13333
- if (parent) {
13334
- const release = parent.completion.track();
13335
- child.completion.whenSubtreeSettled().then(release);
13336
- }
13337
- return child;
13338
- }
13339
13136
  // `origin` is where the message came FROM, pinned now for the same reason an
13340
13137
  // intent's answerPath is: a reply must reach the sender that asked even if a key
13341
13138
  // moved while the message was in flight. Null when nobody is waiting — a host
13342
13139
  // sendAtRoot or a view's own `@on.*` — and ctx.sendReply refuses on that.
13343
- pushSend(path, name, args = [], parent = null, origin = null) {
13140
+ //
13141
+ // The parent's subtree stays open until this message's whole subtree settles. The
13142
+ // unit is taken now, at dispatch — during the parent's handler or afterTransaction,
13143
+ // while the parent's self-unit is still held — so its counter can't reach zero first.
13144
+ pushSend(path, name, args = [], parent = null, origin = null, txnPath = null) {
13344
13145
  const t = new SendEvent(path, this, name, args, parent);
13345
- t.origin = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
13146
+ t.txnPath = txnPath;
13147
+ t.origin = origin;
13148
+ t.originPinned = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
13346
13149
  this.pushTransaction(t);
13347
- return this._link(t, parent);
13150
+ if (parent) t.completion.carry(parent.completion.track());
13151
+ return t;
13348
13152
  }
13349
13153
  // Raise an intent: a job the sender did not address, walked along a route until
13350
13154
  // something answers. `opts.route` is a list of legs (see DEFAULT_ROUTE) and
@@ -13368,7 +13172,7 @@ var init_transactor = __esm({
13368
13172
  return this.transactions.length > 0;
13369
13173
  }
13370
13174
  transactNext() {
13371
- if (this.hasPendingTransactions) this.transact(this.transactions.shift());
13175
+ this.transact(this.transactions.shift());
13372
13176
  }
13373
13177
  transact(transaction) {
13374
13178
  try {
@@ -13381,8 +13185,7 @@ var init_transactor = __esm({
13381
13185
  } else console.warn("undefined new state", { curState, transaction });
13382
13186
  } finally {
13383
13187
  transaction.ensureWalkAdvanced?.();
13384
- transaction._completion?.ensureSelfSettled();
13385
- transaction._completion?.releaseSelf();
13188
+ transaction._completion?.finish();
13386
13189
  }
13387
13190
  }
13388
13191
  transactInputNow(path, event, eventHandler, dragInfo) {
@@ -13392,6 +13195,7 @@ var init_transactor = __esm({
13392
13195
  Transaction = class {
13393
13196
  constructor(path, transactor, parentTransaction = null) {
13394
13197
  this.path = path;
13198
+ this.txnPath = null;
13395
13199
  this.transactor = transactor;
13396
13200
  this.parentTransaction = parentTransaction;
13397
13201
  this._completion = null;
@@ -13411,39 +13215,41 @@ var init_transactor = __esm({
13411
13215
  whenSubtreeSettled() {
13412
13216
  return this.completion.whenSubtreeSettled();
13413
13217
  }
13414
- afterTransaction() {
13415
- }
13416
- // Ending a walk and forwarding belong to the two dispatch buckets; the base warns
13417
- // rather than throwing, so a stray call from the wrong place is a message and not a
13418
- // crash. `walk` is undefined here, which is how ctx.reply/ctx.fail tell the two apart.
13419
- stop() {
13420
- warnNotIntent("stop");
13421
- }
13422
- forward(_opts) {
13423
- console.warn('ctx.forward() needs a "receive" or "intent" handler - ignored');
13424
- }
13425
- // The kind reported to observers (see Transactor.observe); null on the base.
13426
- get observeKind() {
13427
- return null;
13428
- }
13429
- // The name reported to observers; null on the base. Overridden by NameArgs (the
13430
- // dispatched message name) and InputEvent (the DOM event type). Kept separate from
13431
- // `name`/`ctx.name` so it can't change handler-visible behavior.
13432
- get observeName() {
13433
- return null;
13434
- }
13218
+ // Every transaction kind defines `observeKind` / `observeName` (what observers see;
13219
+ // kept apart from `name`/`ctx.name` so they can't change handler-visible behavior),
13220
+ // `getHandlerAndArgs(root, instance, comps)`, and the three `_forward*` hooks below.
13435
13221
  callHandler(root, instance, draft, comps) {
13436
13222
  const [handler, args] = this.getHandlerAndArgs(root, instance, comps);
13437
13223
  this._resolvedHandler = handler;
13438
13224
  return handler.apply(instance, [draft, ...args]);
13439
13225
  }
13440
- getHandlerAndArgs(_root, _instance, _comps) {
13441
- return null;
13226
+ // `forward` is where a name can LEAVE the component: the message being handled is
13227
+ // raised again as an INTENT under the same name (`_forwardName()`) with the same
13228
+ // arguments unless `opts.args` says otherwise, from the position `_forwardPath()`
13229
+ // names. Deferred to afterTransaction so the state the handler produced is in place
13230
+ // before the walk starts. (`IntentEvent` overrides both: in an intent body, forward
13231
+ // amends the walk that is already running.)
13232
+ forward(opts) {
13233
+ this._forward = opts ?? {};
13234
+ }
13235
+ afterTransaction() {
13236
+ const f = this._forward;
13237
+ if (f === void 0) return;
13238
+ this._forward = void 0;
13239
+ const name = this._forwardName();
13240
+ if (name === void 0) {
13241
+ this.transactor.refuse("FORWARD_NO_NAME", {});
13242
+ return;
13243
+ }
13244
+ const { args = this._forwardArgs(), ...rest } = f;
13245
+ this.transactor.pushIntent(this._forwardPath(), name, args, rest, this);
13442
13246
  }
13443
- // The path used to apply the mutation. Teleports dynamic-var renders so it lands on
13444
- // the data's real location (the dispatch `this.path` keeps intermediates).
13247
+ // The path used to apply the mutation: the ACTIVE frame of the dispatch path, so
13248
+ // an event inside a `<x render="*name">` subtree updates the value where it
13249
+ // really lives (the dispatch `this.path` keeps the visual callers, for bubbling).
13250
+ // `txnPath`, when pinned by the sender, wins.
13445
13251
  getTransactionPath() {
13446
- return this.path.toTransactionPath().compact();
13252
+ return this.txnPath ?? this.path.toTransactionPath();
13447
13253
  }
13448
13254
  run(curRoot, comps) {
13449
13255
  const txnPath = this.getTransactionPath();
@@ -13467,12 +13273,20 @@ var init_transactor = __esm({
13467
13273
  this.dragInfo = dragInfo;
13468
13274
  this._dispatchPath = null;
13469
13275
  }
13470
- // Frame steps removed, DynStep + one step per crossed component kept: bubbling
13471
- // it visits every component (including intermediates of a dynamic-var render).
13276
+ // Frame-only steps removed, one step per crossed component kept — inside every
13277
+ // continuation frame independently, so bubbling it visits every component and
13278
+ // then returns to the caller that wrote the `*name`.
13472
13279
  get dispatchPath() {
13473
13280
  this._dispatchPath ??= this.path.compact();
13474
13281
  return this._dispatchPath;
13475
13282
  }
13283
+ // Only a DOM event's path carries frame-only bind steps (they replay the handler's
13284
+ // arguments, but address no state); the compacted path is where the mutation lands,
13285
+ // so a handler inside @each still updates the component that owns the view. Every
13286
+ // other transaction is dispatched at an already-compacted position.
13287
+ getTransactionPath() {
13288
+ return this.dispatchPath.toTransactionPath();
13289
+ }
13476
13290
  // A DOM event is an ADDRESSED message like any other — it reaches the component that
13477
13291
  // owns the view and stops. It keeps its own class only because it resolves its handler
13478
13292
  // from the compiled view and runs synchronously, not because it is a different channel.
@@ -13482,40 +13296,29 @@ var init_transactor = __esm({
13482
13296
  get observeName() {
13483
13297
  return this.e?.type ?? null;
13484
13298
  }
13485
- // A view's name is a message. `forward` is where that name can LEAVE the component:
13486
- // it becomes an intent with the same name and the same arguments, so a view that says
13487
- // `@on.click="saveDraft .text"` never has to change when an ancestor takes the job over.
13488
- // (A `$method` handler has a name too, so forwarding one raises an intent under it.)
13489
- forward(opts) {
13490
- this._forward = opts ?? {};
13491
- }
13492
- afterTransaction() {
13493
- const f = this._forward;
13494
- if (f === void 0) return;
13495
- this._forward = void 0;
13496
- const { args = this._handlerArgs ?? [], ...rest } = f;
13299
+ // A view's name is a message, and forwarding it means a view that says
13300
+ // `@on.click="saveDraft .text"` never has to change when an ancestor takes the job
13301
+ // over. (A `$method` handler has a name too, so forwarding one raises an intent under
13302
+ // it.) `handler` is a NodeEvent, whose own `name` is the DOM event type; the MESSAGE
13303
+ // name is the one the view wrote, on the handler call it wraps.
13304
+ _forwardName() {
13497
13305
  const hv = this.handler?.handlerCall?.handlerVal ?? this.handler?.handlerVal;
13498
- const name = hv?.name;
13499
- if (name === void 0) {
13500
- this.transactor.refuse("FORWARD_NO_NAME", {});
13501
- return;
13502
- }
13503
- this.transactor.pushIntent(this.dispatchPath, name, args, rest, this);
13306
+ return hv?.name;
13307
+ }
13308
+ _forwardArgs() {
13309
+ return this._handlerArgs ?? [];
13310
+ }
13311
+ _forwardPath() {
13312
+ return this.dispatchPath;
13504
13313
  }
13505
13314
  getHandlerAndArgs(root, _instance, comps) {
13506
- const stack = this.path.toTransactionPath().buildStack(Stack.root(comps, root, this));
13315
+ const stack = this.path.buildStack(Stack.root(comps, root, this));
13507
13316
  const [handler, args] = this.handler.getHandlerAndArgs(stack, this);
13508
13317
  this._handlerArgs = [...args];
13509
13318
  const path = this.dispatchPath;
13510
13319
  args.push(new EventContext(path, this.transactor, this));
13511
13320
  return [handler, args];
13512
13321
  }
13513
- // The dispatched DOM event, read only through `e.<member>` handler args
13514
- // (src/value.js EventMemberVal via Stack.lookupEvent). There is no bare
13515
- // implicit-name vocabulary anymore: every arg carries a sigil.
13516
- get event() {
13517
- return this.e;
13518
- }
13519
13322
  };
13520
13323
  NameArgsTransaction = class extends Transaction {
13521
13324
  constructor(path, transactor, name, args, parentTransaction) {
@@ -13533,6 +13336,17 @@ var init_transactor = __esm({
13533
13336
  get observeName() {
13534
13337
  return this.name;
13535
13338
  }
13339
+ // `forward` in a RECEIVE body starts a walk: the message that arrived becomes an
13340
+ // intent, keeping its name and payload.
13341
+ _forwardName() {
13342
+ return this.name;
13343
+ }
13344
+ _forwardArgs() {
13345
+ return this.args;
13346
+ }
13347
+ _forwardPath() {
13348
+ return this.path;
13349
+ }
13536
13350
  getHandlerForName(comp) {
13537
13351
  const handlers = comp?.[this.handlerProp];
13538
13352
  const exact = handlers?.[this.name];
@@ -13563,19 +13377,6 @@ var init_transactor = __esm({
13563
13377
  get observeKind() {
13564
13378
  return this._isAnswer ? "answer" : "receive";
13565
13379
  }
13566
- // `forward` in a RECEIVE body starts a walk: the message that arrived becomes an
13567
- // intent, keeping its name and payload. This is what lets a view's name leave the
13568
- // component without the view changing.
13569
- forward(opts) {
13570
- this._forward = opts ?? {};
13571
- }
13572
- afterTransaction() {
13573
- const f = this._forward;
13574
- if (f === void 0) return;
13575
- this._forward = void 0;
13576
- const { args = this.args, ...rest } = f;
13577
- this.transactor.pushIntent(this.path, this.name, args, rest, this);
13578
- }
13579
13380
  };
13580
13381
  IntentEvent = class extends NameArgsTransaction {
13581
13382
  handlerProp = "intent";
@@ -13589,9 +13390,6 @@ var init_transactor = __esm({
13589
13390
  forward(opts) {
13590
13391
  this.walk.amend(opts, this.path);
13591
13392
  }
13592
- stop() {
13593
- this.walk.finish(null, null);
13594
- }
13595
13393
  afterTransaction() {
13596
13394
  this.ensureWalkAdvanced();
13597
13395
  }
@@ -13629,7 +13427,7 @@ var init_transactor = __esm({
13629
13427
  while (this.legIndex < this.route.length) {
13630
13428
  const leg = this.route[this.legIndex];
13631
13429
  if (leg === "dyn") {
13632
- if (this.dynAt.steps.length === 0) {
13430
+ if (!this.dynAt.canPop()) {
13633
13431
  this.legIndex++;
13634
13432
  continue;
13635
13433
  }
@@ -13641,7 +13439,7 @@ var init_transactor = __esm({
13641
13439
  this.legIndex++;
13642
13440
  return this._tryLex();
13643
13441
  }
13644
- console.warn("unknown intent route leg", leg, '- expected "dyn" or "lex"');
13442
+ warnUnknownLeg("intent", leg);
13645
13443
  this.legIndex++;
13646
13444
  }
13647
13445
  this.exhaust("noHandler");
@@ -13726,11 +13524,11 @@ var init_transactor = __esm({
13726
13524
  if (this.ended) return;
13727
13525
  this.ended = true;
13728
13526
  if (name === null) return this.release?.();
13729
- const path = this.answerPath ?? this.origin;
13730
- const t = new SendEvent(path, this.transactor, name, args, this.parent);
13527
+ const t = new SendEvent(this.origin, this.transactor, name, args, this.parent);
13528
+ t.txnPath = this.answerPath;
13731
13529
  t._isAnswer = true;
13732
13530
  this.transactor.pushTransaction(t);
13733
- if (this.release) t.completion.whenSubtreeSettled().then(this.release);
13531
+ if (this.release) t.completion.carry(this.release);
13734
13532
  }
13735
13533
  };
13736
13534
  Completion = class {
@@ -13738,12 +13536,12 @@ var init_transactor = __esm({
13738
13536
  this.val = void 0;
13739
13537
  this.selfSettled = false;
13740
13538
  this.subtreeSettled = false;
13741
- this.pending = 1;
13539
+ this.pending = 0;
13742
13540
  this._selfResolve = null;
13743
13541
  this._selfPromise = null;
13744
13542
  this._subtreeResolve = null;
13745
13543
  this._subtreePromise = null;
13746
- this._selfReleased = false;
13544
+ this._releaseSelf = this.track();
13747
13545
  }
13748
13546
  whenSettled() {
13749
13547
  if (this.selfSettled) return Promise.resolve(this.val);
@@ -13766,38 +13564,38 @@ var init_transactor = __esm({
13766
13564
  this.val = val;
13767
13565
  this._selfResolve?.(val);
13768
13566
  }
13769
- // Settle self even when no handler produced a value (undefined-state / throw paths).
13770
- ensureSelfSettled() {
13771
- if (!this.selfSettled) this.markSelfSettled(this.val);
13567
+ // The transaction is done processing: self is settled (with the value the handler
13568
+ // produced, or none on the undefined-state / throw paths) and the self-unit goes.
13569
+ finish() {
13570
+ this.markSelfSettled(this.val);
13571
+ this._releaseSelf();
13772
13572
  }
13773
- // Register an outstanding unit; returns a one-shot release.
13573
+ // Register an outstanding unit; returns its one-shot release. The subtree settles
13574
+ // when the last unit is released.
13774
13575
  track() {
13775
13576
  this.pending++;
13776
13577
  let done = false;
13777
13578
  return () => {
13778
13579
  if (done) return;
13779
13580
  done = true;
13780
- this._release();
13581
+ if (--this.pending === 0) {
13582
+ this.subtreeSettled = true;
13583
+ this._subtreeResolve?.(this.val);
13584
+ }
13781
13585
  };
13782
13586
  }
13783
- releaseSelf() {
13784
- if (this._selfReleased) return;
13785
- this._selfReleased = true;
13786
- this._release();
13787
- }
13788
- _release() {
13789
- if (--this.pending === 0) {
13790
- this.subtreeSettled = true;
13791
- this._subtreeResolve?.(this.val);
13792
- }
13587
+ // Hand a unit held elsewhere to this subtree: `release` runs once this transaction
13588
+ // and everything it spawned have settled.
13589
+ carry(release) {
13590
+ this.whenSubtreeSettled().then(release);
13793
13591
  }
13794
13592
  };
13795
13593
  Dispatcher = class {
13796
- constructor(path, transactor, parentTransaction, root = transactor.state.val) {
13594
+ constructor(path, transactor, parentTransaction) {
13797
13595
  this.path = path;
13798
13596
  this.transactor = transactor;
13799
13597
  this.parent = parentTransaction;
13800
- this.root = root;
13598
+ this.root = transactor.state.val;
13801
13599
  }
13802
13600
  // Walk the component instances on this ctx's path, leaf→root, calling
13803
13601
  // callback(Component, instance). Return false from the callback to stop early.
@@ -13822,28 +13620,37 @@ var init_transactor = __esm({
13822
13620
  // replayed; provides are pushed on every component frame either way, so a provide
13823
13621
  // that reads a loop binding is the one case this cannot reproduce faithfully.
13824
13622
  _stack() {
13825
- this._stackMemo ??= this.path.toTransactionPath().buildStack(Stack.root(this.transactor.comps, this.root, this.parent));
13623
+ this._stackMemo ??= this.path.buildStack(
13624
+ Stack.root(this.transactor.comps, this.root, this.parent)
13625
+ );
13826
13626
  return this._stackMemo;
13827
13627
  }
13828
13628
  // Resolve a name the way the renderer would. `opts.route` takes the same legs in
13829
13629
  // the same spelling as `ctx.intent` — `["dyn"]` the render ancestry, `["lex"]` the
13830
13630
  // registration scope, default both.
13631
+ // Legs are tried in route order, like the intent walker's; an unknown leg warns and
13632
+ // is skipped, and an empty route resolves to null.
13831
13633
  lookup(name, opts) {
13832
- return routeLookup(
13833
- opts?.route ?? DEFAULT_ROUTE,
13834
- () => this._lookupLex(name),
13835
- () => this._stack()?.lookupDynamic(name) ?? null
13836
- );
13634
+ for (const leg of opts?.route ?? DEFAULT_ROUTE) {
13635
+ const v = leg === "dyn" ? this._stack()?.lookupDynamic(name) ?? null : leg === "lex" ? this._lookupLex(name) : warnUnknownLeg("lookup", leg);
13636
+ if (v != null) return v;
13637
+ }
13638
+ return null;
13837
13639
  }
13838
13640
  // The `lex` leg without a Stack: the scope of the component whose handler is
13839
- // running, which is the leaf of this ctx's path.
13641
+ // running, which is the leaf of this ctx's path. A type name resolves to the
13642
+ // component registered under it; a value name to a path registered under it
13643
+ // (see ComponentStack.registerPaths), read against the current root.
13840
13644
  _lookupLex(name) {
13841
13645
  let Comp = null;
13842
13646
  this.walkPath((c) => {
13843
13647
  Comp = c;
13844
13648
  return false;
13845
13649
  });
13846
- return Comp?.scope?.lookupComponent(name) ?? null;
13650
+ const scope = Comp?.scope;
13651
+ if (scope == null) return null;
13652
+ if (isTypeName(name)) return scope.lookupComponent(name) ?? null;
13653
+ return scope.lookupPath(name)?.lookup(this.root) ?? null;
13847
13654
  }
13848
13655
  // A component lookup is a value lookup constrained to a component. The `lex` leg
13849
13656
  // can only ever answer with one; the `dyn` leg reads a binding an ancestor
@@ -13904,11 +13711,19 @@ var init_transactor = __esm({
13904
13711
  this.transactor.refuse("NO_SENDER", { name });
13905
13712
  return null;
13906
13713
  }
13907
- return this.sendAtPath(origin, name, args);
13714
+ return this.transactor.pushSend(
13715
+ origin,
13716
+ name,
13717
+ args,
13718
+ this.parent,
13719
+ this.path,
13720
+ this.parent.originPinned
13721
+ );
13908
13722
  }
13909
- // End the walk answering nothing — "served, and no answer".
13723
+ // End the walk answering nothing — "served, and no answer". Like reply/fail,
13724
+ // legal only in an `intent` handler.
13910
13725
  stop() {
13911
- return this.parent.stop();
13726
+ return this.parent.walk === void 0 ? warnNotIntent("stop") : this.parent.walk.finish(null, null);
13912
13727
  }
13913
13728
  // From an `intent` body: hand the intent to the next hop, optionally amending it.
13914
13729
  // From a `receive` body: turn the message that arrived into an intent. One word from
@@ -13987,7 +13802,6 @@ var init_app = __esm({
13987
13802
  this.transactor = new Transactor(comps, null);
13988
13803
  this.ParseContext = ParseContext2;
13989
13804
  this.renderer = renderer;
13990
- this.maxEventNodeDepth = Infinity;
13991
13805
  this._transactNextBatchId = this._evictCacheId = null;
13992
13806
  this._eventNames = new Set(_evs);
13993
13807
  this.dragInfo = this.curDragOver = null;
@@ -14014,12 +13828,11 @@ var init_app = __esm({
14014
13828
  _dispatchEvent(e) {
14015
13829
  const { type: type3 } = e;
14016
13830
  const isDrag = type3 === "dragover" || type3 === "dragstart" || type3 === "dragend" || type3 === "drop";
14017
- const { rootNode: root, maxEventNodeDepth: maxDepth, comps, transactor } = this;
14018
- const [path, handlers] = Path.fromNodeAndEventName(
13831
+ const { rootNode, comps, transactor } = this;
13832
+ const [path, handlers] = DispatchPath.fromNodeAndEventName(
14019
13833
  e.target,
14020
13834
  type3,
14021
- root,
14022
- maxDepth,
13835
+ rootNode,
14023
13836
  comps,
14024
13837
  !isDrag
14025
13838
  );
@@ -14083,7 +13896,7 @@ var init_app = __esm({
14083
13896
  const txnPath = path.compact().toTransactionPath();
14084
13897
  const value = txnPath.lookup(rootValue);
14085
13898
  const dragType = e.target.dataset.dragtype ?? "?";
14086
- const stack = path.toTransactionPath().buildStack(this.makeStack(rootValue));
13899
+ const stack = path.buildStack(this.makeStack(rootValue));
14087
13900
  this.dragInfo = new DragInfo(stack, value, dragType, e.target);
14088
13901
  } else if (type3 === "drop") {
14089
13902
  e.preventDefault();
@@ -14128,10 +13941,9 @@ var init_app = __esm({
14128
13941
  }
14129
13942
  compile() {
14130
13943
  for (const Comp of this.comps.byId.values()) {
14131
- const meta = Comp[COMPONENT];
14132
- meta.compile(this.ParseContext);
14133
- for (const key in meta.views)
14134
- for (const name of meta.views[key].ctx.genEventNames()) this._eventNames.add(name);
13944
+ Comp.compile(this.ParseContext);
13945
+ for (const key in Comp.views)
13946
+ for (const name of Comp.views[key].ctx.genEventNames()) this._eventNames.add(name);
14135
13947
  }
14136
13948
  this._compiled = true;
14137
13949
  }
@@ -14154,11 +13966,13 @@ var init_app = __esm({
14154
13966
  }
14155
13967
  stop() {
14156
13968
  this.stopCacheEvictionInterval();
13969
+ clearTimeout(this._transactNextBatchId);
13970
+ this._transactNextBatchId = null;
14157
13971
  for (const name of this._eventNames)
14158
13972
  this.rootNode.removeEventListener(name, this, listenerOpts(name));
14159
13973
  }
14160
13974
  sendAtRoot(name, args) {
14161
- this.transactor.pushSend(new Path([]), name, args);
13975
+ this.transactor.pushSend(new DispatchPath(), name, args);
14162
13976
  }
14163
13977
  registerComponents(comps, opts) {
14164
13978
  const scope = this.compStack.enter();
@@ -14242,28 +14056,21 @@ var init_cache = __esm({
14242
14056
  set(_keys, _cacheKey, _v) {
14243
14057
  }
14244
14058
  evict() {
14245
- return { hit: 0, miss: 0, badKey: 0 };
14246
14059
  }
14247
14060
  };
14248
14061
  WeakMapDomCache = class {
14249
14062
  constructor() {
14250
- this.hit = this.miss = this.badKey = 0;
14251
14063
  this.keysByLen = /* @__PURE__ */ new Map();
14252
14064
  }
14253
- _returnValue(r) {
14254
- if (r === void 0) this.miss += 1;
14255
- else this.hit += 1;
14256
- return r;
14257
- }
14258
14065
  get(keys, cacheKey) {
14259
14066
  const len = keys.length;
14260
14067
  let cur = this.keysByLen.get(len);
14261
- if (!cur) return this._returnValue(void 0);
14068
+ if (!cur) return void 0;
14262
14069
  for (let i = 0; i < len - 1; i++) {
14263
14070
  cur = cur.get(keys[i]);
14264
- if (!cur) return this._returnValue(void 0);
14071
+ if (!cur) return void 0;
14265
14072
  }
14266
- return this._returnValue(cur.get(keys[len - 1])?.[cacheKey]);
14073
+ return cur.get(keys[len - 1])?.[cacheKey];
14267
14074
  }
14268
14075
  set(keys, cacheKey, v) {
14269
14076
  const len = keys.length;
@@ -14276,10 +14083,7 @@ var init_cache = __esm({
14276
14083
  const key = keys[i];
14277
14084
  let next = cur.get(key);
14278
14085
  if (!next) {
14279
- if (!isWeakKey(key)) {
14280
- this.badKey += 1;
14281
- return;
14282
- }
14086
+ if (!isWeakKey(key)) return;
14283
14087
  next = /* @__PURE__ */ new WeakMap();
14284
14088
  cur.set(key, next);
14285
14089
  }
@@ -14289,31 +14093,48 @@ var init_cache = __esm({
14289
14093
  const leaf = cur.get(lastKey);
14290
14094
  if (leaf) leaf[cacheKey] = v;
14291
14095
  else if (isWeakKey(lastKey)) cur.set(lastKey, { [cacheKey]: v });
14292
- else this.badKey += 1;
14293
14096
  }
14294
14097
  evict() {
14295
- const { hit, miss, badKey } = this;
14296
- this.hit = this.miss = this.badKey = 0;
14297
14098
  this.keysByLen = /* @__PURE__ */ new Map();
14298
- return { hit, miss, badKey };
14299
14099
  }
14300
14100
  };
14301
14101
  }
14302
14102
  });
14303
14103
 
14304
14104
  // src/renderer.js
14305
- var DATASET_ATTRS, Renderer;
14105
+ function stampRenderBase(vdom, text) {
14106
+ if (vdom instanceof VNode)
14107
+ return new VNode(
14108
+ vdom.tag,
14109
+ { ...vdom.attrs, "data-rp": text },
14110
+ vdom.childs,
14111
+ vdom.key,
14112
+ vdom.namespace
14113
+ );
14114
+ if (vdom instanceof VFragment)
14115
+ return new VFragment(vdom.childs.map((c) => stampRenderBase(c, text)));
14116
+ return vdom;
14117
+ }
14118
+ var Renderer;
14306
14119
  var init_renderer = __esm({
14307
14120
  "src/renderer.js"() {
14308
14121
  init_cache();
14309
14122
  init_iteration();
14123
+ init_path();
14124
+ init_value();
14310
14125
  init_vdom();
14311
- DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk"];
14312
14126
  Renderer = class {
14313
14127
  constructor(comps) {
14314
14128
  this.comps = comps;
14315
14129
  this.cache = new WeakMapDomCache();
14316
- this.renderTag = h;
14130
+ }
14131
+ // Parse nodes build their VDOM through the renderer, never by importing vdom.js
14132
+ // themselves: a component may have been compiled by ANOTHER copy of the library
14133
+ // (a docs example imports the dist bundle while the host app runs from src), and
14134
+ // `render()` recognizes VNodes by `instanceof` against ITS copy's classes. Going
14135
+ // through `rx` guarantees the rendering copy makes them.
14136
+ renderTag(tag, attrs, childs, namespace) {
14137
+ return h(tag, attrs, childs, namespace);
14317
14138
  }
14318
14139
  renderFragment(childs) {
14319
14140
  return new VFragment(childs);
@@ -14324,38 +14145,24 @@ var init_renderer = __esm({
14324
14145
  setNullCache() {
14325
14146
  this.cache = new NullDomCache();
14326
14147
  }
14327
- // Library utilities for consumers embedding tutuca: render a value to a detached
14328
- // DOM node / an HTML string. Not called by the framework itself.
14329
- renderToDOM(stack, val) {
14330
- const rootNode = document.createElement("div");
14331
- const rOpts = { document };
14332
- render(h("DIV", null, [this.renderRoot(stack, val)]), rootNode, rOpts);
14333
- return rootNode.childNodes[0];
14334
- }
14335
- renderToString(stack, val, cleanAttrs = true) {
14336
- const dom = this.renderToDOM(stack, val);
14337
- if (cleanAttrs) {
14338
- const nodes = dom.querySelectorAll("[data-nid],[data-cid],[data-eid]");
14339
- for (const { dataset } of nodes) for (const name of DATASET_ATTRS) delete dataset[name];
14340
- }
14341
- return dom.innerHTML;
14342
- }
14343
14148
  renderRoot(stack, val, viewName = null) {
14344
14149
  const comp = this.comps.getCompFor(val);
14345
14150
  if (comp === null) return null;
14346
14151
  return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
14347
14152
  }
14348
- renderIt(stack, node, key, viewName) {
14153
+ // Render `stack.it` at a `<x render*>` site (`node`). `base` is the absolute
14154
+ // path the site resumed at, when it rendered a `*name` (see _rValComp).
14155
+ renderIt(stack, node, viewName, base = null) {
14349
14156
  const comp = this.comps.getCompFor(stack.it);
14350
- return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
14157
+ return comp ? this._rValComp(stack, stack.it, comp, node, "", viewName, base) : null;
14351
14158
  }
14352
14159
  // `node` is the parse node of the render site (`<x render>` / `render-it` /
14353
14160
  // `render-each`, or the view's root anode for the app root). It keys the
14354
14161
  // cache as a globally-unique object: node ids alone are unique only within a
14355
14162
  // single view, so the same value rendered by two components (e.g. through a
14356
14163
  // shared dynamic-var sequence) would otherwise collide in the cache.
14357
- _rValComp(stack, val, comp, node, key, viewName) {
14358
- const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}`;
14164
+ _rValComp(stack, val, comp, node, key, viewName, base = null) {
14165
+ const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}${stack.renderPath.addressKey}`;
14359
14166
  const cachePath = [node, val];
14360
14167
  stack._pushDynBindValuesToArray(cachePath, comp);
14361
14168
  const cachedNode = this.cache.get(cachePath, cacheKey);
@@ -14363,30 +14170,43 @@ var init_renderer = __esm({
14363
14170
  const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
14364
14171
  const body = this.renderView(view, stack);
14365
14172
  if (body == null) return null;
14173
+ const baseJson = base === null ? null : pathToJson(base);
14366
14174
  const meta = this._renderMetadata({
14367
14175
  $: "Comp",
14368
14176
  nid: node?.nodeId ?? null,
14369
14177
  cid: comp.id,
14370
- vid: view.name
14178
+ vid: view.name,
14179
+ ...baseJson === null ? null : { base: baseJson }
14371
14180
  });
14372
- const dom = new VFragment([meta, body]);
14181
+ const dom = new VFragment([
14182
+ meta,
14183
+ baseJson === null ? body : stampRenderBase(body, JSON.stringify(baseJson))
14184
+ ]);
14373
14185
  this.cache.set(cachePath, cacheKey, dom);
14374
14186
  return dom;
14375
14187
  }
14376
14188
  pushEachEntry(r, nid, attrName, key, dom) {
14377
14189
  r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
14378
14190
  }
14379
- renderEachWhen(stack, iterInfo, view, nid) {
14380
- const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
14191
+ renderEachWhen(stack, each2) {
14192
+ const { val: seqVal, node: view, nodeId: nid } = each2;
14193
+ const { seq, filter, loopWith, enricher } = each2.evalIter(stack);
14194
+ const seqPath = seqVal instanceof DynVal ? stack.lookupDynamicLocated(seqVal.name)?.path ?? null : null;
14381
14195
  const r = [];
14382
14196
  const it = stack.it;
14383
14197
  const renderOne = (key, value, attrName, binds) => {
14198
+ const itemBase = seqPath === null ? null : keyedPath(seqPath, key);
14199
+ const itemStep = itemBase === null ? each2.itemStep(key) : null;
14200
+ const itemPath = itemBase !== null ? stack.renderPath.pushFrame(itemBase) : itemStep !== null ? stack.renderPath.pushItem(itemStep) : stack.renderPath;
14384
14201
  const cachePath = enricher ? [view, it, value] : [view, value];
14385
- const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
14202
+ const cacheKey = `${stack.viewsId ?? ""}${nid}${key}${itemPath.addressKey}`;
14386
14203
  const cachedNode = this.cache.get(cachePath, cacheKey);
14387
14204
  if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
14388
14205
  else {
14389
- const dom = this.renderView(view, stack.enter(value, binds, false));
14206
+ const dom = this.renderView(
14207
+ view,
14208
+ stack.enter(value, binds, false, itemPath, itemBase !== null)
14209
+ );
14390
14210
  if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
14391
14211
  this.cache.set(cachePath, cacheKey, dom);
14392
14212
  }
@@ -14424,9 +14244,7 @@ var init_renderer = __esm({
14424
14244
 
14425
14245
  // src/util/render.js
14426
14246
  function reindexComponents(comps) {
14427
- for (let i = 0; i < comps.length; i++) {
14428
- comps[i][COMPONENT].id = i;
14429
- }
14247
+ for (let i = 0; i < comps.length; i++) comps[i].id = i;
14430
14248
  }
14431
14249
  function serializeContainer(container) {
14432
14250
  for (const input of container.querySelectorAll("input")) {
@@ -14435,19 +14253,19 @@ function serializeContainer(container) {
14435
14253
  }
14436
14254
  return container.innerHTML;
14437
14255
  }
14438
- function renderToHTMLNode(document2, components, macros, rootState, ParseContext2, opts = { noCache: true }) {
14256
+ function renderToHTMLNode(document2, components, macros, rootState, ParseContext2, { noCache = true, paths = null, intentHandlers = null, view = null } = {}) {
14439
14257
  const container = document2.createElement("div");
14440
14258
  document2.body.appendChild(container);
14441
14259
  reindexComponents(components);
14442
14260
  const comps = new Components();
14443
14261
  const renderer = new Renderer(comps);
14444
14262
  const app = new App(container, comps, renderer, ParseContext2);
14445
- const scope = app.registerComponents(components);
14263
+ const scope = app.registerComponents(components, { paths });
14446
14264
  if (macros) scope.registerMacros(macros);
14447
- if (opts.intentHandlers) scope.registerIntentHandlers(opts.intentHandlers);
14448
- app.rootViewName = opts.view ?? null;
14265
+ if (intentHandlers) scope.registerIntentHandlers(intentHandlers);
14266
+ app.rootViewName = view;
14449
14267
  app.transactor.state.set(rootState);
14450
- app.start(opts);
14268
+ app.start({ noCache });
14451
14269
  return {
14452
14270
  container,
14453
14271
  app,
@@ -14457,18 +14275,18 @@ function renderToHTMLNode(document2, components, macros, rootState, ParseContext
14457
14275
  }
14458
14276
  };
14459
14277
  }
14460
- async function renderToHTMLDriven(document2, components, macros, rootState, ParseContext2, { phase = null, intentHandlers = null, view = null } = {}) {
14278
+ async function renderToHTMLDriven(document2, components, macros, rootState, ParseContext2, { phase = null, ...opts } = {}) {
14461
14279
  const { container, app, cleanup } = renderToHTMLNode(
14462
14280
  document2,
14463
14281
  components,
14464
14282
  macros,
14465
14283
  rootState,
14466
14284
  ParseContext2,
14467
- { noCache: true, intentHandlers, view }
14285
+ opts
14468
14286
  );
14469
14287
  try {
14470
14288
  if (phase) {
14471
- dispatchPhase(rootDispatcher(app.transactor), new Path([]), phase, app.state.val);
14289
+ dispatchPhase(rootDispatcher(app.transactor), new DispatchPath(), phase, app.state.val);
14472
14290
  await app.transactor.settle();
14473
14291
  }
14474
14292
  return serializeContainer(container);
@@ -14706,7 +14524,7 @@ async function driveStack(stack, value, phase, opts = {}) {
14706
14524
  val
14707
14525
  );
14708
14526
  });
14709
- dispatchPhase(rootDispatcher(transactor), new Path([]), phase, value);
14527
+ dispatchPhase(rootDispatcher(transactor), new DispatchPath(), phase, value);
14710
14528
  await transactor.settle();
14711
14529
  return transactor.state.val;
14712
14530
  }
@@ -15406,23 +15224,30 @@ function installSkill(skill, root, scope, force, dotAgents, dryRun, opts) {
15406
15224
  });
15407
15225
  }
15408
15226
  const target = targetDir(scope, skill.name, dotAgents);
15409
- if (targetHasSkillFiles(target) && !force) {
15410
- emitError(opts, {
15411
- code: CODES.SKILL_TARGET_EXISTS,
15412
- message: `${target} already contains skill files`,
15413
- hint: "Re-run with --force to overwrite, or --dry-run to see what would change."
15414
- });
15415
- }
15416
15227
  const baseDir = dotAgents ? ".agents/skills" : ".claude/skills";
15417
15228
  const rel = scope === "project" ? `${baseDir}/${skill.name}` : target;
15229
+ const exists = targetHasSkillFiles(target);
15418
15230
  if (dryRun) {
15419
15231
  const files = walkFiles(src, { match: () => true }).map((f) => relative(src, f));
15420
15232
  process.stdout.write(`would install ${skill.name} skill → ${rel}
15421
15233
  `);
15422
15234
  for (const f of files) process.stdout.write(` + ${f}
15423
15235
  `);
15236
+ if (exists) {
15237
+ process.stdout.write(
15238
+ ` note: ${rel} already contains skill files; re-run with --force to overwrite them
15239
+ `
15240
+ );
15241
+ }
15424
15242
  return;
15425
15243
  }
15244
+ if (exists && !force) {
15245
+ emitError(opts, {
15246
+ code: CODES.SKILL_TARGET_EXISTS,
15247
+ message: `${target} already contains skill files`,
15248
+ hint: "Re-run with --force to overwrite, or --dry-run to see what would change."
15249
+ });
15250
+ }
15426
15251
  mkdirSync2(target, { recursive: true });
15427
15252
  cpSync(src, target, { recursive: true });
15428
15253
  process.stdout.write(`installed ${skill.name} skill → ${rel}
@@ -15520,16 +15345,16 @@ async function createNodeEnv() {
15520
15345
  const dom = new JSDOM("<!DOCTYPE html><html><head></head><body></body></html>", {
15521
15346
  virtualConsole
15522
15347
  });
15523
- const { document: document2, Text, Comment } = dom.window;
15348
+ const { document: document2 } = dom.window;
15524
15349
  globalThis.document = document2;
15525
15350
  class HeadlessParseContext extends ParseContext {
15526
15351
  constructor() {
15527
- super(document2, Text, Comment);
15352
+ super(document2);
15528
15353
  }
15529
15354
  }
15530
15355
  class HeadlessLintParseContext extends LintParseContext {
15531
15356
  constructor() {
15532
- super(document2, Text, Comment);
15357
+ super(document2);
15533
15358
  }
15534
15359
  }
15535
15360
  return {
@@ -15538,7 +15363,7 @@ async function createNodeEnv() {
15538
15363
  LintParseContext: HeadlessLintParseContext
15539
15364
  };
15540
15365
  }
15541
- var init_env2 = __esm({
15366
+ var init_env = __esm({
15542
15367
  "tools/cli/env.js"() {
15543
15368
  init_anode();
15544
15369
  init_lint_check();
@@ -16039,7 +15864,7 @@ var init_storybook = __esm({
16039
15864
  init_chai2();
16040
15865
  init_module();
16041
15866
  init_test();
16042
- init_env2();
15867
+ init_env();
16043
15868
  init_errors();
16044
15869
  init_pkg();
16045
15870
  init_walk();
@@ -16224,7 +16049,7 @@ export async function resolve(specifier, context, nextResolve) {
16224
16049
  init_errors();
16225
16050
 
16226
16051
  // tools/cli/with-module.js
16227
- init_env2();
16052
+ init_env();
16228
16053
  init_errors();
16229
16054
  import { statSync as statSync2 } from "node:fs";
16230
16055
  import { parseArgs as parseArgs4 } from "node:util";