tutuca 0.12.0 → 0.13.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.
@@ -79,9 +79,6 @@ var Step = class {
79
79
  lookup(_v, dval = null) {
80
80
  return dval;
81
81
  }
82
- setValue(root, _v) {
83
- return root;
84
- }
85
82
  setDraftValue(_root, _v) {
86
83
  }
87
84
  enterFrame(stack, _prev, next) {
@@ -110,9 +107,6 @@ var BindStep = class _BindStep extends Step {
110
107
  lookup(v, _dval) {
111
108
  return v;
112
109
  }
113
- setValue(_root, v) {
114
- return v;
115
- }
116
110
  enterFrame(stack, _prev, next) {
117
111
  return stack.enter(next, { ...this.binds }, false);
118
112
  }
@@ -150,9 +144,6 @@ var FieldStep = class extends Step {
150
144
  lookup(v, dval = null) {
151
145
  return readKey(v, this.field, dval);
152
146
  }
153
- setValue(root, v) {
154
- return (0, immer_exports.produce)(root, (draft) => this.setDraftValue(draft, v));
155
- }
156
147
  setDraftValue(root, v) {
157
148
  writeKey(root, this.field, v);
158
149
  }
@@ -175,9 +166,6 @@ var SeqStep = class extends Step {
175
166
  lookup(v, dval = null) {
176
167
  return readSeqKey(readKey(v, this.field, null), this.key, dval);
177
168
  }
178
- setValue(root, v) {
179
- return (0, immer_exports.produce)(root, (draft) => this.setDraftValue(draft, v));
180
- }
181
169
  setDraftValue(root, v) {
182
170
  const seq = readKey(root, this.field, null);
183
171
  if (seq != null) writeSeqKey(seq, this.key, v);
@@ -200,9 +188,6 @@ var SeqAccessStep = class extends Step {
200
188
  const key = readKey(v, this.keyField, NONE);
201
189
  return key !== NONE && seq !== NONE ? readSeqKey(seq, key, dval) : dval;
202
190
  }
203
- setValue(root, v) {
204
- return (0, immer_exports.produce)(root, (draft) => this.setDraftValue(draft, v));
205
- }
206
191
  setDraftValue(root, v) {
207
192
  const seq = readKey(root, this.seqField, NONE);
208
193
  const key = readKey(root, this.keyField, NONE);
@@ -230,9 +215,6 @@ var EachBindStep = class extends Step {
230
215
  lookup(v, _dval) {
231
216
  return v;
232
217
  }
233
- setValue(_root, v) {
234
- return v;
235
- }
236
218
  // Replay the renderer's per-item binds (key, value + any @enrich-with binds)
237
219
  // so a rebuilt stack matches the one @each rendered with.
238
220
  enterFrame(stack, _prev, next) {
@@ -268,10 +250,6 @@ var DynStep = class extends Step {
268
250
  warnRawDynStep("lookup", this);
269
251
  return dval;
270
252
  }
271
- setValue(root, _v) {
272
- warnRawDynStep("setValue", this);
273
- return root;
274
- }
275
253
  enterFrame(stack, _prev, _next) {
276
254
  warnRawDynStep("enterFrame", this);
277
255
  return stack;
@@ -342,7 +320,7 @@ var Path = class _Path {
342
320
  }
343
321
  // Resolve every field-keyed step (e.g. `SeqAccessStep`) against `root`, freezing the
344
322
  // key as it is *now* so a later lookup/setValue lands on the same item even if the
345
- // keyField changed meanwhile (e.g. the selected tab moved while a request was in
323
+ // keyField changed meanwhile (e.g. the selected tab moved while an intent was in
346
324
  // flight). Returns a new Path with those steps replaced; `this` if nothing pinned.
347
325
  // Must be called on a transaction path (no DynSteps — call toTransactionPath first).
348
326
  pinKeys(root) {
@@ -584,7 +562,6 @@ var K_FIELD = 4;
584
562
  var K_BIND = 8;
585
563
  var K_DYN = 16;
586
564
  var K_NAME = 32;
587
- var K_TYPE = 64;
588
565
  var K_SEQ = 256;
589
566
  var K_METHOD = 1024;
590
567
  var K_EVENT = 2048;
@@ -594,7 +571,7 @@ var G_COMPONENT = K_FIELD | K_SEQ | K_DYN;
594
571
  var G_SEQUENCE = K_FIELD | K_DYN;
595
572
  var G_PROVIDE = K_FIELD | K_SEQ;
596
573
  var G_FIELD = K_FIELD | K_METHOD | K_CONST | K_SEQ;
597
- var G_VALUE = K_FIELD | K_METHOD | K_BIND | K_DYN | K_NAME | K_TYPE | K_CONST;
574
+ var G_VALUE = K_FIELD | K_METHOD | K_BIND | K_DYN | K_NAME | K_CONST;
598
575
  var G_HANDLER_ARG = G_VALUE & ~K_NAME | K_EVENT;
599
576
  var G_ALL = G_VALUE | K_STRTPL | K_SEQ;
600
577
  function sizeOf(v) {
@@ -664,7 +641,6 @@ function parseToken(s, px) {
664
641
  if (m !== null) return new EventMemberVal(s.split(".").slice(1));
665
642
  }
666
643
  if (c0 >= 97 && c0 <= 122) return mkVal(s, NameVal);
667
- if (c0 >= 65 && c0 <= 90) return mkVal(s, TypeVal);
668
644
  return null;
669
645
  }
670
646
  function _parseSeqAccess(s, px) {
@@ -779,7 +755,6 @@ function kindOf(val) {
779
755
  if (val instanceof MethodVal) return K_METHOD;
780
756
  if (val instanceof BindVal) return K_BIND;
781
757
  if (val instanceof DynVal) return K_DYN;
782
- if (val instanceof TypeVal) return K_TYPE;
783
758
  if (val instanceof NameVal) return K_NAME;
784
759
  if (val instanceof EventMemberVal) return K_EVENT;
785
760
  return 0;
@@ -893,9 +868,6 @@ var NameVal = class extends VarVal {
893
868
  super();
894
869
  this.name = name;
895
870
  }
896
- eval(stack) {
897
- return stack.lookupName(this.name);
898
- }
899
871
  toString() {
900
872
  return this.name;
901
873
  }
@@ -916,11 +888,6 @@ var mk404Handler = (stack, type, name) => function(...args) {
916
888
  else console.warn("handler not found", { type, name });
917
889
  return this;
918
890
  };
919
- var TypeVal = class extends NameVal {
920
- eval(stack) {
921
- return stack.lookupType(this.name);
922
- }
923
- };
924
891
  var keyIs = (k) => (e) => e.key === k;
925
892
  var macCtrl = (e) => isMac && e.metaKey || e.ctrlKey;
926
893
  var nullSafe = (fn) => (e, stack) => {
@@ -1053,9 +1020,6 @@ var Attributes = class {
1053
1020
  constructor(items) {
1054
1021
  this.items = items;
1055
1022
  }
1056
- eval(_stack) {
1057
- return {};
1058
- }
1059
1023
  static parse(attributes, px, parseAll = false) {
1060
1024
  return getAttrParser(px).parse(attributes, parseAll);
1061
1025
  }
@@ -1065,11 +1029,26 @@ var Attributes = class {
1065
1029
  };
1066
1030
  var booleanAttrsRaw = "itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly,async,autofocus,autoplay,controls,default,defer,disabled,hidden,inert,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected";
1067
1031
  var booleanAttrs = new Set(booleanAttrsRaw.split(","));
1032
+ function parseDirectiveValue(px, directiveName, source, parser) {
1033
+ const val = parser(source, px);
1034
+ if (val === null) {
1035
+ px.onParseIssue("bad-value", {
1036
+ role: "directive",
1037
+ directive: directiveName,
1038
+ value: source
1039
+ });
1040
+ }
1041
+ return val;
1042
+ }
1043
+ function parseIterationDirectives(attributes, px) {
1044
+ const parseNamed = (name) => {
1045
+ const attr = attributes.getNamedItem(`@${name}`);
1046
+ return attr ? parseDirectiveValue(px, name, attr.value, parseAlterHandler) : null;
1047
+ };
1048
+ return { whenVal: parseNamed("when"), loopWithVal: parseNamed("loop-with") };
1049
+ }
1068
1050
  var AttrParser = class {
1069
1051
  constructor(px) {
1070
- this.clear(px);
1071
- }
1072
- clear(px) {
1073
1052
  this.px = px;
1074
1053
  this.attrs = null;
1075
1054
  this.hasDynamic = false;
@@ -1124,12 +1103,7 @@ var AttrParser = class {
1124
1103
  }
1125
1104
  }
1126
1105
  _parseDirectiveValue(directiveName, s, parserFn) {
1127
- const val = parserFn(s, this.px);
1128
- if (val === null) {
1129
- const info = { role: "directive", directive: directiveName, value: s };
1130
- this.px.onParseIssue("bad-value", info);
1131
- }
1132
- return val;
1106
+ return parseDirectiveValue(this.px, directiveName, s, parserFn);
1133
1107
  }
1134
1108
  parseDirective(s, directiveName) {
1135
1109
  switch (directiveName) {
@@ -1293,11 +1267,8 @@ var IfAttr = class extends BaseAttr {
1293
1267
  return this.condVal.eval(stack) ? this.thenVal.eval(stack) : this.elseVal.eval(stack);
1294
1268
  }
1295
1269
  };
1296
- var _attrParser = null;
1297
1270
  function getAttrParser(px) {
1298
- _attrParser ??= new AttrParser(px);
1299
- _attrParser.clear(px);
1300
- return _attrParser;
1271
+ return new AttrParser(px);
1301
1272
  }
1302
1273
  var EventHandler = class _EventHandler {
1303
1274
  constructor(handlerVal, args = []) {
@@ -1321,70 +1292,77 @@ var IntentHandler = class {
1321
1292
  }
1322
1293
  };
1323
1294
 
1324
- // src/cache.js
1325
- var isWeakKey = (k) => k !== null && (typeof k === "object" || typeof k === "function");
1326
- var NullDomCache = class {
1327
- get(_keys, _cacheKey) {
1328
- }
1329
- set(_keys, _cacheKey, _v) {
1330
- }
1331
- evict() {
1332
- return { hit: 0, miss: 0, badKey: 0 };
1333
- }
1295
+ // src/iteration.js
1296
+ var SEQ_INFO = /* @__PURE__ */ Symbol.for("tutuca.seqInfo");
1297
+ var normalizeRange = (start, end, size) => {
1298
+ let s = start == null ? 0 : start < 0 ? size + start : start;
1299
+ let e = end == null ? size : end < 0 ? size + end : end;
1300
+ s = s < 0 ? 0 : s > size ? size : s;
1301
+ e = e < 0 ? 0 : e > size ? size : e;
1302
+ return [s, e < s ? s : e];
1334
1303
  };
1335
- var WeakMapDomCache = class {
1336
- constructor() {
1337
- this.hit = this.miss = this.badKey = 0;
1338
- this.keysByLen = /* @__PURE__ */ new Map();
1339
- }
1340
- _returnValue(r) {
1341
- if (r === void 0) this.miss += 1;
1342
- else this.hit += 1;
1343
- return r;
1344
- }
1345
- get(keys, cacheKey) {
1346
- const len = keys.length;
1347
- let cur = this.keysByLen.get(len);
1348
- if (!cur) return this._returnValue(void 0);
1349
- for (let i = 0; i < len - 1; i++) {
1350
- cur = cur.get(keys[i]);
1351
- if (!cur) return this._returnValue(void 0);
1352
- }
1353
- return this._returnValue(cur.get(keys[len - 1])?.[cacheKey]);
1354
- }
1355
- set(keys, cacheKey, v) {
1356
- const len = keys.length;
1357
- let cur = this.keysByLen.get(len);
1358
- if (!cur) {
1359
- cur = /* @__PURE__ */ new WeakMap();
1360
- this.keysByLen.set(len, cur);
1361
- }
1362
- for (let i = 0; i < len - 1; i++) {
1363
- const key = keys[i];
1364
- let next = cur.get(key);
1365
- if (!next) {
1366
- if (!isWeakKey(key)) {
1367
- this.badKey += 1;
1368
- return;
1369
- }
1370
- next = /* @__PURE__ */ new WeakMap();
1371
- cur.set(key, next);
1372
- }
1373
- cur = next;
1374
- }
1375
- const lastKey = keys[len - 1];
1376
- const leaf = cur.get(lastKey);
1377
- if (leaf) leaf[cacheKey] = v;
1378
- else if (isWeakKey(lastKey)) cur.set(lastKey, { [cacheKey]: v });
1379
- else this.badKey += 1;
1380
- }
1381
- evict() {
1382
- const { hit, miss, badKey } = this;
1383
- this.hit = this.miss = this.badKey = 0;
1384
- this.keysByLen = /* @__PURE__ */ new Map();
1385
- return { hit, miss, badKey };
1304
+ var nativeIndexedIter = (seq, visit, start, end) => {
1305
+ const [s, e] = normalizeRange(start, end, seqSize(seq));
1306
+ for (let i = s; i < e; i++) visit(i, seq[i], "si");
1307
+ };
1308
+ var nativeKeyedIter = (seq, visit, start, end) => {
1309
+ const [s, e] = normalizeRange(start, end, seqSize(seq));
1310
+ let i = 0;
1311
+ for (const [key, value] of seqEntries(seq)) {
1312
+ if (i >= e) break;
1313
+ if (i >= s) visit(key, value, "sk");
1314
+ i++;
1386
1315
  }
1387
1316
  };
1317
+ var unknownIter = () => {
1318
+ };
1319
+ var getSeqInfo = (seq) => isIndexedSeq(seq) ? nativeIndexedIter : isKeyedSeq(seq) || isSetSeq(seq) ? nativeKeyedIter : seq?.[SEQ_INFO] ?? unknownIter;
1320
+ var filterAlwaysTrue = (_key, _value, _iterData) => true;
1321
+ var nullLoopWith = (seq) => ({ iterData: { seq } });
1322
+ var unpackLoopResult = (result, seq) => {
1323
+ const value = result ?? {};
1324
+ return {
1325
+ iterData: value.iterData ?? { seq },
1326
+ start: value.start,
1327
+ end: value.end,
1328
+ keys: value.keys
1329
+ };
1330
+ };
1331
+ var makeLoopCtx = (stack, filter) => ({
1332
+ lookup: (name) => stack.lookupBind(name),
1333
+ filter: (key, value, iterData) => filter.call(stack.it, key, value, iterData)
1334
+ });
1335
+ var callEnricher = (enricher, it, binds, key, value, iterData) => {
1336
+ enricher.call(it, binds, key, value, iterData);
1337
+ console.assert(
1338
+ binds.key === key && binds.value === value,
1339
+ "@enrich-with handlers must not overwrite binds.key or binds.value"
1340
+ );
1341
+ binds.key = key;
1342
+ binds.value = value;
1343
+ };
1344
+ var visitKeys = (seq, keys, visit) => {
1345
+ const attrName = isIndexedSeq(seq) ? "si" : "sk";
1346
+ for (const key of keys) visit(key, seqGet(seq, key), attrName);
1347
+ };
1348
+ function walkLoopBindings({ seq, it, filter, loopWith, enricher, ctx }, visit) {
1349
+ const { iterData, start, end, keys } = unpackLoopResult(loopWith.call(it, seq, ctx), seq);
1350
+ const visitOne = (key, value, attrName) => {
1351
+ const binds = { key, value };
1352
+ if (enricher) callEnricher(enricher, it, binds, key, value, iterData);
1353
+ visit(key, value, attrName, binds);
1354
+ };
1355
+ if (keys) visitKeys(seq, keys, visitOne);
1356
+ else
1357
+ getSeqInfo(seq)(
1358
+ seq,
1359
+ (key, value, attrName) => {
1360
+ if (filter.call(it, key, value, iterData)) visitOne(key, value, attrName);
1361
+ },
1362
+ start,
1363
+ end
1364
+ );
1365
+ }
1388
1366
 
1389
1367
  // src/vdom.js
1390
1368
  var HTML_NS = "http://www.w3.org/1999/xhtml";
@@ -1729,189 +1707,11 @@ function h(tagName, properties, children, namespace) {
1729
1707
  return new VNode(tag, props, normalizedChildren, key, namespace);
1730
1708
  }
1731
1709
 
1732
- // src/renderer.js
1733
- var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk"];
1734
- var Renderer = class {
1735
- constructor(comps) {
1736
- this.comps = comps;
1737
- this.cache = new WeakMapDomCache();
1738
- this.renderTag = h;
1739
- }
1740
- renderFragment(childs) {
1741
- return new VFragment(childs);
1742
- }
1743
- renderComment(text) {
1744
- return new VComment(text);
1745
- }
1746
- setNullCache() {
1747
- this.cache = new NullDomCache();
1748
- }
1749
- renderToDOM(stack, val) {
1750
- const rootNode = document.createElement("div");
1751
- const rOpts = { document };
1752
- render(h("DIV", null, [this.renderRoot(stack, val)]), rootNode, rOpts);
1753
- return rootNode.childNodes[0];
1754
- }
1755
- renderToString(stack, val, cleanAttrs = true) {
1756
- const dom = this.renderToDOM(stack, val);
1757
- if (cleanAttrs) {
1758
- const nodes = dom.querySelectorAll("[data-nid],[data-cid],[data-eid]");
1759
- for (const { dataset } of nodes) for (const name of DATASET_ATTRS) delete dataset[name];
1760
- }
1761
- return dom.innerHTML;
1762
- }
1763
- renderRoot(stack, val, viewName = null) {
1764
- const comp = this.comps.getCompFor(val);
1765
- if (comp === null) return null;
1766
- return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
1767
- }
1768
- renderIt(stack, node, key, viewName) {
1769
- const comp = this.comps.getCompFor(stack.it);
1770
- return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
1771
- }
1772
- // `node` is the parse node of the render site (`<x render>` / `render-it` /
1773
- // `render-each`, or the view's root anode for the app root). It keys the
1774
- // cache as a globally-unique object: node ids alone are unique only within a
1775
- // single view, so the same value rendered by two components (e.g. through a
1776
- // shared dynamic-var sequence) would otherwise collide in the cache.
1777
- _rValComp(stack, val, comp, node, key, viewName) {
1778
- const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}`;
1779
- const cachePath = [node, val];
1780
- stack._pushDynBindValuesToArray(cachePath, comp);
1781
- const cachedNode = this.cache.get(cachePath, cacheKey);
1782
- if (cachedNode) return cachedNode;
1783
- const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
1784
- const body = this.renderView(view, stack);
1785
- if (body == null) return null;
1786
- const meta = this._renderMetadata({
1787
- $: "Comp",
1788
- nid: node?.nodeId ?? null,
1789
- cid: comp.id,
1790
- vid: view.name
1791
- });
1792
- const dom = new VFragment([meta, body]);
1793
- this.cache.set(cachePath, cacheKey, dom);
1794
- return dom;
1795
- }
1796
- pushEachEntry(r, nid, attrName, key, dom) {
1797
- r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
1798
- }
1799
- renderEachWhen(stack, iterInfo, view, nid) {
1800
- const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
1801
- const r = [];
1802
- const it = stack.it;
1803
- const { iterData, start, end, keys } = unpackLoopResult(
1804
- loopWith.call(it, seq, makeLoopCtx(stack, filter)),
1805
- seq
1806
- );
1807
- const renderOne = (key, value, attrName) => {
1808
- const cachePath = enricher ? [view, it, value] : [view, value];
1809
- const binds = { key, value };
1810
- const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
1811
- if (enricher) callEnricher(enricher, it, binds, key, value, iterData);
1812
- const cachedNode = this.cache.get(cachePath, cacheKey);
1813
- if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
1814
- else {
1815
- const dom = this.renderView(view, stack.enter(value, binds, false));
1816
- if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
1817
- this.cache.set(cachePath, cacheKey, dom);
1818
- }
1819
- };
1820
- if (keys) keysIter(seq, renderOne, keys);
1821
- else
1822
- getSeqInfo(seq)(
1823
- seq,
1824
- (key, value, attrName) => {
1825
- if (filter.call(it, key, value, iterData)) renderOne(key, value, attrName);
1826
- },
1827
- start,
1828
- end
1829
- );
1830
- return r;
1831
- }
1832
- renderView(view, stack) {
1833
- let n = stack.binds[1];
1834
- while (n !== null) {
1835
- const b = n[0];
1836
- if (b.isFrame) {
1837
- if (stack.it !== b.it) break;
1838
- console.error("recursion detected", stack.it, b.it);
1839
- return new VComment("RECURSION AVOIDED");
1840
- }
1841
- n = n[1];
1842
- }
1843
- return view.render(stack, this);
1844
- }
1845
- _renderMetadata(info) {
1846
- return new VComment(`§${JSON.stringify(info)}§`);
1847
- }
1848
- // Prefix a loop-less @enrich-with subtree with a boundary meta so event-path
1849
- // reconstruction replays its binds (mirrors the §Each§ / §Comp§ metas).
1850
- renderScopeMeta(nid, dom) {
1851
- return new VFragment([this._renderMetadata({ $: "Scope", nid }), dom]);
1852
- }
1853
- };
1854
- var getSeqInfo = (seq) => isIndexedSeq(seq) ? nativeIndexedIter : isKeyedSeq(seq) || isSetSeq(seq) ? nativeKeyedIter : seq?.[SEQ_INFO] ?? unkIter;
1855
- var normalizeRange = (start, end, size) => {
1856
- let s = start == null ? 0 : start < 0 ? size + start : start;
1857
- let e = end == null ? size : end < 0 ? size + end : end;
1858
- s = s < 0 ? 0 : s > size ? size : s;
1859
- e = e < 0 ? 0 : e > size ? size : e;
1860
- return [s, e < s ? s : e];
1861
- };
1862
- var callEnricher = (enricher, it, binds, key, value, iterData) => {
1863
- enricher.call(it, binds, key, value, iterData);
1864
- console.assert(
1865
- binds.key === key && binds.value === value,
1866
- "@enrich-with handlers must not overwrite binds.key or binds.value"
1867
- );
1868
- binds.key = key;
1869
- binds.value = value;
1870
- };
1871
- var filterAlwaysTrue = (_v, _k, _seq) => true;
1872
- var nullLoopWith = (seq) => ({ iterData: { seq } });
1873
- var unpackLoopResult = (result, seq) => {
1874
- const r = result ?? {};
1875
- return { iterData: r.iterData ?? { seq }, start: r.start, end: r.end, keys: r.keys };
1876
- };
1877
- var keysIter = (seq, visit, keys) => {
1878
- const attrName = isIndexedSeq(seq) ? "si" : "sk";
1879
- for (const key of keys) visit(key, seqGet(seq, key), attrName);
1880
- };
1881
- var makeLoopCtx = (stack, filter) => ({
1882
- lookup: (name) => stack.lookupBind(name),
1883
- filter: (key, value, iterData) => filter.call(stack.it, key, value, iterData)
1884
- });
1885
- var nativeIndexedIter = (seq, visit, start, end) => {
1886
- const [s, e] = normalizeRange(start, end, seqSize(seq));
1887
- for (let i = s; i < e; i++) visit(i, seq[i], "si");
1888
- };
1889
- var nativeKeyedIter = (seq, visit, start, end) => {
1890
- const [s, e] = normalizeRange(start, end, seqSize(seq));
1891
- let i = 0;
1892
- for (const [k, v] of seqEntries(seq)) {
1893
- if (i >= e) break;
1894
- if (i >= s) visit(k, v, "sk");
1895
- i++;
1896
- }
1897
- };
1898
- var unkIter = () => {
1899
- };
1900
- var SEQ_INFO = /* @__PURE__ */ Symbol.for("tutuca.seqInfo");
1901
-
1902
1710
  // src/anode.js
1903
1711
  function resolveDynProducer(comp, name) {
1904
- let producerComp, producerProvide;
1905
- const lk = comp?.lookup?.[name];
1906
- if (lk != null) {
1907
- producerComp = comp.scope?.lookupComponent(lk.compName);
1908
- producerProvide = producerComp?.provide?.[lk.provideName];
1909
- } else {
1910
- const p = comp?.provide?.[name];
1911
- if (p == null) return null;
1912
- producerComp = comp;
1913
- producerProvide = p;
1914
- }
1712
+ const own = comp?.provide?.[name];
1713
+ const producerComp = own != null ? comp : comp?.scope?.lookupProvider(name) ?? null;
1714
+ const producerProvide = own ?? producerComp?.provide?.[name];
1915
1715
  if (producerComp == null || producerProvide == null) return null;
1916
1716
  const pi = producerProvide.val?.toPathItem?.() ?? null;
1917
1717
  return { producerCompId: producerComp.id, producerSteps: pi ? [pi] : [] };
@@ -2272,16 +2072,10 @@ function parseRenderEach(px, value, as, attrs) {
2272
2072
  const seqVal = parseXOpVal("render-each", value, px, parseSequence);
2273
2073
  if (seqVal === null) return null;
2274
2074
  const renderIt = px.addNode(RenderItNode, as);
2275
- const attrParser = getAttrParser(px);
2276
- const eachAttr = attrParser.pushWrapper("each", value, seqVal);
2277
- attrParser.eachAttr = eachAttr;
2278
- const when = attrs.getNamedItem("@when");
2279
- if (when) attrParser._parseWhen(when.value);
2280
- const lWith = attrs.getNamedItem("@loop-with");
2281
- if (lWith) attrParser._parseLoopWith(lWith.value);
2075
+ const { whenVal, loopWithVal } = parseIterationDirectives(attrs, px);
2282
2076
  const each = px.addNodeIf(EachNode, seqVal);
2283
- each.iterInfo.whenVal = eachAttr.whenVal ?? null;
2284
- each.iterInfo.loopWithVal = eachAttr.loopWithVal ?? null;
2077
+ each.iterInfo.whenVal = whenVal;
2078
+ each.iterInfo.loopWithVal = loopWithVal;
2285
2079
  each.fromRenderEach = true;
2286
2080
  each.wrapNode(renderIt);
2287
2081
  return each;
@@ -2670,7 +2464,7 @@ var Components = class {
2670
2464
  }
2671
2465
  compileStyles() {
2672
2466
  const styles = [];
2673
- for (const comp of this.byId.values()) styles.push(comp.compileStyle());
2467
+ for (const Comp of this.byId.values()) styles.push(Comp[COMPONENT].compileStyle());
2674
2468
  return styles.join("\n");
2675
2469
  }
2676
2470
  };
@@ -2729,32 +2523,39 @@ var ComponentStack = class _ComponentStack {
2729
2523
  lookupComponent(name) {
2730
2524
  return this.byName[name] ?? this.parent?.lookupComponent(name) ?? null;
2731
2525
  }
2526
+ // The component in this scope chain that provides `name`, innermost first. A lookup
2527
+ // names a value without naming its producer, so the render-target teleport
2528
+ // (resolveDynProducer) recovers the producer here instead. Sound because
2529
+ // PROVIDE_NAME_COLLISION keeps a provide name to one producer per chain; with two,
2530
+ // this returns the innermost and the linter has already reported the ambiguity.
2531
+ lookupProvider(name) {
2532
+ for (const compName in this.byName) {
2533
+ const Comp = this.byName[compName];
2534
+ if (Comp.provide?.[name] !== void 0) return Comp;
2535
+ }
2536
+ return this.parent?.lookupProvider(name) ?? null;
2537
+ }
2732
2538
  lookupMacro(name) {
2733
2539
  return this.macros[name] ?? this.parent?.lookupMacro(name) ?? null;
2734
2540
  }
2735
2541
  };
2736
2542
  var ProvideInfo = class {
2737
- constructor(name, val, symbol) {
2543
+ constructor(name, val) {
2738
2544
  this.name = name;
2739
2545
  this.val = val;
2740
- this.symbol = symbol;
2741
2546
  }
2742
2547
  };
2743
2548
  var LookupInfo = class {
2744
- constructor(name, compName, provideName, val) {
2549
+ constructor(name, val) {
2745
2550
  this.name = name;
2746
- this.compName = compName;
2747
- this.provideName = provideName;
2748
2551
  this.val = val;
2749
- this._sym = void 0;
2750
- }
2751
- getProducerSymbol(stack) {
2752
- if (this._sym === void 0)
2753
- this._sym = stack.lookupType(this.compName)?.provide?.[this.provideName]?.symbol ?? null;
2754
- return this._sym;
2755
2552
  }
2756
2553
  };
2757
2554
  var isString = (v) => typeof v === "string";
2555
+ var isTypeName = (s) => {
2556
+ const c = s.charCodeAt(0);
2557
+ return c >= 65 && c <= 90;
2558
+ };
2758
2559
  var _rawSpecKeys = "name view style commonStyle globalStyle receive intent alter views provide lookup fields methods statics";
2759
2560
  var KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
2760
2561
  var _compId = 0;
@@ -2775,8 +2576,9 @@ var Component = class {
2775
2576
  this.views[name] = new View(name, view, style);
2776
2577
  }
2777
2578
  this._rawProvide = o.provide ?? {};
2778
- this._rawLookup = o.lookup ?? {};
2579
+ this._rawLookup = o.lookup ?? [];
2779
2580
  this.provide = {};
2581
+ this.provideType = {};
2780
2582
  this.lookup = {};
2781
2583
  this.scope = null;
2782
2584
  this.spec = o;
@@ -2788,25 +2590,23 @@ var Component = class {
2788
2590
  this.views[name].compile(new ParseContext2(), this.scope, this.id);
2789
2591
  const ctx = this.views.main.ctx;
2790
2592
  for (const key in this._rawProvide) {
2593
+ if (isTypeName(key)) {
2594
+ if (this._rawProvide[key] === "self") this.provideType[key] = this.Class;
2595
+ continue;
2596
+ }
2791
2597
  const val = parseProvide(this._rawProvide[key], ctx);
2792
- if (val) this.provide[key] = new ProvideInfo(key, val, Symbol(key));
2598
+ if (val) this.provide[key] = new ProvideInfo(key, val);
2793
2599
  }
2794
- for (const key in this._rawLookup) {
2795
- const linfo = this._rawLookup[key];
2796
- const forStr = isString(linfo) ? linfo : isString(linfo?.for) ? linfo.for : null;
2797
- const [compName, provideName] = forStr === null ? [] : forStr.split(".");
2798
- if (!isString(compName) || !isString(provideName)) continue;
2799
- const defStr = isString(linfo?.default) ? linfo.default : null;
2800
- const val = defStr === null ? null : parseField(defStr, ctx);
2801
- this.lookup[key] = new LookupInfo(key, compName, provideName, val);
2600
+ for (const entry of this._rawLookup) {
2601
+ const name = isString(entry) ? entry : isString(entry?.name) ? entry.name : null;
2602
+ if (name === null) continue;
2603
+ const defStr = isString(entry?.default) ? entry.default : null;
2604
+ this.lookup[name] = new LookupInfo(name, defStr === null ? null : parseField(defStr, ctx));
2802
2605
  }
2803
2606
  for (const key in this.lookup)
2804
2607
  if (this.provide[key] !== void 0)
2805
2608
  console.warn("name declared in both provide and lookup", this.name, key);
2806
2609
  }
2807
- make(args, opts) {
2808
- return this.Class.make(args, opts ?? { scope: this.scope });
2809
- }
2810
2610
  getView(name) {
2811
2611
  return this.views[name] ?? this.views.main;
2812
2612
  }
@@ -2831,6 +2631,22 @@ var Component = class {
2831
2631
  // src/stack.js
2832
2632
  var STOP = /* @__PURE__ */ Symbol("STOP");
2833
2633
  var NEXT = /* @__PURE__ */ Symbol("NEXT");
2634
+ var DEFAULT_LOOKUP_ROUTE = ["dyn", "lex"];
2635
+ function routeLookup(route, lex, dyn) {
2636
+ for (let i = 0; i < route.length; i++) {
2637
+ const leg = route[i];
2638
+ if (leg === "dyn") {
2639
+ const v = dyn();
2640
+ if (v != null) return v;
2641
+ } else if (leg === "lex") {
2642
+ const v = lex();
2643
+ if (v != null) return v;
2644
+ } else {
2645
+ console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
2646
+ }
2647
+ }
2648
+ return null;
2649
+ }
2834
2650
  function lookup(chain, name, dv = null) {
2835
2651
  let n = chain;
2836
2652
  while (n !== null) {
@@ -2881,14 +2697,21 @@ var Stack = class _Stack {
2881
2697
  this.ctx = ctx;
2882
2698
  }
2883
2699
  // Evaluate every provide the entered component publishes and push them as one
2884
- // dynBinds frame (keyed by each provide's symbol). No-op when there are no provides.
2700
+ // dynBinds frame, keyed by NAME. Published types go in the same frame: a type name
2701
+ // starts A-Z and a value name does not, so the two namespaces cannot collide and
2702
+ // nearest-ancestor-wins falls out of frame order for both. No-op with no provides.
2885
2703
  _pushProvides() {
2886
- const provide = this.comps.getCompFor(this.it)?.provide;
2887
- if (provide == null) return this;
2704
+ const comp = this.comps.getCompFor(this.it);
2705
+ if (comp == null) return this;
2706
+ const { provide, provideType } = comp;
2888
2707
  const dynObj = {};
2889
2708
  let has = false;
2890
2709
  for (const k in provide) {
2891
- dynObj[provide[k].symbol] = provide[k].val.eval(this);
2710
+ dynObj[k] = provide[k].val.eval(this);
2711
+ has = true;
2712
+ }
2713
+ for (const k in provideType) {
2714
+ dynObj[k] = provideType[k];
2892
2715
  has = true;
2893
2716
  }
2894
2717
  if (!has) return this;
@@ -2913,30 +2736,36 @@ var Stack = class _Stack {
2913
2736
  const newViews = [name, views];
2914
2737
  return new _Stack(comps, it, binds, dynBinds, newViews, computeViewsId(newViews), ctx);
2915
2738
  }
2739
+ // Published types are stable per scope and would only churn the render cache, so
2740
+ // the cache key covers values alone.
2916
2741
  _pushDynBindValuesToArray(arr, comp) {
2917
- for (const k in comp.provide) arr.push(this._lookupProvide(comp.provide[k]));
2918
- for (const k in comp.lookup) arr.push(this._lookupAlias(comp.lookup[k]));
2919
- }
2920
- _lookupProvide(p) {
2921
- return lookup(this.dynBinds, p.symbol) ?? p.val.eval(this) ?? null;
2922
- }
2923
- _lookupAlias(lk) {
2924
- const sym = lk.getProducerSymbol(this);
2925
- return (sym != null ? lookup(this.dynBinds, sym) : null) ?? lk.val?.eval(this) ?? null;
2742
+ for (const k in comp.provide) arr.push(this.lookupDynamic(k));
2743
+ for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
2926
2744
  }
2745
+ // `*name`: the nearest binding above (including this component's own provides,
2746
+ // pushed on entering it), else this component's declared default, else null.
2927
2747
  lookupDynamic(name) {
2748
+ const v = lookup(this.dynBinds, name);
2749
+ if (v != null) return v;
2928
2750
  const comp = this.comps.getCompFor(this.it);
2929
- if (comp == null) return null;
2930
- const lk = comp.lookup[name];
2931
- if (lk !== void 0) return this._lookupAlias(lk);
2932
- const p = comp.provide[name];
2933
- return p !== void 0 ? this._lookupProvide(p) : null;
2751
+ return comp?.lookup[name]?.val?.eval(this) ?? null;
2934
2752
  }
2935
2753
  lookupBind(name) {
2936
2754
  return lookup(this.binds, name);
2937
2755
  }
2938
- lookupType(name) {
2939
- return this.comps.getCompFor(this.it).scope.lookupComponent(name);
2756
+ // The `lex` leg: what a name means in the registration scope of the component being
2757
+ // rendered. The scope chain holds components, so this leg only ever answers for a
2758
+ // type name — for a value name it is a miss, which is why one default route serves
2759
+ // both. Guarded: a non-component `it` has no scope to ask.
2760
+ _lookupLex(name) {
2761
+ return this.comps.getCompFor(this.it)?.scope.lookupComponent(name) ?? null;
2762
+ }
2763
+ lookupRouted(name, route = DEFAULT_LOOKUP_ROUTE) {
2764
+ return routeLookup(
2765
+ route,
2766
+ () => this._lookupLex(name),
2767
+ () => this.lookupDynamic(name)
2768
+ );
2940
2769
  }
2941
2770
  lookupFieldRaw(name) {
2942
2771
  return this.it[name] ?? null;
@@ -2945,12 +2774,6 @@ var Stack = class _Stack {
2945
2774
  const fn = this.it[name];
2946
2775
  return fn instanceof Function ? fn.call(this.it) : null;
2947
2776
  }
2948
- // Bare implicit event names are gone: a sigil-less word cannot reach a
2949
- // handler-arg slot anymore, so this only ever serves stale NameVals parsed
2950
- // before the removal (e.g. macro-attr pass-throughs). Always null.
2951
- lookupName(_name) {
2952
- return null;
2953
- }
2954
2777
  // The dispatched DOM event / drag info, read only by EventMemberVal's
2955
2778
  // `e.<member>` handler args. Null outside a live event transaction.
2956
2779
  lookupEvent() {
@@ -2976,11 +2799,6 @@ var Stack = class _Stack {
2976
2799
  // src/oo.js
2977
2800
  var BAD_VALUE = /* @__PURE__ */ Symbol("BadValue");
2978
2801
  var nullCoercer = (v) => v;
2979
- var isPlainObject2 = (v) => {
2980
- if (v === null || typeof v !== "object") return false;
2981
- const proto = Object.getPrototypeOf(v);
2982
- return proto === Object.prototype || proto === null;
2983
- };
2984
2802
  function defaultToData(v) {
2985
2803
  if (v instanceof Map) return [...v.entries()];
2986
2804
  if (v instanceof Set) return [...v.values()];
@@ -3049,7 +2867,7 @@ var CHECK_TYPE_FLOAT = check(Number.isFinite, "Float expected");
3049
2867
  var CHECK_TYPE_BOOL = check((v) => typeof v === "boolean", "Boolean expected");
3050
2868
  var CHECK_TYPE_STRING = check((v) => typeof v === "string", "String expected");
3051
2869
  var CHECK_TYPE_LIST = check(Array.isArray, "Array expected");
3052
- var CHECK_TYPE_OBJECT = check(isPlainObject2, "Plain object expected");
2870
+ var CHECK_TYPE_OBJECT = check(isPlainObject, "Plain object expected");
3053
2871
  var CHECK_TYPE_MAP = check((v) => v instanceof Map, "Map expected");
3054
2872
  var CHECK_TYPE_SET = check((v) => v instanceof Set, "Set expected");
3055
2873
  var FieldBool = class extends Field {
@@ -3118,7 +2936,7 @@ var FieldObject = class extends Field {
3118
2936
  "object",
3119
2937
  name,
3120
2938
  CHECK_TYPE_OBJECT,
3121
- (v) => isPlainObject2(v) ? { ...v } : null,
2939
+ (v) => isPlainObject(v) ? { ...v } : null,
3122
2940
  defaultValue
3123
2941
  );
3124
2942
  }
@@ -3131,7 +2949,7 @@ var FieldMap = class extends Field {
3131
2949
  CHECK_TYPE_MAP,
3132
2950
  (v) => {
3133
2951
  if (v instanceof Map) return new Map(v);
3134
- if (Array.isArray(v) || isPlainObject2(v))
2952
+ if (Array.isArray(v) || isPlainObject(v))
3135
2953
  return new Map(Array.isArray(v) ? v : Object.entries(v));
3136
2954
  return null;
3137
2955
  },
@@ -3256,7 +3074,7 @@ function classFromData(name, { fields = {}, methods, statics }) {
3256
3074
  b.addField(field, dval, FieldCls);
3257
3075
  } else if (value?.component && value?.args !== void 0)
3258
3076
  b.addCompField(field, value.component, value.args);
3259
- else if (isPlainObject2(value)) b.addField(field, { ...value }, FieldObject);
3077
+ else if (isPlainObject(value)) b.addField(field, { ...value }, FieldObject);
3260
3078
  else {
3261
3079
  const FieldCls = value?.[FIELD_CLASS] ?? FieldAny;
3262
3080
  b.addField(field, value, FieldCls);
@@ -3280,10 +3098,24 @@ function validateDraftFields(current, draft) {
3280
3098
  }
3281
3099
  }
3282
3100
  }
3283
- var META_KEYS = "name id fields methods views receive intent alter provide lookup spec extra commonStyle globalStyle scope _rawProvide _rawLookup".split(
3101
+ var META_KEYS = "name id fields methods views receive intent alter provide provideType lookup spec extra commonStyle globalStyle scope _rawProvide _rawLookup".split(
3284
3102
  " "
3285
3103
  );
3104
+ var RESERVED_COMPONENT_STATICS = /* @__PURE__ */ new Set([
3105
+ ...META_KEYS,
3106
+ "make",
3107
+ "getMetaClass",
3108
+ ...Object.getOwnPropertyNames(Component.prototype)
3109
+ ]);
3110
+ function assertNoReservedComponentStatics(statics = {}) {
3111
+ for (const name in statics) {
3112
+ if (RESERVED_COMPONENT_STATICS.has(name)) {
3113
+ throw new TypeError(`component static "${name}" is reserved by the framework`);
3114
+ }
3115
+ }
3116
+ }
3286
3117
  Component.fromSpec = (opts) => {
3118
+ assertNoReservedComponentStatics(opts.statics);
3287
3119
  const Class = classFromData(opts.name, opts);
3288
3120
  const comp = new Component(Class, opts);
3289
3121
  const metaClass = Class.getMetaClass();
@@ -3303,7 +3135,7 @@ Component.fromSpec = (opts) => {
3303
3135
  configurable: true
3304
3136
  });
3305
3137
  for (const key of Object.getOwnPropertyNames(Component.prototype))
3306
- if (key !== "constructor" && key !== "make" && !Object.hasOwn(Class, key))
3138
+ if (key !== "constructor" && !Object.hasOwn(Class, key))
3307
3139
  Class[key] = (...args) => comp[key](...args);
3308
3140
  return Class;
3309
3141
  };
@@ -3323,9 +3155,6 @@ var State = class {
3323
3155
  this.val = val;
3324
3156
  for (const sub of this.changeSubs) sub({ val, old, info, timestamp: Date.now() });
3325
3157
  }
3326
- update(fn, info) {
3327
- return this.set(fn(this.val), info);
3328
- }
3329
3158
  };
3330
3159
  var Transactor = class {
3331
3160
  constructor(comps, rootValue) {
@@ -3386,7 +3215,7 @@ var Transactor = class {
3386
3215
  // Build and dispatch the observer record for a settled transaction. The
3387
3216
  // resolved handler (`_resolvedHandler`/`_matched`) and per-leaf before/after
3388
3217
  // (`_before`/`_after`) were captured while the handler ran (see callHandler /
3389
- // updateRootValue). No-op when nobody is observing.
3218
+ // Transaction.run). No-op when nobody is observing.
3390
3219
  _emitTransaction(transaction, root) {
3391
3220
  if (this._observers.length === 0) return;
3392
3221
  if (transaction._resolvedHandler === void 0) return;
@@ -3418,8 +3247,13 @@ var Transactor = class {
3418
3247
  }
3419
3248
  return child;
3420
3249
  }
3421
- pushSend(path, name, args = [], opts = {}, parent = null) {
3422
- const t = new SendEvent(path, this, name, args, parent, opts);
3250
+ // `origin` is where the message came FROM, pinned now for the same reason an
3251
+ // intent's answerPath is: a reply must reach the sender that asked even if a key
3252
+ // moved while the message was in flight. Null when nobody is waiting — a host
3253
+ // sendAtRoot or a view's own `@on.*` — and ctx.sendReply refuses on that.
3254
+ pushSend(path, name, args = [], parent = null, origin = null) {
3255
+ const t = new SendEvent(path, this, name, args, parent);
3256
+ t.origin = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
3423
3257
  this.pushTransaction(t);
3424
3258
  return this._link(t, parent);
3425
3259
  }
@@ -3432,9 +3266,9 @@ var Transactor = class {
3432
3266
  walk.advance();
3433
3267
  return walk;
3434
3268
  }
3435
- // Drain queued transactions and await in-flight requests until quiescent. Each
3436
- // awaited request enqueues a ResponseEvent, which may dispatch more work, so we
3437
- // loop. `maxTurns` backstops a pathological non-terminating cascade.
3269
+ // Drain queued transactions and await async intent handlers until quiescent. A
3270
+ // completed handler may enqueue an answer, which may dispatch more work, so we loop.
3271
+ // `maxTurns` backstops a pathological non-terminating cascade.
3438
3272
  async settle(maxTurns = 1e4) {
3439
3273
  while ((this.hasPendingTransactions || this._inflight.size) && maxTurns-- > 0) {
3440
3274
  while (this.hasPendingTransactions) this.transactNext();
@@ -3491,9 +3325,6 @@ var Transaction = class {
3491
3325
  whenSubtreeSettled() {
3492
3326
  return this.completion.whenSubtreeSettled();
3493
3327
  }
3494
- run(rootValue, comps) {
3495
- return this.updateRootValue(rootValue, comps);
3496
- }
3497
3328
  afterTransaction() {
3498
3329
  }
3499
3330
  // Ending a walk and forwarding belong to the two dispatch buckets; the base warns
@@ -3505,12 +3336,6 @@ var Transaction = class {
3505
3336
  forward(_opts) {
3506
3337
  console.warn('ctx.forward() needs a "receive" or "intent" handler - ignored');
3507
3338
  }
3508
- buildRootStack(root, comps) {
3509
- return Stack.root(comps, root);
3510
- }
3511
- buildStack(root, comps) {
3512
- return this.path.toTransactionPath().buildStack(this.buildRootStack(root, comps));
3513
- }
3514
3339
  // The kind reported to observers (see Transactor.observe); null on the base.
3515
3340
  get observeKind() {
3516
3341
  return null;
@@ -3530,12 +3355,11 @@ var Transaction = class {
3530
3355
  return null;
3531
3356
  }
3532
3357
  // The path used to apply the mutation. Teleports dynamic-var renders so it lands on
3533
- // the data's real location (the dispatch `this.path` keeps intermediates). A subclass
3534
- // may override to supply a pre-resolved path (see ResponseEvent's pinned keys).
3358
+ // the data's real location (the dispatch `this.path` keeps intermediates).
3535
3359
  getTransactionPath() {
3536
3360
  return this.path.toTransactionPath().compact();
3537
3361
  }
3538
- updateRootValue(curRoot, comps) {
3362
+ run(curRoot, comps) {
3539
3363
  const txnPath = this.getTransactionPath();
3540
3364
  const curLeaf = txnPath.lookup(curRoot);
3541
3365
  const newLeaf = (0, immer_exports.produce)(curLeaf, (draft) => {
@@ -3592,8 +3416,8 @@ var InputEvent = class extends Transaction {
3592
3416
  }
3593
3417
  this.transactor.pushIntent(this.dispatchPath, name, args, rest, this);
3594
3418
  }
3595
- buildRootStack(root, comps) {
3596
- return Stack.root(comps, root, this);
3419
+ buildStack(root, comps) {
3420
+ return this.path.toTransactionPath().buildStack(Stack.root(comps, root, this));
3597
3421
  }
3598
3422
  getHandlerAndArgs(root, _instance, comps) {
3599
3423
  const stack = this.buildStack(root, comps);
@@ -3611,11 +3435,10 @@ var InputEvent = class extends Transaction {
3611
3435
  }
3612
3436
  };
3613
3437
  var NameArgsTransaction = class extends Transaction {
3614
- constructor(path, transactor, name, args, parentTransaction, opts = {}) {
3438
+ constructor(path, transactor, name, args, parentTransaction) {
3615
3439
  super(path, transactor, parentTransaction);
3616
3440
  this.name = name;
3617
3441
  this.args = args;
3618
- this.opts = opts;
3619
3442
  this.targetPath = path;
3620
3443
  }
3621
3444
  handlerProp = null;
@@ -3674,7 +3497,7 @@ var SendEvent = class extends NameArgsTransaction {
3674
3497
  var IntentEvent = class extends NameArgsTransaction {
3675
3498
  handlerProp = "intent";
3676
3499
  constructor(path, transactor, walk) {
3677
- super(path, transactor, walk.name, walk.args, walk.parent, {});
3500
+ super(path, transactor, walk.name, walk.args, walk.parent);
3678
3501
  this.walk = walk;
3679
3502
  this.targetPath = walk.origin;
3680
3503
  }
@@ -3774,7 +3597,7 @@ var IntentWalk = class {
3774
3597
  async _runLex(chain, i) {
3775
3598
  if (this.ended) return;
3776
3599
  if (i >= chain.length) return this.advance();
3777
- const ctx = new IntentContext(this.origin, this.transactor, this.parent);
3600
+ const ctx = new Dispatcher(this.origin, this.transactor, this.parent);
3778
3601
  try {
3779
3602
  const res = await chain[i].fn.apply(null, [...this.args, ctx]);
3780
3603
  if (res === PASS) return this._runLex(chain, i + 1);
@@ -3827,7 +3650,7 @@ var IntentWalk = class {
3827
3650
  this.ended = true;
3828
3651
  if (name === null) return this.release?.();
3829
3652
  const path = this.answerPath ?? this.origin;
3830
- const t = new SendEvent(path, this.transactor, name, args, this.parent, {});
3653
+ const t = new SendEvent(path, this.transactor, name, args, this.parent);
3831
3654
  t._isAnswer = true;
3832
3655
  this.transactor.pushTransaction(t);
3833
3656
  if (this.release) t.completion.whenSubtreeSettled().then(this.release);
@@ -3866,7 +3689,7 @@ var Completion = class {
3866
3689
  this.val = val;
3867
3690
  this._selfResolve?.(val);
3868
3691
  }
3869
- // Settle self even when no handler produced a value (skipSelf / undefined / throw paths).
3692
+ // Settle self even when no handler produced a value (undefined-state / throw paths).
3870
3693
  ensureSelfSettled() {
3871
3694
  if (!this.selfSettled) this.markSelfSettled(this.val);
3872
3695
  }
@@ -3912,12 +3735,64 @@ var Dispatcher = class {
3912
3735
  get at() {
3913
3736
  return new PathChanges(this);
3914
3737
  }
3738
+ // The render-time Stack, rebuilt on demand. A handler does not get one: the stack
3739
+ // that evaluated its arguments is a local in getHandlerAndArgs and is gone by the
3740
+ // time the body runs, and send/intent transactions never build one at all. Both
3741
+ // halves it needs are pinned on this ctx, so the `dyn` leg reconstructs it — and
3742
+ // only the `dyn` leg does, which is why this is lazy rather than eager.
3743
+ //
3744
+ // The path is compacted, so per-item binds (`@each`, `@enrich-with`) are not
3745
+ // replayed; provides are pushed on every component frame either way, so a provide
3746
+ // that reads a loop binding is the one case this cannot reproduce faithfully.
3747
+ _stack() {
3748
+ this._stackMemo ??= this.path.toTransactionPath().buildStack(Stack.root(this.transactor.comps, this.root, this.parent));
3749
+ return this._stackMemo;
3750
+ }
3751
+ // Resolve a name the way the renderer would. `opts.route` takes the same legs in
3752
+ // the same spelling as `ctx.intent` — `["dyn"]` the render ancestry, `["lex"]` the
3753
+ // registration scope, default both.
3754
+ lookup(name, opts) {
3755
+ const route = opts?.route ?? DEFAULT_LOOKUP_ROUTE;
3756
+ return routeLookup(
3757
+ route,
3758
+ () => this._lookupLex(name),
3759
+ () => this._stack()?.lookupDynamic(name) ?? null
3760
+ );
3761
+ }
3762
+ // The `lex` leg without a Stack: the scope of the component whose handler is
3763
+ // running, which is the leaf of this ctx's path.
3764
+ _lookupLex(name) {
3765
+ let Comp = null;
3766
+ this.walkPath((c) => {
3767
+ Comp = c;
3768
+ return false;
3769
+ });
3770
+ return Comp?.scope?.lookupComponent(name) ?? null;
3771
+ }
3772
+ // A component lookup is a value lookup constrained to a component. The `lex` leg
3773
+ // can only ever answer with one; the `dyn` leg reads a binding an ancestor
3774
+ // published, so it is the leg that has to be checked.
3775
+ lookupType(name, opts) {
3776
+ const v = this.lookup(name, opts);
3777
+ if (v == null) {
3778
+ this.transactor.refuse("TYPE_NOT_FOUND", {
3779
+ name,
3780
+ route: opts?.route ?? DEFAULT_LOOKUP_ROUTE
3781
+ });
3782
+ return null;
3783
+ }
3784
+ if (v?.[COMPONENT] == null) {
3785
+ this.transactor.refuse("TYPE_NOT_COMPONENT", { name, got: typeof v });
3786
+ return null;
3787
+ }
3788
+ return v;
3789
+ }
3915
3790
  // A message is ADDRESSED: it names one component and stops there.
3916
- send(name, args, opts) {
3917
- return this.sendAtPath(this.path, name, args, opts);
3791
+ send(name, args) {
3792
+ return this.sendAtPath(this.path, name, args);
3918
3793
  }
3919
- sendAtPath(path, name, args, opts) {
3920
- return this.transactor.pushSend(path, name, args, opts, this.parent);
3794
+ sendAtPath(path, name, args) {
3795
+ return this.transactor.pushSend(path, name, args, this.parent, this.path);
3921
3796
  }
3922
3797
  // An intent is ROUTED: it names a job and walks until something answers. The verb
3923
3798
  // does not decide which scope answers — `opts.route` does, written here at the call
@@ -3928,9 +3803,6 @@ var Dispatcher = class {
3928
3803
  intentAtPath(path, name, args, opts) {
3929
3804
  return this.transactor.pushIntent(path, name, args, opts, this.parent);
3930
3805
  }
3931
- lookupTypeFor(name, inst) {
3932
- return this.transactor.comps.getCompFor(inst).scope.lookupComponent(name);
3933
- }
3934
3806
  };
3935
3807
  var EventContext = class extends Dispatcher {
3936
3808
  get name() {
@@ -3947,6 +3819,19 @@ var EventContext = class extends Dispatcher {
3947
3819
  fail(error) {
3948
3820
  return this.parent.walk === void 0 ? warnNotIntent("fail") : this.parent.walk.answer("Error", error);
3949
3821
  }
3822
+ // Reply to the sender of the MESSAGE being handled. An intent answers under a name
3823
+ // the runtime derives (`<name>Ok`), because the raiser asked a question and declared
3824
+ // arms for it; a message did not, so the replier names the reply itself and the
3825
+ // sender handles it like any other message. Refuses when nobody sent this — a view's
3826
+ // own `@on.*`, or a host sendAtRoot.
3827
+ sendReply(name, args) {
3828
+ const origin = this.parent?.origin ?? null;
3829
+ if (origin == null) {
3830
+ this.transactor.refuse("NO_SENDER", { name });
3831
+ return null;
3832
+ }
3833
+ return this.sendAtPath(origin, name, args);
3834
+ }
3950
3835
  // End the walk answering nothing — "served, and no answer".
3951
3836
  stop() {
3952
3837
  return this.parent.stop();
@@ -3958,8 +3843,6 @@ var EventContext = class extends Dispatcher {
3958
3843
  return this.parent.forward(opts);
3959
3844
  }
3960
3845
  };
3961
- var IntentContext = class extends Dispatcher {
3962
- };
3963
3846
  function warnNotIntent(verb) {
3964
3847
  console.warn(`ctx.${verb}() is only meaningful in an "intent" handler - ignored`);
3965
3848
  }
@@ -3968,8 +3851,8 @@ var PathChanges = class extends PathBuilder {
3968
3851
  super();
3969
3852
  this.dispatcher = dispatcher;
3970
3853
  }
3971
- send(name, args, opts) {
3972
- return this.dispatcher.sendAtPath(this.buildPath(), name, args, opts);
3854
+ send(name, args) {
3855
+ return this.dispatcher.sendAtPath(this.buildPath(), name, args);
3973
3856
  }
3974
3857
  intent(name, args, opts) {
3975
3858
  return this.dispatcher.intentAtPath(this.buildPath(), name, args, opts);
@@ -4126,9 +4009,10 @@ var App = class {
4126
4009
  }
4127
4010
  compile() {
4128
4011
  for (const Comp of this.comps.byId.values()) {
4129
- Comp.compile(this.ParseContext);
4130
- for (const key in Comp.views)
4131
- for (const name of Comp.views[key].ctx.genEventNames()) this._eventNames.add(name);
4012
+ const meta = Comp[COMPONENT];
4013
+ meta.compile(this.ParseContext);
4014
+ for (const key in meta.views)
4015
+ for (const name of meta.views[key].ctx.genEventNames()) this._eventNames.add(name);
4132
4016
  }
4133
4017
  this._compiled = true;
4134
4018
  }
@@ -4154,8 +4038,8 @@ var App = class {
4154
4038
  for (const name of this._eventNames)
4155
4039
  this.rootNode.removeEventListener(name, this, listenerOpts(name));
4156
4040
  }
4157
- sendAtRoot(name, args, opts) {
4158
- this.transactor.pushSend(new Path([]), name, args, opts);
4041
+ sendAtRoot(name, args) {
4042
+ this.transactor.pushSend(new Path([]), name, args);
4159
4043
  }
4160
4044
  registerComponents(comps, opts) {
4161
4045
  const scope = this.compStack.enter();
@@ -4309,6 +4193,182 @@ function collectAppClassesInSet(app) {
4309
4193
  return classes;
4310
4194
  }
4311
4195
 
4196
+ // src/cache.js
4197
+ var isWeakKey = (k) => k !== null && (typeof k === "object" || typeof k === "function");
4198
+ var NullDomCache = class {
4199
+ get(_keys, _cacheKey) {
4200
+ }
4201
+ set(_keys, _cacheKey, _v) {
4202
+ }
4203
+ evict() {
4204
+ return { hit: 0, miss: 0, badKey: 0 };
4205
+ }
4206
+ };
4207
+ var WeakMapDomCache = class {
4208
+ constructor() {
4209
+ this.hit = this.miss = this.badKey = 0;
4210
+ this.keysByLen = /* @__PURE__ */ new Map();
4211
+ }
4212
+ _returnValue(r) {
4213
+ if (r === void 0) this.miss += 1;
4214
+ else this.hit += 1;
4215
+ return r;
4216
+ }
4217
+ get(keys, cacheKey) {
4218
+ const len = keys.length;
4219
+ let cur = this.keysByLen.get(len);
4220
+ if (!cur) return this._returnValue(void 0);
4221
+ for (let i = 0; i < len - 1; i++) {
4222
+ cur = cur.get(keys[i]);
4223
+ if (!cur) return this._returnValue(void 0);
4224
+ }
4225
+ return this._returnValue(cur.get(keys[len - 1])?.[cacheKey]);
4226
+ }
4227
+ set(keys, cacheKey, v) {
4228
+ const len = keys.length;
4229
+ let cur = this.keysByLen.get(len);
4230
+ if (!cur) {
4231
+ cur = /* @__PURE__ */ new WeakMap();
4232
+ this.keysByLen.set(len, cur);
4233
+ }
4234
+ for (let i = 0; i < len - 1; i++) {
4235
+ const key = keys[i];
4236
+ let next = cur.get(key);
4237
+ if (!next) {
4238
+ if (!isWeakKey(key)) {
4239
+ this.badKey += 1;
4240
+ return;
4241
+ }
4242
+ next = /* @__PURE__ */ new WeakMap();
4243
+ cur.set(key, next);
4244
+ }
4245
+ cur = next;
4246
+ }
4247
+ const lastKey = keys[len - 1];
4248
+ const leaf = cur.get(lastKey);
4249
+ if (leaf) leaf[cacheKey] = v;
4250
+ else if (isWeakKey(lastKey)) cur.set(lastKey, { [cacheKey]: v });
4251
+ else this.badKey += 1;
4252
+ }
4253
+ evict() {
4254
+ const { hit, miss, badKey } = this;
4255
+ this.hit = this.miss = this.badKey = 0;
4256
+ this.keysByLen = /* @__PURE__ */ new Map();
4257
+ return { hit, miss, badKey };
4258
+ }
4259
+ };
4260
+
4261
+ // src/renderer.js
4262
+ var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk"];
4263
+ var Renderer = class {
4264
+ constructor(comps) {
4265
+ this.comps = comps;
4266
+ this.cache = new WeakMapDomCache();
4267
+ this.renderTag = h;
4268
+ }
4269
+ renderFragment(childs) {
4270
+ return new VFragment(childs);
4271
+ }
4272
+ renderComment(text) {
4273
+ return new VComment(text);
4274
+ }
4275
+ setNullCache() {
4276
+ this.cache = new NullDomCache();
4277
+ }
4278
+ renderToDOM(stack, val) {
4279
+ const rootNode = document.createElement("div");
4280
+ const rOpts = { document };
4281
+ render(h("DIV", null, [this.renderRoot(stack, val)]), rootNode, rOpts);
4282
+ return rootNode.childNodes[0];
4283
+ }
4284
+ renderToString(stack, val, cleanAttrs = true) {
4285
+ const dom = this.renderToDOM(stack, val);
4286
+ if (cleanAttrs) {
4287
+ const nodes = dom.querySelectorAll("[data-nid],[data-cid],[data-eid]");
4288
+ for (const { dataset } of nodes) for (const name of DATASET_ATTRS) delete dataset[name];
4289
+ }
4290
+ return dom.innerHTML;
4291
+ }
4292
+ renderRoot(stack, val, viewName = null) {
4293
+ const comp = this.comps.getCompFor(val);
4294
+ if (comp === null) return null;
4295
+ return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
4296
+ }
4297
+ renderIt(stack, node, key, viewName) {
4298
+ const comp = this.comps.getCompFor(stack.it);
4299
+ return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
4300
+ }
4301
+ // `node` is the parse node of the render site (`<x render>` / `render-it` /
4302
+ // `render-each`, or the view's root anode for the app root). It keys the
4303
+ // cache as a globally-unique object: node ids alone are unique only within a
4304
+ // single view, so the same value rendered by two components (e.g. through a
4305
+ // shared dynamic-var sequence) would otherwise collide in the cache.
4306
+ _rValComp(stack, val, comp, node, key, viewName) {
4307
+ const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}`;
4308
+ const cachePath = [node, val];
4309
+ stack._pushDynBindValuesToArray(cachePath, comp);
4310
+ const cachedNode = this.cache.get(cachePath, cacheKey);
4311
+ if (cachedNode) return cachedNode;
4312
+ const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
4313
+ const body = this.renderView(view, stack);
4314
+ if (body == null) return null;
4315
+ const meta = this._renderMetadata({
4316
+ $: "Comp",
4317
+ nid: node?.nodeId ?? null,
4318
+ cid: comp.id,
4319
+ vid: view.name
4320
+ });
4321
+ const dom = new VFragment([meta, body]);
4322
+ this.cache.set(cachePath, cacheKey, dom);
4323
+ return dom;
4324
+ }
4325
+ pushEachEntry(r, nid, attrName, key, dom) {
4326
+ r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
4327
+ }
4328
+ renderEachWhen(stack, iterInfo, view, nid) {
4329
+ const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
4330
+ const r = [];
4331
+ const it = stack.it;
4332
+ const renderOne = (key, value, attrName, binds) => {
4333
+ const cachePath = enricher ? [view, it, value] : [view, value];
4334
+ const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
4335
+ const cachedNode = this.cache.get(cachePath, cacheKey);
4336
+ if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
4337
+ else {
4338
+ const dom = this.renderView(view, stack.enter(value, binds, false));
4339
+ if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
4340
+ this.cache.set(cachePath, cacheKey, dom);
4341
+ }
4342
+ };
4343
+ walkLoopBindings(
4344
+ { seq, it, filter, loopWith, enricher, ctx: makeLoopCtx(stack, filter) },
4345
+ renderOne
4346
+ );
4347
+ return r;
4348
+ }
4349
+ renderView(view, stack) {
4350
+ let n = stack.binds[1];
4351
+ while (n !== null) {
4352
+ const b = n[0];
4353
+ if (b.isFrame) {
4354
+ if (stack.it !== b.it) break;
4355
+ console.error("recursion detected", stack.it, b.it);
4356
+ return new VComment("RECURSION AVOIDED");
4357
+ }
4358
+ n = n[1];
4359
+ }
4360
+ return view.render(stack, this);
4361
+ }
4362
+ _renderMetadata(info) {
4363
+ return new VComment(`§${JSON.stringify(info)}§`);
4364
+ }
4365
+ // Prefix a loop-less @enrich-with subtree with a boundary meta so event-path
4366
+ // reconstruction replays its binds (mirrors the §Each§ / §Comp§ metas).
4367
+ renderScopeMeta(nid, dom) {
4368
+ return new VFragment([this._renderMetadata({ $: "Scope", nid }), dom]);
4369
+ }
4370
+ };
4371
+
4312
4372
  // src/on.js
4313
4373
  var OP_KINDS = ["send", "intent"];
4314
4374
  function phaseOps(phase) {
@@ -4326,7 +4386,7 @@ function dispatchPhase(dispatcher, targetPath, phase, self) {
4326
4386
  const args = resolveArgs(op.args, self);
4327
4387
  switch (op.type) {
4328
4388
  case "send":
4329
- dispatcher.sendAtPath(targetPath, op.name, args, op.opts);
4389
+ dispatcher.sendAtPath(targetPath, op.name, args);
4330
4390
  break;
4331
4391
  case "intent":
4332
4392
  dispatcher.intentAtPath(targetPath, op.name, args, op.opts);