tutuca 0.13.3 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tutuca.js CHANGED
@@ -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;
@@ -2379,13 +2464,16 @@ function h(tagName, properties, children, namespace) {
2379
2464
  }
2380
2465
 
2381
2466
  // src/anode.js
2382
- function resolveDynProducer(comp, name) {
2383
- const own = comp?.provide?.[name];
2384
- const producerComp = own != null ? comp : comp?.scope?.lookupProvider(name) ?? null;
2385
- const producerProvide = own ?? producerComp?.provide?.[name];
2386
- if (producerComp == null || producerProvide == null) return null;
2387
- const pi = producerProvide.val?.toPathItem?.() ?? null;
2388
- 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];
2389
2477
  }
2390
2478
  var BaseNode = class {
2391
2479
  render(_stack, _rx) {
@@ -2707,33 +2795,31 @@ var RenderViewId = class extends ANode {
2707
2795
  setDataAttr(_key, _val) {
2708
2796
  }
2709
2797
  };
2710
- function dynRenderStep(comp, name, key) {
2711
- const p = resolveDynProducer(comp, name);
2712
- if (!p) return null;
2713
- return key === void 0 ? new DynStep(p.producerCompId, p.producerSteps) : new DynEachStep(p.producerCompId, p.producerSteps, key);
2714
- }
2715
2798
  var RenderNode = class extends RenderViewId {
2716
2799
  render(stack, rx) {
2717
- const newStack = stack.enter(this.val.eval(stack), {}, true);
2718
- 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);
2719
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.
2720
2806
  toPathStep(ctx) {
2721
- if (this.val instanceof DynVal) return dynRenderStep(ctx.comp, this.val.name);
2807
+ if (this.val instanceof DynVal) return null;
2722
2808
  return super.toPathStep(ctx);
2723
2809
  }
2724
2810
  };
2725
2811
  var RenderItNode = class extends RenderViewId {
2726
2812
  render(stack, rx) {
2727
- const newStack = stack.enter(stack.it, {}, true);
2728
- 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);
2729
2816
  }
2730
2817
  toPathStep(ctx) {
2731
2818
  const next = ctx.next();
2732
2819
  if (next === null) return null;
2733
2820
  const nextNode = next.resolveNode();
2734
2821
  if (nextNode instanceof EachNode && next.hasKey) {
2735
- if (nextNode.val instanceof DynVal)
2736
- return dynRenderStep(ctx.comp, nextNode.val.name, next.key);
2822
+ if (nextNode.val instanceof DynVal) return null;
2737
2823
  return new EachRenderItStep(nextNode.val.name, next.key);
2738
2824
  }
2739
2825
  return null;
@@ -2835,11 +2921,19 @@ var EachNode = class extends WrapperNode {
2835
2921
  this.iterInfo = new IterInfo(val, null, null, null);
2836
2922
  }
2837
2923
  render(stack, rx) {
2838
- return rx.renderEachWhen(stack, this.iterInfo, this.node, this.nodeId);
2924
+ return rx.renderEachWhen(stack, this);
2839
2925
  }
2840
2926
  toPathStep(ctx) {
2841
2927
  return ctx.hasKey ? new EachBindStep(this.iterInfo, ctx.key) : null;
2842
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
+ }
2843
2937
  static register = true;
2844
2938
  };
2845
2939
  var IterInfo = class {
@@ -3100,6 +3194,215 @@ function compileModifiers(eventName, names) {
3100
3194
  };
3101
3195
  }
3102
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
+
3103
3406
  // src/components.js
3104
3407
  var COMPONENT = /* @__PURE__ */ Symbol.for("tutuca.component");
3105
3408
  var Components = class {
@@ -3138,12 +3441,14 @@ var ComponentStack = class _ComponentStack {
3138
3441
  this.byName = {};
3139
3442
  this.intentsByName = {};
3140
3443
  this.macros = {};
3444
+ this.paths = {};
3141
3445
  }
3142
3446
  enter() {
3143
3447
  return new _ComponentStack(this.comps, this);
3144
3448
  }
3145
3449
  registerComponents(comps, opts) {
3146
- const { aliases = {} } = opts ?? {};
3450
+ const { aliases = {}, paths } = opts ?? {};
3451
+ if (paths) this.registerPaths(paths);
3147
3452
  for (let i = 0; i < comps.length; i++) {
3148
3453
  const Comp = comps[i];
3149
3454
  Comp[COMPONENT].scope = this.enter();
@@ -3157,6 +3462,27 @@ var ComponentStack = class _ComponentStack {
3157
3462
  else console.warn("alias", alias, "to inexistent component", aliases[alias]);
3158
3463
  }
3159
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
+ }
3160
3486
  registerMacros(macros) {
3161
3487
  for (const key in macros) {
3162
3488
  const lower = key.toLowerCase();
@@ -3188,17 +3514,16 @@ var ComponentStack = class _ComponentStack {
3188
3514
  lookupComponent(name) {
3189
3515
  return this.byName[name] ?? this.parent?.lookupComponent(name) ?? null;
3190
3516
  }
3191
- // The component in this scope chain that provides `name`, innermost first. A lookup
3192
- // names a value without naming its producer, so the render-target teleport
3193
- // (resolveDynProducer) recovers the producer here instead. Sound because
3194
- // PROVIDE_NAME_COLLISION keeps a provide name to one producer per chain; with two,
3195
- // this returns the innermost and the linter has already reported the ambiguity.
3196
- 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) {
3197
3523
  for (const compName in this.byName) {
3198
- const Comp = this.byName[compName];
3199
- if (Comp.provide?.[name] !== void 0) return Comp;
3524
+ if (this.byName[compName].provide?.[name] !== void 0) return true;
3200
3525
  }
3201
- return this.parent?.lookupProvider(name) ?? null;
3526
+ return this.parent?.hasProvider(name) ?? false;
3202
3527
  }
3203
3528
  lookupMacro(name) {
3204
3529
  return this.macros[name] ?? this.parent?.lookupMacro(name) ?? null;
@@ -3215,10 +3540,6 @@ var LookupInfo = class {
3215
3540
  }
3216
3541
  };
3217
3542
  var isString = (v) => typeof v === "string";
3218
- var isTypeName = (s) => {
3219
- const c = s.charCodeAt(0);
3220
- return c >= 65 && c <= 90;
3221
- };
3222
3543
  var _rawSpecKeys = "name view style commonStyle globalStyle receive intent alter views provide lookup fields methods statics";
3223
3544
  var KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
3224
3545
  var _compId = 0;
@@ -3291,160 +3612,6 @@ var Component = class {
3291
3612
  }
3292
3613
  };
3293
3614
 
3294
- // src/stack.js
3295
- var STOP = /* @__PURE__ */ Symbol("STOP");
3296
- var NEXT = /* @__PURE__ */ Symbol("NEXT");
3297
- var DEFAULT_ROUTE = ["dyn", "lex"];
3298
- function routeLookup(route, lex, dyn) {
3299
- for (let i = 0; i < route.length; i++) {
3300
- const leg = route[i];
3301
- if (leg === "dyn") {
3302
- const v = dyn();
3303
- if (v != null) return v;
3304
- } else if (leg === "lex") {
3305
- const v = lex();
3306
- if (v != null) return v;
3307
- } else {
3308
- console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
3309
- }
3310
- }
3311
- return null;
3312
- }
3313
- function lookup(chain, name, dv = null) {
3314
- let n = chain;
3315
- while (n !== null) {
3316
- const r = n[0].lookup(name);
3317
- if (r === STOP) return dv;
3318
- if (r !== NEXT) return r;
3319
- n = n[1];
3320
- }
3321
- return dv;
3322
- }
3323
- var BindFrame = class {
3324
- constructor(it, binds, isFrame) {
3325
- this.it = it;
3326
- this.binds = binds;
3327
- this.isFrame = isFrame;
3328
- }
3329
- lookup(name) {
3330
- const v = this.binds[name];
3331
- return v === void 0 ? this.isFrame ? STOP : NEXT : v;
3332
- }
3333
- };
3334
- var ObjectFrame = class {
3335
- constructor(binds) {
3336
- this.binds = binds;
3337
- }
3338
- lookup(key) {
3339
- const v = this.binds[key];
3340
- return v === void 0 ? NEXT : v;
3341
- }
3342
- };
3343
- function computeViewsId(views) {
3344
- let s = "";
3345
- let n = views;
3346
- while (n !== null) {
3347
- s += n[0];
3348
- n = n[1];
3349
- }
3350
- return s === "main" ? "" : s;
3351
- }
3352
- var Stack = class _Stack {
3353
- constructor(comps, it, binds, dynBinds, views, viewsId, ctx = null) {
3354
- this.comps = comps;
3355
- this.it = it;
3356
- this.binds = binds;
3357
- this.dynBinds = dynBinds;
3358
- this.views = views;
3359
- this.viewsId = viewsId;
3360
- this.ctx = ctx;
3361
- }
3362
- // Evaluate every provide the entered component publishes and push them as one
3363
- // dynBinds frame, keyed by NAME. Published types go in the same frame: a type name
3364
- // starts A-Z and a value name does not, so the two namespaces cannot collide and
3365
- // nearest-ancestor-wins falls out of frame order for both. No-op with no provides.
3366
- _pushProvides() {
3367
- const comp = this.comps.getCompFor(this.it);
3368
- if (comp == null) return this;
3369
- const { provide, provideType } = comp;
3370
- const dynObj = {};
3371
- let has2 = false;
3372
- for (const k in provide) {
3373
- dynObj[k] = provide[k].val.eval(this);
3374
- has2 = true;
3375
- }
3376
- for (const k in provideType) {
3377
- dynObj[k] = provideType[k];
3378
- has2 = true;
3379
- }
3380
- if (!has2) return this;
3381
- const newDynBinds = [new ObjectFrame(dynObj), this.dynBinds];
3382
- const { comps, it, binds, views, viewsId, ctx } = this;
3383
- return new _Stack(comps, it, binds, newDynBinds, views, viewsId, ctx);
3384
- }
3385
- static root(comps, it, ctx) {
3386
- const binds = [new BindFrame(it, {}, true), null];
3387
- const dynBinds = [new ObjectFrame({}), null];
3388
- const views = ["main", null];
3389
- return new _Stack(comps, it, binds, dynBinds, views, "", ctx)._pushProvides();
3390
- }
3391
- enter(it, bindings = {}, isFrame = true) {
3392
- const { comps, binds, dynBinds, views, viewsId, ctx } = this;
3393
- const newBinds = [new BindFrame(it, bindings, isFrame), binds];
3394
- const stack = new _Stack(comps, it, newBinds, dynBinds, views, viewsId, ctx);
3395
- return isFrame ? stack._pushProvides() : stack;
3396
- }
3397
- pushViewName(name) {
3398
- const { comps, it, binds, dynBinds, views, ctx } = this;
3399
- const newViews = [name, views];
3400
- return new _Stack(comps, it, binds, dynBinds, newViews, computeViewsId(newViews), ctx);
3401
- }
3402
- // Published types are stable per scope and would only churn the render cache, so
3403
- // the cache key covers values alone.
3404
- _pushDynBindValuesToArray(arr, comp) {
3405
- for (const k in comp.provide) arr.push(this.lookupDynamic(k));
3406
- for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
3407
- }
3408
- // `*name`: the nearest binding above (including this component's own provides,
3409
- // pushed on entering it), else this component's declared default, else null.
3410
- lookupDynamic(name) {
3411
- const v = lookup(this.dynBinds, name);
3412
- if (v != null) return v;
3413
- const comp = this.comps.getCompFor(this.it);
3414
- return comp?.lookup[name]?.val?.eval(this) ?? null;
3415
- }
3416
- lookupBind(name) {
3417
- return lookup(this.binds, name);
3418
- }
3419
- lookupFieldRaw(name) {
3420
- return this.it[name] ?? null;
3421
- }
3422
- lookupMethod(name) {
3423
- const fn = this.it[name];
3424
- return fn instanceof Function ? fn.call(this.it) : null;
3425
- }
3426
- // The dispatched DOM event / drag info, read only by EventMemberVal's
3427
- // `e.<member>` handler args. Null outside a live event transaction.
3428
- lookupEvent() {
3429
- return this.ctx?.event ?? null;
3430
- }
3431
- lookupDragInfo() {
3432
- return this.ctx?.dragInfo ?? null;
3433
- }
3434
- getHandlerFor(name, key) {
3435
- return this.comps.getHandlerFor(this.it, name, key);
3436
- }
3437
- lookupBestView(views, defaultViewName) {
3438
- let n = this.views;
3439
- while (n !== null) {
3440
- const view = views[n[0]];
3441
- if (view !== void 0) return view;
3442
- n = n[1];
3443
- }
3444
- return views[defaultViewName];
3445
- }
3446
- };
3447
-
3448
3615
  // src/oo.js
3449
3616
  var BAD_VALUE = /* @__PURE__ */ Symbol("BadValue");
3450
3617
  var nullCoercer = (v) => v;
@@ -3790,9 +3957,9 @@ var Transactor = class {
3790
3957
  // `.a[.selId]` render target reconstructed from a DOM event) against the root the
3791
3958
  // handler read, so pathKeys carries concrete keys instead of dropping the dynamic
3792
3959
  // step. No-op when nobody is observing.
3793
- _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 }) {
3794
3961
  if (this._observers.length === 0) return;
3795
- const pinned = path.pinKeys(root);
3962
+ const pinned = path2.pinKeys(root);
3796
3963
  this._emit({
3797
3964
  kind,
3798
3965
  name,
@@ -3843,18 +4010,20 @@ var Transactor = class {
3843
4010
  // intent's answerPath is: a reply must reach the sender that asked even if a key
3844
4011
  // moved while the message was in flight. Null when nobody is waiting — a host
3845
4012
  // sendAtRoot or a view's own `@on.*` — and ctx.sendReply refuses on that.
3846
- pushSend(path, name, args = [], parent = null, origin = null) {
3847
- const t = new SendEvent(path, this, name, args, parent);
3848
- 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);
3849
4018
  this.pushTransaction(t);
3850
4019
  return this._link(t, parent);
3851
4020
  }
3852
4021
  // Raise an intent: a job the sender did not address, walked along a route until
3853
4022
  // something answers. `opts.route` is a list of legs (see DEFAULT_ROUTE) and
3854
4023
  // `opts.livePath` opts out of pinning the answer's destination keys.
3855
- pushIntent(path, name, args = [], opts = {}, parent = null) {
4024
+ pushIntent(path2, name, args = [], opts = {}, parent = null) {
3856
4025
  const release = parent ? parent.completion.track() : null;
3857
- const walk = new IntentWalk(this, path, name, args, opts, parent, release);
4026
+ const walk = new IntentWalk(this, path2, name, args, opts, parent, release);
3858
4027
  walk.advance();
3859
4028
  return walk;
3860
4029
  }
@@ -3888,16 +4057,17 @@ var Transactor = class {
3888
4057
  transaction._completion?.releaseSelf();
3889
4058
  }
3890
4059
  }
3891
- transactInputNow(path, event, eventHandler, dragInfo) {
3892
- 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));
3893
4062
  }
3894
4063
  };
3895
4064
  function nullHandler() {
3896
4065
  return this;
3897
4066
  }
3898
4067
  var Transaction = class {
3899
- constructor(path, transactor, parentTransaction = null) {
3900
- this.path = path;
4068
+ constructor(path2, transactor, parentTransaction = null) {
4069
+ this.path = path2;
4070
+ this.txnPath = null;
3901
4071
  this.transactor = transactor;
3902
4072
  this.parentTransaction = parentTransaction;
3903
4073
  this._completion = null;
@@ -3946,9 +4116,11 @@ var Transaction = class {
3946
4116
  getHandlerAndArgs(_root, _instance, _comps) {
3947
4117
  return null;
3948
4118
  }
3949
- // The path used to apply the mutation. Teleports dynamic-var renders so it lands on
3950
- // 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).
3951
4122
  getTransactionPath() {
4123
+ if (this.txnPath !== null) return this.txnPath;
3952
4124
  return this.path.toTransactionPath().compact();
3953
4125
  }
3954
4126
  run(curRoot, comps) {
@@ -3966,15 +4138,16 @@ var Transaction = class {
3966
4138
  }
3967
4139
  };
3968
4140
  var InputEvent = class extends Transaction {
3969
- constructor(path, e, handler, transactor, dragInfo) {
3970
- super(path, transactor);
4141
+ constructor(path2, e, handler, transactor, dragInfo) {
4142
+ super(path2, transactor);
3971
4143
  this.e = e;
3972
4144
  this.handler = handler;
3973
4145
  this.dragInfo = dragInfo;
3974
4146
  this._dispatchPath = null;
3975
4147
  }
3976
- // Frame steps removed, DynStep + one step per crossed component kept: bubbling
3977
- // 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`.
3978
4151
  get dispatchPath() {
3979
4152
  this._dispatchPath ??= this.path.compact();
3980
4153
  return this._dispatchPath;
@@ -4009,11 +4182,11 @@ var InputEvent = class extends Transaction {
4009
4182
  this.transactor.pushIntent(this.dispatchPath, name, args, rest, this);
4010
4183
  }
4011
4184
  getHandlerAndArgs(root, _instance, comps) {
4012
- const stack = this.path.toTransactionPath().buildStack(Stack.root(comps, root, this));
4185
+ const stack = this.path.buildStack(Stack.root(comps, root, this));
4013
4186
  const [handler, args] = this.handler.getHandlerAndArgs(stack, this);
4014
4187
  this._handlerArgs = [...args];
4015
- const path = this.dispatchPath;
4016
- args.push(new EventContext(path, this.transactor, this));
4188
+ const path2 = this.dispatchPath;
4189
+ args.push(new EventContext(path2, this.transactor, this));
4017
4190
  return [handler, args];
4018
4191
  }
4019
4192
  // The dispatched DOM event, read only through `e.<member>` handler args
@@ -4024,11 +4197,11 @@ var InputEvent = class extends Transaction {
4024
4197
  }
4025
4198
  };
4026
4199
  var NameArgsTransaction = class extends Transaction {
4027
- constructor(path, transactor, name, args, parentTransaction) {
4028
- super(path, transactor, parentTransaction);
4200
+ constructor(path2, transactor, name, args, parentTransaction) {
4201
+ super(path2, transactor, parentTransaction);
4029
4202
  this.name = name;
4030
4203
  this.args = args;
4031
- this.targetPath = path;
4204
+ this.targetPath = path2;
4032
4205
  }
4033
4206
  handlerProp = null;
4034
4207
  // NameArgs verbs map their handler bucket straight to the observed kind:
@@ -4085,8 +4258,8 @@ var SendEvent = class extends NameArgsTransaction {
4085
4258
  };
4086
4259
  var IntentEvent = class extends NameArgsTransaction {
4087
4260
  handlerProp = "intent";
4088
- constructor(path, transactor, walk) {
4089
- super(path, transactor, walk.name, walk.args, walk.parent);
4261
+ constructor(path2, transactor, walk) {
4262
+ super(path2, transactor, walk.name, walk.args, walk.parent);
4090
4263
  this.walk = walk;
4091
4264
  this.targetPath = walk.origin;
4092
4265
  }
@@ -4113,17 +4286,17 @@ var PASS = /* @__PURE__ */ Symbol.for("tutuca.intent.pass");
4113
4286
  var REFUSAL_RING_CAP = 200;
4114
4287
  var INTENT_DEPTH = 64;
4115
4288
  var IntentWalk = class {
4116
- constructor(transactor, path, name, args, opts, parent, release) {
4289
+ constructor(transactor, path2, name, args, opts, parent, release) {
4117
4290
  this.transactor = transactor;
4118
4291
  this.name = name;
4119
4292
  this.args = args;
4120
4293
  this.route = opts?.route ?? DEFAULT_ROUTE;
4121
4294
  this.parent = parent;
4122
4295
  this.release = release;
4123
- this.origin = path;
4124
- 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);
4125
4298
  this.legIndex = 0;
4126
- this.dynAt = path;
4299
+ this.dynAt = path2;
4127
4300
  this.hops = 0;
4128
4301
  this.ended = false;
4129
4302
  }
@@ -4135,7 +4308,7 @@ var IntentWalk = class {
4135
4308
  while (this.legIndex < this.route.length) {
4136
4309
  const leg = this.route[this.legIndex];
4137
4310
  if (leg === "dyn") {
4138
- if (this.dynAt.steps.length === 0) {
4311
+ if (!this.dynAt.canPop()) {
4139
4312
  this.legIndex++;
4140
4313
  continue;
4141
4314
  }
@@ -4232,8 +4405,8 @@ var IntentWalk = class {
4232
4405
  if (this.ended) return;
4233
4406
  this.ended = true;
4234
4407
  if (name === null) return this.release?.();
4235
- const path = this.answerPath ?? this.origin;
4236
- 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;
4237
4410
  t._isAnswer = true;
4238
4411
  this.transactor.pushTransaction(t);
4239
4412
  if (this.release) t.completion.whenSubtreeSettled().then(this.release);
@@ -4299,8 +4472,8 @@ var Completion = class {
4299
4472
  }
4300
4473
  };
4301
4474
  var Dispatcher = class {
4302
- constructor(path, transactor, parentTransaction, root = transactor.state.val) {
4303
- this.path = path;
4475
+ constructor(path2, transactor, parentTransaction, root = transactor.state.val) {
4476
+ this.path = path2;
4304
4477
  this.transactor = transactor;
4305
4478
  this.parent = parentTransaction;
4306
4479
  this.root = root;
@@ -4328,7 +4501,9 @@ var Dispatcher = class {
4328
4501
  // replayed; provides are pushed on every component frame either way, so a provide
4329
4502
  // that reads a loop binding is the one case this cannot reproduce faithfully.
4330
4503
  _stack() {
4331
- 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
+ );
4332
4507
  return this._stackMemo;
4333
4508
  }
4334
4509
  // Resolve a name the way the renderer would. `opts.route` takes the same legs in
@@ -4342,14 +4517,19 @@ var Dispatcher = class {
4342
4517
  );
4343
4518
  }
4344
4519
  // The `lex` leg without a Stack: the scope of the component whose handler is
4345
- // 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.
4346
4523
  _lookupLex(name) {
4347
4524
  let Comp = null;
4348
4525
  this.walkPath((c) => {
4349
4526
  Comp = c;
4350
4527
  return false;
4351
4528
  });
4352
- 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;
4353
4533
  }
4354
4534
  // A component lookup is a value lookup constrained to a component. The `lex` leg
4355
4535
  // can only ever answer with one; the `dyn` leg reads a binding an ancestor
@@ -4371,8 +4551,8 @@ var Dispatcher = class {
4371
4551
  send(name, args) {
4372
4552
  return this.sendAtPath(this.path, name, args);
4373
4553
  }
4374
- sendAtPath(path, name, args) {
4375
- 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);
4376
4556
  }
4377
4557
  // An intent is ROUTED: it names a job and walks until something answers. The verb
4378
4558
  // does not decide which scope answers — `opts.route` does, written here at the call
@@ -4380,8 +4560,8 @@ var Dispatcher = class {
4380
4560
  intent(name, args, opts) {
4381
4561
  return this.intentAtPath(this.path, name, args, opts);
4382
4562
  }
4383
- intentAtPath(path, name, args, opts) {
4384
- 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);
4385
4565
  }
4386
4566
  };
4387
4567
  var EventContext = class extends Dispatcher {
@@ -4410,7 +4590,14 @@ var EventContext = class extends Dispatcher {
4410
4590
  this.transactor.refuse("NO_SENDER", { name });
4411
4591
  return null;
4412
4592
  }
4413
- 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
+ );
4414
4601
  }
4415
4602
  // End the walk answering nothing — "served, and no answer".
4416
4603
  stop() {
@@ -4442,7 +4629,7 @@ var PathChanges = class extends PathBuilder {
4442
4629
  }
4443
4630
  };
4444
4631
  function rootDispatcher(transactor) {
4445
- return new Dispatcher(new Path([]), transactor, null);
4632
+ return new Dispatcher(new DispatchPath(), transactor, null);
4446
4633
  }
4447
4634
 
4448
4635
  // src/app.js
@@ -4483,7 +4670,7 @@ var App = class {
4483
4670
  const { type } = e;
4484
4671
  const isDrag = type === "dragover" || type === "dragstart" || type === "dragend" || type === "drop";
4485
4672
  const { rootNode: root, maxEventNodeDepth: maxDepth, comps, transactor } = this;
4486
- const [path, handlers] = Path.fromNodeAndEventName(
4673
+ const [path2, handlers] = DispatchPath.fromNodeAndEventName(
4487
4674
  e.target,
4488
4675
  type,
4489
4676
  root,
@@ -4491,9 +4678,9 @@ var App = class {
4491
4678
  comps,
4492
4679
  !isDrag
4493
4680
  );
4494
- if (isDrag) this._handleDragEvent(e, type, path);
4495
- if (path !== null && handlers !== null)
4496
- 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);
4497
4684
  }
4498
4685
  _handleTouchEvent(e) {
4499
4686
  const { type } = e;
@@ -4536,7 +4723,7 @@ var App = class {
4536
4723
  this._touch = null;
4537
4724
  }
4538
4725
  }
4539
- _handleDragEvent(e, type, path) {
4726
+ _handleDragEvent(e, type, path2) {
4540
4727
  if (type === "dragover") {
4541
4728
  const dropTarget = getClosestDropTarget(e.target, this.rootNode, Infinity);
4542
4729
  if (dropTarget !== null) {
@@ -4548,10 +4735,10 @@ var App = class {
4548
4735
  } else if (type === "dragstart") {
4549
4736
  e.target.dataset.dragging = 1;
4550
4737
  const rootValue = this.state.val;
4551
- const txnPath = path.compact().toTransactionPath();
4738
+ const txnPath = path2.compact().toTransactionPath();
4552
4739
  const value = txnPath.lookup(rootValue);
4553
4740
  const dragType = e.target.dataset.dragtype ?? "?";
4554
- const stack = path.toTransactionPath().buildStack(this.makeStack(rootValue));
4741
+ const stack = path2.buildStack(this.makeStack(rootValue));
4555
4742
  this.dragInfo = new DragInfo(stack, value, dragType, e.target);
4556
4743
  } else if (type === "drop") {
4557
4744
  e.preventDefault();
@@ -4626,7 +4813,7 @@ var App = class {
4626
4813
  this.rootNode.removeEventListener(name, this, listenerOpts(name));
4627
4814
  }
4628
4815
  sendAtRoot(name, args) {
4629
- this.transactor.pushSend(new Path([]), name, args);
4816
+ this.transactor.pushSend(new DispatchPath(), name, args);
4630
4817
  }
4631
4818
  registerComponents(comps, opts) {
4632
4819
  const scope = this.compStack.enter();
@@ -4766,7 +4953,20 @@ var WeakMapDomCache = class {
4766
4953
  };
4767
4954
 
4768
4955
  // src/renderer.js
4769
- var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk"];
4956
+ var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk", "rp"];
4957
+ function stampRenderBase(vdom, text) {
4958
+ if (vdom instanceof VNode)
4959
+ return new VNode(
4960
+ vdom.tag,
4961
+ { ...vdom.attrs, "data-rp": text },
4962
+ vdom.childs,
4963
+ vdom.key,
4964
+ vdom.namespace
4965
+ );
4966
+ if (vdom instanceof VFragment)
4967
+ return new VFragment(vdom.childs.map((c) => stampRenderBase(c, text)));
4968
+ return vdom;
4969
+ }
4770
4970
  var Renderer = class {
4771
4971
  constructor(comps) {
4772
4972
  this.comps = comps;
@@ -4803,17 +5003,17 @@ var Renderer = class {
4803
5003
  if (comp === null) return null;
4804
5004
  return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
4805
5005
  }
4806
- renderIt(stack, node, key, viewName) {
5006
+ renderIt(stack, node, key, viewName, base = null) {
4807
5007
  const comp = this.comps.getCompFor(stack.it);
4808
- return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
5008
+ return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName, base) : null;
4809
5009
  }
4810
5010
  // `node` is the parse node of the render site (`<x render>` / `render-it` /
4811
5011
  // `render-each`, or the view's root anode for the app root). It keys the
4812
5012
  // cache as a globally-unique object: node ids alone are unique only within a
4813
5013
  // single view, so the same value rendered by two components (e.g. through a
4814
5014
  // shared dynamic-var sequence) would otherwise collide in the cache.
4815
- _rValComp(stack, val, comp, node, key, viewName) {
4816
- const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}`;
5015
+ _rValComp(stack, val, comp, node, key, viewName, base = null) {
5016
+ const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}${stack.renderPath.addressKey}`;
4817
5017
  const cachePath = [node, val];
4818
5018
  stack._pushDynBindValuesToArray(cachePath, comp);
4819
5019
  const cachedNode = this.cache.get(cachePath, cacheKey);
@@ -4821,30 +5021,43 @@ var Renderer = class {
4821
5021
  const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
4822
5022
  const body = this.renderView(view, stack);
4823
5023
  if (body == null) return null;
5024
+ const baseJson = base === null ? null : pathToJson(base);
4824
5025
  const meta = this._renderMetadata({
4825
5026
  $: "Comp",
4826
5027
  nid: node?.nodeId ?? null,
4827
5028
  cid: comp.id,
4828
- vid: view.name
5029
+ vid: view.name,
5030
+ ...baseJson === null ? null : { base: baseJson }
4829
5031
  });
4830
- const dom = new VFragment([meta, body]);
5032
+ const dom = new VFragment([
5033
+ meta,
5034
+ baseJson === null ? body : stampRenderBase(body, JSON.stringify(baseJson))
5035
+ ]);
4831
5036
  this.cache.set(cachePath, cacheKey, dom);
4832
5037
  return dom;
4833
5038
  }
4834
5039
  pushEachEntry(r, nid, attrName, key, dom) {
4835
5040
  r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
4836
5041
  }
4837
- renderEachWhen(stack, iterInfo, view, nid) {
5042
+ renderEachWhen(stack, each2) {
5043
+ const { iterInfo, node: view, nodeId: nid } = each2;
4838
5044
  const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
5045
+ const seqPath = iterInfo.val instanceof DynVal ? stack.lookupDynamicLocated(iterInfo.val.name)?.path ?? null : null;
4839
5046
  const r = [];
4840
5047
  const it = stack.it;
4841
5048
  const renderOne = (key, value, attrName, binds) => {
5049
+ const itemBase = seqPath === null ? null : keyedPath(seqPath, key);
5050
+ const itemStep = itemBase === null ? each2.itemStep(key) : null;
5051
+ const itemPath = itemBase !== null ? stack.renderPath.pushFrame(itemBase) : itemStep !== null ? stack.renderPath.pushItem(itemStep) : stack.renderPath;
4842
5052
  const cachePath = enricher ? [view, it, value] : [view, value];
4843
- const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
5053
+ const cacheKey = `${stack.viewsId ?? ""}${nid}${key}${itemPath.addressKey}`;
4844
5054
  const cachedNode = this.cache.get(cachePath, cacheKey);
4845
5055
  if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
4846
5056
  else {
4847
- const dom = this.renderView(view, stack.enter(value, binds, false));
5057
+ const dom = this.renderView(
5058
+ view,
5059
+ stack.enter(value, binds, false, itemPath, itemBase !== null)
5060
+ );
4848
5061
  if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
4849
5062
  this.cache.set(cachePath, cacheKey, dom);
4850
5063
  }
@@ -4940,6 +5153,7 @@ export {
4940
5153
  html,
4941
5154
  injectCss,
4942
5155
  macro,
5156
+ path,
4943
5157
  phaseOps,
4944
5158
  resolveArgs,
4945
5159
  rootDispatcher,