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.
@@ -782,9 +782,6 @@ var Step = class {
782
782
  lookup(_v, dval = null) {
783
783
  return dval;
784
784
  }
785
- setValue(root, _v) {
786
- return root;
787
- }
788
785
  setDraftValue(_root, _v) {
789
786
  }
790
787
  enterFrame(stack, _prev, next) {
@@ -813,9 +810,6 @@ var BindStep = class _BindStep extends Step {
813
810
  lookup(v, _dval) {
814
811
  return v;
815
812
  }
816
- setValue(_root, v) {
817
- return v;
818
- }
819
813
  enterFrame(stack, _prev, next) {
820
814
  return stack.enter(next, { ...this.binds }, false);
821
815
  }
@@ -853,9 +847,6 @@ var FieldStep = class extends Step {
853
847
  lookup(v, dval = null) {
854
848
  return readKey(v, this.field, dval);
855
849
  }
856
- setValue(root, v) {
857
- return produce(root, (draft) => this.setDraftValue(draft, v));
858
- }
859
850
  setDraftValue(root, v) {
860
851
  writeKey(root, this.field, v);
861
852
  }
@@ -878,9 +869,6 @@ var SeqStep = class extends Step {
878
869
  lookup(v, dval = null) {
879
870
  return readSeqKey(readKey(v, this.field, null), this.key, dval);
880
871
  }
881
- setValue(root, v) {
882
- return produce(root, (draft) => this.setDraftValue(draft, v));
883
- }
884
872
  setDraftValue(root, v) {
885
873
  const seq = readKey(root, this.field, null);
886
874
  if (seq != null) writeSeqKey(seq, this.key, v);
@@ -903,9 +891,6 @@ var SeqAccessStep = class extends Step {
903
891
  const key = readKey(v, this.keyField, NONE);
904
892
  return key !== NONE && seq !== NONE ? readSeqKey(seq, key, dval) : dval;
905
893
  }
906
- setValue(root, v) {
907
- return produce(root, (draft) => this.setDraftValue(draft, v));
908
- }
909
894
  setDraftValue(root, v) {
910
895
  const seq = readKey(root, this.seqField, NONE);
911
896
  const key = readKey(root, this.keyField, NONE);
@@ -933,9 +918,6 @@ var EachBindStep = class extends Step {
933
918
  lookup(v, _dval) {
934
919
  return v;
935
920
  }
936
- setValue(_root, v) {
937
- return v;
938
- }
939
921
  // Replay the renderer's per-item binds (key, value + any @enrich-with binds)
940
922
  // so a rebuilt stack matches the one @each rendered with.
941
923
  enterFrame(stack, _prev, next) {
@@ -971,10 +953,6 @@ var DynStep = class extends Step {
971
953
  warnRawDynStep("lookup", this);
972
954
  return dval;
973
955
  }
974
- setValue(root, _v) {
975
- warnRawDynStep("setValue", this);
976
- return root;
977
- }
978
956
  enterFrame(stack, _prev, _next) {
979
957
  warnRawDynStep("enterFrame", this);
980
958
  return stack;
@@ -1045,7 +1023,7 @@ var Path = class _Path {
1045
1023
  }
1046
1024
  // Resolve every field-keyed step (e.g. `SeqAccessStep`) against `root`, freezing the
1047
1025
  // key as it is *now* so a later lookup/setValue lands on the same item even if the
1048
- // keyField changed meanwhile (e.g. the selected tab moved while a request was in
1026
+ // keyField changed meanwhile (e.g. the selected tab moved while an intent was in
1049
1027
  // flight). Returns a new Path with those steps replaced; `this` if nothing pinned.
1050
1028
  // Must be called on a transaction path (no DynSteps — call toTransactionPath first).
1051
1029
  pinKeys(root) {
@@ -1287,7 +1265,6 @@ var K_FIELD = 4;
1287
1265
  var K_BIND = 8;
1288
1266
  var K_DYN = 16;
1289
1267
  var K_NAME = 32;
1290
- var K_TYPE = 64;
1291
1268
  var K_SEQ = 256;
1292
1269
  var K_METHOD = 1024;
1293
1270
  var K_EVENT = 2048;
@@ -1297,7 +1274,7 @@ var G_COMPONENT = K_FIELD | K_SEQ | K_DYN;
1297
1274
  var G_SEQUENCE = K_FIELD | K_DYN;
1298
1275
  var G_PROVIDE = K_FIELD | K_SEQ;
1299
1276
  var G_FIELD = K_FIELD | K_METHOD | K_CONST | K_SEQ;
1300
- var G_VALUE = K_FIELD | K_METHOD | K_BIND | K_DYN | K_NAME | K_TYPE | K_CONST;
1277
+ var G_VALUE = K_FIELD | K_METHOD | K_BIND | K_DYN | K_NAME | K_CONST;
1301
1278
  var G_HANDLER_ARG = G_VALUE & ~K_NAME | K_EVENT;
1302
1279
  var G_ALL = G_VALUE | K_STRTPL | K_SEQ;
1303
1280
  function sizeOf(v) {
@@ -1367,7 +1344,6 @@ function parseToken(s, px) {
1367
1344
  if (m !== null) return new EventMemberVal(s.split(".").slice(1));
1368
1345
  }
1369
1346
  if (c0 >= 97 && c0 <= 122) return mkVal(s, NameVal);
1370
- if (c0 >= 65 && c0 <= 90) return mkVal(s, TypeVal);
1371
1347
  return null;
1372
1348
  }
1373
1349
  function _parseSeqAccess(s, px) {
@@ -1482,7 +1458,6 @@ function kindOf(val) {
1482
1458
  if (val instanceof MethodVal) return K_METHOD;
1483
1459
  if (val instanceof BindVal) return K_BIND;
1484
1460
  if (val instanceof DynVal) return K_DYN;
1485
- if (val instanceof TypeVal) return K_TYPE;
1486
1461
  if (val instanceof NameVal) return K_NAME;
1487
1462
  if (val instanceof EventMemberVal) return K_EVENT;
1488
1463
  return 0;
@@ -1596,9 +1571,6 @@ var NameVal = class extends VarVal {
1596
1571
  super();
1597
1572
  this.name = name;
1598
1573
  }
1599
- eval(stack) {
1600
- return stack.lookupName(this.name);
1601
- }
1602
1574
  toString() {
1603
1575
  return this.name;
1604
1576
  }
@@ -1619,11 +1591,6 @@ var mk404Handler = (stack, type, name) => function(...args) {
1619
1591
  else console.warn("handler not found", { type, name });
1620
1592
  return this;
1621
1593
  };
1622
- var TypeVal = class extends NameVal {
1623
- eval(stack) {
1624
- return stack.lookupType(this.name);
1625
- }
1626
- };
1627
1594
  var keyIs = (k) => (e) => e.key === k;
1628
1595
  var macCtrl = (e) => isMac && e.metaKey || e.ctrlKey;
1629
1596
  var nullSafe = (fn) => (e, stack) => {
@@ -1756,9 +1723,6 @@ var Attributes = class {
1756
1723
  constructor(items) {
1757
1724
  this.items = items;
1758
1725
  }
1759
- eval(_stack) {
1760
- return {};
1761
- }
1762
1726
  static parse(attributes, px, parseAll = false) {
1763
1727
  return getAttrParser(px).parse(attributes, parseAll);
1764
1728
  }
@@ -1768,11 +1732,26 @@ var Attributes = class {
1768
1732
  };
1769
1733
  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";
1770
1734
  var booleanAttrs = new Set(booleanAttrsRaw.split(","));
1735
+ function parseDirectiveValue(px, directiveName, source, parser) {
1736
+ const val = parser(source, px);
1737
+ if (val === null) {
1738
+ px.onParseIssue("bad-value", {
1739
+ role: "directive",
1740
+ directive: directiveName,
1741
+ value: source
1742
+ });
1743
+ }
1744
+ return val;
1745
+ }
1746
+ function parseIterationDirectives(attributes, px) {
1747
+ const parseNamed = (name) => {
1748
+ const attr = attributes.getNamedItem(`@${name}`);
1749
+ return attr ? parseDirectiveValue(px, name, attr.value, parseAlterHandler) : null;
1750
+ };
1751
+ return { whenVal: parseNamed("when"), loopWithVal: parseNamed("loop-with") };
1752
+ }
1771
1753
  var AttrParser = class {
1772
1754
  constructor(px) {
1773
- this.clear(px);
1774
- }
1775
- clear(px) {
1776
1755
  this.px = px;
1777
1756
  this.attrs = null;
1778
1757
  this.hasDynamic = false;
@@ -1827,12 +1806,7 @@ var AttrParser = class {
1827
1806
  }
1828
1807
  }
1829
1808
  _parseDirectiveValue(directiveName, s, parserFn) {
1830
- const val = parserFn(s, this.px);
1831
- if (val === null) {
1832
- const info = { role: "directive", directive: directiveName, value: s };
1833
- this.px.onParseIssue("bad-value", info);
1834
- }
1835
- return val;
1809
+ return parseDirectiveValue(this.px, directiveName, s, parserFn);
1836
1810
  }
1837
1811
  parseDirective(s, directiveName) {
1838
1812
  switch (directiveName) {
@@ -1996,11 +1970,8 @@ var IfAttr = class extends BaseAttr {
1996
1970
  return this.condVal.eval(stack) ? this.thenVal.eval(stack) : this.elseVal.eval(stack);
1997
1971
  }
1998
1972
  };
1999
- var _attrParser = null;
2000
1973
  function getAttrParser(px) {
2001
- _attrParser ??= new AttrParser(px);
2002
- _attrParser.clear(px);
2003
- return _attrParser;
1974
+ return new AttrParser(px);
2004
1975
  }
2005
1976
  var EventHandler = class _EventHandler {
2006
1977
  constructor(handlerVal, args = []) {
@@ -2024,70 +1995,77 @@ var IntentHandler = class {
2024
1995
  }
2025
1996
  };
2026
1997
 
2027
- // src/cache.js
2028
- var isWeakKey = (k) => k !== null && (typeof k === "object" || typeof k === "function");
2029
- var NullDomCache = class {
2030
- get(_keys, _cacheKey) {
2031
- }
2032
- set(_keys, _cacheKey, _v) {
2033
- }
2034
- evict() {
2035
- return { hit: 0, miss: 0, badKey: 0 };
2036
- }
1998
+ // src/iteration.js
1999
+ var SEQ_INFO = /* @__PURE__ */ Symbol.for("tutuca.seqInfo");
2000
+ var normalizeRange = (start, end, size) => {
2001
+ let s = start == null ? 0 : start < 0 ? size + start : start;
2002
+ let e = end == null ? size : end < 0 ? size + end : end;
2003
+ s = s < 0 ? 0 : s > size ? size : s;
2004
+ e = e < 0 ? 0 : e > size ? size : e;
2005
+ return [s, e < s ? s : e];
2037
2006
  };
2038
- var WeakMapDomCache = class {
2039
- constructor() {
2040
- this.hit = this.miss = this.badKey = 0;
2041
- this.keysByLen = /* @__PURE__ */ new Map();
2042
- }
2043
- _returnValue(r) {
2044
- if (r === void 0) this.miss += 1;
2045
- else this.hit += 1;
2046
- return r;
2047
- }
2048
- get(keys, cacheKey) {
2049
- const len = keys.length;
2050
- let cur = this.keysByLen.get(len);
2051
- if (!cur) return this._returnValue(void 0);
2052
- for (let i = 0; i < len - 1; i++) {
2053
- cur = cur.get(keys[i]);
2054
- if (!cur) return this._returnValue(void 0);
2055
- }
2056
- return this._returnValue(cur.get(keys[len - 1])?.[cacheKey]);
2057
- }
2058
- set(keys, cacheKey, v) {
2059
- const len = keys.length;
2060
- let cur = this.keysByLen.get(len);
2061
- if (!cur) {
2062
- cur = /* @__PURE__ */ new WeakMap();
2063
- this.keysByLen.set(len, cur);
2064
- }
2065
- for (let i = 0; i < len - 1; i++) {
2066
- const key = keys[i];
2067
- let next = cur.get(key);
2068
- if (!next) {
2069
- if (!isWeakKey(key)) {
2070
- this.badKey += 1;
2071
- return;
2072
- }
2073
- next = /* @__PURE__ */ new WeakMap();
2074
- cur.set(key, next);
2075
- }
2076
- cur = next;
2077
- }
2078
- const lastKey = keys[len - 1];
2079
- const leaf = cur.get(lastKey);
2080
- if (leaf) leaf[cacheKey] = v;
2081
- else if (isWeakKey(lastKey)) cur.set(lastKey, { [cacheKey]: v });
2082
- else this.badKey += 1;
2083
- }
2084
- evict() {
2085
- const { hit, miss, badKey } = this;
2086
- this.hit = this.miss = this.badKey = 0;
2087
- this.keysByLen = /* @__PURE__ */ new Map();
2088
- return { hit, miss, badKey };
2007
+ var nativeIndexedIter = (seq, visit, start, end) => {
2008
+ const [s, e] = normalizeRange(start, end, seqSize(seq));
2009
+ for (let i = s; i < e; i++) visit(i, seq[i], "si");
2010
+ };
2011
+ var nativeKeyedIter = (seq, visit, start, end) => {
2012
+ const [s, e] = normalizeRange(start, end, seqSize(seq));
2013
+ let i = 0;
2014
+ for (const [key, value] of seqEntries(seq)) {
2015
+ if (i >= e) break;
2016
+ if (i >= s) visit(key, value, "sk");
2017
+ i++;
2089
2018
  }
2090
2019
  };
2020
+ var unknownIter = () => {
2021
+ };
2022
+ var getSeqInfo = (seq) => isIndexedSeq(seq) ? nativeIndexedIter : isKeyedSeq(seq) || isSetSeq(seq) ? nativeKeyedIter : seq?.[SEQ_INFO] ?? unknownIter;
2023
+ var filterAlwaysTrue = (_key, _value, _iterData) => true;
2024
+ var nullLoopWith = (seq) => ({ iterData: { seq } });
2025
+ var unpackLoopResult = (result, seq) => {
2026
+ const value = result ?? {};
2027
+ return {
2028
+ iterData: value.iterData ?? { seq },
2029
+ start: value.start,
2030
+ end: value.end,
2031
+ keys: value.keys
2032
+ };
2033
+ };
2034
+ var makeLoopCtx = (stack, filter) => ({
2035
+ lookup: (name) => stack.lookupBind(name),
2036
+ filter: (key, value, iterData) => filter.call(stack.it, key, value, iterData)
2037
+ });
2038
+ var callEnricher = (enricher, it, binds, key, value, iterData) => {
2039
+ enricher.call(it, binds, key, value, iterData);
2040
+ console.assert(
2041
+ binds.key === key && binds.value === value,
2042
+ "@enrich-with handlers must not overwrite binds.key or binds.value"
2043
+ );
2044
+ binds.key = key;
2045
+ binds.value = value;
2046
+ };
2047
+ var visitKeys = (seq, keys, visit) => {
2048
+ const attrName = isIndexedSeq(seq) ? "si" : "sk";
2049
+ for (const key of keys) visit(key, seqGet(seq, key), attrName);
2050
+ };
2051
+ function walkLoopBindings({ seq, it, filter, loopWith, enricher, ctx }, visit) {
2052
+ const { iterData, start, end, keys } = unpackLoopResult(loopWith.call(it, seq, ctx), seq);
2053
+ const visitOne = (key, value, attrName) => {
2054
+ const binds = { key, value };
2055
+ if (enricher) callEnricher(enricher, it, binds, key, value, iterData);
2056
+ visit(key, value, attrName, binds);
2057
+ };
2058
+ if (keys) visitKeys(seq, keys, visitOne);
2059
+ else
2060
+ getSeqInfo(seq)(
2061
+ seq,
2062
+ (key, value, attrName) => {
2063
+ if (filter.call(it, key, value, iterData)) visitOne(key, value, attrName);
2064
+ },
2065
+ start,
2066
+ end
2067
+ );
2068
+ }
2091
2069
 
2092
2070
  // src/vdom.js
2093
2071
  var HTML_NS = "http://www.w3.org/1999/xhtml";
@@ -2432,189 +2410,11 @@ function h(tagName, properties, children, namespace) {
2432
2410
  return new VNode(tag, props, normalizedChildren, key, namespace);
2433
2411
  }
2434
2412
 
2435
- // src/renderer.js
2436
- var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk"];
2437
- var Renderer = class {
2438
- constructor(comps) {
2439
- this.comps = comps;
2440
- this.cache = new WeakMapDomCache();
2441
- this.renderTag = h;
2442
- }
2443
- renderFragment(childs) {
2444
- return new VFragment(childs);
2445
- }
2446
- renderComment(text) {
2447
- return new VComment(text);
2448
- }
2449
- setNullCache() {
2450
- this.cache = new NullDomCache();
2451
- }
2452
- renderToDOM(stack, val) {
2453
- const rootNode = document.createElement("div");
2454
- const rOpts = { document };
2455
- render(h("DIV", null, [this.renderRoot(stack, val)]), rootNode, rOpts);
2456
- return rootNode.childNodes[0];
2457
- }
2458
- renderToString(stack, val, cleanAttrs = true) {
2459
- const dom = this.renderToDOM(stack, val);
2460
- if (cleanAttrs) {
2461
- const nodes = dom.querySelectorAll("[data-nid],[data-cid],[data-eid]");
2462
- for (const { dataset } of nodes) for (const name of DATASET_ATTRS) delete dataset[name];
2463
- }
2464
- return dom.innerHTML;
2465
- }
2466
- renderRoot(stack, val, viewName = null) {
2467
- const comp = this.comps.getCompFor(val);
2468
- if (comp === null) return null;
2469
- return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
2470
- }
2471
- renderIt(stack, node, key, viewName) {
2472
- const comp = this.comps.getCompFor(stack.it);
2473
- return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
2474
- }
2475
- // `node` is the parse node of the render site (`<x render>` / `render-it` /
2476
- // `render-each`, or the view's root anode for the app root). It keys the
2477
- // cache as a globally-unique object: node ids alone are unique only within a
2478
- // single view, so the same value rendered by two components (e.g. through a
2479
- // shared dynamic-var sequence) would otherwise collide in the cache.
2480
- _rValComp(stack, val, comp, node, key, viewName) {
2481
- const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}`;
2482
- const cachePath = [node, val];
2483
- stack._pushDynBindValuesToArray(cachePath, comp);
2484
- const cachedNode = this.cache.get(cachePath, cacheKey);
2485
- if (cachedNode) return cachedNode;
2486
- const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
2487
- const body = this.renderView(view, stack);
2488
- if (body == null) return null;
2489
- const meta = this._renderMetadata({
2490
- $: "Comp",
2491
- nid: node?.nodeId ?? null,
2492
- cid: comp.id,
2493
- vid: view.name
2494
- });
2495
- const dom = new VFragment([meta, body]);
2496
- this.cache.set(cachePath, cacheKey, dom);
2497
- return dom;
2498
- }
2499
- pushEachEntry(r, nid, attrName, key, dom) {
2500
- r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
2501
- }
2502
- renderEachWhen(stack, iterInfo, view, nid) {
2503
- const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
2504
- const r = [];
2505
- const it = stack.it;
2506
- const { iterData, start, end, keys } = unpackLoopResult(
2507
- loopWith.call(it, seq, makeLoopCtx(stack, filter)),
2508
- seq
2509
- );
2510
- const renderOne = (key, value, attrName) => {
2511
- const cachePath = enricher ? [view, it, value] : [view, value];
2512
- const binds = { key, value };
2513
- const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
2514
- if (enricher) callEnricher(enricher, it, binds, key, value, iterData);
2515
- const cachedNode = this.cache.get(cachePath, cacheKey);
2516
- if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
2517
- else {
2518
- const dom = this.renderView(view, stack.enter(value, binds, false));
2519
- if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
2520
- this.cache.set(cachePath, cacheKey, dom);
2521
- }
2522
- };
2523
- if (keys) keysIter(seq, renderOne, keys);
2524
- else
2525
- getSeqInfo(seq)(
2526
- seq,
2527
- (key, value, attrName) => {
2528
- if (filter.call(it, key, value, iterData)) renderOne(key, value, attrName);
2529
- },
2530
- start,
2531
- end
2532
- );
2533
- return r;
2534
- }
2535
- renderView(view, stack) {
2536
- let n = stack.binds[1];
2537
- while (n !== null) {
2538
- const b = n[0];
2539
- if (b.isFrame) {
2540
- if (stack.it !== b.it) break;
2541
- console.error("recursion detected", stack.it, b.it);
2542
- return new VComment("RECURSION AVOIDED");
2543
- }
2544
- n = n[1];
2545
- }
2546
- return view.render(stack, this);
2547
- }
2548
- _renderMetadata(info) {
2549
- return new VComment(`§${JSON.stringify(info)}§`);
2550
- }
2551
- // Prefix a loop-less @enrich-with subtree with a boundary meta so event-path
2552
- // reconstruction replays its binds (mirrors the §Each§ / §Comp§ metas).
2553
- renderScopeMeta(nid, dom) {
2554
- return new VFragment([this._renderMetadata({ $: "Scope", nid }), dom]);
2555
- }
2556
- };
2557
- var getSeqInfo = (seq) => isIndexedSeq(seq) ? nativeIndexedIter : isKeyedSeq(seq) || isSetSeq(seq) ? nativeKeyedIter : seq?.[SEQ_INFO] ?? unkIter;
2558
- var normalizeRange = (start, end, size) => {
2559
- let s = start == null ? 0 : start < 0 ? size + start : start;
2560
- let e = end == null ? size : end < 0 ? size + end : end;
2561
- s = s < 0 ? 0 : s > size ? size : s;
2562
- e = e < 0 ? 0 : e > size ? size : e;
2563
- return [s, e < s ? s : e];
2564
- };
2565
- var callEnricher = (enricher, it, binds, key, value, iterData) => {
2566
- enricher.call(it, binds, key, value, iterData);
2567
- console.assert(
2568
- binds.key === key && binds.value === value,
2569
- "@enrich-with handlers must not overwrite binds.key or binds.value"
2570
- );
2571
- binds.key = key;
2572
- binds.value = value;
2573
- };
2574
- var filterAlwaysTrue = (_v, _k, _seq) => true;
2575
- var nullLoopWith = (seq) => ({ iterData: { seq } });
2576
- var unpackLoopResult = (result, seq) => {
2577
- const r = result ?? {};
2578
- return { iterData: r.iterData ?? { seq }, start: r.start, end: r.end, keys: r.keys };
2579
- };
2580
- var keysIter = (seq, visit, keys) => {
2581
- const attrName = isIndexedSeq(seq) ? "si" : "sk";
2582
- for (const key of keys) visit(key, seqGet(seq, key), attrName);
2583
- };
2584
- var makeLoopCtx = (stack, filter) => ({
2585
- lookup: (name) => stack.lookupBind(name),
2586
- filter: (key, value, iterData) => filter.call(stack.it, key, value, iterData)
2587
- });
2588
- var nativeIndexedIter = (seq, visit, start, end) => {
2589
- const [s, e] = normalizeRange(start, end, seqSize(seq));
2590
- for (let i = s; i < e; i++) visit(i, seq[i], "si");
2591
- };
2592
- var nativeKeyedIter = (seq, visit, start, end) => {
2593
- const [s, e] = normalizeRange(start, end, seqSize(seq));
2594
- let i = 0;
2595
- for (const [k, v] of seqEntries(seq)) {
2596
- if (i >= e) break;
2597
- if (i >= s) visit(k, v, "sk");
2598
- i++;
2599
- }
2600
- };
2601
- var unkIter = () => {
2602
- };
2603
- var SEQ_INFO = /* @__PURE__ */ Symbol.for("tutuca.seqInfo");
2604
-
2605
2413
  // src/anode.js
2606
2414
  function resolveDynProducer(comp, name) {
2607
- let producerComp, producerProvide;
2608
- const lk = comp?.lookup?.[name];
2609
- if (lk != null) {
2610
- producerComp = comp.scope?.lookupComponent(lk.compName);
2611
- producerProvide = producerComp?.provide?.[lk.provideName];
2612
- } else {
2613
- const p = comp?.provide?.[name];
2614
- if (p == null) return null;
2615
- producerComp = comp;
2616
- producerProvide = p;
2617
- }
2415
+ const own = comp?.provide?.[name];
2416
+ const producerComp = own != null ? comp : comp?.scope?.lookupProvider(name) ?? null;
2417
+ const producerProvide = own ?? producerComp?.provide?.[name];
2618
2418
  if (producerComp == null || producerProvide == null) return null;
2619
2419
  const pi = producerProvide.val?.toPathItem?.() ?? null;
2620
2420
  return { producerCompId: producerComp.id, producerSteps: pi ? [pi] : [] };
@@ -2975,16 +2775,10 @@ function parseRenderEach(px, value, as, attrs) {
2975
2775
  const seqVal = parseXOpVal("render-each", value, px, parseSequence);
2976
2776
  if (seqVal === null) return null;
2977
2777
  const renderIt = px.addNode(RenderItNode, as);
2978
- const attrParser = getAttrParser(px);
2979
- const eachAttr = attrParser.pushWrapper("each", value, seqVal);
2980
- attrParser.eachAttr = eachAttr;
2981
- const when = attrs.getNamedItem("@when");
2982
- if (when) attrParser._parseWhen(when.value);
2983
- const lWith = attrs.getNamedItem("@loop-with");
2984
- if (lWith) attrParser._parseLoopWith(lWith.value);
2778
+ const { whenVal, loopWithVal } = parseIterationDirectives(attrs, px);
2985
2779
  const each2 = px.addNodeIf(EachNode, seqVal);
2986
- each2.iterInfo.whenVal = eachAttr.whenVal ?? null;
2987
- each2.iterInfo.loopWithVal = eachAttr.loopWithVal ?? null;
2780
+ each2.iterInfo.whenVal = whenVal;
2781
+ each2.iterInfo.loopWithVal = loopWithVal;
2988
2782
  each2.fromRenderEach = true;
2989
2783
  each2.wrapNode(renderIt);
2990
2784
  return each2;
@@ -3373,7 +3167,7 @@ var Components = class {
3373
3167
  }
3374
3168
  compileStyles() {
3375
3169
  const styles = [];
3376
- for (const comp of this.byId.values()) styles.push(comp.compileStyle());
3170
+ for (const Comp of this.byId.values()) styles.push(Comp[COMPONENT].compileStyle());
3377
3171
  return styles.join("\n");
3378
3172
  }
3379
3173
  };
@@ -3432,32 +3226,39 @@ var ComponentStack = class _ComponentStack {
3432
3226
  lookupComponent(name) {
3433
3227
  return this.byName[name] ?? this.parent?.lookupComponent(name) ?? null;
3434
3228
  }
3229
+ // The component in this scope chain that provides `name`, innermost first. A lookup
3230
+ // names a value without naming its producer, so the render-target teleport
3231
+ // (resolveDynProducer) recovers the producer here instead. Sound because
3232
+ // PROVIDE_NAME_COLLISION keeps a provide name to one producer per chain; with two,
3233
+ // this returns the innermost and the linter has already reported the ambiguity.
3234
+ lookupProvider(name) {
3235
+ for (const compName in this.byName) {
3236
+ const Comp = this.byName[compName];
3237
+ if (Comp.provide?.[name] !== void 0) return Comp;
3238
+ }
3239
+ return this.parent?.lookupProvider(name) ?? null;
3240
+ }
3435
3241
  lookupMacro(name) {
3436
3242
  return this.macros[name] ?? this.parent?.lookupMacro(name) ?? null;
3437
3243
  }
3438
3244
  };
3439
3245
  var ProvideInfo = class {
3440
- constructor(name, val, symbol) {
3246
+ constructor(name, val) {
3441
3247
  this.name = name;
3442
3248
  this.val = val;
3443
- this.symbol = symbol;
3444
3249
  }
3445
3250
  };
3446
3251
  var LookupInfo = class {
3447
- constructor(name, compName, provideName, val) {
3252
+ constructor(name, val) {
3448
3253
  this.name = name;
3449
- this.compName = compName;
3450
- this.provideName = provideName;
3451
3254
  this.val = val;
3452
- this._sym = void 0;
3453
- }
3454
- getProducerSymbol(stack) {
3455
- if (this._sym === void 0)
3456
- this._sym = stack.lookupType(this.compName)?.provide?.[this.provideName]?.symbol ?? null;
3457
- return this._sym;
3458
3255
  }
3459
3256
  };
3460
3257
  var isString = (v) => typeof v === "string";
3258
+ var isTypeName = (s) => {
3259
+ const c = s.charCodeAt(0);
3260
+ return c >= 65 && c <= 90;
3261
+ };
3461
3262
  var _rawSpecKeys = "name view style commonStyle globalStyle receive intent alter views provide lookup fields methods statics";
3462
3263
  var KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
3463
3264
  var _compId = 0;
@@ -3478,8 +3279,9 @@ var Component = class {
3478
3279
  this.views[name] = new View(name, view, style);
3479
3280
  }
3480
3281
  this._rawProvide = o.provide ?? {};
3481
- this._rawLookup = o.lookup ?? {};
3282
+ this._rawLookup = o.lookup ?? [];
3482
3283
  this.provide = {};
3284
+ this.provideType = {};
3483
3285
  this.lookup = {};
3484
3286
  this.scope = null;
3485
3287
  this.spec = o;
@@ -3491,25 +3293,23 @@ var Component = class {
3491
3293
  this.views[name].compile(new ParseContext2(), this.scope, this.id);
3492
3294
  const ctx = this.views.main.ctx;
3493
3295
  for (const key in this._rawProvide) {
3296
+ if (isTypeName(key)) {
3297
+ if (this._rawProvide[key] === "self") this.provideType[key] = this.Class;
3298
+ continue;
3299
+ }
3494
3300
  const val = parseProvide(this._rawProvide[key], ctx);
3495
- if (val) this.provide[key] = new ProvideInfo(key, val, Symbol(key));
3301
+ if (val) this.provide[key] = new ProvideInfo(key, val);
3496
3302
  }
3497
- for (const key in this._rawLookup) {
3498
- const linfo = this._rawLookup[key];
3499
- const forStr = isString(linfo) ? linfo : isString(linfo?.for) ? linfo.for : null;
3500
- const [compName, provideName] = forStr === null ? [] : forStr.split(".");
3501
- if (!isString(compName) || !isString(provideName)) continue;
3502
- const defStr = isString(linfo?.default) ? linfo.default : null;
3503
- const val = defStr === null ? null : parseField(defStr, ctx);
3504
- this.lookup[key] = new LookupInfo(key, compName, provideName, val);
3303
+ for (const entry of this._rawLookup) {
3304
+ const name = isString(entry) ? entry : isString(entry?.name) ? entry.name : null;
3305
+ if (name === null) continue;
3306
+ const defStr = isString(entry?.default) ? entry.default : null;
3307
+ this.lookup[name] = new LookupInfo(name, defStr === null ? null : parseField(defStr, ctx));
3505
3308
  }
3506
3309
  for (const key in this.lookup)
3507
3310
  if (this.provide[key] !== void 0)
3508
3311
  console.warn("name declared in both provide and lookup", this.name, key);
3509
3312
  }
3510
- make(args, opts) {
3511
- return this.Class.make(args, opts ?? { scope: this.scope });
3512
- }
3513
3313
  getView(name) {
3514
3314
  return this.views[name] ?? this.views.main;
3515
3315
  }
@@ -3534,6 +3334,22 @@ var Component = class {
3534
3334
  // src/stack.js
3535
3335
  var STOP = /* @__PURE__ */ Symbol("STOP");
3536
3336
  var NEXT = /* @__PURE__ */ Symbol("NEXT");
3337
+ var DEFAULT_LOOKUP_ROUTE = ["dyn", "lex"];
3338
+ function routeLookup(route, lex, dyn) {
3339
+ for (let i = 0; i < route.length; i++) {
3340
+ const leg = route[i];
3341
+ if (leg === "dyn") {
3342
+ const v = dyn();
3343
+ if (v != null) return v;
3344
+ } else if (leg === "lex") {
3345
+ const v = lex();
3346
+ if (v != null) return v;
3347
+ } else {
3348
+ console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
3349
+ }
3350
+ }
3351
+ return null;
3352
+ }
3537
3353
  function lookup(chain, name, dv = null) {
3538
3354
  let n = chain;
3539
3355
  while (n !== null) {
@@ -3584,14 +3400,21 @@ var Stack = class _Stack {
3584
3400
  this.ctx = ctx;
3585
3401
  }
3586
3402
  // Evaluate every provide the entered component publishes and push them as one
3587
- // dynBinds frame (keyed by each provide's symbol). No-op when there are no provides.
3403
+ // dynBinds frame, keyed by NAME. Published types go in the same frame: a type name
3404
+ // starts A-Z and a value name does not, so the two namespaces cannot collide and
3405
+ // nearest-ancestor-wins falls out of frame order for both. No-op with no provides.
3588
3406
  _pushProvides() {
3589
- const provide = this.comps.getCompFor(this.it)?.provide;
3590
- if (provide == null) return this;
3407
+ const comp = this.comps.getCompFor(this.it);
3408
+ if (comp == null) return this;
3409
+ const { provide, provideType } = comp;
3591
3410
  const dynObj = {};
3592
3411
  let has2 = false;
3593
3412
  for (const k in provide) {
3594
- dynObj[provide[k].symbol] = provide[k].val.eval(this);
3413
+ dynObj[k] = provide[k].val.eval(this);
3414
+ has2 = true;
3415
+ }
3416
+ for (const k in provideType) {
3417
+ dynObj[k] = provideType[k];
3595
3418
  has2 = true;
3596
3419
  }
3597
3420
  if (!has2) return this;
@@ -3616,30 +3439,36 @@ var Stack = class _Stack {
3616
3439
  const newViews = [name, views];
3617
3440
  return new _Stack(comps, it, binds, dynBinds, newViews, computeViewsId(newViews), ctx);
3618
3441
  }
3442
+ // Published types are stable per scope and would only churn the render cache, so
3443
+ // the cache key covers values alone.
3619
3444
  _pushDynBindValuesToArray(arr, comp) {
3620
- for (const k in comp.provide) arr.push(this._lookupProvide(comp.provide[k]));
3621
- for (const k in comp.lookup) arr.push(this._lookupAlias(comp.lookup[k]));
3622
- }
3623
- _lookupProvide(p) {
3624
- return lookup(this.dynBinds, p.symbol) ?? p.val.eval(this) ?? null;
3625
- }
3626
- _lookupAlias(lk) {
3627
- const sym = lk.getProducerSymbol(this);
3628
- return (sym != null ? lookup(this.dynBinds, sym) : null) ?? lk.val?.eval(this) ?? null;
3445
+ for (const k in comp.provide) arr.push(this.lookupDynamic(k));
3446
+ for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
3629
3447
  }
3448
+ // `*name`: the nearest binding above (including this component's own provides,
3449
+ // pushed on entering it), else this component's declared default, else null.
3630
3450
  lookupDynamic(name) {
3451
+ const v = lookup(this.dynBinds, name);
3452
+ if (v != null) return v;
3631
3453
  const comp = this.comps.getCompFor(this.it);
3632
- if (comp == null) return null;
3633
- const lk = comp.lookup[name];
3634
- if (lk !== void 0) return this._lookupAlias(lk);
3635
- const p = comp.provide[name];
3636
- return p !== void 0 ? this._lookupProvide(p) : null;
3454
+ return comp?.lookup[name]?.val?.eval(this) ?? null;
3637
3455
  }
3638
3456
  lookupBind(name) {
3639
3457
  return lookup(this.binds, name);
3640
3458
  }
3641
- lookupType(name) {
3642
- return this.comps.getCompFor(this.it).scope.lookupComponent(name);
3459
+ // The `lex` leg: what a name means in the registration scope of the component being
3460
+ // rendered. The scope chain holds components, so this leg only ever answers for a
3461
+ // type name — for a value name it is a miss, which is why one default route serves
3462
+ // both. Guarded: a non-component `it` has no scope to ask.
3463
+ _lookupLex(name) {
3464
+ return this.comps.getCompFor(this.it)?.scope.lookupComponent(name) ?? null;
3465
+ }
3466
+ lookupRouted(name, route = DEFAULT_LOOKUP_ROUTE) {
3467
+ return routeLookup(
3468
+ route,
3469
+ () => this._lookupLex(name),
3470
+ () => this.lookupDynamic(name)
3471
+ );
3643
3472
  }
3644
3473
  lookupFieldRaw(name) {
3645
3474
  return this.it[name] ?? null;
@@ -3648,12 +3477,6 @@ var Stack = class _Stack {
3648
3477
  const fn = this.it[name];
3649
3478
  return fn instanceof Function ? fn.call(this.it) : null;
3650
3479
  }
3651
- // Bare implicit event names are gone: a sigil-less word cannot reach a
3652
- // handler-arg slot anymore, so this only ever serves stale NameVals parsed
3653
- // before the removal (e.g. macro-attr pass-throughs). Always null.
3654
- lookupName(_name) {
3655
- return null;
3656
- }
3657
3480
  // The dispatched DOM event / drag info, read only by EventMemberVal's
3658
3481
  // `e.<member>` handler args. Null outside a live event transaction.
3659
3482
  lookupEvent() {
@@ -3679,11 +3502,6 @@ var Stack = class _Stack {
3679
3502
  // src/oo.js
3680
3503
  var BAD_VALUE = /* @__PURE__ */ Symbol("BadValue");
3681
3504
  var nullCoercer = (v) => v;
3682
- var isPlainObject3 = (v) => {
3683
- if (v === null || typeof v !== "object") return false;
3684
- const proto = Object.getPrototypeOf(v);
3685
- return proto === Object.prototype || proto === null;
3686
- };
3687
3505
  function defaultToData(v) {
3688
3506
  if (v instanceof Map) return [...v.entries()];
3689
3507
  if (v instanceof Set) return [...v.values()];
@@ -3752,7 +3570,7 @@ var CHECK_TYPE_FLOAT = check(Number.isFinite, "Float expected");
3752
3570
  var CHECK_TYPE_BOOL = check((v) => typeof v === "boolean", "Boolean expected");
3753
3571
  var CHECK_TYPE_STRING = check((v) => typeof v === "string", "String expected");
3754
3572
  var CHECK_TYPE_LIST = check(Array.isArray, "Array expected");
3755
- var CHECK_TYPE_OBJECT = check(isPlainObject3, "Plain object expected");
3573
+ var CHECK_TYPE_OBJECT = check(isPlainObject, "Plain object expected");
3756
3574
  var CHECK_TYPE_MAP = check((v) => v instanceof Map, "Map expected");
3757
3575
  var CHECK_TYPE_SET = check((v) => v instanceof Set, "Set expected");
3758
3576
  var FieldBool = class extends Field {
@@ -3821,7 +3639,7 @@ var FieldObject = class extends Field {
3821
3639
  "object",
3822
3640
  name,
3823
3641
  CHECK_TYPE_OBJECT,
3824
- (v) => isPlainObject3(v) ? { ...v } : null,
3642
+ (v) => isPlainObject(v) ? { ...v } : null,
3825
3643
  defaultValue
3826
3644
  );
3827
3645
  }
@@ -3834,7 +3652,7 @@ var FieldMap = class extends Field {
3834
3652
  CHECK_TYPE_MAP,
3835
3653
  (v) => {
3836
3654
  if (v instanceof Map) return new Map(v);
3837
- if (Array.isArray(v) || isPlainObject3(v))
3655
+ if (Array.isArray(v) || isPlainObject(v))
3838
3656
  return new Map(Array.isArray(v) ? v : Object.entries(v));
3839
3657
  return null;
3840
3658
  },
@@ -3959,7 +3777,7 @@ function classFromData(name, { fields = {}, methods, statics }) {
3959
3777
  b.addField(field, dval, FieldCls);
3960
3778
  } else if (value?.component && value?.args !== void 0)
3961
3779
  b.addCompField(field, value.component, value.args);
3962
- else if (isPlainObject3(value)) b.addField(field, { ...value }, FieldObject);
3780
+ else if (isPlainObject(value)) b.addField(field, { ...value }, FieldObject);
3963
3781
  else {
3964
3782
  const FieldCls = value?.[FIELD_CLASS] ?? FieldAny;
3965
3783
  b.addField(field, value, FieldCls);
@@ -3983,10 +3801,24 @@ function validateDraftFields(current2, draft) {
3983
3801
  }
3984
3802
  }
3985
3803
  }
3986
- var META_KEYS = "name id fields methods views receive intent alter provide lookup spec extra commonStyle globalStyle scope _rawProvide _rawLookup".split(
3804
+ var META_KEYS = "name id fields methods views receive intent alter provide provideType lookup spec extra commonStyle globalStyle scope _rawProvide _rawLookup".split(
3987
3805
  " "
3988
3806
  );
3807
+ var RESERVED_COMPONENT_STATICS = /* @__PURE__ */ new Set([
3808
+ ...META_KEYS,
3809
+ "make",
3810
+ "getMetaClass",
3811
+ ...Object.getOwnPropertyNames(Component.prototype)
3812
+ ]);
3813
+ function assertNoReservedComponentStatics(statics = {}) {
3814
+ for (const name in statics) {
3815
+ if (RESERVED_COMPONENT_STATICS.has(name)) {
3816
+ throw new TypeError(`component static "${name}" is reserved by the framework`);
3817
+ }
3818
+ }
3819
+ }
3989
3820
  Component.fromSpec = (opts) => {
3821
+ assertNoReservedComponentStatics(opts.statics);
3990
3822
  const Class = classFromData(opts.name, opts);
3991
3823
  const comp = new Component(Class, opts);
3992
3824
  const metaClass = Class.getMetaClass();
@@ -4006,7 +3838,7 @@ Component.fromSpec = (opts) => {
4006
3838
  configurable: true
4007
3839
  });
4008
3840
  for (const key of Object.getOwnPropertyNames(Component.prototype))
4009
- if (key !== "constructor" && key !== "make" && !Object.hasOwn(Class, key))
3841
+ if (key !== "constructor" && !Object.hasOwn(Class, key))
4010
3842
  Class[key] = (...args) => comp[key](...args);
4011
3843
  return Class;
4012
3844
  };
@@ -4026,9 +3858,6 @@ var State = class {
4026
3858
  this.val = val;
4027
3859
  for (const sub of this.changeSubs) sub({ val, old, info, timestamp: Date.now() });
4028
3860
  }
4029
- update(fn, info) {
4030
- return this.set(fn(this.val), info);
4031
- }
4032
3861
  };
4033
3862
  var Transactor = class {
4034
3863
  constructor(comps, rootValue) {
@@ -4089,7 +3918,7 @@ var Transactor = class {
4089
3918
  // Build and dispatch the observer record for a settled transaction. The
4090
3919
  // resolved handler (`_resolvedHandler`/`_matched`) and per-leaf before/after
4091
3920
  // (`_before`/`_after`) were captured while the handler ran (see callHandler /
4092
- // updateRootValue). No-op when nobody is observing.
3921
+ // Transaction.run). No-op when nobody is observing.
4093
3922
  _emitTransaction(transaction, root) {
4094
3923
  if (this._observers.length === 0) return;
4095
3924
  if (transaction._resolvedHandler === void 0) return;
@@ -4121,8 +3950,13 @@ var Transactor = class {
4121
3950
  }
4122
3951
  return child;
4123
3952
  }
4124
- pushSend(path, name, args = [], opts = {}, parent = null) {
4125
- const t = new SendEvent(path, this, name, args, parent, opts);
3953
+ // `origin` is where the message came FROM, pinned now for the same reason an
3954
+ // intent's answerPath is: a reply must reach the sender that asked even if a key
3955
+ // moved while the message was in flight. Null when nobody is waiting — a host
3956
+ // sendAtRoot or a view's own `@on.*` — and ctx.sendReply refuses on that.
3957
+ pushSend(path, name, args = [], parent = null, origin = null) {
3958
+ const t = new SendEvent(path, this, name, args, parent);
3959
+ t.origin = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
4126
3960
  this.pushTransaction(t);
4127
3961
  return this._link(t, parent);
4128
3962
  }
@@ -4135,9 +3969,9 @@ var Transactor = class {
4135
3969
  walk.advance();
4136
3970
  return walk;
4137
3971
  }
4138
- // Drain queued transactions and await in-flight requests until quiescent. Each
4139
- // awaited request enqueues a ResponseEvent, which may dispatch more work, so we
4140
- // loop. `maxTurns` backstops a pathological non-terminating cascade.
3972
+ // Drain queued transactions and await async intent handlers until quiescent. A
3973
+ // completed handler may enqueue an answer, which may dispatch more work, so we loop.
3974
+ // `maxTurns` backstops a pathological non-terminating cascade.
4141
3975
  async settle(maxTurns = 1e4) {
4142
3976
  while ((this.hasPendingTransactions || this._inflight.size) && maxTurns-- > 0) {
4143
3977
  while (this.hasPendingTransactions) this.transactNext();
@@ -4194,9 +4028,6 @@ var Transaction = class {
4194
4028
  whenSubtreeSettled() {
4195
4029
  return this.completion.whenSubtreeSettled();
4196
4030
  }
4197
- run(rootValue, comps) {
4198
- return this.updateRootValue(rootValue, comps);
4199
- }
4200
4031
  afterTransaction() {
4201
4032
  }
4202
4033
  // Ending a walk and forwarding belong to the two dispatch buckets; the base warns
@@ -4208,12 +4039,6 @@ var Transaction = class {
4208
4039
  forward(_opts) {
4209
4040
  console.warn('ctx.forward() needs a "receive" or "intent" handler - ignored');
4210
4041
  }
4211
- buildRootStack(root, comps) {
4212
- return Stack.root(comps, root);
4213
- }
4214
- buildStack(root, comps) {
4215
- return this.path.toTransactionPath().buildStack(this.buildRootStack(root, comps));
4216
- }
4217
4042
  // The kind reported to observers (see Transactor.observe); null on the base.
4218
4043
  get observeKind() {
4219
4044
  return null;
@@ -4233,12 +4058,11 @@ var Transaction = class {
4233
4058
  return null;
4234
4059
  }
4235
4060
  // The path used to apply the mutation. Teleports dynamic-var renders so it lands on
4236
- // the data's real location (the dispatch `this.path` keeps intermediates). A subclass
4237
- // may override to supply a pre-resolved path (see ResponseEvent's pinned keys).
4061
+ // the data's real location (the dispatch `this.path` keeps intermediates).
4238
4062
  getTransactionPath() {
4239
4063
  return this.path.toTransactionPath().compact();
4240
4064
  }
4241
- updateRootValue(curRoot, comps) {
4065
+ run(curRoot, comps) {
4242
4066
  const txnPath = this.getTransactionPath();
4243
4067
  const curLeaf = txnPath.lookup(curRoot);
4244
4068
  const newLeaf = produce(curLeaf, (draft) => {
@@ -4295,8 +4119,8 @@ var InputEvent = class extends Transaction {
4295
4119
  }
4296
4120
  this.transactor.pushIntent(this.dispatchPath, name, args, rest, this);
4297
4121
  }
4298
- buildRootStack(root, comps) {
4299
- return Stack.root(comps, root, this);
4122
+ buildStack(root, comps) {
4123
+ return this.path.toTransactionPath().buildStack(Stack.root(comps, root, this));
4300
4124
  }
4301
4125
  getHandlerAndArgs(root, _instance, comps) {
4302
4126
  const stack = this.buildStack(root, comps);
@@ -4314,11 +4138,10 @@ var InputEvent = class extends Transaction {
4314
4138
  }
4315
4139
  };
4316
4140
  var NameArgsTransaction = class extends Transaction {
4317
- constructor(path, transactor, name, args, parentTransaction, opts = {}) {
4141
+ constructor(path, transactor, name, args, parentTransaction) {
4318
4142
  super(path, transactor, parentTransaction);
4319
4143
  this.name = name;
4320
4144
  this.args = args;
4321
- this.opts = opts;
4322
4145
  this.targetPath = path;
4323
4146
  }
4324
4147
  handlerProp = null;
@@ -4377,7 +4200,7 @@ var SendEvent = class extends NameArgsTransaction {
4377
4200
  var IntentEvent = class extends NameArgsTransaction {
4378
4201
  handlerProp = "intent";
4379
4202
  constructor(path, transactor, walk) {
4380
- super(path, transactor, walk.name, walk.args, walk.parent, {});
4203
+ super(path, transactor, walk.name, walk.args, walk.parent);
4381
4204
  this.walk = walk;
4382
4205
  this.targetPath = walk.origin;
4383
4206
  }
@@ -4477,7 +4300,7 @@ var IntentWalk = class {
4477
4300
  async _runLex(chain, i) {
4478
4301
  if (this.ended) return;
4479
4302
  if (i >= chain.length) return this.advance();
4480
- const ctx = new IntentContext(this.origin, this.transactor, this.parent);
4303
+ const ctx = new Dispatcher(this.origin, this.transactor, this.parent);
4481
4304
  try {
4482
4305
  const res = await chain[i].fn.apply(null, [...this.args, ctx]);
4483
4306
  if (res === PASS) return this._runLex(chain, i + 1);
@@ -4530,7 +4353,7 @@ var IntentWalk = class {
4530
4353
  this.ended = true;
4531
4354
  if (name === null) return this.release?.();
4532
4355
  const path = this.answerPath ?? this.origin;
4533
- const t = new SendEvent(path, this.transactor, name, args, this.parent, {});
4356
+ const t = new SendEvent(path, this.transactor, name, args, this.parent);
4534
4357
  t._isAnswer = true;
4535
4358
  this.transactor.pushTransaction(t);
4536
4359
  if (this.release) t.completion.whenSubtreeSettled().then(this.release);
@@ -4569,7 +4392,7 @@ var Completion = class {
4569
4392
  this.val = val;
4570
4393
  this._selfResolve?.(val);
4571
4394
  }
4572
- // Settle self even when no handler produced a value (skipSelf / undefined / throw paths).
4395
+ // Settle self even when no handler produced a value (undefined-state / throw paths).
4573
4396
  ensureSelfSettled() {
4574
4397
  if (!this.selfSettled) this.markSelfSettled(this.val);
4575
4398
  }
@@ -4615,12 +4438,64 @@ var Dispatcher = class {
4615
4438
  get at() {
4616
4439
  return new PathChanges(this);
4617
4440
  }
4441
+ // The render-time Stack, rebuilt on demand. A handler does not get one: the stack
4442
+ // that evaluated its arguments is a local in getHandlerAndArgs and is gone by the
4443
+ // time the body runs, and send/intent transactions never build one at all. Both
4444
+ // halves it needs are pinned on this ctx, so the `dyn` leg reconstructs it — and
4445
+ // only the `dyn` leg does, which is why this is lazy rather than eager.
4446
+ //
4447
+ // The path is compacted, so per-item binds (`@each`, `@enrich-with`) are not
4448
+ // replayed; provides are pushed on every component frame either way, so a provide
4449
+ // that reads a loop binding is the one case this cannot reproduce faithfully.
4450
+ _stack() {
4451
+ this._stackMemo ??= this.path.toTransactionPath().buildStack(Stack.root(this.transactor.comps, this.root, this.parent));
4452
+ return this._stackMemo;
4453
+ }
4454
+ // Resolve a name the way the renderer would. `opts.route` takes the same legs in
4455
+ // the same spelling as `ctx.intent` — `["dyn"]` the render ancestry, `["lex"]` the
4456
+ // registration scope, default both.
4457
+ lookup(name, opts) {
4458
+ const route = opts?.route ?? DEFAULT_LOOKUP_ROUTE;
4459
+ return routeLookup(
4460
+ route,
4461
+ () => this._lookupLex(name),
4462
+ () => this._stack()?.lookupDynamic(name) ?? null
4463
+ );
4464
+ }
4465
+ // The `lex` leg without a Stack: the scope of the component whose handler is
4466
+ // running, which is the leaf of this ctx's path.
4467
+ _lookupLex(name) {
4468
+ let Comp = null;
4469
+ this.walkPath((c) => {
4470
+ Comp = c;
4471
+ return false;
4472
+ });
4473
+ return Comp?.scope?.lookupComponent(name) ?? null;
4474
+ }
4475
+ // A component lookup is a value lookup constrained to a component. The `lex` leg
4476
+ // can only ever answer with one; the `dyn` leg reads a binding an ancestor
4477
+ // published, so it is the leg that has to be checked.
4478
+ lookupType(name, opts) {
4479
+ const v = this.lookup(name, opts);
4480
+ if (v == null) {
4481
+ this.transactor.refuse("TYPE_NOT_FOUND", {
4482
+ name,
4483
+ route: opts?.route ?? DEFAULT_LOOKUP_ROUTE
4484
+ });
4485
+ return null;
4486
+ }
4487
+ if (v?.[COMPONENT] == null) {
4488
+ this.transactor.refuse("TYPE_NOT_COMPONENT", { name, got: typeof v });
4489
+ return null;
4490
+ }
4491
+ return v;
4492
+ }
4618
4493
  // A message is ADDRESSED: it names one component and stops there.
4619
- send(name, args, opts) {
4620
- return this.sendAtPath(this.path, name, args, opts);
4494
+ send(name, args) {
4495
+ return this.sendAtPath(this.path, name, args);
4621
4496
  }
4622
- sendAtPath(path, name, args, opts) {
4623
- return this.transactor.pushSend(path, name, args, opts, this.parent);
4497
+ sendAtPath(path, name, args) {
4498
+ return this.transactor.pushSend(path, name, args, this.parent, this.path);
4624
4499
  }
4625
4500
  // An intent is ROUTED: it names a job and walks until something answers. The verb
4626
4501
  // does not decide which scope answers — `opts.route` does, written here at the call
@@ -4631,9 +4506,6 @@ var Dispatcher = class {
4631
4506
  intentAtPath(path, name, args, opts) {
4632
4507
  return this.transactor.pushIntent(path, name, args, opts, this.parent);
4633
4508
  }
4634
- lookupTypeFor(name, inst) {
4635
- return this.transactor.comps.getCompFor(inst).scope.lookupComponent(name);
4636
- }
4637
4509
  };
4638
4510
  var EventContext = class extends Dispatcher {
4639
4511
  get name() {
@@ -4650,6 +4522,19 @@ var EventContext = class extends Dispatcher {
4650
4522
  fail(error) {
4651
4523
  return this.parent.walk === void 0 ? warnNotIntent("fail") : this.parent.walk.answer("Error", error);
4652
4524
  }
4525
+ // Reply to the sender of the MESSAGE being handled. An intent answers under a name
4526
+ // the runtime derives (`<name>Ok`), because the raiser asked a question and declared
4527
+ // arms for it; a message did not, so the replier names the reply itself and the
4528
+ // sender handles it like any other message. Refuses when nobody sent this — a view's
4529
+ // own `@on.*`, or a host sendAtRoot.
4530
+ sendReply(name, args) {
4531
+ const origin = this.parent?.origin ?? null;
4532
+ if (origin == null) {
4533
+ this.transactor.refuse("NO_SENDER", { name });
4534
+ return null;
4535
+ }
4536
+ return this.sendAtPath(origin, name, args);
4537
+ }
4653
4538
  // End the walk answering nothing — "served, and no answer".
4654
4539
  stop() {
4655
4540
  return this.parent.stop();
@@ -4661,8 +4546,6 @@ var EventContext = class extends Dispatcher {
4661
4546
  return this.parent.forward(opts);
4662
4547
  }
4663
4548
  };
4664
- var IntentContext = class extends Dispatcher {
4665
- };
4666
4549
  function warnNotIntent(verb) {
4667
4550
  console.warn(`ctx.${verb}() is only meaningful in an "intent" handler - ignored`);
4668
4551
  }
@@ -4671,8 +4554,8 @@ var PathChanges = class extends PathBuilder {
4671
4554
  super();
4672
4555
  this.dispatcher = dispatcher;
4673
4556
  }
4674
- send(name, args, opts) {
4675
- return this.dispatcher.sendAtPath(this.buildPath(), name, args, opts);
4557
+ send(name, args) {
4558
+ return this.dispatcher.sendAtPath(this.buildPath(), name, args);
4676
4559
  }
4677
4560
  intent(name, args, opts) {
4678
4561
  return this.dispatcher.intentAtPath(this.buildPath(), name, args, opts);
@@ -4829,9 +4712,10 @@ var App = class {
4829
4712
  }
4830
4713
  compile() {
4831
4714
  for (const Comp of this.comps.byId.values()) {
4832
- Comp.compile(this.ParseContext);
4833
- for (const key in Comp.views)
4834
- for (const name of Comp.views[key].ctx.genEventNames()) this._eventNames.add(name);
4715
+ const meta = Comp[COMPONENT];
4716
+ meta.compile(this.ParseContext);
4717
+ for (const key in meta.views)
4718
+ for (const name of meta.views[key].ctx.genEventNames()) this._eventNames.add(name);
4835
4719
  }
4836
4720
  this._compiled = true;
4837
4721
  }
@@ -4857,8 +4741,8 @@ var App = class {
4857
4741
  for (const name of this._eventNames)
4858
4742
  this.rootNode.removeEventListener(name, this, listenerOpts(name));
4859
4743
  }
4860
- sendAtRoot(name, args, opts) {
4861
- this.transactor.pushSend(new Path([]), name, args, opts);
4744
+ sendAtRoot(name, args) {
4745
+ this.transactor.pushSend(new Path([]), name, args);
4862
4746
  }
4863
4747
  registerComponents(comps, opts) {
4864
4748
  const scope = this.compStack.enter();
@@ -5012,6 +4896,182 @@ function collectAppClassesInSet(app) {
5012
4896
  return classes;
5013
4897
  }
5014
4898
 
4899
+ // src/cache.js
4900
+ var isWeakKey = (k) => k !== null && (typeof k === "object" || typeof k === "function");
4901
+ var NullDomCache = class {
4902
+ get(_keys, _cacheKey) {
4903
+ }
4904
+ set(_keys, _cacheKey, _v) {
4905
+ }
4906
+ evict() {
4907
+ return { hit: 0, miss: 0, badKey: 0 };
4908
+ }
4909
+ };
4910
+ var WeakMapDomCache = class {
4911
+ constructor() {
4912
+ this.hit = this.miss = this.badKey = 0;
4913
+ this.keysByLen = /* @__PURE__ */ new Map();
4914
+ }
4915
+ _returnValue(r) {
4916
+ if (r === void 0) this.miss += 1;
4917
+ else this.hit += 1;
4918
+ return r;
4919
+ }
4920
+ get(keys, cacheKey) {
4921
+ const len = keys.length;
4922
+ let cur = this.keysByLen.get(len);
4923
+ if (!cur) return this._returnValue(void 0);
4924
+ for (let i = 0; i < len - 1; i++) {
4925
+ cur = cur.get(keys[i]);
4926
+ if (!cur) return this._returnValue(void 0);
4927
+ }
4928
+ return this._returnValue(cur.get(keys[len - 1])?.[cacheKey]);
4929
+ }
4930
+ set(keys, cacheKey, v) {
4931
+ const len = keys.length;
4932
+ let cur = this.keysByLen.get(len);
4933
+ if (!cur) {
4934
+ cur = /* @__PURE__ */ new WeakMap();
4935
+ this.keysByLen.set(len, cur);
4936
+ }
4937
+ for (let i = 0; i < len - 1; i++) {
4938
+ const key = keys[i];
4939
+ let next = cur.get(key);
4940
+ if (!next) {
4941
+ if (!isWeakKey(key)) {
4942
+ this.badKey += 1;
4943
+ return;
4944
+ }
4945
+ next = /* @__PURE__ */ new WeakMap();
4946
+ cur.set(key, next);
4947
+ }
4948
+ cur = next;
4949
+ }
4950
+ const lastKey = keys[len - 1];
4951
+ const leaf = cur.get(lastKey);
4952
+ if (leaf) leaf[cacheKey] = v;
4953
+ else if (isWeakKey(lastKey)) cur.set(lastKey, { [cacheKey]: v });
4954
+ else this.badKey += 1;
4955
+ }
4956
+ evict() {
4957
+ const { hit, miss, badKey } = this;
4958
+ this.hit = this.miss = this.badKey = 0;
4959
+ this.keysByLen = /* @__PURE__ */ new Map();
4960
+ return { hit, miss, badKey };
4961
+ }
4962
+ };
4963
+
4964
+ // src/renderer.js
4965
+ var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk"];
4966
+ var Renderer = class {
4967
+ constructor(comps) {
4968
+ this.comps = comps;
4969
+ this.cache = new WeakMapDomCache();
4970
+ this.renderTag = h;
4971
+ }
4972
+ renderFragment(childs) {
4973
+ return new VFragment(childs);
4974
+ }
4975
+ renderComment(text) {
4976
+ return new VComment(text);
4977
+ }
4978
+ setNullCache() {
4979
+ this.cache = new NullDomCache();
4980
+ }
4981
+ renderToDOM(stack, val) {
4982
+ const rootNode = document.createElement("div");
4983
+ const rOpts = { document };
4984
+ render(h("DIV", null, [this.renderRoot(stack, val)]), rootNode, rOpts);
4985
+ return rootNode.childNodes[0];
4986
+ }
4987
+ renderToString(stack, val, cleanAttrs = true) {
4988
+ const dom = this.renderToDOM(stack, val);
4989
+ if (cleanAttrs) {
4990
+ const nodes = dom.querySelectorAll("[data-nid],[data-cid],[data-eid]");
4991
+ for (const { dataset } of nodes) for (const name of DATASET_ATTRS) delete dataset[name];
4992
+ }
4993
+ return dom.innerHTML;
4994
+ }
4995
+ renderRoot(stack, val, viewName = null) {
4996
+ const comp = this.comps.getCompFor(val);
4997
+ if (comp === null) return null;
4998
+ return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
4999
+ }
5000
+ renderIt(stack, node, key, viewName) {
5001
+ const comp = this.comps.getCompFor(stack.it);
5002
+ return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
5003
+ }
5004
+ // `node` is the parse node of the render site (`<x render>` / `render-it` /
5005
+ // `render-each`, or the view's root anode for the app root). It keys the
5006
+ // cache as a globally-unique object: node ids alone are unique only within a
5007
+ // single view, so the same value rendered by two components (e.g. through a
5008
+ // shared dynamic-var sequence) would otherwise collide in the cache.
5009
+ _rValComp(stack, val, comp, node, key, viewName) {
5010
+ const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}`;
5011
+ const cachePath = [node, val];
5012
+ stack._pushDynBindValuesToArray(cachePath, comp);
5013
+ const cachedNode = this.cache.get(cachePath, cacheKey);
5014
+ if (cachedNode) return cachedNode;
5015
+ const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
5016
+ const body = this.renderView(view, stack);
5017
+ if (body == null) return null;
5018
+ const meta = this._renderMetadata({
5019
+ $: "Comp",
5020
+ nid: node?.nodeId ?? null,
5021
+ cid: comp.id,
5022
+ vid: view.name
5023
+ });
5024
+ const dom = new VFragment([meta, body]);
5025
+ this.cache.set(cachePath, cacheKey, dom);
5026
+ return dom;
5027
+ }
5028
+ pushEachEntry(r, nid, attrName, key, dom) {
5029
+ r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
5030
+ }
5031
+ renderEachWhen(stack, iterInfo, view, nid) {
5032
+ const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
5033
+ const r = [];
5034
+ const it = stack.it;
5035
+ const renderOne = (key, value, attrName, binds) => {
5036
+ const cachePath = enricher ? [view, it, value] : [view, value];
5037
+ const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
5038
+ const cachedNode = this.cache.get(cachePath, cacheKey);
5039
+ if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
5040
+ else {
5041
+ const dom = this.renderView(view, stack.enter(value, binds, false));
5042
+ if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
5043
+ this.cache.set(cachePath, cacheKey, dom);
5044
+ }
5045
+ };
5046
+ walkLoopBindings(
5047
+ { seq, it, filter, loopWith, enricher, ctx: makeLoopCtx(stack, filter) },
5048
+ renderOne
5049
+ );
5050
+ return r;
5051
+ }
5052
+ renderView(view, stack) {
5053
+ let n = stack.binds[1];
5054
+ while (n !== null) {
5055
+ const b = n[0];
5056
+ if (b.isFrame) {
5057
+ if (stack.it !== b.it) break;
5058
+ console.error("recursion detected", stack.it, b.it);
5059
+ return new VComment("RECURSION AVOIDED");
5060
+ }
5061
+ n = n[1];
5062
+ }
5063
+ return view.render(stack, this);
5064
+ }
5065
+ _renderMetadata(info) {
5066
+ return new VComment(`§${JSON.stringify(info)}§`);
5067
+ }
5068
+ // Prefix a loop-less @enrich-with subtree with a boundary meta so event-path
5069
+ // reconstruction replays its binds (mirrors the §Each§ / §Comp§ metas).
5070
+ renderScopeMeta(nid, dom) {
5071
+ return new VFragment([this._renderMetadata({ $: "Scope", nid }), dom]);
5072
+ }
5073
+ };
5074
+
5015
5075
  // src/on.js
5016
5076
  var OP_KINDS = ["send", "intent"];
5017
5077
  function phaseOps(phase) {
@@ -5029,7 +5089,7 @@ function dispatchPhase(dispatcher, targetPath, phase, self) {
5029
5089
  const args = resolveArgs(op.args, self);
5030
5090
  switch (op.type) {
5031
5091
  case "send":
5032
- dispatcher.sendAtPath(targetPath, op.name, args, op.opts);
5092
+ dispatcher.sendAtPath(targetPath, op.name, args);
5033
5093
  break;
5034
5094
  case "intent":
5035
5095
  dispatcher.intentAtPath(targetPath, op.name, args, op.opts);