tutuca 0.13.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -293,7 +293,7 @@ function processResult(result, scope) {
293
293
  }
294
294
  return result !== NOTHING ? result : void 0;
295
295
  }
296
- function finalize(rootScope, value, path) {
296
+ function finalize(rootScope, value, path2) {
297
297
  if (isFrozen(value))
298
298
  return value;
299
299
  const useStrictIteration = rootScope.immer_.shouldUseStrictIteration();
@@ -301,7 +301,7 @@ function finalize(rootScope, value, path) {
301
301
  if (!state) {
302
302
  each(
303
303
  value,
304
- (key, childValue) => finalizeProperty(rootScope, state, value, key, childValue, path),
304
+ (key, childValue) => finalizeProperty(rootScope, state, value, key, childValue, path2),
305
305
  useStrictIteration
306
306
  );
307
307
  return value;
@@ -331,16 +331,16 @@ function finalize(rootScope, value, path) {
331
331
  result,
332
332
  key,
333
333
  childValue,
334
- path,
334
+ path2,
335
335
  isSet2
336
336
  ),
337
337
  useStrictIteration
338
338
  );
339
339
  maybeFreeze(rootScope, result, false);
340
- if (path && rootScope.patches_) {
340
+ if (path2 && rootScope.patches_) {
341
341
  getPlugin("Patches").generatePatches_(
342
342
  state,
343
- path,
343
+ path2,
344
344
  rootScope.patches_,
345
345
  rootScope.inversePatches_
346
346
  );
@@ -362,9 +362,9 @@ function finalizeProperty(rootScope, parentState, targetObject, prop, childValue
362
362
  if (childValue === targetObject)
363
363
  die(5);
364
364
  if (isDraft(childValue)) {
365
- const path = rootPath && parentState && parentState.type_ !== 3 && // Set objects are atomic since they have no keys.
365
+ const path2 = rootPath && parentState && parentState.type_ !== 3 && // Set objects are atomic since they have no keys.
366
366
  !has(parentState.assigned_, prop) ? rootPath.concat(prop) : void 0;
367
- const res = finalize(rootScope, childValue, path);
367
+ const res = finalize(rootScope, childValue, path2);
368
368
  set(targetObject, prop, res);
369
369
  if (isDraft(res)) {
370
370
  rootScope.canAutoFreeze_ = false;
@@ -779,8 +779,11 @@ var Step = class {
779
779
  }
780
780
  setDraftValue(_root, _v) {
781
781
  }
782
- enterFrame(stack, next) {
783
- return stack.enter(next, {}, true);
782
+ // Re-enter this step while rebuilding a stack. `renderPath` is the dispatch
783
+ // position AFTER this step: a rebuilt frame must carry the same render path
784
+ // the renderer had there, or the provides it publishes would be located wrong.
785
+ enterFrame(stack, next, renderPath) {
786
+ return stack.enter(next, {}, true, renderPath);
784
787
  }
785
788
  toAbstractPathStep() {
786
789
  return this;
@@ -805,8 +808,8 @@ var BindStep = class _BindStep extends Step {
805
808
  lookup(v, _dval) {
806
809
  return v;
807
810
  }
808
- enterFrame(stack, next) {
809
- return stack.enter(next, { ...this.binds }, false);
811
+ enterFrame(stack, next, renderPath) {
812
+ return stack.enter(next, { ...this.binds }, false, renderPath);
810
813
  }
811
814
  withIndex(i) {
812
815
  return new _BindStep({ ...this.binds, key: i });
@@ -823,9 +826,9 @@ var ScopeBindStep = class _ScopeBindStep extends BindStep {
823
826
  super(binds);
824
827
  this.val = val;
825
828
  }
826
- enterFrame(stack, next) {
829
+ enterFrame(stack, next, renderPath) {
827
830
  const dyn = this.val.evalAsHandler(stack)?.call(stack.it) ?? {};
828
- return stack.enter(next, { ...this.binds, ...dyn }, false);
831
+ return stack.enter(next, { ...this.binds, ...dyn }, false, renderPath);
829
832
  }
830
833
  withIndex(i) {
831
834
  return new _ScopeBindStep(this.val, { ...this.binds, key: i });
@@ -868,8 +871,8 @@ var SeqStep = class extends Step {
868
871
  const seq = readKey(root, this.field, null);
869
872
  if (seq != null) writeSeqKey(seq, this.key, v);
870
873
  }
871
- enterFrame(stack, next) {
872
- return stack.enter(next, { key: this.key }, true);
874
+ enterFrame(stack, next, renderPath) {
875
+ return stack.enter(next, { key: this.key }, true, renderPath);
873
876
  }
874
877
  toKey() {
875
878
  return { field: this.field, key: this.key };
@@ -915,60 +918,52 @@ var EachBindStep = class extends Step {
915
918
  }
916
919
  // Replay the renderer's per-item binds (key, value + any @enrich-with binds)
917
920
  // so a rebuilt stack matches the one @each rendered with.
918
- enterFrame(stack, next) {
919
- return stack.enter(next, this.iterInfo.enrichBinds(stack, this.key), false);
921
+ enterFrame(stack, next, renderPath) {
922
+ return stack.enter(next, this.iterInfo.enrichBinds(stack, this.key), false, renderPath);
920
923
  }
921
924
  toAbstractPathStep() {
922
925
  return null;
923
926
  }
924
927
  };
925
928
  var EachRenderItStep = class extends SeqStep {
926
- enterFrame(stack, next) {
927
- return stack.enter(next, { key: this.key, value: next }, false).enter(next, {}, true);
929
+ enterFrame(stack, next, renderPath) {
930
+ return stack.enter(next, { key: this.key, value: next }, false, renderPath).enter(next, {}, true, renderPath);
928
931
  }
929
932
  toAbstractPathStep() {
930
933
  return new SeqStep(this.field, this.key);
931
934
  }
932
935
  };
933
- function warnRawDynStep(op, step) {
934
- console.warn(`Path.${op} reached a DynStep: call toTransactionPath() first`, step);
936
+ function stepToJson(step) {
937
+ if (step instanceof SeqStep) return { f: step.field, k: step.key };
938
+ if (step instanceof FieldStep) return { f: step.field };
939
+ if (step instanceof SeqAccessStep) return { f: step.seqField, a: step.keyField };
940
+ return null;
935
941
  }
936
- var DynStep = class extends Step {
937
- constructor(producerCompId, producerSteps) {
938
- super();
939
- this.producerCompId = producerCompId;
940
- this.producerSteps = producerSteps;
941
- this.interiorCids = /* @__PURE__ */ new Set();
942
- }
943
- // Steps spliced into the transaction path in place of this marker.
944
- teleportSteps() {
945
- return this.producerSteps;
946
- }
947
- lookup(_v, dval = null) {
948
- warnRawDynStep("lookup", this);
949
- return dval;
950
- }
951
- enterFrame(stack, _next) {
952
- warnRawDynStep("enterFrame", this);
953
- return stack;
954
- }
955
- };
956
- var DynEachStep = class extends DynStep {
957
- constructor(producerCompId, producerSteps, key) {
958
- super(producerCompId, producerSteps);
959
- this.key = key;
960
- }
961
- teleportSteps() {
962
- const { producerSteps, key } = this;
963
- if (producerSteps.length === 0) return producerSteps;
964
- const last = producerSteps[producerSteps.length - 1];
965
- if (!(last instanceof FieldStep)) {
966
- console.warn("DynEachStep: seq-access dynamic cannot be iterated", this);
967
- return producerSteps;
968
- }
969
- return producerSteps.slice(0, -1).concat(new SeqStep(last.field, key));
942
+ function stepFromJson(j) {
943
+ if (j == null || typeof j.f !== "string") return null;
944
+ if (j.k !== void 0) return new SeqStep(j.f, j.k);
945
+ if (j.a !== void 0) return new SeqAccessStep(j.f, j.a);
946
+ return new FieldStep(j.f);
947
+ }
948
+ function pathToJson(path2) {
949
+ const out = [];
950
+ for (const step of path2.steps) {
951
+ const j = stepToJson(step);
952
+ if (j === null) return null;
953
+ out.push(j);
970
954
  }
971
- };
955
+ return out;
956
+ }
957
+ function pathFromJson(j) {
958
+ if (!Array.isArray(j)) return null;
959
+ const steps = [];
960
+ for (const item of j) {
961
+ const step = stepFromJson(item);
962
+ if (step === null) return null;
963
+ steps.push(step);
964
+ }
965
+ return new Path(steps);
966
+ }
972
967
  var Path = class _Path {
973
968
  constructor(steps = []) {
974
969
  this.steps = steps;
@@ -979,40 +974,13 @@ var Path = class _Path {
979
974
  popStep() {
980
975
  return new _Path(this.steps.slice(0, -1));
981
976
  }
982
- // The dispatch path: frame-only steps removed, one step per crossed component
983
- // (DynStep included). `popStep` over it bubbles through every component.
977
+ // Frame-only steps removed, one step per crossed component: `popStep` over the
978
+ // result bubbles through every component.
984
979
  compact() {
985
980
  const out = [];
986
981
  for (const step of this.steps) {
987
982
  const s = step.toAbstractPathStep();
988
- if (s !== null) {
989
- if (s !== step) s._originCid = step._originCid;
990
- out.push(s);
991
- }
992
- }
993
- return new _Path(out);
994
- }
995
- // The abstract path used to apply a transaction: every DynStep is teleported —
996
- // the steps interior to its producer..consumer span are dropped and the
997
- // producer's own path spliced in — so a mutation lands on the data's real
998
- // location. A path with no DynStep is returned unchanged.
999
- toTransactionPath() {
1000
- let hasDyn = false;
1001
- for (const step of this.steps)
1002
- if (step instanceof DynStep) {
1003
- hasDyn = true;
1004
- break;
1005
- }
1006
- if (!hasDyn) return this;
1007
- const out = [];
1008
- for (const step of this.steps) {
1009
- if (step instanceof DynStep) {
1010
- while (out.length > 0 && step.interiorCids.has(out[out.length - 1]._originCid)) out.pop();
1011
- for (const ts of step.teleportSteps()) {
1012
- ts._originCid = step.producerCompId;
1013
- out.push(ts);
1014
- }
1015
- } else out.push(step);
983
+ if (s !== null) out.push(s);
1016
984
  }
1017
985
  return new _Path(out);
1018
986
  }
@@ -1020,7 +988,6 @@ var Path = class _Path {
1020
988
  // key as it is *now* so a later lookup/setValue lands on the same item even if the
1021
989
  // keyField changed meanwhile (e.g. the selected tab moved while an intent was in
1022
990
  // flight). Returns a new Path with those steps replaced; `this` if nothing pinned.
1023
- // Must be called on a transaction path (no DynSteps — call toTransactionPath first).
1024
991
  pinKeys(root) {
1025
992
  let curVal = root;
1026
993
  let out = null;
@@ -1043,8 +1010,8 @@ var Path = class _Path {
1043
1010
  }
1044
1011
  // The values entered along the path, root→leaf (root included): index 0 is `root`,
1045
1012
  // the last entry is the leaf this path resolves to. Stops early at the first
1046
- // unresolvable step. Call on a transaction path (no DynSteps). Used to walk the
1047
- // component instances on a dispatch path (filter via Components.getCompFor).
1013
+ // unresolvable step. Used to walk the component instances on a dispatch path
1014
+ // (filter via Components.getCompFor).
1048
1015
  resolveChain(root) {
1049
1016
  const out = [root];
1050
1017
  let curVal = root;
@@ -1058,7 +1025,7 @@ var Path = class _Path {
1058
1025
  // A flat `[{ field, key? }]` list of the addressing steps, skipping frame-only
1059
1026
  // steps (binds). Generic path introspection so tooling (e.g. the storybook
1060
1027
  // activity log) can identify which subtree a transaction touched without
1061
- // depending on Step internals. Call on a transaction path (no DynSteps).
1028
+ // depending on Step internals.
1062
1029
  toKeys() {
1063
1030
  const out = [];
1064
1031
  for (const step of this.steps) {
@@ -1079,62 +1046,147 @@ var Path = class _Path {
1079
1046
  });
1080
1047
  }
1081
1048
  buildStack(stack) {
1082
- let prev = stack.it;
1083
- for (const step of this.steps) {
1084
- const next = step.lookup(prev, NONE);
1085
- if (next === NONE) {
1086
- console.warn("bad PathItem", { root: stack.it, step, path: this });
1087
- return null;
1049
+ return walkItems(stack, this.steps, stack.renderPath ?? new DispatchPath(), this)?.[0] ?? null;
1050
+ }
1051
+ };
1052
+ function walkItems(stack, items, renderPath, path2) {
1053
+ let prev = stack.it;
1054
+ for (const step of items) {
1055
+ const next = step.lookup(prev, NONE);
1056
+ if (next === NONE) {
1057
+ console.warn("bad PathItem", { root: stack.it, step, path: path2 });
1058
+ return null;
1059
+ }
1060
+ renderPath = renderPath.pushItem(step);
1061
+ stack = step.enterFrame(stack, next, renderPath);
1062
+ prev = next;
1063
+ }
1064
+ return [stack, renderPath];
1065
+ }
1066
+ var EMPTY_PATH = new Path([]);
1067
+ var DispatchPath = class _DispatchPath {
1068
+ constructor(frames = [{ base: EMPTY_PATH, items: [] }]) {
1069
+ this.frames = frames;
1070
+ }
1071
+ // Plain addressing steps in one frame based at the root: what a caller means
1072
+ // by "this position" when it has no continuation of its own.
1073
+ static ofSteps(steps) {
1074
+ return new _DispatchPath([{ base: EMPTY_PATH, items: steps.slice() }]);
1075
+ }
1076
+ get top() {
1077
+ return this.frames[this.frames.length - 1];
1078
+ }
1079
+ _withTopItems(items) {
1080
+ const frames = this.frames.slice();
1081
+ frames[frames.length - 1] = { base: this.top.base, items };
1082
+ return new _DispatchPath(frames);
1083
+ }
1084
+ concat(steps) {
1085
+ if (this.frames.length === 0) return _DispatchPath.ofSteps(steps);
1086
+ return this._withTopItems(this.top.items.concat(steps));
1087
+ }
1088
+ pushItem(step) {
1089
+ return this.concat([step]);
1090
+ }
1091
+ pushFrame(base) {
1092
+ return new _DispatchPath(this.frames.concat({ base, items: [] }));
1093
+ }
1094
+ // Whether bubbling has anywhere left to go: another step in this frame, or a
1095
+ // visual caller underneath it.
1096
+ canPop() {
1097
+ const n = this.frames.length;
1098
+ return n > 1 || n === 1 && this.frames[0].items.length > 0;
1099
+ }
1100
+ isRoot() {
1101
+ return this.toTransactionPath().steps.length === 0;
1102
+ }
1103
+ // One component closer to the root. At the top of a frame that is popping back
1104
+ // to the visual caller, not to the producer's own parent — the caller is where
1105
+ // the `*name` was written, and where an unhandled message should keep going.
1106
+ popStep() {
1107
+ const n = this.frames.length;
1108
+ if (n === 0) return this;
1109
+ const top = this.frames[n - 1];
1110
+ if (top.items.length > 0) return this._withTopItems(top.items.slice(0, -1));
1111
+ if (n > 1) return new _DispatchPath(this.frames.slice(0, -1));
1112
+ return this;
1113
+ }
1114
+ // Drop frame-only steps inside every frame independently; a frame's base is
1115
+ // already addressing-only.
1116
+ compact() {
1117
+ return new _DispatchPath(
1118
+ this.frames.map(({ base, items }) => ({ base, items: new Path(items).compact().steps }))
1119
+ );
1120
+ }
1121
+ // A stable string for the ADDRESS this path denotes, for the render cache. The
1122
+ // same immutable value can sit at two places in the tree, and a subtree rendered
1123
+ // at one of them bakes that address in — the provides it publishes are located
1124
+ // there. Frame-only steps address nothing, so they are compacted out: two sites
1125
+ // that differ only in binds do render the same subtree.
1126
+ get addressKey() {
1127
+ this._addressKey ??= renderPathText(this.toTransactionPath().compact());
1128
+ return this._addressKey;
1129
+ }
1130
+ // The active transaction address: the top frame's absolute base followed by
1131
+ // its ordinary descendant steps. This is where a mutation lands.
1132
+ toTransactionPath() {
1133
+ const top = this.top;
1134
+ return top === void 0 ? EMPTY_PATH : top.base.concat(top.items);
1135
+ }
1136
+ // Rebuild the render stack this path was dispatched from. A frame with a base
1137
+ // re-enters at that absolute value first — replaying the resume a `*name`
1138
+ // render performed — and then walks its ordinary items.
1139
+ buildStack(stack) {
1140
+ let renderPath = new _DispatchPath();
1141
+ for (let i = 0; i < this.frames.length; i++) {
1142
+ const { base, items } = this.frames[i];
1143
+ if (i > 0 || base.steps.length > 0) {
1144
+ const baseValue = base.lookup(stack.root, NONE);
1145
+ if (baseValue === NONE) {
1146
+ console.warn("bad frame base", { base, path: this });
1147
+ return null;
1148
+ }
1149
+ renderPath = renderPath.pushFrame(base);
1150
+ stack = stack.enter(baseValue, {}, true, renderPath);
1088
1151
  }
1089
- stack = step.enterFrame(stack, next);
1090
- prev = next;
1152
+ const walked = walkItems(stack, items, renderPath, this);
1153
+ if (walked === null) return null;
1154
+ [stack, renderPath] = walked;
1091
1155
  }
1092
1156
  return stack;
1093
1157
  }
1094
1158
  static fromNodeAndEventName(node, eventName, rootNode, maxDepth, comps, stopOnNoEvent = true) {
1095
- const pathSteps = [];
1096
- const pendingDyns = [];
1159
+ const parts = [];
1097
1160
  const bubbles = BUBBLING_EVENTS.has(eventName);
1098
1161
  let depth = 0;
1099
1162
  let eventIds = [];
1100
1163
  let handlers = null;
1101
- let nodeIds = [];
1164
+ let nodeRefs = [];
1102
1165
  let isLeafComponent = true;
1103
1166
  const crossComponent = (cidNum, vid) => {
1104
1167
  const comp = comps.getComponentForId(cidNum);
1105
- let pushStep = true;
1168
+ let pushPart = true;
1106
1169
  if (handlers === null && (isLeafComponent || bubbles)) {
1107
1170
  handlers = findHandlers(comp, eventIds, vid, eventName);
1108
1171
  if (handlers === null) {
1109
1172
  if (isLeafComponent && stopOnNoEvent && !bubbles) return false;
1110
1173
  } else if (!isLeafComponent) {
1111
- pathSteps.length = 0;
1112
- pendingDyns.length = 0;
1113
- pushStep = false;
1174
+ parts.length = 0;
1175
+ pushPart = false;
1114
1176
  }
1115
1177
  }
1116
1178
  isLeafComponent = false;
1117
- for (const dyn of pendingDyns) dyn.interiorCids.add(cidNum);
1118
- if (pushStep) {
1119
- const step = resolvePathStep(comp, nodeIds, vid);
1120
- if (step) {
1121
- step._originCid = cidNum;
1122
- pathSteps.push(step);
1123
- if (step instanceof DynStep) {
1124
- step.interiorCids.add(cidNum);
1125
- pendingDyns.push(step);
1126
- }
1127
- }
1179
+ if (pushPart) {
1180
+ const part = resolvePathPart(comp, nodeRefs, vid);
1181
+ if (part) parts.push(part);
1128
1182
  }
1129
- for (let i = pendingDyns.length - 1; i >= 0; i--)
1130
- if (pendingDyns[i].producerCompId === cidNum) pendingDyns.splice(i, 1);
1131
1183
  eventIds = [];
1132
- nodeIds = [];
1184
+ nodeRefs = [];
1133
1185
  return true;
1134
1186
  };
1135
1187
  while (node && node !== rootNode && depth < maxDepth) {
1136
1188
  if (node?.dataset) {
1137
- const { eid, cid, vid } = node.dataset;
1189
+ const { eid, cid, vid, rp } = node.dataset;
1138
1190
  if (eid !== void 0) eventIds.push(eid);
1139
1191
  const metas = metaChain(node.previousSibling);
1140
1192
  let sawComp = false;
@@ -1142,21 +1194,47 @@ var Path = class _Path {
1142
1194
  if (m.$ === "Comp") {
1143
1195
  sawComp = true;
1144
1196
  if (!crossComponent(m.cid, m.vid)) return NO_EVENT_INFO;
1145
- nodeIds.push({ nid: m.nid });
1197
+ nodeRefs.push({ nid: m.nid, base: pathFromJson(m.base) });
1146
1198
  } else {
1147
- nodeIds.push({ nid: m.nid, si: m.si, sk: m.sk });
1199
+ nodeRefs.push({ nid: m.nid, si: m.si, sk: m.sk });
1200
+ }
1201
+ }
1202
+ if (!sawComp && cid !== void 0) {
1203
+ if (!crossComponent(+cid, vid)) return NO_EVENT_INFO;
1204
+ if (rp !== void 0) {
1205
+ const base = parseRenderPath(rp);
1206
+ if (base !== null) nodeRefs.push({ base });
1148
1207
  }
1149
1208
  }
1150
- if (!sawComp && cid !== void 0 && !crossComponent(+cid, vid)) return NO_EVENT_INFO;
1151
1209
  }
1152
1210
  depth += 1;
1153
1211
  node = node.parentNode;
1154
1212
  }
1155
- if (pendingDyns.length > 0)
1156
- console.warn("event reconstruction: dynamic-var producer not found", pendingDyns);
1157
- return [new _Path(pathSteps.reverse()), handlers];
1213
+ parts.reverse();
1214
+ let path2 = new _DispatchPath();
1215
+ for (const part of parts)
1216
+ path2 = part.base !== void 0 ? path2.pushFrame(part.base) : path2.pushItem(part.step);
1217
+ return [path2, handlers];
1218
+ }
1219
+ };
1220
+ function keyedPath(path2, key) {
1221
+ const steps = path2.steps;
1222
+ const last = steps[steps.length - 1];
1223
+ if (last instanceof SeqStep || last instanceof FieldStep)
1224
+ return new Path(steps.slice(0, -1).concat(new SeqStep(last.field, key)));
1225
+ return null;
1226
+ }
1227
+ function renderPathText(path2) {
1228
+ return JSON.stringify(pathToJson(path2));
1229
+ }
1230
+ function parseRenderPath(text) {
1231
+ try {
1232
+ return pathFromJson(JSON.parse(text));
1233
+ } catch (err) {
1234
+ console.warn("bad render path", err, text);
1235
+ return null;
1158
1236
  }
1159
- };
1237
+ }
1160
1238
  function metaChain(n) {
1161
1239
  const out = [];
1162
1240
  while (n?.nodeType === 8 && n.textContent[0] === "§") {
@@ -1209,20 +1287,27 @@ var StepCtx = class _StepCtx {
1209
1287
  return pi;
1210
1288
  }
1211
1289
  };
1212
- function resolvePathStep(comp, nodeIds, vid) {
1213
- for (let i = 0; i < nodeIds.length; i++) {
1214
- const ctx = new StepCtx(comp, nodeIds, i, vid);
1215
- const step = ctx.resolveNode().toPathStep(ctx);
1216
- if (step !== null) return step;
1290
+ function resolvePathPart(comp, nodeRefs, vid) {
1291
+ for (let i = 0; i < nodeRefs.length; i++) {
1292
+ const ref = nodeRefs[i];
1293
+ if (ref.base != null) return { base: ref.base };
1294
+ if (ref.nid === void 0 || ref.nid === null) continue;
1295
+ const ctx = new StepCtx(comp, nodeRefs, i, vid);
1296
+ const step = ctx.resolveNode()?.toPathStep(ctx) ?? null;
1297
+ if (step !== null) return { step };
1217
1298
  }
1218
1299
  return null;
1219
1300
  }
1220
1301
  var NO_EVENT_INFO = [null, null];
1221
1302
  var BUBBLING_EVENTS = /* @__PURE__ */ new Set(["drop"]);
1303
+ var path = () => new PathBuilder();
1222
1304
  var PathBuilder = class {
1223
1305
  constructor() {
1224
1306
  this.pathChanges = [];
1225
1307
  }
1308
+ toPath() {
1309
+ return new Path(this.pathChanges);
1310
+ }
1226
1311
  add(pathChange) {
1227
1312
  this.pathChanges.push(pathChange);
1228
1313
  return this;
@@ -1359,31 +1444,14 @@ function parseBool(s, px) {
1359
1444
  const val = parseToken(tokens[0], px);
1360
1445
  return val !== null && kindOf(val) & G_BOOL ? val : null;
1361
1446
  }
1362
- function parseText(s, px) {
1363
- return _parseSingle(s, px, G_TEXT);
1364
- }
1365
- function parseComponent(s, px) {
1366
- return _parseSingle(s, px, G_COMPONENT);
1367
- }
1368
- function parseSequence(s, px) {
1369
- return _parseSingle(s, px, G_SEQUENCE);
1370
- }
1371
- function parseField(s, px) {
1372
- return _parseSingle(s, px, G_FIELD);
1373
- }
1374
- function parseProvide(s, px) {
1375
- return _parseSingle(s, px, G_PROVIDE);
1376
- }
1377
- function parseMacroAttr(s, px) {
1378
- return _parseSingle(s, px, G_ALL);
1379
- }
1380
- function parseReceiveHandler(s, px) {
1381
- return _parseHandler(s, px, "receive", true, true, false);
1382
- }
1383
- function parseAlterHandler(s, px) {
1384
- const r = _parseHandler(s, px, "alter", false, false, true);
1385
- return r === null ? null : r.handlerVal;
1386
- }
1447
+ var parseText = (s, px) => _parseSingle(s, px, G_TEXT);
1448
+ var parseComponent = (s, px) => _parseSingle(s, px, G_COMPONENT);
1449
+ var parseSequence = (s, px) => _parseSingle(s, px, G_SEQUENCE);
1450
+ var parseField = (s, px) => _parseSingle(s, px, G_FIELD);
1451
+ var parseProvide = (s, px) => _parseSingle(s, px, G_PROVIDE);
1452
+ var parseMacroAttr = (s, px) => _parseSingle(s, px, G_ALL);
1453
+ var parseReceiveHandler = (s, px) => _parseHandler(s, px, "receive", true, true, false);
1454
+ var parseAlterHandler = (s, px) => _parseHandler(s, px, "alter", false, false, true)?.handlerVal ?? null;
1387
1455
  function _parseHandler(s, px, namespace, allowArgs, report, allowMethod) {
1388
1456
  const tokens = tokenizeValue(s.trim());
1389
1457
  const headTok = tokens[0] ?? "";
@@ -2072,19 +2140,9 @@ function childOpts(vnode, ns, opts) {
2072
2140
  const target = ns === SVG_NS && isForeignObject(vnode.tag) ? null : ns;
2073
2141
  return target === (opts.namespace ?? null) ? opts : { ...opts, namespace: target };
2074
2142
  }
2075
- var NEVER_ASSIGN = /* @__PURE__ */ new Set([
2076
- "width",
2077
- "height",
2078
- "href",
2079
- "list",
2080
- "form",
2081
- "tabIndex",
2082
- "download",
2083
- "rowSpan",
2084
- "colSpan",
2085
- "role",
2086
- "popover"
2087
- ]);
2143
+ var NEVER_ASSIGN = new Set(
2144
+ "width height href list form tabIndex download rowSpan colSpan role popover".split(" ")
2145
+ );
2088
2146
  var PROP_ATTR_NAME = { className: "class", htmlFor: "for" };
2089
2147
  function applyProperties(node, props) {
2090
2148
  const namespaced = isNamespaced(node);
@@ -2363,6 +2421,10 @@ function morphChildren(parentDom, oldChilds, newChilds, opts) {
2363
2421
  if (!used[i] && domNodes[i].parentNode === parentDom) parentDom.removeChild(domNodes[i]);
2364
2422
  }
2365
2423
  function render(vnode, container, options, prev) {
2424
+ if (vnode == null) {
2425
+ container.replaceChildren();
2426
+ return { vnode: null, dom: null };
2427
+ }
2366
2428
  const isFragment = vnode instanceof VFragment;
2367
2429
  if (prev && prev.vnode instanceof VFragment === isFragment) {
2368
2430
  const oldDom = isFragment ? container : prev.dom;
@@ -2402,13 +2464,16 @@ function h(tagName, properties, children, namespace) {
2402
2464
  }
2403
2465
 
2404
2466
  // src/anode.js
2405
- function resolveDynProducer(comp, name) {
2406
- const own = comp?.provide?.[name];
2407
- const producerComp = own != null ? comp : comp?.scope?.lookupProvider(name) ?? null;
2408
- const producerProvide = own ?? producerComp?.provide?.[name];
2409
- if (producerComp == null || producerProvide == null) return null;
2410
- const pi = producerProvide.val?.toPathItem?.() ?? null;
2411
- return { producerCompId: producerComp.id, producerSteps: pi ? [pi] : [] };
2467
+ function renderTarget(val, stack) {
2468
+ if (val instanceof DynVal) {
2469
+ const loc = stack.lookupDynamicLocated(val.name);
2470
+ if (loc?.path == null) return [loc?.value ?? null, stack.renderPath, null];
2471
+ return [loc.value, stack.renderPath.pushFrame(loc.path), loc.path];
2472
+ }
2473
+ const step = val.toPathItem?.() ?? null;
2474
+ if (step === null) return [val.eval(stack), stack.renderPath, null];
2475
+ const path2 = stack.renderPath.pushItem(step);
2476
+ return [val.eval(stack), path2, stack.pendingFrame ? path2.toTransactionPath() : null];
2412
2477
  }
2413
2478
  var BaseNode = class {
2414
2479
  render(_stack, _rx) {
@@ -2730,33 +2795,31 @@ var RenderViewId = class extends ANode {
2730
2795
  setDataAttr(_key, _val) {
2731
2796
  }
2732
2797
  };
2733
- function dynRenderStep(comp, name, key) {
2734
- const p = resolveDynProducer(comp, name);
2735
- if (!p) return null;
2736
- return key === void 0 ? new DynStep(p.producerCompId, p.producerSteps) : new DynEachStep(p.producerCompId, p.producerSteps, key);
2737
- }
2738
2798
  var RenderNode = class extends RenderViewId {
2739
2799
  render(stack, rx) {
2740
- const newStack = stack.enter(this.val.eval(stack), {}, true);
2741
- return rx.renderIt(newStack, this, "", this.evalViewName(stack));
2800
+ const [value, renderPath, base] = renderTarget(this.val, stack);
2801
+ const newStack = stack.enter(value, {}, true, renderPath, false);
2802
+ return rx.renderIt(newStack, this, "", this.evalViewName(stack), base);
2742
2803
  }
2804
+ // A `*name` target contributes no step: the site recorded the absolute base it
2805
+ // resumed at, and event reconstruction turns that into a continuation frame.
2743
2806
  toPathStep(ctx) {
2744
- if (this.val instanceof DynVal) return dynRenderStep(ctx.comp, this.val.name);
2807
+ if (this.val instanceof DynVal) return null;
2745
2808
  return super.toPathStep(ctx);
2746
2809
  }
2747
2810
  };
2748
2811
  var RenderItNode = class extends RenderViewId {
2749
2812
  render(stack, rx) {
2750
- const newStack = stack.enter(stack.it, {}, true);
2751
- return rx.renderIt(newStack, this, "", this.evalViewName(stack));
2813
+ const base = stack.pendingFrame ? stack.renderPath.toTransactionPath() : null;
2814
+ const newStack = stack.enter(stack.it, {}, true, stack.renderPath, false);
2815
+ return rx.renderIt(newStack, this, "", this.evalViewName(stack), base);
2752
2816
  }
2753
2817
  toPathStep(ctx) {
2754
2818
  const next = ctx.next();
2755
2819
  if (next === null) return null;
2756
2820
  const nextNode = next.resolveNode();
2757
2821
  if (nextNode instanceof EachNode && next.hasKey) {
2758
- if (nextNode.val instanceof DynVal)
2759
- return dynRenderStep(ctx.comp, nextNode.val.name, next.key);
2822
+ if (nextNode.val instanceof DynVal) return null;
2760
2823
  return new EachRenderItStep(nextNode.val.name, next.key);
2761
2824
  }
2762
2825
  return null;
@@ -2858,11 +2921,19 @@ var EachNode = class extends WrapperNode {
2858
2921
  this.iterInfo = new IterInfo(val, null, null, null);
2859
2922
  }
2860
2923
  render(stack, rx) {
2861
- return rx.renderEachWhen(stack, this.iterInfo, this.node, this.nodeId);
2924
+ return rx.renderEachWhen(stack, this);
2862
2925
  }
2863
2926
  toPathStep(ctx) {
2864
2927
  return ctx.hasKey ? new EachBindStep(this.iterInfo, ctx.key) : null;
2865
2928
  }
2929
+ // Where one item lives, for the render path. `@each` re-binds `it` to the item
2930
+ // whether or not the body is a component, so the position moves either way and
2931
+ // the step has to address it — `.rows` iterated at `key` IS `rows[key]`.
2932
+ // Null for a dynamic sequence: `*rows` carries its own absolute path and enters
2933
+ // a continuation frame instead (see Renderer.renderEachWhen).
2934
+ itemStep(key) {
2935
+ return this.val instanceof FieldVal ? new SeqStep(this.val.name, key) : null;
2936
+ }
2866
2937
  static register = true;
2867
2938
  };
2868
2939
  var IterInfo = class {
@@ -3123,6 +3194,215 @@ function compileModifiers(eventName, names) {
3123
3194
  };
3124
3195
  }
3125
3196
 
3197
+ // src/stack.js
3198
+ var STOP = /* @__PURE__ */ Symbol("STOP");
3199
+ var NEXT = /* @__PURE__ */ Symbol("NEXT");
3200
+ var DEFAULT_ROUTE = ["dyn", "lex"];
3201
+ function routeLookup(route, lex, dyn) {
3202
+ for (let i = 0; i < route.length; i++) {
3203
+ const leg = route[i];
3204
+ if (leg === "dyn") {
3205
+ const v = dyn();
3206
+ if (v != null) return v;
3207
+ } else if (leg === "lex") {
3208
+ const v = lex();
3209
+ if (v != null) return v;
3210
+ } else {
3211
+ console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
3212
+ }
3213
+ }
3214
+ return null;
3215
+ }
3216
+ var isTypeName = (s) => {
3217
+ const c = s.charCodeAt(0);
3218
+ return c >= 65 && c <= 90;
3219
+ };
3220
+ function lookup(chain, name, dv = null) {
3221
+ let n = chain;
3222
+ while (n !== null) {
3223
+ const r = n[0].lookup(name);
3224
+ if (r === STOP) return dv;
3225
+ if (r !== NEXT) return r;
3226
+ n = n[1];
3227
+ }
3228
+ return dv;
3229
+ }
3230
+ var BindFrame = class {
3231
+ constructor(it, binds, isFrame) {
3232
+ this.it = it;
3233
+ this.binds = binds;
3234
+ this.isFrame = isFrame;
3235
+ }
3236
+ lookup(name) {
3237
+ const v = this.binds[name];
3238
+ return v === void 0 ? this.isFrame ? STOP : NEXT : v;
3239
+ }
3240
+ };
3241
+ var DynFrame = class {
3242
+ constructor(binds, types) {
3243
+ this.binds = binds;
3244
+ this.types = types;
3245
+ }
3246
+ lookup(name) {
3247
+ const v = (isTypeName(name) ? this.types : this.binds)[name];
3248
+ return v === void 0 ? NEXT : v;
3249
+ }
3250
+ };
3251
+ function computeViewsId(views) {
3252
+ let s = "";
3253
+ let n = views;
3254
+ while (n !== null) {
3255
+ s += n[0];
3256
+ n = n[1];
3257
+ }
3258
+ return s === "main" ? "" : s;
3259
+ }
3260
+ var Stack = class _Stack {
3261
+ constructor(fields) {
3262
+ Object.assign(this, fields);
3263
+ }
3264
+ _with(patch) {
3265
+ return new _Stack({ ...this, ...patch });
3266
+ }
3267
+ // Evaluate every provide the entered component publishes and push them as one
3268
+ // dynBinds frame, keyed by NAME. A value is published together with the absolute
3269
+ // path it lives at: the same declaration is read as `*name` AND resumed at by
3270
+ // `<x render="*name">`, so a consumer needs both halves. Types go in the same
3271
+ // frame's other map. No-op with no provides.
3272
+ _pushProvides() {
3273
+ const comp = this.comps.getCompFor(this.it);
3274
+ if (comp == null) return this;
3275
+ const { provide, provideType } = comp;
3276
+ const binds = {};
3277
+ const types = {};
3278
+ let has2 = false;
3279
+ const base = this._publishBase();
3280
+ for (const k in provide) {
3281
+ const step = provide[k].val.toPathItem?.() ?? null;
3282
+ if (step === null) continue;
3283
+ const value = provide[k].val.eval(this);
3284
+ binds[k] = { value, path: base === null ? null : base.concat([step]) };
3285
+ has2 = true;
3286
+ }
3287
+ for (const k in provideType) {
3288
+ types[k] = provideType[k];
3289
+ has2 = true;
3290
+ }
3291
+ if (!has2) return this;
3292
+ return this._with({ dynBinds: [new DynFrame(binds, types), this.dynBinds] });
3293
+ }
3294
+ // The absolute address of the component being entered, or null when this
3295
+ // position cannot be written down as one. Frame-only steps carry bindings and
3296
+ // address nothing, so they are compacted away first; what is left is checked
3297
+ // against the value actually being rendered, because a scope CAN move the
3298
+ // render position without contributing an addressing step (a plain `@each`
3299
+ // body re-binds `it` to the item while its rebuild step is an identity).
3300
+ //
3301
+ // A null base still publishes the VALUE — `*name` reads it fine — but there is
3302
+ // nowhere to resume, so `<x render="*name">` renders nothing rather than
3303
+ // editing whatever happens to live at the address we guessed.
3304
+ _publishBase() {
3305
+ const base = this.renderPath.toTransactionPath().compact();
3306
+ return base.lookup(this.root, NOT_FOUND) === this.it ? base : null;
3307
+ }
3308
+ static root(comps, it, ctx = null) {
3309
+ return new _Stack({
3310
+ comps,
3311
+ root: it,
3312
+ it,
3313
+ binds: [new BindFrame(it, {}, true), null],
3314
+ dynBinds: [new DynFrame({}, {}), null],
3315
+ views: ["main", null],
3316
+ viewsId: "",
3317
+ renderPath: new DispatchPath(),
3318
+ pendingFrame: false,
3319
+ ctx
3320
+ })._pushProvides();
3321
+ }
3322
+ // `renderPath` defaults to this stack's own: an ordinary scope does not move.
3323
+ // `pendingFrame` clears on a component frame (which emits the base) and is
3324
+ // inherited by transparent scopes, which have to carry it to the next boundary.
3325
+ enter(it, bindings = {}, isFrame = true, renderPath = this.renderPath, pendingFrame = null) {
3326
+ const stack = this._with({
3327
+ it,
3328
+ binds: [new BindFrame(it, bindings, isFrame), this.binds],
3329
+ renderPath,
3330
+ pendingFrame: pendingFrame ?? (isFrame ? false : this.pendingFrame)
3331
+ });
3332
+ return isFrame ? stack._pushProvides() : stack;
3333
+ }
3334
+ pushViewName(name) {
3335
+ const views = [name, this.views];
3336
+ return this._with({ views, viewsId: computeViewsId(views) });
3337
+ }
3338
+ // Published types are stable per scope and would only churn the render cache, so
3339
+ // the cache key covers values alone.
3340
+ _pushDynBindValuesToArray(arr, comp) {
3341
+ for (const k in comp.provide) arr.push(this.lookupDynamic(k));
3342
+ for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
3343
+ }
3344
+ // `*name`: the nearest binding above (including this component's own provides,
3345
+ // pushed on entering it), else a path registered in the component's lexical
3346
+ // scope, else this component's declared default, else null.
3347
+ //
3348
+ // One chain walk and no producer resolution: a lookup names what it WANTS, so the
3349
+ // frame it wants is keyed by that name. The default belongs to the CONSUMER's
3350
+ // declaration and is evaluated against the consumer's stack, which is why it is
3351
+ // consulted only after the whole chain has missed.
3352
+ lookupDynamicLocated(name) {
3353
+ if (isTypeName(name)) return null;
3354
+ const v = lookup(this.dynBinds, name);
3355
+ if (v != null) return v;
3356
+ const comp = this.comps.getCompFor(this.it);
3357
+ if (comp == null) return null;
3358
+ const path2 = comp.scope?.lookupPath?.(name) ?? null;
3359
+ if (path2 !== null) {
3360
+ const value2 = path2.lookup(this.root, NOT_FOUND);
3361
+ if (value2 !== NOT_FOUND) return { value: value2, path: path2 };
3362
+ }
3363
+ const dval = comp.lookup[name]?.val ?? null;
3364
+ if (dval === null) return null;
3365
+ const step = dval.toPathItem?.() ?? null;
3366
+ const value = dval.eval(this);
3367
+ return step === null ? { value, path: null } : { value, path: this.renderPath.toTransactionPath().concat([step]) };
3368
+ }
3369
+ lookupDynamic(name) {
3370
+ if (isTypeName(name)) return lookup(this.dynBinds, name);
3371
+ return this.lookupDynamicLocated(name)?.value ?? null;
3372
+ }
3373
+ lookupBind(name) {
3374
+ return lookup(this.binds, name);
3375
+ }
3376
+ lookupFieldRaw(name) {
3377
+ return this.it[name] ?? null;
3378
+ }
3379
+ lookupMethod(name) {
3380
+ const fn = this.it[name];
3381
+ return fn instanceof Function ? fn.call(this.it) : null;
3382
+ }
3383
+ // The dispatched DOM event / drag info, read only by EventMemberVal's
3384
+ // `e.<member>` handler args. Null outside a live event transaction.
3385
+ lookupEvent() {
3386
+ return this.ctx?.event ?? null;
3387
+ }
3388
+ lookupDragInfo() {
3389
+ return this.ctx?.dragInfo ?? null;
3390
+ }
3391
+ getHandlerFor(name, key) {
3392
+ return this.comps.getHandlerFor(this.it, name, key);
3393
+ }
3394
+ lookupBestView(views, defaultViewName) {
3395
+ let n = this.views;
3396
+ while (n !== null) {
3397
+ const view = views[n[0]];
3398
+ if (view !== void 0) return view;
3399
+ n = n[1];
3400
+ }
3401
+ return views[defaultViewName];
3402
+ }
3403
+ };
3404
+ var NOT_FOUND = /* @__PURE__ */ Symbol("NOT_FOUND");
3405
+
3126
3406
  // src/components.js
3127
3407
  var COMPONENT = /* @__PURE__ */ Symbol.for("tutuca.component");
3128
3408
  var Components = class {
@@ -3161,12 +3441,14 @@ var ComponentStack = class _ComponentStack {
3161
3441
  this.byName = {};
3162
3442
  this.intentsByName = {};
3163
3443
  this.macros = {};
3444
+ this.paths = {};
3164
3445
  }
3165
3446
  enter() {
3166
3447
  return new _ComponentStack(this.comps, this);
3167
3448
  }
3168
3449
  registerComponents(comps, opts) {
3169
- const { aliases = {} } = opts ?? {};
3450
+ const { aliases = {}, paths } = opts ?? {};
3451
+ if (paths) this.registerPaths(paths);
3170
3452
  for (let i = 0; i < comps.length; i++) {
3171
3453
  const Comp = comps[i];
3172
3454
  Comp[COMPONENT].scope = this.enter();
@@ -3180,6 +3462,27 @@ var ComponentStack = class _ComponentStack {
3180
3462
  else console.warn("alias", alias, "to inexistent component", aliases[alias]);
3181
3463
  }
3182
3464
  }
3465
+ // Register lowercase names as absolute paths from the app state root. A
3466
+ // descendant that declares one in its `lookup` reads and renders `*name` without
3467
+ // anything above it publishing one — which is what makes a session, a theme or a
3468
+ // host-owned value available in its natural registration scope, instead of forcing
3469
+ // an application root whose only job is to `provide` it. Register on a nested
3470
+ // scope to narrow a name; nearest registration wins.
3471
+ //
3472
+ // Uppercase names are ignored: a component TYPE is what `lookupComponent` already
3473
+ // answers, and a type has no path.
3474
+ registerPaths(paths) {
3475
+ for (const name in paths) {
3476
+ if (isTypeName(name)) {
3477
+ console.warn("registerPaths: a type name has no path", name);
3478
+ continue;
3479
+ }
3480
+ this.paths[name] = paths[name].toPath?.() ?? paths[name];
3481
+ }
3482
+ }
3483
+ lookupPath(name) {
3484
+ return this.paths[name] ?? this.parent?.lookupPath(name) ?? null;
3485
+ }
3183
3486
  registerMacros(macros) {
3184
3487
  for (const key in macros) {
3185
3488
  const lower = key.toLowerCase();
@@ -3211,17 +3514,16 @@ var ComponentStack = class _ComponentStack {
3211
3514
  lookupComponent(name) {
3212
3515
  return this.byName[name] ?? this.parent?.lookupComponent(name) ?? null;
3213
3516
  }
3214
- // The component in this scope chain that provides `name`, innermost first. A lookup
3215
- // names a value without naming its producer, so the render-target teleport
3216
- // (resolveDynProducer) recovers the producer here instead. Sound because
3217
- // PROVIDE_NAME_COLLISION keeps a provide name to one producer per chain; with two,
3218
- // this returns the innermost and the linter has already reported the ambiguity.
3219
- lookupProvider(name) {
3517
+ // Whether anything in this scope chain provides `name`. Existence only: a lookup
3518
+ // names what it WANTS and takes whoever is nearest above it at render time, so
3519
+ // there is no producer to identify — several components may publish one name and
3520
+ // the live render ancestry decides. Used by the linter to tell a lookup that can
3521
+ // be satisfied from one that never will be.
3522
+ hasProvider(name) {
3220
3523
  for (const compName in this.byName) {
3221
- const Comp = this.byName[compName];
3222
- if (Comp.provide?.[name] !== void 0) return Comp;
3524
+ if (this.byName[compName].provide?.[name] !== void 0) return true;
3223
3525
  }
3224
- return this.parent?.lookupProvider(name) ?? null;
3526
+ return this.parent?.hasProvider(name) ?? false;
3225
3527
  }
3226
3528
  lookupMacro(name) {
3227
3529
  return this.macros[name] ?? this.parent?.lookupMacro(name) ?? null;
@@ -3238,10 +3540,6 @@ var LookupInfo = class {
3238
3540
  }
3239
3541
  };
3240
3542
  var isString = (v) => typeof v === "string";
3241
- var isTypeName = (s) => {
3242
- const c = s.charCodeAt(0);
3243
- return c >= 65 && c <= 90;
3244
- };
3245
3543
  var _rawSpecKeys = "name view style commonStyle globalStyle receive intent alter views provide lookup fields methods statics";
3246
3544
  var KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
3247
3545
  var _compId = 0;
@@ -3314,160 +3612,6 @@ var Component = class {
3314
3612
  }
3315
3613
  };
3316
3614
 
3317
- // src/stack.js
3318
- var STOP = /* @__PURE__ */ Symbol("STOP");
3319
- var NEXT = /* @__PURE__ */ Symbol("NEXT");
3320
- var DEFAULT_ROUTE = ["dyn", "lex"];
3321
- function routeLookup(route, lex, dyn) {
3322
- for (let i = 0; i < route.length; i++) {
3323
- const leg = route[i];
3324
- if (leg === "dyn") {
3325
- const v = dyn();
3326
- if (v != null) return v;
3327
- } else if (leg === "lex") {
3328
- const v = lex();
3329
- if (v != null) return v;
3330
- } else {
3331
- console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
3332
- }
3333
- }
3334
- return null;
3335
- }
3336
- function lookup(chain, name, dv = null) {
3337
- let n = chain;
3338
- while (n !== null) {
3339
- const r = n[0].lookup(name);
3340
- if (r === STOP) return dv;
3341
- if (r !== NEXT) return r;
3342
- n = n[1];
3343
- }
3344
- return dv;
3345
- }
3346
- var BindFrame = class {
3347
- constructor(it, binds, isFrame) {
3348
- this.it = it;
3349
- this.binds = binds;
3350
- this.isFrame = isFrame;
3351
- }
3352
- lookup(name) {
3353
- const v = this.binds[name];
3354
- return v === void 0 ? this.isFrame ? STOP : NEXT : v;
3355
- }
3356
- };
3357
- var ObjectFrame = class {
3358
- constructor(binds) {
3359
- this.binds = binds;
3360
- }
3361
- lookup(key) {
3362
- const v = this.binds[key];
3363
- return v === void 0 ? NEXT : v;
3364
- }
3365
- };
3366
- function computeViewsId(views) {
3367
- let s = "";
3368
- let n = views;
3369
- while (n !== null) {
3370
- s += n[0];
3371
- n = n[1];
3372
- }
3373
- return s === "main" ? "" : s;
3374
- }
3375
- var Stack = class _Stack {
3376
- constructor(comps, it, binds, dynBinds, views, viewsId, ctx = null) {
3377
- this.comps = comps;
3378
- this.it = it;
3379
- this.binds = binds;
3380
- this.dynBinds = dynBinds;
3381
- this.views = views;
3382
- this.viewsId = viewsId;
3383
- this.ctx = ctx;
3384
- }
3385
- // Evaluate every provide the entered component publishes and push them as one
3386
- // dynBinds frame, keyed by NAME. Published types go in the same frame: a type name
3387
- // starts A-Z and a value name does not, so the two namespaces cannot collide and
3388
- // nearest-ancestor-wins falls out of frame order for both. No-op with no provides.
3389
- _pushProvides() {
3390
- const comp = this.comps.getCompFor(this.it);
3391
- if (comp == null) return this;
3392
- const { provide, provideType } = comp;
3393
- const dynObj = {};
3394
- let has2 = false;
3395
- for (const k in provide) {
3396
- dynObj[k] = provide[k].val.eval(this);
3397
- has2 = true;
3398
- }
3399
- for (const k in provideType) {
3400
- dynObj[k] = provideType[k];
3401
- has2 = true;
3402
- }
3403
- if (!has2) return this;
3404
- const newDynBinds = [new ObjectFrame(dynObj), this.dynBinds];
3405
- const { comps, it, binds, views, viewsId, ctx } = this;
3406
- return new _Stack(comps, it, binds, newDynBinds, views, viewsId, ctx);
3407
- }
3408
- static root(comps, it, ctx) {
3409
- const binds = [new BindFrame(it, {}, true), null];
3410
- const dynBinds = [new ObjectFrame({}), null];
3411
- const views = ["main", null];
3412
- return new _Stack(comps, it, binds, dynBinds, views, "", ctx)._pushProvides();
3413
- }
3414
- enter(it, bindings = {}, isFrame = true) {
3415
- const { comps, binds, dynBinds, views, viewsId, ctx } = this;
3416
- const newBinds = [new BindFrame(it, bindings, isFrame), binds];
3417
- const stack = new _Stack(comps, it, newBinds, dynBinds, views, viewsId, ctx);
3418
- return isFrame ? stack._pushProvides() : stack;
3419
- }
3420
- pushViewName(name) {
3421
- const { comps, it, binds, dynBinds, views, ctx } = this;
3422
- const newViews = [name, views];
3423
- return new _Stack(comps, it, binds, dynBinds, newViews, computeViewsId(newViews), ctx);
3424
- }
3425
- // Published types are stable per scope and would only churn the render cache, so
3426
- // the cache key covers values alone.
3427
- _pushDynBindValuesToArray(arr, comp) {
3428
- for (const k in comp.provide) arr.push(this.lookupDynamic(k));
3429
- for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
3430
- }
3431
- // `*name`: the nearest binding above (including this component's own provides,
3432
- // pushed on entering it), else this component's declared default, else null.
3433
- lookupDynamic(name) {
3434
- const v = lookup(this.dynBinds, name);
3435
- if (v != null) return v;
3436
- const comp = this.comps.getCompFor(this.it);
3437
- return comp?.lookup[name]?.val?.eval(this) ?? null;
3438
- }
3439
- lookupBind(name) {
3440
- return lookup(this.binds, name);
3441
- }
3442
- lookupFieldRaw(name) {
3443
- return this.it[name] ?? null;
3444
- }
3445
- lookupMethod(name) {
3446
- const fn = this.it[name];
3447
- return fn instanceof Function ? fn.call(this.it) : null;
3448
- }
3449
- // The dispatched DOM event / drag info, read only by EventMemberVal's
3450
- // `e.<member>` handler args. Null outside a live event transaction.
3451
- lookupEvent() {
3452
- return this.ctx?.event ?? null;
3453
- }
3454
- lookupDragInfo() {
3455
- return this.ctx?.dragInfo ?? null;
3456
- }
3457
- getHandlerFor(name, key) {
3458
- return this.comps.getHandlerFor(this.it, name, key);
3459
- }
3460
- lookupBestView(views, defaultViewName) {
3461
- let n = this.views;
3462
- while (n !== null) {
3463
- const view = views[n[0]];
3464
- if (view !== void 0) return view;
3465
- n = n[1];
3466
- }
3467
- return views[defaultViewName];
3468
- }
3469
- };
3470
-
3471
3615
  // src/oo.js
3472
3616
  var BAD_VALUE = /* @__PURE__ */ Symbol("BadValue");
3473
3617
  var nullCoercer = (v) => v;
@@ -3500,9 +3644,22 @@ var CHECK_TYPE_LIST = Array.isArray;
3500
3644
  var CHECK_TYPE_OBJECT = isPlainObject;
3501
3645
  var CHECK_TYPE_MAP = (v) => v instanceof Map;
3502
3646
  var CHECK_TYPE_SET = (v) => v instanceof Set;
3647
+ var COERCE_NONE = (_v) => null;
3648
+ var COERCE_BOOL = (v) => !!v;
3649
+ var COERCE_STRING = (v) => v?.toString?.() ?? "";
3650
+ var COERCE_INT = (v) => Number.isFinite(v) ? Math.trunc(v) : null;
3651
+ var COERCE_LIST = (v) => Array.isArray(v) ? [...v] : null;
3652
+ var COERCE_OBJECT = (v) => isPlainObject(v) ? { ...v } : null;
3653
+ var COERCE_MAP = (v) => {
3654
+ if (v instanceof Map) return new Map(v);
3655
+ if (Array.isArray(v) || isPlainObject(v))
3656
+ return new Map(Array.isArray(v) ? v : Object.entries(v));
3657
+ return null;
3658
+ };
3659
+ var COERCE_SET = (v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null;
3503
3660
  var FieldBool = class extends Field {
3504
3661
  constructor(name, defaultValue = false) {
3505
- super("bool", name, CHECK_TYPE_BOOL, (v) => !!v, defaultValue);
3662
+ super("bool", name, CHECK_TYPE_BOOL, COERCE_BOOL, defaultValue);
3506
3663
  }
3507
3664
  };
3508
3665
  var FieldAny = class extends Field {
@@ -3512,23 +3669,17 @@ var FieldAny = class extends Field {
3512
3669
  };
3513
3670
  var FieldString = class extends Field {
3514
3671
  constructor(name, defaultValue = "") {
3515
- super("text", name, CHECK_TYPE_STRING, (v) => v?.toString?.() ?? "", defaultValue);
3672
+ super("text", name, CHECK_TYPE_STRING, COERCE_STRING, defaultValue);
3516
3673
  }
3517
3674
  };
3518
3675
  var FieldInt = class extends Field {
3519
3676
  constructor(name, defaultValue = 0) {
3520
- super(
3521
- "int",
3522
- name,
3523
- CHECK_TYPE_INT,
3524
- (v) => Number.isFinite(v) ? Math.trunc(v) : null,
3525
- defaultValue
3526
- );
3677
+ super("int", name, CHECK_TYPE_INT, COERCE_INT, defaultValue);
3527
3678
  }
3528
3679
  };
3529
3680
  var FieldFloat = class extends Field {
3530
3681
  constructor(name, defaultValue = 0) {
3531
- super("float", name, CHECK_TYPE_FLOAT, (_) => null, defaultValue);
3682
+ super("float", name, CHECK_TYPE_FLOAT, COERCE_NONE, defaultValue);
3532
3683
  }
3533
3684
  };
3534
3685
  var metaOf = (v) => v?.constructor?.[COMPONENT] ?? v?.constructor?.getMetaClass?.();
@@ -3541,45 +3692,22 @@ var FieldComp = class extends Field {
3541
3692
  };
3542
3693
  var FieldList = class extends Field {
3543
3694
  constructor(name, defaultValue = []) {
3544
- super("list", name, CHECK_TYPE_LIST, (v) => Array.isArray(v) ? [...v] : null, defaultValue);
3695
+ super("list", name, CHECK_TYPE_LIST, COERCE_LIST, defaultValue);
3545
3696
  }
3546
3697
  };
3547
3698
  var FieldObject = class extends Field {
3548
3699
  constructor(name, defaultValue = {}) {
3549
- super(
3550
- "object",
3551
- name,
3552
- CHECK_TYPE_OBJECT,
3553
- (v) => isPlainObject(v) ? { ...v } : null,
3554
- defaultValue
3555
- );
3700
+ super("object", name, CHECK_TYPE_OBJECT, COERCE_OBJECT, defaultValue);
3556
3701
  }
3557
3702
  };
3558
3703
  var FieldMap = class extends Field {
3559
3704
  constructor(name, defaultValue = /* @__PURE__ */ new Map()) {
3560
- super(
3561
- "map",
3562
- name,
3563
- CHECK_TYPE_MAP,
3564
- (v) => {
3565
- if (v instanceof Map) return new Map(v);
3566
- if (Array.isArray(v) || isPlainObject(v))
3567
- return new Map(Array.isArray(v) ? v : Object.entries(v));
3568
- return null;
3569
- },
3570
- defaultValue
3571
- );
3705
+ super("map", name, CHECK_TYPE_MAP, COERCE_MAP, defaultValue);
3572
3706
  }
3573
3707
  };
3574
3708
  var FieldSet = class extends Field {
3575
3709
  constructor(name, defaultValue = /* @__PURE__ */ new Set()) {
3576
- super(
3577
- "set",
3578
- name,
3579
- CHECK_TYPE_SET,
3580
- (v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null,
3581
- defaultValue
3582
- );
3710
+ super("set", name, CHECK_TYPE_SET, COERCE_SET, defaultValue);
3583
3711
  }
3584
3712
  };
3585
3713
  function mkCompField(field, scope, args) {
@@ -3677,8 +3805,7 @@ function classFromData(name, { fields = {}, methods, statics }) {
3677
3805
  const value = fields[field];
3678
3806
  const type = typeof value;
3679
3807
  if (type === "string") b.addField(field, value, FieldString);
3680
- else if (type === "number")
3681
- b.addField(field, value, Number.isInteger(value) ? FieldInt : FieldFloat);
3808
+ else if (type === "number") b.addField(field, value, FieldFloat);
3682
3809
  else if (type === "boolean") b.addField(field, value, FieldBool);
3683
3810
  else if (Array.isArray(value)) b.addField(field, [...value], FieldList);
3684
3811
  else if (value instanceof Set) b.addField(field, new Set(value), FieldSet);
@@ -3830,9 +3957,9 @@ var Transactor = class {
3830
3957
  // `.a[.selId]` render target reconstructed from a DOM event) against the root the
3831
3958
  // handler read, so pathKeys carries concrete keys instead of dropping the dynamic
3832
3959
  // step. No-op when nobody is observing.
3833
- _emitRecord(root, { kind, name, args, path, targetPath, handler, handlerName, matched, before, after, parent }) {
3960
+ _emitRecord(root, { kind, name, args, path: path2, targetPath, handler, handlerName, matched, before, after, parent }) {
3834
3961
  if (this._observers.length === 0) return;
3835
- const pinned = path.pinKeys(root);
3962
+ const pinned = path2.pinKeys(root);
3836
3963
  this._emit({
3837
3964
  kind,
3838
3965
  name,
@@ -3883,18 +4010,20 @@ var Transactor = class {
3883
4010
  // intent's answerPath is: a reply must reach the sender that asked even if a key
3884
4011
  // moved while the message was in flight. Null when nobody is waiting — a host
3885
4012
  // sendAtRoot or a view's own `@on.*` — and ctx.sendReply refuses on that.
3886
- pushSend(path, name, args = [], parent = null, origin = null) {
3887
- const t = new SendEvent(path, this, name, args, parent);
3888
- t.origin = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
4013
+ pushSend(path2, name, args = [], parent = null, origin = null, txnPath = null) {
4014
+ const t = new SendEvent(path2, this, name, args, parent);
4015
+ t.txnPath = txnPath;
4016
+ t.origin = origin;
4017
+ t.originPinned = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
3889
4018
  this.pushTransaction(t);
3890
4019
  return this._link(t, parent);
3891
4020
  }
3892
4021
  // Raise an intent: a job the sender did not address, walked along a route until
3893
4022
  // something answers. `opts.route` is a list of legs (see DEFAULT_ROUTE) and
3894
4023
  // `opts.livePath` opts out of pinning the answer's destination keys.
3895
- pushIntent(path, name, args = [], opts = {}, parent = null) {
4024
+ pushIntent(path2, name, args = [], opts = {}, parent = null) {
3896
4025
  const release = parent ? parent.completion.track() : null;
3897
- const walk = new IntentWalk(this, path, name, args, opts, parent, release);
4026
+ const walk = new IntentWalk(this, path2, name, args, opts, parent, release);
3898
4027
  walk.advance();
3899
4028
  return walk;
3900
4029
  }
@@ -3928,16 +4057,17 @@ var Transactor = class {
3928
4057
  transaction._completion?.releaseSelf();
3929
4058
  }
3930
4059
  }
3931
- transactInputNow(path, event, eventHandler, dragInfo) {
3932
- this.transact(new InputEvent(path, event, eventHandler, this, dragInfo));
4060
+ transactInputNow(path2, event, eventHandler, dragInfo) {
4061
+ this.transact(new InputEvent(path2, event, eventHandler, this, dragInfo));
3933
4062
  }
3934
4063
  };
3935
4064
  function nullHandler() {
3936
4065
  return this;
3937
4066
  }
3938
4067
  var Transaction = class {
3939
- constructor(path, transactor, parentTransaction = null) {
3940
- this.path = path;
4068
+ constructor(path2, transactor, parentTransaction = null) {
4069
+ this.path = path2;
4070
+ this.txnPath = null;
3941
4071
  this.transactor = transactor;
3942
4072
  this.parentTransaction = parentTransaction;
3943
4073
  this._completion = null;
@@ -3986,9 +4116,11 @@ var Transaction = class {
3986
4116
  getHandlerAndArgs(_root, _instance, _comps) {
3987
4117
  return null;
3988
4118
  }
3989
- // The path used to apply the mutation. Teleports dynamic-var renders so it lands on
3990
- // the data's real location (the dispatch `this.path` keeps intermediates).
4119
+ // The path used to apply the mutation: the ACTIVE frame of the dispatch path, so
4120
+ // an event inside a `<x render="*name">` subtree updates the value where it
4121
+ // really lives (the dispatch `this.path` keeps the visual callers, for bubbling).
3991
4122
  getTransactionPath() {
4123
+ if (this.txnPath !== null) return this.txnPath;
3992
4124
  return this.path.toTransactionPath().compact();
3993
4125
  }
3994
4126
  run(curRoot, comps) {
@@ -4006,15 +4138,16 @@ var Transaction = class {
4006
4138
  }
4007
4139
  };
4008
4140
  var InputEvent = class extends Transaction {
4009
- constructor(path, e, handler, transactor, dragInfo) {
4010
- super(path, transactor);
4141
+ constructor(path2, e, handler, transactor, dragInfo) {
4142
+ super(path2, transactor);
4011
4143
  this.e = e;
4012
4144
  this.handler = handler;
4013
4145
  this.dragInfo = dragInfo;
4014
4146
  this._dispatchPath = null;
4015
4147
  }
4016
- // Frame steps removed, DynStep + one step per crossed component kept: bubbling
4017
- // it visits every component (including intermediates of a dynamic-var render).
4148
+ // Frame-only steps removed, one step per crossed component kept — inside every
4149
+ // continuation frame independently, so bubbling it visits every component and
4150
+ // then returns to the caller that wrote the `*name`.
4018
4151
  get dispatchPath() {
4019
4152
  this._dispatchPath ??= this.path.compact();
4020
4153
  return this._dispatchPath;
@@ -4049,11 +4182,11 @@ var InputEvent = class extends Transaction {
4049
4182
  this.transactor.pushIntent(this.dispatchPath, name, args, rest, this);
4050
4183
  }
4051
4184
  getHandlerAndArgs(root, _instance, comps) {
4052
- const stack = this.path.toTransactionPath().buildStack(Stack.root(comps, root, this));
4185
+ const stack = this.path.buildStack(Stack.root(comps, root, this));
4053
4186
  const [handler, args] = this.handler.getHandlerAndArgs(stack, this);
4054
4187
  this._handlerArgs = [...args];
4055
- const path = this.dispatchPath;
4056
- args.push(new EventContext(path, this.transactor, this));
4188
+ const path2 = this.dispatchPath;
4189
+ args.push(new EventContext(path2, this.transactor, this));
4057
4190
  return [handler, args];
4058
4191
  }
4059
4192
  // The dispatched DOM event, read only through `e.<member>` handler args
@@ -4064,11 +4197,11 @@ var InputEvent = class extends Transaction {
4064
4197
  }
4065
4198
  };
4066
4199
  var NameArgsTransaction = class extends Transaction {
4067
- constructor(path, transactor, name, args, parentTransaction) {
4068
- super(path, transactor, parentTransaction);
4200
+ constructor(path2, transactor, name, args, parentTransaction) {
4201
+ super(path2, transactor, parentTransaction);
4069
4202
  this.name = name;
4070
4203
  this.args = args;
4071
- this.targetPath = path;
4204
+ this.targetPath = path2;
4072
4205
  }
4073
4206
  handlerProp = null;
4074
4207
  // NameArgs verbs map their handler bucket straight to the observed kind:
@@ -4125,8 +4258,8 @@ var SendEvent = class extends NameArgsTransaction {
4125
4258
  };
4126
4259
  var IntentEvent = class extends NameArgsTransaction {
4127
4260
  handlerProp = "intent";
4128
- constructor(path, transactor, walk) {
4129
- super(path, transactor, walk.name, walk.args, walk.parent);
4261
+ constructor(path2, transactor, walk) {
4262
+ super(path2, transactor, walk.name, walk.args, walk.parent);
4130
4263
  this.walk = walk;
4131
4264
  this.targetPath = walk.origin;
4132
4265
  }
@@ -4153,17 +4286,17 @@ var PASS = /* @__PURE__ */ Symbol.for("tutuca.intent.pass");
4153
4286
  var REFUSAL_RING_CAP = 200;
4154
4287
  var INTENT_DEPTH = 64;
4155
4288
  var IntentWalk = class {
4156
- constructor(transactor, path, name, args, opts, parent, release) {
4289
+ constructor(transactor, path2, name, args, opts, parent, release) {
4157
4290
  this.transactor = transactor;
4158
4291
  this.name = name;
4159
4292
  this.args = args;
4160
4293
  this.route = opts?.route ?? DEFAULT_ROUTE;
4161
4294
  this.parent = parent;
4162
4295
  this.release = release;
4163
- this.origin = path;
4164
- this.answerPath = opts?.livePath ? null : path.toTransactionPath().pinKeys(transactor.state.val);
4296
+ this.origin = path2;
4297
+ this.answerPath = opts?.livePath ? null : path2.toTransactionPath().pinKeys(transactor.state.val);
4165
4298
  this.legIndex = 0;
4166
- this.dynAt = path;
4299
+ this.dynAt = path2;
4167
4300
  this.hops = 0;
4168
4301
  this.ended = false;
4169
4302
  }
@@ -4175,7 +4308,7 @@ var IntentWalk = class {
4175
4308
  while (this.legIndex < this.route.length) {
4176
4309
  const leg = this.route[this.legIndex];
4177
4310
  if (leg === "dyn") {
4178
- if (this.dynAt.steps.length === 0) {
4311
+ if (!this.dynAt.canPop()) {
4179
4312
  this.legIndex++;
4180
4313
  continue;
4181
4314
  }
@@ -4272,8 +4405,8 @@ var IntentWalk = class {
4272
4405
  if (this.ended) return;
4273
4406
  this.ended = true;
4274
4407
  if (name === null) return this.release?.();
4275
- const path = this.answerPath ?? this.origin;
4276
- const t = new SendEvent(path, this.transactor, name, args, this.parent);
4408
+ const t = new SendEvent(this.origin, this.transactor, name, args, this.parent);
4409
+ t.txnPath = this.answerPath;
4277
4410
  t._isAnswer = true;
4278
4411
  this.transactor.pushTransaction(t);
4279
4412
  if (this.release) t.completion.whenSubtreeSettled().then(this.release);
@@ -4339,8 +4472,8 @@ var Completion = class {
4339
4472
  }
4340
4473
  };
4341
4474
  var Dispatcher = class {
4342
- constructor(path, transactor, parentTransaction, root = transactor.state.val) {
4343
- this.path = path;
4475
+ constructor(path2, transactor, parentTransaction, root = transactor.state.val) {
4476
+ this.path = path2;
4344
4477
  this.transactor = transactor;
4345
4478
  this.parent = parentTransaction;
4346
4479
  this.root = root;
@@ -4368,7 +4501,9 @@ var Dispatcher = class {
4368
4501
  // replayed; provides are pushed on every component frame either way, so a provide
4369
4502
  // that reads a loop binding is the one case this cannot reproduce faithfully.
4370
4503
  _stack() {
4371
- this._stackMemo ??= this.path.toTransactionPath().buildStack(Stack.root(this.transactor.comps, this.root, this.parent));
4504
+ this._stackMemo ??= this.path.buildStack(
4505
+ Stack.root(this.transactor.comps, this.root, this.parent)
4506
+ );
4372
4507
  return this._stackMemo;
4373
4508
  }
4374
4509
  // Resolve a name the way the renderer would. `opts.route` takes the same legs in
@@ -4382,14 +4517,19 @@ var Dispatcher = class {
4382
4517
  );
4383
4518
  }
4384
4519
  // The `lex` leg without a Stack: the scope of the component whose handler is
4385
- // running, which is the leaf of this ctx's path.
4520
+ // running, which is the leaf of this ctx's path. A type name resolves to the
4521
+ // component registered under it; a value name to a path registered under it
4522
+ // (see ComponentStack.registerPaths), read against the current root.
4386
4523
  _lookupLex(name) {
4387
4524
  let Comp = null;
4388
4525
  this.walkPath((c) => {
4389
4526
  Comp = c;
4390
4527
  return false;
4391
4528
  });
4392
- return Comp?.scope?.lookupComponent(name) ?? null;
4529
+ const scope = Comp?.scope;
4530
+ if (scope == null) return null;
4531
+ if (isTypeName(name)) return scope.lookupComponent(name) ?? null;
4532
+ return scope.lookupPath(name)?.lookup(this.root) ?? null;
4393
4533
  }
4394
4534
  // A component lookup is a value lookup constrained to a component. The `lex` leg
4395
4535
  // can only ever answer with one; the `dyn` leg reads a binding an ancestor
@@ -4411,8 +4551,8 @@ var Dispatcher = class {
4411
4551
  send(name, args) {
4412
4552
  return this.sendAtPath(this.path, name, args);
4413
4553
  }
4414
- sendAtPath(path, name, args) {
4415
- return this.transactor.pushSend(path, name, args, this.parent, this.path);
4554
+ sendAtPath(path2, name, args) {
4555
+ return this.transactor.pushSend(path2, name, args, this.parent, this.path);
4416
4556
  }
4417
4557
  // An intent is ROUTED: it names a job and walks until something answers. The verb
4418
4558
  // does not decide which scope answers — `opts.route` does, written here at the call
@@ -4420,8 +4560,8 @@ var Dispatcher = class {
4420
4560
  intent(name, args, opts) {
4421
4561
  return this.intentAtPath(this.path, name, args, opts);
4422
4562
  }
4423
- intentAtPath(path, name, args, opts) {
4424
- return this.transactor.pushIntent(path, name, args, opts, this.parent);
4563
+ intentAtPath(path2, name, args, opts) {
4564
+ return this.transactor.pushIntent(path2, name, args, opts, this.parent);
4425
4565
  }
4426
4566
  };
4427
4567
  var EventContext = class extends Dispatcher {
@@ -4450,7 +4590,14 @@ var EventContext = class extends Dispatcher {
4450
4590
  this.transactor.refuse("NO_SENDER", { name });
4451
4591
  return null;
4452
4592
  }
4453
- return this.sendAtPath(origin, name, args);
4593
+ return this.transactor.pushSend(
4594
+ origin,
4595
+ name,
4596
+ args,
4597
+ this.parent,
4598
+ this.path,
4599
+ this.parent.originPinned
4600
+ );
4454
4601
  }
4455
4602
  // End the walk answering nothing — "served, and no answer".
4456
4603
  stop() {
@@ -4482,7 +4629,7 @@ var PathChanges = class extends PathBuilder {
4482
4629
  }
4483
4630
  };
4484
4631
  function rootDispatcher(transactor) {
4485
- return new Dispatcher(new Path([]), transactor, null);
4632
+ return new Dispatcher(new DispatchPath(), transactor, null);
4486
4633
  }
4487
4634
 
4488
4635
  // src/app.js
@@ -4523,7 +4670,7 @@ var App = class {
4523
4670
  const { type } = e;
4524
4671
  const isDrag = type === "dragover" || type === "dragstart" || type === "dragend" || type === "drop";
4525
4672
  const { rootNode: root, maxEventNodeDepth: maxDepth, comps, transactor } = this;
4526
- const [path, handlers] = Path.fromNodeAndEventName(
4673
+ const [path2, handlers] = DispatchPath.fromNodeAndEventName(
4527
4674
  e.target,
4528
4675
  type,
4529
4676
  root,
@@ -4531,9 +4678,9 @@ var App = class {
4531
4678
  comps,
4532
4679
  !isDrag
4533
4680
  );
4534
- if (isDrag) this._handleDragEvent(e, type, path);
4535
- if (path !== null && handlers !== null)
4536
- for (const handler of handlers) transactor.transactInputNow(path, e, handler, this.dragInfo);
4681
+ if (isDrag) this._handleDragEvent(e, type, path2);
4682
+ if (path2 !== null && handlers !== null)
4683
+ for (const handler of handlers) transactor.transactInputNow(path2, e, handler, this.dragInfo);
4537
4684
  }
4538
4685
  _handleTouchEvent(e) {
4539
4686
  const { type } = e;
@@ -4576,7 +4723,7 @@ var App = class {
4576
4723
  this._touch = null;
4577
4724
  }
4578
4725
  }
4579
- _handleDragEvent(e, type, path) {
4726
+ _handleDragEvent(e, type, path2) {
4580
4727
  if (type === "dragover") {
4581
4728
  const dropTarget = getClosestDropTarget(e.target, this.rootNode, Infinity);
4582
4729
  if (dropTarget !== null) {
@@ -4588,10 +4735,10 @@ var App = class {
4588
4735
  } else if (type === "dragstart") {
4589
4736
  e.target.dataset.dragging = 1;
4590
4737
  const rootValue = this.state.val;
4591
- const txnPath = path.compact().toTransactionPath();
4738
+ const txnPath = path2.compact().toTransactionPath();
4592
4739
  const value = txnPath.lookup(rootValue);
4593
4740
  const dragType = e.target.dataset.dragtype ?? "?";
4594
- const stack = path.toTransactionPath().buildStack(this.makeStack(rootValue));
4741
+ const stack = path2.buildStack(this.makeStack(rootValue));
4595
4742
  this.dragInfo = new DragInfo(stack, value, dragType, e.target);
4596
4743
  } else if (type === "drop") {
4597
4744
  e.preventDefault();
@@ -4666,7 +4813,7 @@ var App = class {
4666
4813
  this.rootNode.removeEventListener(name, this, listenerOpts(name));
4667
4814
  }
4668
4815
  sendAtRoot(name, args) {
4669
- this.transactor.pushSend(new Path([]), name, args);
4816
+ this.transactor.pushSend(new DispatchPath(), name, args);
4670
4817
  }
4671
4818
  registerComponents(comps, opts) {
4672
4819
  const scope = this.compStack.enter();
@@ -4884,7 +5031,20 @@ var WeakMapDomCache = class {
4884
5031
  };
4885
5032
 
4886
5033
  // src/renderer.js
4887
- var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk"];
5034
+ var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk", "rp"];
5035
+ function stampRenderBase(vdom, text) {
5036
+ if (vdom instanceof VNode)
5037
+ return new VNode(
5038
+ vdom.tag,
5039
+ { ...vdom.attrs, "data-rp": text },
5040
+ vdom.childs,
5041
+ vdom.key,
5042
+ vdom.namespace
5043
+ );
5044
+ if (vdom instanceof VFragment)
5045
+ return new VFragment(vdom.childs.map((c) => stampRenderBase(c, text)));
5046
+ return vdom;
5047
+ }
4888
5048
  var Renderer = class {
4889
5049
  constructor(comps) {
4890
5050
  this.comps = comps;
@@ -4921,17 +5081,17 @@ var Renderer = class {
4921
5081
  if (comp === null) return null;
4922
5082
  return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
4923
5083
  }
4924
- renderIt(stack, node, key, viewName) {
5084
+ renderIt(stack, node, key, viewName, base = null) {
4925
5085
  const comp = this.comps.getCompFor(stack.it);
4926
- return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
5086
+ return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName, base) : null;
4927
5087
  }
4928
5088
  // `node` is the parse node of the render site (`<x render>` / `render-it` /
4929
5089
  // `render-each`, or the view's root anode for the app root). It keys the
4930
5090
  // cache as a globally-unique object: node ids alone are unique only within a
4931
5091
  // single view, so the same value rendered by two components (e.g. through a
4932
5092
  // shared dynamic-var sequence) would otherwise collide in the cache.
4933
- _rValComp(stack, val, comp, node, key, viewName) {
4934
- const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}`;
5093
+ _rValComp(stack, val, comp, node, key, viewName, base = null) {
5094
+ const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}${stack.renderPath.addressKey}`;
4935
5095
  const cachePath = [node, val];
4936
5096
  stack._pushDynBindValuesToArray(cachePath, comp);
4937
5097
  const cachedNode = this.cache.get(cachePath, cacheKey);
@@ -4939,30 +5099,43 @@ var Renderer = class {
4939
5099
  const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
4940
5100
  const body = this.renderView(view, stack);
4941
5101
  if (body == null) return null;
5102
+ const baseJson = base === null ? null : pathToJson(base);
4942
5103
  const meta = this._renderMetadata({
4943
5104
  $: "Comp",
4944
5105
  nid: node?.nodeId ?? null,
4945
5106
  cid: comp.id,
4946
- vid: view.name
5107
+ vid: view.name,
5108
+ ...baseJson === null ? null : { base: baseJson }
4947
5109
  });
4948
- const dom = new VFragment([meta, body]);
5110
+ const dom = new VFragment([
5111
+ meta,
5112
+ baseJson === null ? body : stampRenderBase(body, JSON.stringify(baseJson))
5113
+ ]);
4949
5114
  this.cache.set(cachePath, cacheKey, dom);
4950
5115
  return dom;
4951
5116
  }
4952
5117
  pushEachEntry(r, nid, attrName, key, dom) {
4953
5118
  r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
4954
5119
  }
4955
- renderEachWhen(stack, iterInfo, view, nid) {
5120
+ renderEachWhen(stack, each2) {
5121
+ const { iterInfo, node: view, nodeId: nid } = each2;
4956
5122
  const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
5123
+ const seqPath = iterInfo.val instanceof DynVal ? stack.lookupDynamicLocated(iterInfo.val.name)?.path ?? null : null;
4957
5124
  const r = [];
4958
5125
  const it = stack.it;
4959
5126
  const renderOne = (key, value, attrName, binds) => {
5127
+ const itemBase = seqPath === null ? null : keyedPath(seqPath, key);
5128
+ const itemStep = itemBase === null ? each2.itemStep(key) : null;
5129
+ const itemPath = itemBase !== null ? stack.renderPath.pushFrame(itemBase) : itemStep !== null ? stack.renderPath.pushItem(itemStep) : stack.renderPath;
4960
5130
  const cachePath = enricher ? [view, it, value] : [view, value];
4961
- const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
5131
+ const cacheKey = `${stack.viewsId ?? ""}${nid}${key}${itemPath.addressKey}`;
4962
5132
  const cachedNode = this.cache.get(cachePath, cacheKey);
4963
5133
  if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
4964
5134
  else {
4965
- const dom = this.renderView(view, stack.enter(value, binds, false));
5135
+ const dom = this.renderView(
5136
+ view,
5137
+ stack.enter(value, binds, false, itemPath, itemBase !== null)
5138
+ );
4966
5139
  if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
4967
5140
  this.cache.set(cachePath, cacheKey, dom);
4968
5141
  }
@@ -5074,6 +5247,7 @@ export {
5074
5247
  html,
5075
5248
  injectCss,
5076
5249
  macro,
5250
+ path,
5077
5251
  phaseOps,
5078
5252
  resolveArgs,
5079
5253
  rootDispatcher,