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.
@@ -237,9 +237,6 @@ var Step = class {
237
237
  lookup(_v, dval = null) {
238
238
  return dval;
239
239
  }
240
- setValue(root, _v) {
241
- return root;
242
- }
243
240
  setDraftValue(_root, _v) {
244
241
  }
245
242
  enterFrame(stack, _prev, next) {
@@ -268,9 +265,6 @@ var BindStep = class _BindStep extends Step {
268
265
  lookup(v, _dval) {
269
266
  return v;
270
267
  }
271
- setValue(_root, v) {
272
- return v;
273
- }
274
268
  enterFrame(stack, _prev, next) {
275
269
  return stack.enter(next, { ...this.binds }, false);
276
270
  }
@@ -308,9 +302,6 @@ var FieldStep = class extends Step {
308
302
  lookup(v, dval = null) {
309
303
  return readKey(v, this.field, dval);
310
304
  }
311
- setValue(root, v) {
312
- return (0, immer_exports.produce)(root, (draft) => this.setDraftValue(draft, v));
313
- }
314
305
  setDraftValue(root, v) {
315
306
  writeKey(root, this.field, v);
316
307
  }
@@ -333,9 +324,6 @@ var SeqStep = class extends Step {
333
324
  lookup(v, dval = null) {
334
325
  return readSeqKey(readKey(v, this.field, null), this.key, dval);
335
326
  }
336
- setValue(root, v) {
337
- return (0, immer_exports.produce)(root, (draft) => this.setDraftValue(draft, v));
338
- }
339
327
  setDraftValue(root, v) {
340
328
  const seq = readKey(root, this.field, null);
341
329
  if (seq != null) writeSeqKey(seq, this.key, v);
@@ -358,9 +346,6 @@ var SeqAccessStep = class extends Step {
358
346
  const key = readKey(v, this.keyField, NONE);
359
347
  return key !== NONE && seq !== NONE ? readSeqKey(seq, key, dval) : dval;
360
348
  }
361
- setValue(root, v) {
362
- return (0, immer_exports.produce)(root, (draft) => this.setDraftValue(draft, v));
363
- }
364
349
  setDraftValue(root, v) {
365
350
  const seq = readKey(root, this.seqField, NONE);
366
351
  const key = readKey(root, this.keyField, NONE);
@@ -388,9 +373,6 @@ var EachBindStep = class extends Step {
388
373
  lookup(v, _dval) {
389
374
  return v;
390
375
  }
391
- setValue(_root, v) {
392
- return v;
393
- }
394
376
  // Replay the renderer's per-item binds (key, value + any @enrich-with binds)
395
377
  // so a rebuilt stack matches the one @each rendered with.
396
378
  enterFrame(stack, _prev, next) {
@@ -426,10 +408,6 @@ var DynStep = class extends Step {
426
408
  warnRawDynStep("lookup", this);
427
409
  return dval;
428
410
  }
429
- setValue(root, _v) {
430
- warnRawDynStep("setValue", this);
431
- return root;
432
- }
433
411
  enterFrame(stack, _prev, _next) {
434
412
  warnRawDynStep("enterFrame", this);
435
413
  return stack;
@@ -500,7 +478,7 @@ var Path = class _Path {
500
478
  }
501
479
  // Resolve every field-keyed step (e.g. `SeqAccessStep`) against `root`, freezing the
502
480
  // key as it is *now* so a later lookup/setValue lands on the same item even if the
503
- // keyField changed meanwhile (e.g. the selected tab moved while a request was in
481
+ // keyField changed meanwhile (e.g. the selected tab moved while an intent was in
504
482
  // flight). Returns a new Path with those steps replaced; `this` if nothing pinned.
505
483
  // Must be called on a transaction path (no DynSteps — call toTransactionPath first).
506
484
  pinKeys(root) {
@@ -742,7 +720,6 @@ var K_FIELD = 4;
742
720
  var K_BIND = 8;
743
721
  var K_DYN = 16;
744
722
  var K_NAME = 32;
745
- var K_TYPE = 64;
746
723
  var K_SEQ = 256;
747
724
  var K_METHOD = 1024;
748
725
  var K_EVENT = 2048;
@@ -752,7 +729,7 @@ var G_COMPONENT = K_FIELD | K_SEQ | K_DYN;
752
729
  var G_SEQUENCE = K_FIELD | K_DYN;
753
730
  var G_PROVIDE = K_FIELD | K_SEQ;
754
731
  var G_FIELD = K_FIELD | K_METHOD | K_CONST | K_SEQ;
755
- var G_VALUE = K_FIELD | K_METHOD | K_BIND | K_DYN | K_NAME | K_TYPE | K_CONST;
732
+ var G_VALUE = K_FIELD | K_METHOD | K_BIND | K_DYN | K_NAME | K_CONST;
756
733
  var G_HANDLER_ARG = G_VALUE & ~K_NAME | K_EVENT;
757
734
  var G_ALL = G_VALUE | K_STRTPL | K_SEQ;
758
735
  function sizeOf(v) {
@@ -822,7 +799,6 @@ function parseToken(s, px) {
822
799
  if (m !== null) return new EventMemberVal(s.split(".").slice(1));
823
800
  }
824
801
  if (c0 >= 97 && c0 <= 122) return mkVal(s, NameVal);
825
- if (c0 >= 65 && c0 <= 90) return mkVal(s, TypeVal);
826
802
  return null;
827
803
  }
828
804
  function _parseSeqAccess(s, px) {
@@ -937,7 +913,6 @@ function kindOf(val) {
937
913
  if (val instanceof MethodVal) return K_METHOD;
938
914
  if (val instanceof BindVal) return K_BIND;
939
915
  if (val instanceof DynVal) return K_DYN;
940
- if (val instanceof TypeVal) return K_TYPE;
941
916
  if (val instanceof NameVal) return K_NAME;
942
917
  if (val instanceof EventMemberVal) return K_EVENT;
943
918
  return 0;
@@ -1051,9 +1026,6 @@ var NameVal = class extends VarVal {
1051
1026
  super();
1052
1027
  this.name = name;
1053
1028
  }
1054
- eval(stack) {
1055
- return stack.lookupName(this.name);
1056
- }
1057
1029
  toString() {
1058
1030
  return this.name;
1059
1031
  }
@@ -1074,11 +1046,6 @@ var mk404Handler = (stack, type, name) => function(...args) {
1074
1046
  else console.warn("handler not found", { type, name });
1075
1047
  return this;
1076
1048
  };
1077
- var TypeVal = class extends NameVal {
1078
- eval(stack) {
1079
- return stack.lookupType(this.name);
1080
- }
1081
- };
1082
1049
  var keyIs = (k) => (e) => e.key === k;
1083
1050
  var macCtrl = (e) => isMac && e.metaKey || e.ctrlKey;
1084
1051
  var nullSafe = (fn) => (e, stack) => {
@@ -1211,9 +1178,6 @@ var Attributes = class {
1211
1178
  constructor(items) {
1212
1179
  this.items = items;
1213
1180
  }
1214
- eval(_stack) {
1215
- return {};
1216
- }
1217
1181
  static parse(attributes, px, parseAll = false) {
1218
1182
  return getAttrParser(px).parse(attributes, parseAll);
1219
1183
  }
@@ -1223,11 +1187,26 @@ var Attributes = class {
1223
1187
  };
1224
1188
  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";
1225
1189
  var booleanAttrs = new Set(booleanAttrsRaw.split(","));
1190
+ function parseDirectiveValue(px, directiveName, source, parser) {
1191
+ const val = parser(source, px);
1192
+ if (val === null) {
1193
+ px.onParseIssue("bad-value", {
1194
+ role: "directive",
1195
+ directive: directiveName,
1196
+ value: source
1197
+ });
1198
+ }
1199
+ return val;
1200
+ }
1201
+ function parseIterationDirectives(attributes, px) {
1202
+ const parseNamed = (name) => {
1203
+ const attr = attributes.getNamedItem(`@${name}`);
1204
+ return attr ? parseDirectiveValue(px, name, attr.value, parseAlterHandler) : null;
1205
+ };
1206
+ return { whenVal: parseNamed("when"), loopWithVal: parseNamed("loop-with") };
1207
+ }
1226
1208
  var AttrParser = class {
1227
1209
  constructor(px) {
1228
- this.clear(px);
1229
- }
1230
- clear(px) {
1231
1210
  this.px = px;
1232
1211
  this.attrs = null;
1233
1212
  this.hasDynamic = false;
@@ -1282,12 +1261,7 @@ var AttrParser = class {
1282
1261
  }
1283
1262
  }
1284
1263
  _parseDirectiveValue(directiveName, s, parserFn) {
1285
- const val = parserFn(s, this.px);
1286
- if (val === null) {
1287
- const info = { role: "directive", directive: directiveName, value: s };
1288
- this.px.onParseIssue("bad-value", info);
1289
- }
1290
- return val;
1264
+ return parseDirectiveValue(this.px, directiveName, s, parserFn);
1291
1265
  }
1292
1266
  parseDirective(s, directiveName) {
1293
1267
  switch (directiveName) {
@@ -1451,11 +1425,8 @@ var IfAttr = class extends BaseAttr {
1451
1425
  return this.condVal.eval(stack) ? this.thenVal.eval(stack) : this.elseVal.eval(stack);
1452
1426
  }
1453
1427
  };
1454
- var _attrParser = null;
1455
1428
  function getAttrParser(px) {
1456
- _attrParser ??= new AttrParser(px);
1457
- _attrParser.clear(px);
1458
- return _attrParser;
1429
+ return new AttrParser(px);
1459
1430
  }
1460
1431
  var EventHandler = class _EventHandler {
1461
1432
  constructor(handlerVal, args = []) {
@@ -1479,70 +1450,77 @@ var IntentHandler = class {
1479
1450
  }
1480
1451
  };
1481
1452
 
1482
- // src/cache.js
1483
- var isWeakKey = (k) => k !== null && (typeof k === "object" || typeof k === "function");
1484
- var NullDomCache = class {
1485
- get(_keys, _cacheKey) {
1486
- }
1487
- set(_keys, _cacheKey, _v) {
1488
- }
1489
- evict() {
1490
- return { hit: 0, miss: 0, badKey: 0 };
1491
- }
1453
+ // src/iteration.js
1454
+ var SEQ_INFO = /* @__PURE__ */ Symbol.for("tutuca.seqInfo");
1455
+ var normalizeRange = (start, end, size) => {
1456
+ let s = start == null ? 0 : start < 0 ? size + start : start;
1457
+ let e = end == null ? size : end < 0 ? size + end : end;
1458
+ s = s < 0 ? 0 : s > size ? size : s;
1459
+ e = e < 0 ? 0 : e > size ? size : e;
1460
+ return [s, e < s ? s : e];
1492
1461
  };
1493
- var WeakMapDomCache = class {
1494
- constructor() {
1495
- this.hit = this.miss = this.badKey = 0;
1496
- this.keysByLen = /* @__PURE__ */ new Map();
1497
- }
1498
- _returnValue(r) {
1499
- if (r === void 0) this.miss += 1;
1500
- else this.hit += 1;
1501
- return r;
1502
- }
1503
- get(keys, cacheKey) {
1504
- const len = keys.length;
1505
- let cur = this.keysByLen.get(len);
1506
- if (!cur) return this._returnValue(void 0);
1507
- for (let i = 0; i < len - 1; i++) {
1508
- cur = cur.get(keys[i]);
1509
- if (!cur) return this._returnValue(void 0);
1510
- }
1511
- return this._returnValue(cur.get(keys[len - 1])?.[cacheKey]);
1512
- }
1513
- set(keys, cacheKey, v) {
1514
- const len = keys.length;
1515
- let cur = this.keysByLen.get(len);
1516
- if (!cur) {
1517
- cur = /* @__PURE__ */ new WeakMap();
1518
- this.keysByLen.set(len, cur);
1519
- }
1520
- for (let i = 0; i < len - 1; i++) {
1521
- const key = keys[i];
1522
- let next = cur.get(key);
1523
- if (!next) {
1524
- if (!isWeakKey(key)) {
1525
- this.badKey += 1;
1526
- return;
1527
- }
1528
- next = /* @__PURE__ */ new WeakMap();
1529
- cur.set(key, next);
1530
- }
1531
- cur = next;
1532
- }
1533
- const lastKey = keys[len - 1];
1534
- const leaf = cur.get(lastKey);
1535
- if (leaf) leaf[cacheKey] = v;
1536
- else if (isWeakKey(lastKey)) cur.set(lastKey, { [cacheKey]: v });
1537
- else this.badKey += 1;
1538
- }
1539
- evict() {
1540
- const { hit, miss, badKey } = this;
1541
- this.hit = this.miss = this.badKey = 0;
1542
- this.keysByLen = /* @__PURE__ */ new Map();
1543
- return { hit, miss, badKey };
1462
+ var nativeIndexedIter = (seq, visit, start, end) => {
1463
+ const [s, e] = normalizeRange(start, end, seqSize(seq));
1464
+ for (let i = s; i < e; i++) visit(i, seq[i], "si");
1465
+ };
1466
+ var nativeKeyedIter = (seq, visit, start, end) => {
1467
+ const [s, e] = normalizeRange(start, end, seqSize(seq));
1468
+ let i = 0;
1469
+ for (const [key, value] of seqEntries(seq)) {
1470
+ if (i >= e) break;
1471
+ if (i >= s) visit(key, value, "sk");
1472
+ i++;
1544
1473
  }
1545
1474
  };
1475
+ var unknownIter = () => {
1476
+ };
1477
+ var getSeqInfo = (seq) => isIndexedSeq(seq) ? nativeIndexedIter : isKeyedSeq(seq) || isSetSeq(seq) ? nativeKeyedIter : seq?.[SEQ_INFO] ?? unknownIter;
1478
+ var filterAlwaysTrue = (_key, _value, _iterData) => true;
1479
+ var nullLoopWith = (seq) => ({ iterData: { seq } });
1480
+ var unpackLoopResult = (result, seq) => {
1481
+ const value = result ?? {};
1482
+ return {
1483
+ iterData: value.iterData ?? { seq },
1484
+ start: value.start,
1485
+ end: value.end,
1486
+ keys: value.keys
1487
+ };
1488
+ };
1489
+ var makeLoopCtx = (stack, filter) => ({
1490
+ lookup: (name) => stack.lookupBind(name),
1491
+ filter: (key, value, iterData) => filter.call(stack.it, key, value, iterData)
1492
+ });
1493
+ var callEnricher = (enricher, it, binds, key, value, iterData) => {
1494
+ enricher.call(it, binds, key, value, iterData);
1495
+ console.assert(
1496
+ binds.key === key && binds.value === value,
1497
+ "@enrich-with handlers must not overwrite binds.key or binds.value"
1498
+ );
1499
+ binds.key = key;
1500
+ binds.value = value;
1501
+ };
1502
+ var visitKeys = (seq, keys, visit) => {
1503
+ const attrName = isIndexedSeq(seq) ? "si" : "sk";
1504
+ for (const key of keys) visit(key, seqGet(seq, key), attrName);
1505
+ };
1506
+ function walkLoopBindings({ seq, it, filter, loopWith, enricher, ctx }, visit) {
1507
+ const { iterData, start, end, keys } = unpackLoopResult(loopWith.call(it, seq, ctx), seq);
1508
+ const visitOne = (key, value, attrName) => {
1509
+ const binds = { key, value };
1510
+ if (enricher) callEnricher(enricher, it, binds, key, value, iterData);
1511
+ visit(key, value, attrName, binds);
1512
+ };
1513
+ if (keys) visitKeys(seq, keys, visitOne);
1514
+ else
1515
+ getSeqInfo(seq)(
1516
+ seq,
1517
+ (key, value, attrName) => {
1518
+ if (filter.call(it, key, value, iterData)) visitOne(key, value, attrName);
1519
+ },
1520
+ start,
1521
+ end
1522
+ );
1523
+ }
1546
1524
 
1547
1525
  // src/vdom.js
1548
1526
  var HTML_NS = "http://www.w3.org/1999/xhtml";
@@ -1887,189 +1865,11 @@ function h(tagName, properties, children, namespace) {
1887
1865
  return new VNode(tag, props, normalizedChildren, key, namespace);
1888
1866
  }
1889
1867
 
1890
- // src/renderer.js
1891
- var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk"];
1892
- var Renderer = class {
1893
- constructor(comps) {
1894
- this.comps = comps;
1895
- this.cache = new WeakMapDomCache();
1896
- this.renderTag = h;
1897
- }
1898
- renderFragment(childs) {
1899
- return new VFragment(childs);
1900
- }
1901
- renderComment(text) {
1902
- return new VComment(text);
1903
- }
1904
- setNullCache() {
1905
- this.cache = new NullDomCache();
1906
- }
1907
- renderToDOM(stack, val) {
1908
- const rootNode = document.createElement("div");
1909
- const rOpts = { document };
1910
- render(h("DIV", null, [this.renderRoot(stack, val)]), rootNode, rOpts);
1911
- return rootNode.childNodes[0];
1912
- }
1913
- renderToString(stack, val, cleanAttrs = true) {
1914
- const dom = this.renderToDOM(stack, val);
1915
- if (cleanAttrs) {
1916
- const nodes = dom.querySelectorAll("[data-nid],[data-cid],[data-eid]");
1917
- for (const { dataset } of nodes) for (const name of DATASET_ATTRS) delete dataset[name];
1918
- }
1919
- return dom.innerHTML;
1920
- }
1921
- renderRoot(stack, val, viewName = null) {
1922
- const comp = this.comps.getCompFor(val);
1923
- if (comp === null) return null;
1924
- return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
1925
- }
1926
- renderIt(stack, node, key, viewName) {
1927
- const comp = this.comps.getCompFor(stack.it);
1928
- return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
1929
- }
1930
- // `node` is the parse node of the render site (`<x render>` / `render-it` /
1931
- // `render-each`, or the view's root anode for the app root). It keys the
1932
- // cache as a globally-unique object: node ids alone are unique only within a
1933
- // single view, so the same value rendered by two components (e.g. through a
1934
- // shared dynamic-var sequence) would otherwise collide in the cache.
1935
- _rValComp(stack, val, comp, node, key, viewName) {
1936
- const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}`;
1937
- const cachePath = [node, val];
1938
- stack._pushDynBindValuesToArray(cachePath, comp);
1939
- const cachedNode = this.cache.get(cachePath, cacheKey);
1940
- if (cachedNode) return cachedNode;
1941
- const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
1942
- const body = this.renderView(view, stack);
1943
- if (body == null) return null;
1944
- const meta = this._renderMetadata({
1945
- $: "Comp",
1946
- nid: node?.nodeId ?? null,
1947
- cid: comp.id,
1948
- vid: view.name
1949
- });
1950
- const dom = new VFragment([meta, body]);
1951
- this.cache.set(cachePath, cacheKey, dom);
1952
- return dom;
1953
- }
1954
- pushEachEntry(r, nid, attrName, key, dom) {
1955
- r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
1956
- }
1957
- renderEachWhen(stack, iterInfo, view, nid) {
1958
- const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
1959
- const r = [];
1960
- const it = stack.it;
1961
- const { iterData, start, end, keys } = unpackLoopResult(
1962
- loopWith.call(it, seq, makeLoopCtx(stack, filter)),
1963
- seq
1964
- );
1965
- const renderOne = (key, value, attrName) => {
1966
- const cachePath = enricher ? [view, it, value] : [view, value];
1967
- const binds = { key, value };
1968
- const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
1969
- if (enricher) callEnricher(enricher, it, binds, key, value, iterData);
1970
- const cachedNode = this.cache.get(cachePath, cacheKey);
1971
- if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
1972
- else {
1973
- const dom = this.renderView(view, stack.enter(value, binds, false));
1974
- if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
1975
- this.cache.set(cachePath, cacheKey, dom);
1976
- }
1977
- };
1978
- if (keys) keysIter(seq, renderOne, keys);
1979
- else
1980
- getSeqInfo(seq)(
1981
- seq,
1982
- (key, value, attrName) => {
1983
- if (filter.call(it, key, value, iterData)) renderOne(key, value, attrName);
1984
- },
1985
- start,
1986
- end
1987
- );
1988
- return r;
1989
- }
1990
- renderView(view, stack) {
1991
- let n = stack.binds[1];
1992
- while (n !== null) {
1993
- const b = n[0];
1994
- if (b.isFrame) {
1995
- if (stack.it !== b.it) break;
1996
- console.error("recursion detected", stack.it, b.it);
1997
- return new VComment("RECURSION AVOIDED");
1998
- }
1999
- n = n[1];
2000
- }
2001
- return view.render(stack, this);
2002
- }
2003
- _renderMetadata(info) {
2004
- return new VComment(`§${JSON.stringify(info)}§`);
2005
- }
2006
- // Prefix a loop-less @enrich-with subtree with a boundary meta so event-path
2007
- // reconstruction replays its binds (mirrors the §Each§ / §Comp§ metas).
2008
- renderScopeMeta(nid, dom) {
2009
- return new VFragment([this._renderMetadata({ $: "Scope", nid }), dom]);
2010
- }
2011
- };
2012
- var getSeqInfo = (seq) => isIndexedSeq(seq) ? nativeIndexedIter : isKeyedSeq(seq) || isSetSeq(seq) ? nativeKeyedIter : seq?.[SEQ_INFO] ?? unkIter;
2013
- var normalizeRange = (start, end, size) => {
2014
- let s = start == null ? 0 : start < 0 ? size + start : start;
2015
- let e = end == null ? size : end < 0 ? size + end : end;
2016
- s = s < 0 ? 0 : s > size ? size : s;
2017
- e = e < 0 ? 0 : e > size ? size : e;
2018
- return [s, e < s ? s : e];
2019
- };
2020
- var callEnricher = (enricher, it, binds, key, value, iterData) => {
2021
- enricher.call(it, binds, key, value, iterData);
2022
- console.assert(
2023
- binds.key === key && binds.value === value,
2024
- "@enrich-with handlers must not overwrite binds.key or binds.value"
2025
- );
2026
- binds.key = key;
2027
- binds.value = value;
2028
- };
2029
- var filterAlwaysTrue = (_v, _k, _seq) => true;
2030
- var nullLoopWith = (seq) => ({ iterData: { seq } });
2031
- var unpackLoopResult = (result, seq) => {
2032
- const r = result ?? {};
2033
- return { iterData: r.iterData ?? { seq }, start: r.start, end: r.end, keys: r.keys };
2034
- };
2035
- var keysIter = (seq, visit, keys) => {
2036
- const attrName = isIndexedSeq(seq) ? "si" : "sk";
2037
- for (const key of keys) visit(key, seqGet(seq, key), attrName);
2038
- };
2039
- var makeLoopCtx = (stack, filter) => ({
2040
- lookup: (name) => stack.lookupBind(name),
2041
- filter: (key, value, iterData) => filter.call(stack.it, key, value, iterData)
2042
- });
2043
- var nativeIndexedIter = (seq, visit, start, end) => {
2044
- const [s, e] = normalizeRange(start, end, seqSize(seq));
2045
- for (let i = s; i < e; i++) visit(i, seq[i], "si");
2046
- };
2047
- var nativeKeyedIter = (seq, visit, start, end) => {
2048
- const [s, e] = normalizeRange(start, end, seqSize(seq));
2049
- let i = 0;
2050
- for (const [k, v] of seqEntries(seq)) {
2051
- if (i >= e) break;
2052
- if (i >= s) visit(k, v, "sk");
2053
- i++;
2054
- }
2055
- };
2056
- var unkIter = () => {
2057
- };
2058
- var SEQ_INFO = /* @__PURE__ */ Symbol.for("tutuca.seqInfo");
2059
-
2060
1868
  // src/anode.js
2061
1869
  function resolveDynProducer(comp, name) {
2062
- let producerComp, producerProvide;
2063
- const lk = comp?.lookup?.[name];
2064
- if (lk != null) {
2065
- producerComp = comp.scope?.lookupComponent(lk.compName);
2066
- producerProvide = producerComp?.provide?.[lk.provideName];
2067
- } else {
2068
- const p = comp?.provide?.[name];
2069
- if (p == null) return null;
2070
- producerComp = comp;
2071
- producerProvide = p;
2072
- }
1870
+ const own = comp?.provide?.[name];
1871
+ const producerComp = own != null ? comp : comp?.scope?.lookupProvider(name) ?? null;
1872
+ const producerProvide = own ?? producerComp?.provide?.[name];
2073
1873
  if (producerComp == null || producerProvide == null) return null;
2074
1874
  const pi = producerProvide.val?.toPathItem?.() ?? null;
2075
1875
  return { producerCompId: producerComp.id, producerSteps: pi ? [pi] : [] };
@@ -2430,16 +2230,10 @@ function parseRenderEach(px, value, as, attrs) {
2430
2230
  const seqVal = parseXOpVal("render-each", value, px, parseSequence);
2431
2231
  if (seqVal === null) return null;
2432
2232
  const renderIt = px.addNode(RenderItNode, as);
2433
- const attrParser = getAttrParser(px);
2434
- const eachAttr = attrParser.pushWrapper("each", value, seqVal);
2435
- attrParser.eachAttr = eachAttr;
2436
- const when = attrs.getNamedItem("@when");
2437
- if (when) attrParser._parseWhen(when.value);
2438
- const lWith = attrs.getNamedItem("@loop-with");
2439
- if (lWith) attrParser._parseLoopWith(lWith.value);
2233
+ const { whenVal, loopWithVal } = parseIterationDirectives(attrs, px);
2440
2234
  const each = px.addNodeIf(EachNode, seqVal);
2441
- each.iterInfo.whenVal = eachAttr.whenVal ?? null;
2442
- each.iterInfo.loopWithVal = eachAttr.loopWithVal ?? null;
2235
+ each.iterInfo.whenVal = whenVal;
2236
+ each.iterInfo.loopWithVal = loopWithVal;
2443
2237
  each.fromRenderEach = true;
2444
2238
  each.wrapNode(renderIt);
2445
2239
  return each;
@@ -2906,7 +2700,7 @@ var Components = class {
2906
2700
  }
2907
2701
  compileStyles() {
2908
2702
  const styles = [];
2909
- for (const comp of this.byId.values()) styles.push(comp.compileStyle());
2703
+ for (const Comp of this.byId.values()) styles.push(Comp[COMPONENT].compileStyle());
2910
2704
  return styles.join("\n");
2911
2705
  }
2912
2706
  };
@@ -2965,32 +2759,39 @@ var ComponentStack = class _ComponentStack {
2965
2759
  lookupComponent(name) {
2966
2760
  return this.byName[name] ?? this.parent?.lookupComponent(name) ?? null;
2967
2761
  }
2762
+ // The component in this scope chain that provides `name`, innermost first. A lookup
2763
+ // names a value without naming its producer, so the render-target teleport
2764
+ // (resolveDynProducer) recovers the producer here instead. Sound because
2765
+ // PROVIDE_NAME_COLLISION keeps a provide name to one producer per chain; with two,
2766
+ // this returns the innermost and the linter has already reported the ambiguity.
2767
+ lookupProvider(name) {
2768
+ for (const compName in this.byName) {
2769
+ const Comp = this.byName[compName];
2770
+ if (Comp.provide?.[name] !== void 0) return Comp;
2771
+ }
2772
+ return this.parent?.lookupProvider(name) ?? null;
2773
+ }
2968
2774
  lookupMacro(name) {
2969
2775
  return this.macros[name] ?? this.parent?.lookupMacro(name) ?? null;
2970
2776
  }
2971
2777
  };
2972
2778
  var ProvideInfo = class {
2973
- constructor(name, val, symbol) {
2779
+ constructor(name, val) {
2974
2780
  this.name = name;
2975
2781
  this.val = val;
2976
- this.symbol = symbol;
2977
2782
  }
2978
2783
  };
2979
2784
  var LookupInfo = class {
2980
- constructor(name, compName, provideName, val) {
2785
+ constructor(name, val) {
2981
2786
  this.name = name;
2982
- this.compName = compName;
2983
- this.provideName = provideName;
2984
2787
  this.val = val;
2985
- this._sym = void 0;
2986
- }
2987
- getProducerSymbol(stack) {
2988
- if (this._sym === void 0)
2989
- this._sym = stack.lookupType(this.compName)?.provide?.[this.provideName]?.symbol ?? null;
2990
- return this._sym;
2991
2788
  }
2992
2789
  };
2993
2790
  var isString = (v) => typeof v === "string";
2791
+ var isTypeName = (s) => {
2792
+ const c = s.charCodeAt(0);
2793
+ return c >= 65 && c <= 90;
2794
+ };
2994
2795
  var _rawSpecKeys = "name view style commonStyle globalStyle receive intent alter views provide lookup fields methods statics";
2995
2796
  var KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
2996
2797
  var _compId = 0;
@@ -3011,8 +2812,9 @@ var Component = class {
3011
2812
  this.views[name] = new View(name, view, style);
3012
2813
  }
3013
2814
  this._rawProvide = o.provide ?? {};
3014
- this._rawLookup = o.lookup ?? {};
2815
+ this._rawLookup = o.lookup ?? [];
3015
2816
  this.provide = {};
2817
+ this.provideType = {};
3016
2818
  this.lookup = {};
3017
2819
  this.scope = null;
3018
2820
  this.spec = o;
@@ -3024,25 +2826,23 @@ var Component = class {
3024
2826
  this.views[name].compile(new ParseContext2(), this.scope, this.id);
3025
2827
  const ctx = this.views.main.ctx;
3026
2828
  for (const key in this._rawProvide) {
2829
+ if (isTypeName(key)) {
2830
+ if (this._rawProvide[key] === "self") this.provideType[key] = this.Class;
2831
+ continue;
2832
+ }
3027
2833
  const val = parseProvide(this._rawProvide[key], ctx);
3028
- if (val) this.provide[key] = new ProvideInfo(key, val, Symbol(key));
2834
+ if (val) this.provide[key] = new ProvideInfo(key, val);
3029
2835
  }
3030
- for (const key in this._rawLookup) {
3031
- const linfo = this._rawLookup[key];
3032
- const forStr = isString(linfo) ? linfo : isString(linfo?.for) ? linfo.for : null;
3033
- const [compName, provideName] = forStr === null ? [] : forStr.split(".");
3034
- if (!isString(compName) || !isString(provideName)) continue;
3035
- const defStr = isString(linfo?.default) ? linfo.default : null;
3036
- const val = defStr === null ? null : parseField(defStr, ctx);
3037
- this.lookup[key] = new LookupInfo(key, compName, provideName, val);
2836
+ for (const entry of this._rawLookup) {
2837
+ const name = isString(entry) ? entry : isString(entry?.name) ? entry.name : null;
2838
+ if (name === null) continue;
2839
+ const defStr = isString(entry?.default) ? entry.default : null;
2840
+ this.lookup[name] = new LookupInfo(name, defStr === null ? null : parseField(defStr, ctx));
3038
2841
  }
3039
2842
  for (const key in this.lookup)
3040
2843
  if (this.provide[key] !== void 0)
3041
2844
  console.warn("name declared in both provide and lookup", this.name, key);
3042
2845
  }
3043
- make(args, opts) {
3044
- return this.Class.make(args, opts ?? { scope: this.scope });
3045
- }
3046
2846
  getView(name) {
3047
2847
  return this.views[name] ?? this.views.main;
3048
2848
  }
@@ -5345,13 +5145,19 @@ var KNOWN_DIRECTIVE_NAMES = /* @__PURE__ */ new Set([
5345
5145
  "then",
5346
5146
  "else"
5347
5147
  ]);
5148
+ var isTypeName2 = (s) => {
5149
+ const c = s.charCodeAt(0);
5150
+ return c >= 65 && c <= 90;
5151
+ };
5348
5152
  var ALT_HANDLER_NOT_DEFINED = "ALT_HANDLER_NOT_DEFINED";
5349
5153
  var ALT_HANDLER_NOT_REFERENCED = "ALT_HANDLER_NOT_REFERENCED";
5350
5154
  var DYN_VAL_NOT_DEFINED = "DYN_VAL_NOT_DEFINED";
5351
5155
  var DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED";
5352
5156
  var PROVIDE_NOT_ADDRESSABLE = "PROVIDE_NOT_ADDRESSABLE";
5157
+ var PROVIDE_TYPE_BAD_SHAPE = "PROVIDE_TYPE_BAD_SHAPE";
5158
+ var PROVIDE_NAME_COLLISION = "PROVIDE_NAME_COLLISION";
5353
5159
  var LOOKUP_BAD_SHAPE = "LOOKUP_BAD_SHAPE";
5354
- var LOOKUP_TARGET_MALFORMED = "LOOKUP_TARGET_MALFORMED";
5160
+ var LOOKUP_NO_PROVIDER = "LOOKUP_NO_PROVIDER";
5355
5161
  var RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP";
5356
5162
  var UNKNOWN_EVENT_MODIFIER = "UNKNOWN_EVENT_MODIFIER";
5357
5163
  var RECEIVE_HANDLER_NOT_IMPLEMENTED = "RECEIVE_HANDLER_NOT_IMPLEMENTED";
@@ -5471,7 +5277,11 @@ function checkComponent(Comp, lx = new LintContext(), { wellKnownExtras = EMPTY_
5471
5277
  checkFieldDeclarations(lx, Comp);
5472
5278
  checkFieldMethodNameCollisions(lx, Comp);
5473
5279
  checkProvidesAreAddressable(lx, Comp);
5280
+ checkProvidedTypes(lx, Comp);
5281
+ checkProvideNameCollisions(lx, Comp);
5474
5282
  checkLookupShapes(lx, Comp);
5283
+ checkLookupTypesResolve(lx, Comp);
5284
+ checkLookupsHaveProviders(lx, Comp);
5475
5285
  checkHandlersNotAsync(lx, Comp);
5476
5286
  checkHandlerNameCollisions(lx, Comp);
5477
5287
  checkScopedStyleTopLevel(lx, Comp);
@@ -5806,16 +5616,6 @@ var ATTR_VAL_CHECKERS = {
5806
5616
  recurse(val.seqVal);
5807
5617
  recurse(val.keyVal);
5808
5618
  },
5809
- TypeVal({ lx, val, env, errCtx }) {
5810
- if (env.scope.lookupComponent(val.name) === null)
5811
- reportUnknownName(
5812
- lx,
5813
- UNKNOWN_COMPONENT_NAME,
5814
- val.name,
5815
- scopeKeysAlong(env.scope, "byName"),
5816
- errCtx
5817
- );
5818
- },
5819
5619
  // NameVals on a macro call-site attribute are macro-param bindings, not
5820
5620
  // handler args — their role is determined inside the macro body after
5821
5621
  // ^-substitution, where re-parsing handles validation. No checker: a NameVal
@@ -6149,48 +5949,90 @@ function checkScopedStyleTopLevel(lx, Comp) {
6149
5949
  }
6150
5950
  function checkProvidesAreAddressable(lx, Comp) {
6151
5951
  for (const name in Comp._rawProvide) {
5952
+ if (isTypeName2(name)) continue;
6152
5953
  if (Comp.provide[name] === void 0) {
6153
5954
  lx.error(PROVIDE_NOT_ADDRESSABLE, { name, value: Comp._rawProvide[name] });
6154
5955
  }
6155
5956
  }
6156
5957
  }
6157
- var KNOWN_LOOKUP_KEYS = /* @__PURE__ */ new Set(["for", "default"]);
5958
+ function checkProvidedTypes(lx, Comp) {
5959
+ for (const name in Comp._rawProvide) {
5960
+ if (!isTypeName2(name)) continue;
5961
+ const raw = Comp._rawProvide[name];
5962
+ if (raw !== "self") lx.error(PROVIDE_TYPE_BAD_SHAPE, { name, value: raw });
5963
+ }
5964
+ }
5965
+ function checkProvideNameCollisions(lx, Comp) {
5966
+ const scope = Comp.scope;
5967
+ if (!scope) return;
5968
+ for (const name in Comp.provide) {
5969
+ for (let s = scope; s; s = s.parent) {
5970
+ for (const otherName in s.byName) {
5971
+ const Other = s.byName[otherName];
5972
+ if (Other !== Comp && Other.provide?.[name] !== void 0)
5973
+ lx.error(PROVIDE_NAME_COLLISION, { name, other: Other.name });
5974
+ }
5975
+ }
5976
+ }
5977
+ }
5978
+ var KNOWN_LOOKUP_KEYS = /* @__PURE__ */ new Set(["name", "default"]);
6158
5979
  function checkLookupShapes(lx, Comp) {
6159
- for (const name in Comp._rawLookup) {
6160
- const raw = Comp._rawLookup[name];
6161
- let target;
6162
- if (typeof raw === "string") {
6163
- target = raw;
6164
- } else if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
5980
+ const raw = Comp._rawLookup;
5981
+ if (!Array.isArray(raw)) {
5982
+ if (raw !== void 0 && Object.keys(raw).length > 0)
5983
+ lx.error(LOOKUP_BAD_SHAPE, { name: "lookup", problem: "must be a list of names" });
5984
+ return;
5985
+ }
5986
+ for (const entry of raw) {
5987
+ if (typeof entry === "string") continue;
5988
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
6165
5989
  lx.error(LOOKUP_BAD_SHAPE, {
6166
- name,
6167
- problem: 'must be a "Producer.provideName" string or a { for, default } object'
5990
+ name: String(entry),
5991
+ problem: 'must be a "name" string or a { name, default } object'
5992
+ });
5993
+ continue;
5994
+ }
5995
+ const extra = Object.keys(entry).filter((k) => !KNOWN_LOOKUP_KEYS.has(k));
5996
+ if (extra.length > 0) {
5997
+ lx.error(LOOKUP_BAD_SHAPE, {
5998
+ name: String(entry.name),
5999
+ problem: `unknown key(s): ${extra.join(", ")}`
6168
6000
  });
6169
6001
  continue;
6170
- } else {
6171
- const extra = Object.keys(raw).filter((k) => !KNOWN_LOOKUP_KEYS.has(k));
6172
- if (extra.length > 0) {
6173
- lx.error(LOOKUP_BAD_SHAPE, { name, problem: `unknown key(s): ${extra.join(", ")}` });
6174
- continue;
6175
- }
6176
- if (typeof raw.for !== "string") {
6177
- lx.error(LOOKUP_BAD_SHAPE, { name, problem: "'for' is required and must be a string" });
6178
- continue;
6179
- }
6180
- if (raw.default !== void 0 && typeof raw.default !== "string") {
6181
- lx.error(LOOKUP_BAD_SHAPE, { name, problem: "'default' must be a string" });
6182
- continue;
6183
- }
6184
- target = raw.for;
6185
6002
  }
6186
- const parts = target.split(".");
6187
- if (parts.length !== 2 || !parts[0] || !parts[1]) {
6188
- lx.error(LOOKUP_TARGET_MALFORMED, { name, target });
6003
+ if (typeof entry.name !== "string" || entry.name === "") {
6004
+ lx.error(LOOKUP_BAD_SHAPE, {
6005
+ name: String(entry.name),
6006
+ problem: "'name' is required and must be a string"
6007
+ });
6008
+ continue;
6189
6009
  }
6010
+ if (entry.default !== void 0 && typeof entry.default !== "string")
6011
+ lx.error(LOOKUP_BAD_SHAPE, { name: entry.name, problem: "'default' must be a string" });
6012
+ }
6013
+ }
6014
+ function checkLookupsHaveProviders(lx, Comp) {
6015
+ const scope = Comp.scope;
6016
+ if (!scope) return;
6017
+ for (const name in Comp.lookup) {
6018
+ if (isTypeName2(name)) continue;
6019
+ if (scope.lookupProvider?.(name)) continue;
6020
+ const info = { name, hasDefault: Comp.lookup[name].val != null };
6021
+ if (info.hasDefault) lx.hint(LOOKUP_NO_PROVIDER, info);
6022
+ else lx.error(LOOKUP_NO_PROVIDER, info);
6023
+ }
6024
+ }
6025
+ function checkLookupTypesResolve(lx, Comp) {
6026
+ if (!Comp.scope) return;
6027
+ for (const name in Comp.lookup) {
6028
+ if (!isTypeName2(name)) continue;
6029
+ if (Comp.scope.lookupComponent(name) === null)
6030
+ reportUnknownName(lx, UNKNOWN_COMPONENT_NAME, name, scopeKeysAlong(Comp.scope, "byName"), {});
6190
6031
  }
6191
6032
  }
6192
6033
  function checkUnreferencedDynamics(lx, Comp, referencedDynamics) {
6193
6034
  for (const name in Comp.lookup) {
6035
+ if (isTypeName2(name)) continue;
6194
6036
  if (!referencedDynamics.has(name)) {
6195
6037
  lx.hint(DYN_ALIAS_NOT_REFERENCED, { name });
6196
6038
  }
@@ -6255,7 +6097,7 @@ function dispatchPhase(dispatcher, targetPath, phase, self) {
6255
6097
  const args = resolveArgs(op.args, self);
6256
6098
  switch (op.type) {
6257
6099
  case "send":
6258
- dispatcher.sendAtPath(targetPath, op.name, args, op.opts);
6100
+ dispatcher.sendAtPath(targetPath, op.name, args);
6259
6101
  break;
6260
6102
  case "intent":
6261
6103
  dispatcher.intentAtPath(targetPath, op.name, args, op.opts);
@@ -6267,11 +6109,6 @@ function dispatchPhase(dispatcher, targetPath, phase, self) {
6267
6109
  // src/oo.js
6268
6110
  var BAD_VALUE2 = /* @__PURE__ */ Symbol("BadValue");
6269
6111
  var nullCoercer = (v) => v;
6270
- var isPlainObject2 = (v) => {
6271
- if (v === null || typeof v !== "object") return false;
6272
- const proto = Object.getPrototypeOf(v);
6273
- return proto === Object.prototype || proto === null;
6274
- };
6275
6112
  function defaultToData(v) {
6276
6113
  if (v instanceof Map) return [...v.entries()];
6277
6114
  if (v instanceof Set) return [...v.values()];
@@ -6340,7 +6177,7 @@ var CHECK_TYPE_FLOAT = check(Number.isFinite, "Float expected");
6340
6177
  var CHECK_TYPE_BOOL = check((v) => typeof v === "boolean", "Boolean expected");
6341
6178
  var CHECK_TYPE_STRING = check((v) => typeof v === "string", "String expected");
6342
6179
  var CHECK_TYPE_LIST = check(Array.isArray, "Array expected");
6343
- var CHECK_TYPE_OBJECT = check(isPlainObject2, "Plain object expected");
6180
+ var CHECK_TYPE_OBJECT = check(isPlainObject, "Plain object expected");
6344
6181
  var CHECK_TYPE_MAP = check((v) => v instanceof Map, "Map expected");
6345
6182
  var CHECK_TYPE_SET = check((v) => v instanceof Set, "Set expected");
6346
6183
  var FieldBool = class extends Field {
@@ -6409,7 +6246,7 @@ var FieldObject = class extends Field {
6409
6246
  "object",
6410
6247
  name,
6411
6248
  CHECK_TYPE_OBJECT,
6412
- (v) => isPlainObject2(v) ? { ...v } : null,
6249
+ (v) => isPlainObject(v) ? { ...v } : null,
6413
6250
  defaultValue
6414
6251
  );
6415
6252
  }
@@ -6422,7 +6259,7 @@ var FieldMap = class extends Field {
6422
6259
  CHECK_TYPE_MAP,
6423
6260
  (v) => {
6424
6261
  if (v instanceof Map) return new Map(v);
6425
- if (Array.isArray(v) || isPlainObject2(v))
6262
+ if (Array.isArray(v) || isPlainObject(v))
6426
6263
  return new Map(Array.isArray(v) ? v : Object.entries(v));
6427
6264
  return null;
6428
6265
  },
@@ -6547,7 +6384,7 @@ function classFromData(name, { fields = {}, methods, statics }) {
6547
6384
  b.addField(field, dval, FieldCls);
6548
6385
  } else if (value?.component && value?.args !== void 0)
6549
6386
  b.addCompField(field, value.component, value.args);
6550
- else if (isPlainObject2(value)) b.addField(field, { ...value }, FieldObject);
6387
+ else if (isPlainObject(value)) b.addField(field, { ...value }, FieldObject);
6551
6388
  else {
6552
6389
  const FieldCls = value?.[FIELD_CLASS] ?? FieldAny;
6553
6390
  b.addField(field, value, FieldCls);
@@ -6571,10 +6408,24 @@ function validateDraftFields(current, draft) {
6571
6408
  }
6572
6409
  }
6573
6410
  }
6574
- var META_KEYS = "name id fields methods views receive intent alter provide lookup spec extra commonStyle globalStyle scope _rawProvide _rawLookup".split(
6411
+ var META_KEYS = "name id fields methods views receive intent alter provide provideType lookup spec extra commonStyle globalStyle scope _rawProvide _rawLookup".split(
6575
6412
  " "
6576
6413
  );
6414
+ var RESERVED_COMPONENT_STATICS = /* @__PURE__ */ new Set([
6415
+ ...META_KEYS,
6416
+ "make",
6417
+ "getMetaClass",
6418
+ ...Object.getOwnPropertyNames(Component.prototype)
6419
+ ]);
6420
+ function assertNoReservedComponentStatics(statics = {}) {
6421
+ for (const name in statics) {
6422
+ if (RESERVED_COMPONENT_STATICS.has(name)) {
6423
+ throw new TypeError(`component static "${name}" is reserved by the framework`);
6424
+ }
6425
+ }
6426
+ }
6577
6427
  Component.fromSpec = (opts) => {
6428
+ assertNoReservedComponentStatics(opts.statics);
6578
6429
  const Class = classFromData(opts.name, opts);
6579
6430
  const comp = new Component(Class, opts);
6580
6431
  const metaClass = Class.getMetaClass();
@@ -6594,7 +6445,7 @@ Component.fromSpec = (opts) => {
6594
6445
  configurable: true
6595
6446
  });
6596
6447
  for (const key of Object.getOwnPropertyNames(Component.prototype))
6597
- if (key !== "constructor" && key !== "make" && !Object.hasOwn(Class, key))
6448
+ if (key !== "constructor" && !Object.hasOwn(Class, key))
6598
6449
  Class[key] = (...args) => comp[key](...args);
6599
6450
  return Class;
6600
6451
  };
@@ -6603,6 +6454,22 @@ var component = (opts) => Component.fromSpec(opts);
6603
6454
  // src/stack.js
6604
6455
  var STOP = /* @__PURE__ */ Symbol("STOP");
6605
6456
  var NEXT = /* @__PURE__ */ Symbol("NEXT");
6457
+ var DEFAULT_LOOKUP_ROUTE = ["dyn", "lex"];
6458
+ function routeLookup(route, lex, dyn) {
6459
+ for (let i = 0; i < route.length; i++) {
6460
+ const leg = route[i];
6461
+ if (leg === "dyn") {
6462
+ const v = dyn();
6463
+ if (v != null) return v;
6464
+ } else if (leg === "lex") {
6465
+ const v = lex();
6466
+ if (v != null) return v;
6467
+ } else {
6468
+ console.warn("unknown lookup route leg", leg, '- expected "dyn" or "lex"');
6469
+ }
6470
+ }
6471
+ return null;
6472
+ }
6606
6473
  function lookup(chain, name, dv = null) {
6607
6474
  let n = chain;
6608
6475
  while (n !== null) {
@@ -6653,14 +6520,21 @@ var Stack = class _Stack {
6653
6520
  this.ctx = ctx;
6654
6521
  }
6655
6522
  // Evaluate every provide the entered component publishes and push them as one
6656
- // dynBinds frame (keyed by each provide's symbol). No-op when there are no provides.
6523
+ // dynBinds frame, keyed by NAME. Published types go in the same frame: a type name
6524
+ // starts A-Z and a value name does not, so the two namespaces cannot collide and
6525
+ // nearest-ancestor-wins falls out of frame order for both. No-op with no provides.
6657
6526
  _pushProvides() {
6658
- const provide = this.comps.getCompFor(this.it)?.provide;
6659
- if (provide == null) return this;
6527
+ const comp = this.comps.getCompFor(this.it);
6528
+ if (comp == null) return this;
6529
+ const { provide, provideType } = comp;
6660
6530
  const dynObj = {};
6661
6531
  let has = false;
6662
6532
  for (const k in provide) {
6663
- dynObj[provide[k].symbol] = provide[k].val.eval(this);
6533
+ dynObj[k] = provide[k].val.eval(this);
6534
+ has = true;
6535
+ }
6536
+ for (const k in provideType) {
6537
+ dynObj[k] = provideType[k];
6664
6538
  has = true;
6665
6539
  }
6666
6540
  if (!has) return this;
@@ -6685,30 +6559,36 @@ var Stack = class _Stack {
6685
6559
  const newViews = [name, views];
6686
6560
  return new _Stack(comps, it, binds, dynBinds, newViews, computeViewsId(newViews), ctx);
6687
6561
  }
6562
+ // Published types are stable per scope and would only churn the render cache, so
6563
+ // the cache key covers values alone.
6688
6564
  _pushDynBindValuesToArray(arr, comp) {
6689
- for (const k in comp.provide) arr.push(this._lookupProvide(comp.provide[k]));
6690
- for (const k in comp.lookup) arr.push(this._lookupAlias(comp.lookup[k]));
6691
- }
6692
- _lookupProvide(p) {
6693
- return lookup(this.dynBinds, p.symbol) ?? p.val.eval(this) ?? null;
6694
- }
6695
- _lookupAlias(lk) {
6696
- const sym = lk.getProducerSymbol(this);
6697
- return (sym != null ? lookup(this.dynBinds, sym) : null) ?? lk.val?.eval(this) ?? null;
6565
+ for (const k in comp.provide) arr.push(this.lookupDynamic(k));
6566
+ for (const k in comp.lookup) arr.push(this.lookupDynamic(k));
6698
6567
  }
6568
+ // `*name`: the nearest binding above (including this component's own provides,
6569
+ // pushed on entering it), else this component's declared default, else null.
6699
6570
  lookupDynamic(name) {
6571
+ const v = lookup(this.dynBinds, name);
6572
+ if (v != null) return v;
6700
6573
  const comp = this.comps.getCompFor(this.it);
6701
- if (comp == null) return null;
6702
- const lk = comp.lookup[name];
6703
- if (lk !== void 0) return this._lookupAlias(lk);
6704
- const p = comp.provide[name];
6705
- return p !== void 0 ? this._lookupProvide(p) : null;
6574
+ return comp?.lookup[name]?.val?.eval(this) ?? null;
6706
6575
  }
6707
6576
  lookupBind(name) {
6708
6577
  return lookup(this.binds, name);
6709
6578
  }
6710
- lookupType(name) {
6711
- return this.comps.getCompFor(this.it).scope.lookupComponent(name);
6579
+ // The `lex` leg: what a name means in the registration scope of the component being
6580
+ // rendered. The scope chain holds components, so this leg only ever answers for a
6581
+ // type name — for a value name it is a miss, which is why one default route serves
6582
+ // both. Guarded: a non-component `it` has no scope to ask.
6583
+ _lookupLex(name) {
6584
+ return this.comps.getCompFor(this.it)?.scope.lookupComponent(name) ?? null;
6585
+ }
6586
+ lookupRouted(name, route = DEFAULT_LOOKUP_ROUTE) {
6587
+ return routeLookup(
6588
+ route,
6589
+ () => this._lookupLex(name),
6590
+ () => this.lookupDynamic(name)
6591
+ );
6712
6592
  }
6713
6593
  lookupFieldRaw(name) {
6714
6594
  return this.it[name] ?? null;
@@ -6717,12 +6597,6 @@ var Stack = class _Stack {
6717
6597
  const fn = this.it[name];
6718
6598
  return fn instanceof Function ? fn.call(this.it) : null;
6719
6599
  }
6720
- // Bare implicit event names are gone: a sigil-less word cannot reach a
6721
- // handler-arg slot anymore, so this only ever serves stale NameVals parsed
6722
- // before the removal (e.g. macro-attr pass-throughs). Always null.
6723
- lookupName(_name) {
6724
- return null;
6725
- }
6726
6600
  // The dispatched DOM event / drag info, read only by EventMemberVal's
6727
6601
  // `e.<member>` handler args. Null outside a live event transaction.
6728
6602
  lookupEvent() {
@@ -6759,9 +6633,6 @@ var State2 = class {
6759
6633
  this.val = val;
6760
6634
  for (const sub of this.changeSubs) sub({ val, old, info, timestamp: Date.now() });
6761
6635
  }
6762
- update(fn, info) {
6763
- return this.set(fn(this.val), info);
6764
- }
6765
6636
  };
6766
6637
  var Transactor = class {
6767
6638
  constructor(comps, rootValue) {
@@ -6822,7 +6693,7 @@ var Transactor = class {
6822
6693
  // Build and dispatch the observer record for a settled transaction. The
6823
6694
  // resolved handler (`_resolvedHandler`/`_matched`) and per-leaf before/after
6824
6695
  // (`_before`/`_after`) were captured while the handler ran (see callHandler /
6825
- // updateRootValue). No-op when nobody is observing.
6696
+ // Transaction.run). No-op when nobody is observing.
6826
6697
  _emitTransaction(transaction, root) {
6827
6698
  if (this._observers.length === 0) return;
6828
6699
  if (transaction._resolvedHandler === void 0) return;
@@ -6854,8 +6725,13 @@ var Transactor = class {
6854
6725
  }
6855
6726
  return child;
6856
6727
  }
6857
- pushSend(path, name, args = [], opts = {}, parent = null) {
6858
- const t = new SendEvent(path, this, name, args, parent, opts);
6728
+ // `origin` is where the message came FROM, pinned now for the same reason an
6729
+ // intent's answerPath is: a reply must reach the sender that asked even if a key
6730
+ // moved while the message was in flight. Null when nobody is waiting — a host
6731
+ // sendAtRoot or a view's own `@on.*` — and ctx.sendReply refuses on that.
6732
+ pushSend(path, name, args = [], parent = null, origin = null) {
6733
+ const t = new SendEvent(path, this, name, args, parent);
6734
+ t.origin = origin === null ? null : origin.toTransactionPath().pinKeys(this.state.val);
6859
6735
  this.pushTransaction(t);
6860
6736
  return this._link(t, parent);
6861
6737
  }
@@ -6868,9 +6744,9 @@ var Transactor = class {
6868
6744
  walk.advance();
6869
6745
  return walk;
6870
6746
  }
6871
- // Drain queued transactions and await in-flight requests until quiescent. Each
6872
- // awaited request enqueues a ResponseEvent, which may dispatch more work, so we
6873
- // loop. `maxTurns` backstops a pathological non-terminating cascade.
6747
+ // Drain queued transactions and await async intent handlers until quiescent. A
6748
+ // completed handler may enqueue an answer, which may dispatch more work, so we loop.
6749
+ // `maxTurns` backstops a pathological non-terminating cascade.
6874
6750
  async settle(maxTurns = 1e4) {
6875
6751
  while ((this.hasPendingTransactions || this._inflight.size) && maxTurns-- > 0) {
6876
6752
  while (this.hasPendingTransactions) this.transactNext();
@@ -6927,9 +6803,6 @@ var Transaction = class {
6927
6803
  whenSubtreeSettled() {
6928
6804
  return this.completion.whenSubtreeSettled();
6929
6805
  }
6930
- run(rootValue, comps) {
6931
- return this.updateRootValue(rootValue, comps);
6932
- }
6933
6806
  afterTransaction() {
6934
6807
  }
6935
6808
  // Ending a walk and forwarding belong to the two dispatch buckets; the base warns
@@ -6941,12 +6814,6 @@ var Transaction = class {
6941
6814
  forward(_opts) {
6942
6815
  console.warn('ctx.forward() needs a "receive" or "intent" handler - ignored');
6943
6816
  }
6944
- buildRootStack(root, comps) {
6945
- return Stack.root(comps, root);
6946
- }
6947
- buildStack(root, comps) {
6948
- return this.path.toTransactionPath().buildStack(this.buildRootStack(root, comps));
6949
- }
6950
6817
  // The kind reported to observers (see Transactor.observe); null on the base.
6951
6818
  get observeKind() {
6952
6819
  return null;
@@ -6966,12 +6833,11 @@ var Transaction = class {
6966
6833
  return null;
6967
6834
  }
6968
6835
  // The path used to apply the mutation. Teleports dynamic-var renders so it lands on
6969
- // the data's real location (the dispatch `this.path` keeps intermediates). A subclass
6970
- // may override to supply a pre-resolved path (see ResponseEvent's pinned keys).
6836
+ // the data's real location (the dispatch `this.path` keeps intermediates).
6971
6837
  getTransactionPath() {
6972
6838
  return this.path.toTransactionPath().compact();
6973
6839
  }
6974
- updateRootValue(curRoot, comps) {
6840
+ run(curRoot, comps) {
6975
6841
  const txnPath = this.getTransactionPath();
6976
6842
  const curLeaf = txnPath.lookup(curRoot);
6977
6843
  const newLeaf = (0, immer_exports.produce)(curLeaf, (draft) => {
@@ -7028,8 +6894,8 @@ var InputEvent = class extends Transaction {
7028
6894
  }
7029
6895
  this.transactor.pushIntent(this.dispatchPath, name, args, rest, this);
7030
6896
  }
7031
- buildRootStack(root, comps) {
7032
- return Stack.root(comps, root, this);
6897
+ buildStack(root, comps) {
6898
+ return this.path.toTransactionPath().buildStack(Stack.root(comps, root, this));
7033
6899
  }
7034
6900
  getHandlerAndArgs(root, _instance, comps) {
7035
6901
  const stack = this.buildStack(root, comps);
@@ -7047,11 +6913,10 @@ var InputEvent = class extends Transaction {
7047
6913
  }
7048
6914
  };
7049
6915
  var NameArgsTransaction = class extends Transaction {
7050
- constructor(path, transactor, name, args, parentTransaction, opts = {}) {
6916
+ constructor(path, transactor, name, args, parentTransaction) {
7051
6917
  super(path, transactor, parentTransaction);
7052
6918
  this.name = name;
7053
6919
  this.args = args;
7054
- this.opts = opts;
7055
6920
  this.targetPath = path;
7056
6921
  }
7057
6922
  handlerProp = null;
@@ -7110,7 +6975,7 @@ var SendEvent = class extends NameArgsTransaction {
7110
6975
  var IntentEvent = class extends NameArgsTransaction {
7111
6976
  handlerProp = "intent";
7112
6977
  constructor(path, transactor, walk) {
7113
- super(path, transactor, walk.name, walk.args, walk.parent, {});
6978
+ super(path, transactor, walk.name, walk.args, walk.parent);
7114
6979
  this.walk = walk;
7115
6980
  this.targetPath = walk.origin;
7116
6981
  }
@@ -7210,7 +7075,7 @@ var IntentWalk = class {
7210
7075
  async _runLex(chain, i) {
7211
7076
  if (this.ended) return;
7212
7077
  if (i >= chain.length) return this.advance();
7213
- const ctx = new IntentContext(this.origin, this.transactor, this.parent);
7078
+ const ctx = new Dispatcher(this.origin, this.transactor, this.parent);
7214
7079
  try {
7215
7080
  const res = await chain[i].fn.apply(null, [...this.args, ctx]);
7216
7081
  if (res === PASS) return this._runLex(chain, i + 1);
@@ -7263,7 +7128,7 @@ var IntentWalk = class {
7263
7128
  this.ended = true;
7264
7129
  if (name === null) return this.release?.();
7265
7130
  const path = this.answerPath ?? this.origin;
7266
- const t = new SendEvent(path, this.transactor, name, args, this.parent, {});
7131
+ const t = new SendEvent(path, this.transactor, name, args, this.parent);
7267
7132
  t._isAnswer = true;
7268
7133
  this.transactor.pushTransaction(t);
7269
7134
  if (this.release) t.completion.whenSubtreeSettled().then(this.release);
@@ -7302,7 +7167,7 @@ var Completion = class {
7302
7167
  this.val = val;
7303
7168
  this._selfResolve?.(val);
7304
7169
  }
7305
- // Settle self even when no handler produced a value (skipSelf / undefined / throw paths).
7170
+ // Settle self even when no handler produced a value (undefined-state / throw paths).
7306
7171
  ensureSelfSettled() {
7307
7172
  if (!this.selfSettled) this.markSelfSettled(this.val);
7308
7173
  }
@@ -7348,12 +7213,64 @@ var Dispatcher = class {
7348
7213
  get at() {
7349
7214
  return new PathChanges(this);
7350
7215
  }
7216
+ // The render-time Stack, rebuilt on demand. A handler does not get one: the stack
7217
+ // that evaluated its arguments is a local in getHandlerAndArgs and is gone by the
7218
+ // time the body runs, and send/intent transactions never build one at all. Both
7219
+ // halves it needs are pinned on this ctx, so the `dyn` leg reconstructs it — and
7220
+ // only the `dyn` leg does, which is why this is lazy rather than eager.
7221
+ //
7222
+ // The path is compacted, so per-item binds (`@each`, `@enrich-with`) are not
7223
+ // replayed; provides are pushed on every component frame either way, so a provide
7224
+ // that reads a loop binding is the one case this cannot reproduce faithfully.
7225
+ _stack() {
7226
+ this._stackMemo ??= this.path.toTransactionPath().buildStack(Stack.root(this.transactor.comps, this.root, this.parent));
7227
+ return this._stackMemo;
7228
+ }
7229
+ // Resolve a name the way the renderer would. `opts.route` takes the same legs in
7230
+ // the same spelling as `ctx.intent` — `["dyn"]` the render ancestry, `["lex"]` the
7231
+ // registration scope, default both.
7232
+ lookup(name, opts) {
7233
+ const route = opts?.route ?? DEFAULT_LOOKUP_ROUTE;
7234
+ return routeLookup(
7235
+ route,
7236
+ () => this._lookupLex(name),
7237
+ () => this._stack()?.lookupDynamic(name) ?? null
7238
+ );
7239
+ }
7240
+ // The `lex` leg without a Stack: the scope of the component whose handler is
7241
+ // running, which is the leaf of this ctx's path.
7242
+ _lookupLex(name) {
7243
+ let Comp = null;
7244
+ this.walkPath((c) => {
7245
+ Comp = c;
7246
+ return false;
7247
+ });
7248
+ return Comp?.scope?.lookupComponent(name) ?? null;
7249
+ }
7250
+ // A component lookup is a value lookup constrained to a component. The `lex` leg
7251
+ // can only ever answer with one; the `dyn` leg reads a binding an ancestor
7252
+ // published, so it is the leg that has to be checked.
7253
+ lookupType(name, opts) {
7254
+ const v = this.lookup(name, opts);
7255
+ if (v == null) {
7256
+ this.transactor.refuse("TYPE_NOT_FOUND", {
7257
+ name,
7258
+ route: opts?.route ?? DEFAULT_LOOKUP_ROUTE
7259
+ });
7260
+ return null;
7261
+ }
7262
+ if (v?.[COMPONENT] == null) {
7263
+ this.transactor.refuse("TYPE_NOT_COMPONENT", { name, got: typeof v });
7264
+ return null;
7265
+ }
7266
+ return v;
7267
+ }
7351
7268
  // A message is ADDRESSED: it names one component and stops there.
7352
- send(name, args, opts) {
7353
- return this.sendAtPath(this.path, name, args, opts);
7269
+ send(name, args) {
7270
+ return this.sendAtPath(this.path, name, args);
7354
7271
  }
7355
- sendAtPath(path, name, args, opts) {
7356
- return this.transactor.pushSend(path, name, args, opts, this.parent);
7272
+ sendAtPath(path, name, args) {
7273
+ return this.transactor.pushSend(path, name, args, this.parent, this.path);
7357
7274
  }
7358
7275
  // An intent is ROUTED: it names a job and walks until something answers. The verb
7359
7276
  // does not decide which scope answers — `opts.route` does, written here at the call
@@ -7364,9 +7281,6 @@ var Dispatcher = class {
7364
7281
  intentAtPath(path, name, args, opts) {
7365
7282
  return this.transactor.pushIntent(path, name, args, opts, this.parent);
7366
7283
  }
7367
- lookupTypeFor(name, inst) {
7368
- return this.transactor.comps.getCompFor(inst).scope.lookupComponent(name);
7369
- }
7370
7284
  };
7371
7285
  var EventContext = class extends Dispatcher {
7372
7286
  get name() {
@@ -7383,6 +7297,19 @@ var EventContext = class extends Dispatcher {
7383
7297
  fail(error) {
7384
7298
  return this.parent.walk === void 0 ? warnNotIntent("fail") : this.parent.walk.answer("Error", error);
7385
7299
  }
7300
+ // Reply to the sender of the MESSAGE being handled. An intent answers under a name
7301
+ // the runtime derives (`<name>Ok`), because the raiser asked a question and declared
7302
+ // arms for it; a message did not, so the replier names the reply itself and the
7303
+ // sender handles it like any other message. Refuses when nobody sent this — a view's
7304
+ // own `@on.*`, or a host sendAtRoot.
7305
+ sendReply(name, args) {
7306
+ const origin = this.parent?.origin ?? null;
7307
+ if (origin == null) {
7308
+ this.transactor.refuse("NO_SENDER", { name });
7309
+ return null;
7310
+ }
7311
+ return this.sendAtPath(origin, name, args);
7312
+ }
7386
7313
  // End the walk answering nothing — "served, and no answer".
7387
7314
  stop() {
7388
7315
  return this.parent.stop();
@@ -7394,8 +7321,6 @@ var EventContext = class extends Dispatcher {
7394
7321
  return this.parent.forward(opts);
7395
7322
  }
7396
7323
  };
7397
- var IntentContext = class extends Dispatcher {
7398
- };
7399
7324
  function warnNotIntent(verb) {
7400
7325
  console.warn(`ctx.${verb}() is only meaningful in an "intent" handler - ignored`);
7401
7326
  }
@@ -7404,8 +7329,8 @@ var PathChanges = class extends PathBuilder {
7404
7329
  super();
7405
7330
  this.dispatcher = dispatcher;
7406
7331
  }
7407
- send(name, args, opts) {
7408
- return this.dispatcher.sendAtPath(this.buildPath(), name, args, opts);
7332
+ send(name, args) {
7333
+ return this.dispatcher.sendAtPath(this.buildPath(), name, args);
7409
7334
  }
7410
7335
  intent(name, args, opts) {
7411
7336
  return this.dispatcher.intentAtPath(this.buildPath(), name, args, opts);
@@ -7962,8 +7887,12 @@ function lintIdToMessage(id, info) {
7962
7887
  return `Provide '${info.name}' value '${info.value}' must be a field ('.f') or seq-access ('.s[.k]') — a method/constant can't be a render target`;
7963
7888
  case "LOOKUP_BAD_SHAPE":
7964
7889
  return `Lookup '${info.name}' has an invalid shape: ${info.problem}`;
7965
- case "LOOKUP_TARGET_MALFORMED":
7966
- return `Lookup '${info.name}' target '${info.target}' must be 'Producer.provideName' (a string, or the 'for' of { for, default })`;
7890
+ case "LOOKUP_NO_PROVIDER":
7891
+ return info.hasDefault ? `Lookup '${info.name}' is provided by no component in scope, so it always resolves to its default` : `Lookup '${info.name}' is provided by no component in scope — add a 'provide' for it, or give this lookup a default`;
7892
+ case "PROVIDE_TYPE_BAD_SHAPE":
7893
+ return `Provide '${info.name}' starts uppercase, so it publishes a component type — its value must be 'self', not '${info.value}'`;
7894
+ case "PROVIDE_NAME_COLLISION":
7895
+ return `Provide '${info.name}' is also provided by '${info.other}' in the same scope — one name, one provider, so a lookup can find it`;
7967
7896
  case "UNKNOWN_MACRO_ARG":
7968
7897
  return `Argument '${info.name}' is not declared in macro '${info.macroName}'`;
7969
7898
  case "UNKNOWN_DIRECTIVE":
@@ -8235,9 +8164,10 @@ var App = class {
8235
8164
  }
8236
8165
  compile() {
8237
8166
  for (const Comp of this.comps.byId.values()) {
8238
- Comp.compile(this.ParseContext);
8239
- for (const key in Comp.views)
8240
- for (const name of Comp.views[key].ctx.genEventNames()) this._eventNames.add(name);
8167
+ const meta = Comp[COMPONENT];
8168
+ meta.compile(this.ParseContext);
8169
+ for (const key in meta.views)
8170
+ for (const name of meta.views[key].ctx.genEventNames()) this._eventNames.add(name);
8241
8171
  }
8242
8172
  this._compiled = true;
8243
8173
  }
@@ -8263,8 +8193,8 @@ var App = class {
8263
8193
  for (const name of this._eventNames)
8264
8194
  this.rootNode.removeEventListener(name, this, listenerOpts(name));
8265
8195
  }
8266
- sendAtRoot(name, args, opts) {
8267
- this.transactor.pushSend(new Path([]), name, args, opts);
8196
+ sendAtRoot(name, args) {
8197
+ this.transactor.pushSend(new Path([]), name, args);
8268
8198
  }
8269
8199
  registerComponents(comps, opts) {
8270
8200
  const scope = this.compStack.enter();
@@ -8340,6 +8270,182 @@ var DragInfo = class {
8340
8270
  }
8341
8271
  };
8342
8272
 
8273
+ // src/cache.js
8274
+ var isWeakKey = (k) => k !== null && (typeof k === "object" || typeof k === "function");
8275
+ var NullDomCache = class {
8276
+ get(_keys, _cacheKey) {
8277
+ }
8278
+ set(_keys, _cacheKey, _v) {
8279
+ }
8280
+ evict() {
8281
+ return { hit: 0, miss: 0, badKey: 0 };
8282
+ }
8283
+ };
8284
+ var WeakMapDomCache = class {
8285
+ constructor() {
8286
+ this.hit = this.miss = this.badKey = 0;
8287
+ this.keysByLen = /* @__PURE__ */ new Map();
8288
+ }
8289
+ _returnValue(r) {
8290
+ if (r === void 0) this.miss += 1;
8291
+ else this.hit += 1;
8292
+ return r;
8293
+ }
8294
+ get(keys, cacheKey) {
8295
+ const len = keys.length;
8296
+ let cur = this.keysByLen.get(len);
8297
+ if (!cur) return this._returnValue(void 0);
8298
+ for (let i = 0; i < len - 1; i++) {
8299
+ cur = cur.get(keys[i]);
8300
+ if (!cur) return this._returnValue(void 0);
8301
+ }
8302
+ return this._returnValue(cur.get(keys[len - 1])?.[cacheKey]);
8303
+ }
8304
+ set(keys, cacheKey, v) {
8305
+ const len = keys.length;
8306
+ let cur = this.keysByLen.get(len);
8307
+ if (!cur) {
8308
+ cur = /* @__PURE__ */ new WeakMap();
8309
+ this.keysByLen.set(len, cur);
8310
+ }
8311
+ for (let i = 0; i < len - 1; i++) {
8312
+ const key = keys[i];
8313
+ let next = cur.get(key);
8314
+ if (!next) {
8315
+ if (!isWeakKey(key)) {
8316
+ this.badKey += 1;
8317
+ return;
8318
+ }
8319
+ next = /* @__PURE__ */ new WeakMap();
8320
+ cur.set(key, next);
8321
+ }
8322
+ cur = next;
8323
+ }
8324
+ const lastKey = keys[len - 1];
8325
+ const leaf = cur.get(lastKey);
8326
+ if (leaf) leaf[cacheKey] = v;
8327
+ else if (isWeakKey(lastKey)) cur.set(lastKey, { [cacheKey]: v });
8328
+ else this.badKey += 1;
8329
+ }
8330
+ evict() {
8331
+ const { hit, miss, badKey } = this;
8332
+ this.hit = this.miss = this.badKey = 0;
8333
+ this.keysByLen = /* @__PURE__ */ new Map();
8334
+ return { hit, miss, badKey };
8335
+ }
8336
+ };
8337
+
8338
+ // src/renderer.js
8339
+ var DATASET_ATTRS = ["nid", "cid", "eid", "vid", "si", "sk"];
8340
+ var Renderer = class {
8341
+ constructor(comps) {
8342
+ this.comps = comps;
8343
+ this.cache = new WeakMapDomCache();
8344
+ this.renderTag = h;
8345
+ }
8346
+ renderFragment(childs) {
8347
+ return new VFragment(childs);
8348
+ }
8349
+ renderComment(text) {
8350
+ return new VComment(text);
8351
+ }
8352
+ setNullCache() {
8353
+ this.cache = new NullDomCache();
8354
+ }
8355
+ renderToDOM(stack, val) {
8356
+ const rootNode = document.createElement("div");
8357
+ const rOpts = { document };
8358
+ render(h("DIV", null, [this.renderRoot(stack, val)]), rootNode, rOpts);
8359
+ return rootNode.childNodes[0];
8360
+ }
8361
+ renderToString(stack, val, cleanAttrs = true) {
8362
+ const dom = this.renderToDOM(stack, val);
8363
+ if (cleanAttrs) {
8364
+ const nodes = dom.querySelectorAll("[data-nid],[data-cid],[data-eid]");
8365
+ for (const { dataset } of nodes) for (const name of DATASET_ATTRS) delete dataset[name];
8366
+ }
8367
+ return dom.innerHTML;
8368
+ }
8369
+ renderRoot(stack, val, viewName = null) {
8370
+ const comp = this.comps.getCompFor(val);
8371
+ if (comp === null) return null;
8372
+ return this._rValComp(stack, val, comp, comp.getView(viewName).anode, "ROOT", viewName);
8373
+ }
8374
+ renderIt(stack, node, key, viewName) {
8375
+ const comp = this.comps.getCompFor(stack.it);
8376
+ return comp ? this._rValComp(stack, stack.it, comp, node, key, viewName) : null;
8377
+ }
8378
+ // `node` is the parse node of the render site (`<x render>` / `render-it` /
8379
+ // `render-each`, or the view's root anode for the app root). It keys the
8380
+ // cache as a globally-unique object: node ids alone are unique only within a
8381
+ // single view, so the same value rendered by two components (e.g. through a
8382
+ // shared dynamic-var sequence) would otherwise collide in the cache.
8383
+ _rValComp(stack, val, comp, node, key, viewName) {
8384
+ const cacheKey = `${viewName ?? ""}${stack.viewsId ?? ""}${key}`;
8385
+ const cachePath = [node, val];
8386
+ stack._pushDynBindValuesToArray(cachePath, comp);
8387
+ const cachedNode = this.cache.get(cachePath, cacheKey);
8388
+ if (cachedNode) return cachedNode;
8389
+ const view = viewName ? comp.getView(viewName) : stack.lookupBestView(comp.views, "main");
8390
+ const body = this.renderView(view, stack);
8391
+ if (body == null) return null;
8392
+ const meta = this._renderMetadata({
8393
+ $: "Comp",
8394
+ nid: node?.nodeId ?? null,
8395
+ cid: comp.id,
8396
+ vid: view.name
8397
+ });
8398
+ const dom = new VFragment([meta, body]);
8399
+ this.cache.set(cachePath, cacheKey, dom);
8400
+ return dom;
8401
+ }
8402
+ pushEachEntry(r, nid, attrName, key, dom) {
8403
+ r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
8404
+ }
8405
+ renderEachWhen(stack, iterInfo, view, nid) {
8406
+ const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
8407
+ const r = [];
8408
+ const it = stack.it;
8409
+ const renderOne = (key, value, attrName, binds) => {
8410
+ const cachePath = enricher ? [view, it, value] : [view, value];
8411
+ const cacheKey = `${stack.viewsId ?? ""}${nid}${key}`;
8412
+ const cachedNode = this.cache.get(cachePath, cacheKey);
8413
+ if (cachedNode) this.pushEachEntry(r, nid, attrName, key, cachedNode);
8414
+ else {
8415
+ const dom = this.renderView(view, stack.enter(value, binds, false));
8416
+ if (dom != null) this.pushEachEntry(r, nid, attrName, key, dom);
8417
+ this.cache.set(cachePath, cacheKey, dom);
8418
+ }
8419
+ };
8420
+ walkLoopBindings(
8421
+ { seq, it, filter, loopWith, enricher, ctx: makeLoopCtx(stack, filter) },
8422
+ renderOne
8423
+ );
8424
+ return r;
8425
+ }
8426
+ renderView(view, stack) {
8427
+ let n = stack.binds[1];
8428
+ while (n !== null) {
8429
+ const b = n[0];
8430
+ if (b.isFrame) {
8431
+ if (stack.it !== b.it) break;
8432
+ console.error("recursion detected", stack.it, b.it);
8433
+ return new VComment("RECURSION AVOIDED");
8434
+ }
8435
+ n = n[1];
8436
+ }
8437
+ return view.render(stack, this);
8438
+ }
8439
+ _renderMetadata(info) {
8440
+ return new VComment(`§${JSON.stringify(info)}§`);
8441
+ }
8442
+ // Prefix a loop-less @enrich-with subtree with a boundary meta so event-path
8443
+ // reconstruction replays its binds (mirrors the §Each§ / §Comp§ metas).
8444
+ renderScopeMeta(nid, dom) {
8445
+ return new VFragment([this._renderMetadata({ $: "Scope", nid }), dom]);
8446
+ }
8447
+ };
8448
+
8343
8449
  // index.js
8344
8450
  var css = String.raw;
8345
8451
  var html = String.raw;
@@ -8366,24 +8472,6 @@ async function compileClassesToStyleText(app, compileClasses, Ctx = ParseCtxClas
8366
8472
  }
8367
8473
 
8368
8474
  // src/util/testing.js
8369
- var plainArrayIter = (seq, visit, start, end) => {
8370
- const [s, e] = normalizeRange(start, end, seq.length);
8371
- for (let i = s; i < e; i++) visit(i, seq[i]);
8372
- };
8373
- var plainMapIter = (seq, visit, start, end) => {
8374
- const [s, e] = normalizeRange(start, end, seq.size);
8375
- let i = 0;
8376
- for (const [k, v] of seq.entries()) {
8377
- if (i >= e) break;
8378
- if (i >= s) visit(k, v);
8379
- i++;
8380
- }
8381
- };
8382
- function pickIter(seq) {
8383
- if (Array.isArray(seq)) return plainArrayIter;
8384
- if (seq instanceof Map) return plainMapIter;
8385
- return getSeqInfo(seq);
8386
- }
8387
8475
  function resolveAlter(Comp, name) {
8388
8476
  if (name == null) return null;
8389
8477
  const fn = Comp.alter?.[name];
@@ -8401,30 +8489,12 @@ function collectIterBindings(Comp, compInstance, seq, opts = {}) {
8401
8489
  const scope = scopeEnrichFn ? scopeEnrichFn.call(it) ?? {} : opts.scope ?? {};
8402
8490
  const ctx = {
8403
8491
  lookup: (name) => scope[name],
8404
- filter: (key, value, iterData2) => whenFn.call(it, key, value, iterData2)
8492
+ filter: (key, value, iterData) => whenFn.call(it, key, value, iterData)
8405
8493
  };
8406
- const { iterData, start, end, keys } = unpackLoopResult(loopWithFn.call(it, seq, ctx), seq);
8407
8494
  const out = [];
8408
- if (keys) {
8409
- for (const key of keys) {
8410
- const value = seqGet(seq, key);
8411
- const binds = { key, value };
8412
- if (enrichFn) enrichFn.call(it, binds, key, value, iterData);
8413
- out.push(binds);
8414
- }
8415
- return out;
8416
- }
8417
- const iter = pickIter(seq);
8418
- iter(
8419
- seq,
8420
- (key, value) => {
8421
- if (!whenFn.call(it, key, value, iterData)) return;
8422
- const binds = { key, value };
8423
- if (enrichFn) enrichFn.call(it, binds, key, value, iterData);
8424
- out.push(binds);
8425
- },
8426
- start,
8427
- end
8495
+ walkLoopBindings(
8496
+ { seq, it, filter: whenFn, loopWith: loopWithFn, enricher: enrichFn, ctx },
8497
+ (_key, _value, _attrName, binds) => out.push(binds)
8428
8498
  );
8429
8499
  return out;
8430
8500
  }
@@ -8583,7 +8653,7 @@ export {
8583
8653
  HANDLER_NAME_COLLISION,
8584
8654
  IF_NO_BRANCH_SET,
8585
8655
  LOOKUP_BAD_SHAPE,
8586
- LOOKUP_TARGET_MALFORMED,
8656
+ LOOKUP_NO_PROVIDER,
8587
8657
  LintClassCollectorCtx,
8588
8658
  LintComponentResult,
8589
8659
  LintContext,
@@ -8599,7 +8669,9 @@ export {
8599
8669
  ModuleTests,
8600
8670
  PASS,
8601
8671
  PLACEHOLDERLESS_TEMPLATE_STRING,
8672
+ PROVIDE_NAME_COLLISION,
8602
8673
  PROVIDE_NOT_ADDRESSABLE,
8674
+ PROVIDE_TYPE_BAD_SHAPE,
8603
8675
  ParseContext,
8604
8676
  RECEIVE_HANDLER_NOT_IMPLEMENTED,
8605
8677
  REDUNDANT_TEMPLATE_STRING,