tutuca 0.9.106 → 0.9.108

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.
@@ -4248,6 +4248,9 @@ class Step {
4248
4248
  pinKey(_v) {
4249
4249
  return this;
4250
4250
  }
4251
+ toKey() {
4252
+ return null;
4253
+ }
4251
4254
  }
4252
4255
  function warnRawDynStep(op, step) {
4253
4256
  console.warn(`Path.${op} reached a DynStep: call toTransactionPath() first`, step);
@@ -4332,6 +4335,15 @@ class Path {
4332
4335
  }
4333
4336
  return out;
4334
4337
  }
4338
+ toKeys() {
4339
+ const out = [];
4340
+ for (const step of this.steps) {
4341
+ const k = step.toKey();
4342
+ if (k !== null)
4343
+ out.push(k);
4344
+ }
4345
+ return out;
4346
+ }
4335
4347
  setValue(root, v) {
4336
4348
  const intermediates = new Array(this.steps.length);
4337
4349
  let curVal = root;
@@ -4412,19 +4424,12 @@ class Path {
4412
4424
  eventIds.push(eid);
4413
4425
  const metas = metaChain(node.previousSibling);
4414
4426
  let sawComp = false;
4415
- for (let i = 0;i < metas.length; i++) {
4416
- const m = metas[i];
4427
+ for (const m of metas) {
4417
4428
  if (m.$ === "Comp") {
4418
4429
  sawComp = true;
4419
4430
  if (!crossComponent(m.cid, m.vid))
4420
4431
  return NO_EVENT_INFO;
4421
- const outer = metas[i + 1];
4422
- if (outer?.$ === "Each" && outer.nid === m.nid) {
4423
- nodeIds.push({ nid: outer.nid, si: outer.si, sk: outer.sk });
4424
- i += 1;
4425
- } else {
4426
- nodeIds.push({ nid: m.nid });
4427
- }
4432
+ nodeIds.push({ nid: m.nid });
4428
4433
  } else {
4429
4434
  nodeIds.push({ nid: m.nid, si: m.si, sk: m.sk });
4430
4435
  }
@@ -4589,6 +4594,9 @@ var init_path = __esm(() => {
4589
4594
  withKey(k) {
4590
4595
  return new SeqStep(this.field, k);
4591
4596
  }
4597
+ toKey() {
4598
+ return { field: this.field };
4599
+ }
4592
4600
  };
4593
4601
  SeqStep = class SeqStep extends Step {
4594
4602
  constructor(field, key) {
@@ -4607,6 +4615,9 @@ var init_path = __esm(() => {
4607
4615
  enterFrame(stack, _prev, next) {
4608
4616
  return stack.enter(next, { key: this.key }, true);
4609
4617
  }
4618
+ toKey() {
4619
+ return { field: this.field, key: this.key };
4620
+ }
4610
4621
  };
4611
4622
  SeqAccessStep = class SeqAccessStep extends Step {
4612
4623
  constructor(seqField, keyField) {
@@ -4628,6 +4639,9 @@ var init_path = __esm(() => {
4628
4639
  const key = v?.get(this.keyField, NONE);
4629
4640
  return key === NONE ? this : new SeqStep(this.seqField, key);
4630
4641
  }
4642
+ toKey() {
4643
+ return { field: this.seqField };
4644
+ }
4631
4645
  };
4632
4646
  EachBindStep = class EachBindStep extends Step {
4633
4647
  constructor(iterInfo, key) {
@@ -4722,7 +4736,7 @@ class ValParser {
4722
4736
  parseToken(s, px) {
4723
4737
  const c0 = s.charCodeAt(0);
4724
4738
  if (c0 === 39)
4725
- return s.length >= 2 && s.charCodeAt(s.length - 1) === 39 ? new ConstVal(unescapeStr(s.slice(1, -1)), K_STR | K_STRTPL) : null;
4739
+ return s.length >= 2 && s.charCodeAt(s.length - 1) === 39 ? new ConstVal(unescapeStr(s.slice(1, -1))) : null;
4726
4740
  if (c0 === 36 && s.charCodeAt(1) === 39)
4727
4741
  return s.length >= 3 && s.charCodeAt(s.length - 1) === 39 ? StrTplVal.parse(s.slice(2, -1), px) : null;
4728
4742
  if (s.indexOf("[") !== -1 || s.indexOf("]") !== -1)
@@ -4747,14 +4761,18 @@ class ValParser {
4747
4761
  }
4748
4762
  case 36:
4749
4763
  return mkVal(s.slice(1), MethodVal);
4750
- case 64:
4751
- return mkVal(s.slice(1), BindVal);
4764
+ case 64: {
4765
+ const dot = s.indexOf(".", 1);
4766
+ if (dot === -1)
4767
+ return mkVal(s.slice(1), BindVal);
4768
+ const name = s.slice(1, dot);
4769
+ const member = s.slice(dot + 1);
4770
+ return isValidValId(name) && isValidValId(member) ? new BindMemberVal(name, member) : null;
4771
+ }
4752
4772
  case 42:
4753
4773
  return mkVal(s.slice(1), DynVal);
4754
4774
  case 46:
4755
4775
  return mkVal(s.slice(1), FieldVal);
4756
- case 33:
4757
- return mkVal(s.slice(1), RequestVal);
4758
4776
  }
4759
4777
  const num = VALID_FLOAT_RE.test(s) ? parseFloat(s) : null;
4760
4778
  if (Number.isFinite(num))
@@ -4807,7 +4825,7 @@ class ValParser {
4807
4825
  return this._parseSingle(s, px, G_PROVIDE);
4808
4826
  }
4809
4827
  parseHandlerArg(s, px) {
4810
- return this._parseSingle(s, px, G_HANDLER_ARG);
4828
+ return this._parseSingle(s, px, G_VALUE);
4811
4829
  }
4812
4830
  parseMacroAttr(s, px) {
4813
4831
  return this._parseSingle(s, px, G_ALL);
@@ -4839,7 +4857,7 @@ class ValParser {
4839
4857
  const args = new Array(tokens.length - 1);
4840
4858
  for (let i = 1;i < tokens.length; i++) {
4841
4859
  const val = this.parseToken(tokens[i], px);
4842
- if (val !== null && kindOf(val) & G_HANDLER_ARG)
4860
+ if (val !== null && kindOf(val) & G_VALUE)
4843
4861
  args[i - 1] = val;
4844
4862
  else {
4845
4863
  if (report)
@@ -4865,7 +4883,7 @@ class ValParser {
4865
4883
  for (let i = 0;i < arity; i++) {
4866
4884
  const tok = tokens[i + 1];
4867
4885
  const val = this.parseToken(tok, px);
4868
- if (val === null || !(kindOf(val) & G_PRED_ARG)) {
4886
+ if (val === null || !(kindOf(val) & G_BOOL)) {
4869
4887
  px.onParseIssue("bad-value", { role: "predicate-arg", value: tok });
4870
4888
  return null;
4871
4889
  }
@@ -4880,7 +4898,7 @@ function kindOf(val) {
4880
4898
  if (val instanceof ConstVal)
4881
4899
  return val.kind;
4882
4900
  if (val instanceof StrTplVal)
4883
- return K_STRTPL;
4901
+ return val.kind;
4884
4902
  if (val instanceof SeqAccessVal)
4885
4903
  return K_SEQ;
4886
4904
  if (val instanceof FieldVal)
@@ -4891,8 +4909,6 @@ function kindOf(val) {
4891
4909
  return K_BIND;
4892
4910
  if (val instanceof DynVal)
4893
4911
  return K_DYN;
4894
- if (val instanceof RequestVal)
4895
- return K_REQUEST;
4896
4912
  if (val instanceof TypeVal)
4897
4913
  return K_TYPE;
4898
4914
  if (val instanceof NameVal)
@@ -4910,13 +4926,13 @@ class BaseVal {
4910
4926
  return this.eval(stack);
4911
4927
  }
4912
4928
  }
4913
- var VALID_VAL_ID_RE, isValidValId = (name) => VALID_VAL_ID_RE.test(name), VALID_FLOAT_RE, STR_TPL_SPLIT_RE, mkVal = (name, Cls) => isValidValId(name) ? new Cls(name) : null, VAL_TOKEN_RE, tokenizeValue = (s) => s.match(VAL_TOKEN_RE) ?? [], unescapeStr = (s) => s.replace(/\\(['\\])/g, "$1"), K_CONST = 1, K_STRTPL = 2, K_FIELD = 4, K_BIND = 8, K_DYN = 16, K_NAME = 32, K_TYPE = 64, K_REQUEST = 128, K_SEQ = 256, K_STR = 512, K_METHOD = 1024, G_BOOL, G_TEXT, G_COMPONENT, G_SEQUENCE, G_PROVIDE, G_FIELD, G_VALUE, G_PRED_ARG, G_HANDLER_ARG, G_ALL, predTruthy = (v) => {
4929
+ var VALID_VAL_ID_RE, isValidValId = (name) => VALID_VAL_ID_RE.test(name), VALID_FLOAT_RE, STR_TPL_SPLIT_RE, mkVal = (name, Cls) => isValidValId(name) ? new Cls(name) : null, VAL_TOKEN_RE, tokenizeValue = (s) => s.match(VAL_TOKEN_RE) ?? [], unescapeStr = (s) => s.replace(/\\(['\\])/g, "$1"), K_CONST = 1, K_STRTPL = 2, K_FIELD = 4, K_BIND = 8, K_DYN = 16, K_NAME = 32, K_TYPE = 64, K_SEQ = 256, K_METHOD = 1024, G_BOOL, G_TEXT, G_COMPONENT, G_SEQUENCE, G_PROVIDE, G_FIELD, G_VALUE, G_ALL, predTruthy = (v) => {
4914
4930
  const n = sizeOf(v);
4915
4931
  return n === null ? !!v : n > 0;
4916
4932
  }, PREDICATES, ConstVal, PredicateVal, VarVal, StrTplVal, NameVal, HandlerNameVal, mk404Handler = (type, name) => function(...args) {
4917
4933
  console.warn("handler not found", { type, name, args }, this);
4918
4934
  return this;
4919
- }, TypeVal, RequestVal, RenderVal, RenderNameVal, BindVal, DynVal, FieldVal, MethodVal, SeqAccessVal, vp;
4935
+ }, TypeVal, RenderVal, RenderNameVal, BindVal, BindMemberVal, DynVal, FieldVal, MethodVal, SeqAccessVal, vp;
4920
4936
  var init_value = __esm(() => {
4921
4937
  init_immutable();
4922
4938
  init_path();
@@ -4929,10 +4945,8 @@ var init_value = __esm(() => {
4929
4945
  G_COMPONENT = K_FIELD | K_SEQ | K_DYN;
4930
4946
  G_SEQUENCE = K_FIELD | K_DYN;
4931
4947
  G_PROVIDE = K_FIELD | K_SEQ;
4932
- G_FIELD = K_FIELD | K_METHOD | K_CONST | K_STR | K_SEQ;
4933
- G_VALUE = K_FIELD | K_METHOD | K_BIND | K_DYN | K_NAME | K_TYPE | K_REQUEST | K_CONST;
4934
- G_PRED_ARG = G_BOOL | K_STR;
4935
- G_HANDLER_ARG = G_VALUE | K_STR;
4948
+ G_FIELD = K_FIELD | K_METHOD | K_CONST | K_SEQ;
4949
+ G_VALUE = K_FIELD | K_METHOD | K_BIND | K_DYN | K_NAME | K_TYPE | K_CONST;
4936
4950
  G_ALL = G_VALUE | K_STRTPL | K_SEQ;
4937
4951
  PREDICATES = {
4938
4952
  "empty?": { name: "empty?", arity: 1, fn: (v) => v == null || sizeOf(v) === 0 },
@@ -4981,6 +4995,13 @@ var init_value = __esm(() => {
4981
4995
  constructor(vals) {
4982
4996
  super();
4983
4997
  this.vals = vals;
4998
+ this.kind = this.isLiteral() ? K_CONST : K_STRTPL;
4999
+ }
5000
+ isLiteral() {
5001
+ for (const v of this.vals)
5002
+ if (!(v instanceof ConstVal) || v.fromMacroVar)
5003
+ return false;
5004
+ return true;
4984
5005
  }
4985
5006
  render(stack, _rx) {
4986
5007
  return this.eval(stack);
@@ -4992,12 +5013,11 @@ var init_value = __esm(() => {
4992
5013
  return strs.join("");
4993
5014
  }
4994
5015
  toLiteralSource() {
5016
+ if (!this.isLiteral())
5017
+ return null;
4995
5018
  let out = "";
4996
- for (const v of this.vals) {
4997
- if (!(v instanceof ConstVal) || v.fromMacroVar)
4998
- return null;
5019
+ for (const v of this.vals)
4999
5020
  out += v.val;
5000
- }
5001
5021
  return new ConstVal(out).toString();
5002
5022
  }
5003
5023
  static parse(s, px) {
@@ -5044,14 +5064,6 @@ var init_value = __esm(() => {
5044
5064
  return stack.lookupType(this.name);
5045
5065
  }
5046
5066
  };
5047
- RequestVal = class RequestVal extends NameVal {
5048
- eval(stack) {
5049
- return stack.lookupRequest(this.name);
5050
- }
5051
- toString() {
5052
- return `!${this.name}`;
5053
- }
5054
- };
5055
5067
  RenderVal = class RenderVal extends BaseVal {
5056
5068
  render(stack, _rx) {
5057
5069
  return this.eval(stack);
@@ -5071,6 +5083,19 @@ var init_value = __esm(() => {
5071
5083
  return `@${this.name}`;
5072
5084
  }
5073
5085
  };
5086
+ BindMemberVal = class BindMemberVal extends BindVal {
5087
+ constructor(name, member) {
5088
+ super(name);
5089
+ this.member = member;
5090
+ }
5091
+ eval(stack) {
5092
+ const v = stack.lookupBind(this.name);
5093
+ return typeof v?.get === "function" ? v.get(this.member, null) : v?.[this.member] ?? null;
5094
+ }
5095
+ toString() {
5096
+ return `@${this.name}.${this.member}`;
5097
+ }
5098
+ };
5074
5099
  DynVal = class DynVal extends RenderNameVal {
5075
5100
  eval(stack) {
5076
5101
  return stack.lookupDynamic(this.name);
@@ -5213,9 +5238,6 @@ class AttrParser {
5213
5238
  this.attrs.push(new RawHtmlAttr(this._parseDirectiveValue(directiveName, s, vp.parseText)));
5214
5239
  this.hasDynamic = true;
5215
5240
  return;
5216
- case "slot":
5217
- this.pushWrapper("slot", s, vp.const(s));
5218
- return;
5219
5241
  case "push-view":
5220
5242
  this.pushWrapper("push-view", s, this._parseDirectiveValue(directiveName, s, vp.parseText));
5221
5243
  return;
@@ -5325,11 +5347,6 @@ class RequestHandler {
5325
5347
  this.name = name;
5326
5348
  this.fn = fn;
5327
5349
  }
5328
- toHandlerArg(disp) {
5329
- const f = (...args) => disp.request(this.name, args);
5330
- f.withOpts = (...args) => disp.request(this.name, args.slice(0, -1), args.at(-1));
5331
- return f;
5332
- }
5333
5350
  }
5334
5351
  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", booleanAttrs, ConstAttrs, DynAttrs, Attr, ConstAttr, RawHtmlAttr, NOT_SET_VAL, IfAttr, _attrParser = null;
5335
5352
  var init_attribute = __esm(() => {
@@ -5921,24 +5938,6 @@ class Renderer {
5921
5938
  pushEachEntry(r, nid, attrName, key, dom) {
5922
5939
  r.push(this._renderMetadata({ $: "Each", nid, [attrName]: key }), dom);
5923
5940
  }
5924
- renderEach(stack, iterInfo, node, viewName) {
5925
- const { seq, filter, loopWith } = iterInfo.eval(stack);
5926
- const r = [];
5927
- const { iterData, start, end, keys } = unpackLoopResult(loopWith.call(stack.it, seq, makeLoopCtx(stack, filter)), seq);
5928
- const renderOne = (key, value, attrName) => {
5929
- const dom = this.renderIt(stack.enter(value, { key }, true), node, key, viewName);
5930
- if (dom != null)
5931
- this.pushEachEntry(r, node.nodeId, attrName, key, dom);
5932
- };
5933
- if (keys)
5934
- imKeysIter(seq, renderOne, keys);
5935
- else
5936
- getSeqInfo(seq)(seq, (key, value, attrName) => {
5937
- if (filter.call(stack.it, key, value, iterData))
5938
- renderOne(key, value, attrName);
5939
- }, start, end);
5940
- return r;
5941
- }
5942
5941
  renderEachWhen(stack, iterInfo, view, nid) {
5943
5942
  const { seq, filter, loopWith, enricher } = iterInfo.eval(stack);
5944
5943
  const r = [];
@@ -5949,7 +5948,7 @@ class Renderer {
5949
5948
  const binds = { key, value };
5950
5949
  const cacheKey = `${stack.viewsId ?? ""}\x1F${nid}\x1F${key}`;
5951
5950
  if (enricher)
5952
- enricher.call(it, binds, key, value, iterData);
5951
+ callEnricher(enricher, it, binds, key, value, iterData);
5953
5952
  const cachedNode = this.cache.get(cachePath, cacheKey);
5954
5953
  if (cachedNode)
5955
5954
  this.pushEachEntry(r, nid, attrName, key, cachedNode);
@@ -5996,6 +5995,11 @@ var DATASET_ATTRS, getSeqInfo = (seq) => isIndexed(seq) ? imIndexedIter : isKeye
5996
5995
  s = s < 0 ? 0 : s > size ? size : s;
5997
5996
  e = e < 0 ? 0 : e > size ? size : e;
5998
5997
  return [s, e < s ? s : e];
5998
+ }, callEnricher = (enricher, it, binds, key, value, iterData) => {
5999
+ enricher.call(it, binds, key, value, iterData);
6000
+ console.assert(binds.key === key && binds.value === value, "@enrich-with handlers must not overwrite binds.key or binds.value");
6001
+ binds.key = key;
6002
+ binds.value = value;
5999
6003
  }, filterAlwaysTrue = (_v, _k, _seq) => true, nullLoopWith = (seq) => ({ iterData: { seq } }), unpackLoopResult = (result, seq) => {
6000
6004
  const r = result ?? {};
6001
6005
  return { iterData: r.iterData ?? { seq }, start: r.start, end: r.end, keys: r.keys };
@@ -6064,6 +6068,9 @@ class BaseNode {
6064
6068
  isConstant() {
6065
6069
  return false;
6066
6070
  }
6071
+ isWhiteSpace() {
6072
+ return false;
6073
+ }
6067
6074
  optimize() {}
6068
6075
  }
6069
6076
  function optimizeChilds(childs) {
@@ -6085,6 +6092,8 @@ function parseXOp(attrs, childs, opIdx, px) {
6085
6092
  if (attrs.length <= opIdx)
6086
6093
  return maybeFragment(childs);
6087
6094
  const { name, value } = attrs[opIdx];
6095
+ if (X_OPS[name]?.ignoresChildren && hasMeaningfulChilds(childs))
6096
+ px.onParseIssue("x-op-ignores-children", { op: name });
6088
6097
  const asAttr = attrs.getNamedItem("as")?.value ?? null;
6089
6098
  const as = asAttr === null ? null : parseViewName(asAttr, px);
6090
6099
  let node;
@@ -6102,7 +6111,7 @@ function parseXOp(attrs, childs, opIdx, px) {
6102
6111
  node = px.addNodeIf(RenderItNode, vp.bindValIt, as);
6103
6112
  break;
6104
6113
  case "render-each":
6105
- node = RenderEachNode.parse(px, vp, value, as, attrs);
6114
+ node = parseRenderEach(px, value, as, attrs);
6106
6115
  break;
6107
6116
  case "show": {
6108
6117
  const val = parseXOpVal(name, value, px, vp.parseBool);
@@ -6137,8 +6146,12 @@ function processXExtras(node, attrs, opName, startIdx, px) {
6137
6146
  const aName = a.name;
6138
6147
  if (consumed.has(aName))
6139
6148
  continue;
6140
- const wrapper = wrappable ? X_OPS[aName]?.wrapper : null;
6149
+ const atPrefixed = aName.charCodeAt(0) === 64;
6150
+ const baseName = atPrefixed ? aName.slice(1) : aName;
6151
+ const wrapper = wrappable ? X_OPS[baseName]?.wrapper : null;
6141
6152
  if (wrapper) {
6153
+ if (!atPrefixed)
6154
+ maybeDeprecateBareXDirective(px, opName, baseName);
6142
6155
  wrappers.push([wrapper, vp.parseBool(a.value, px)]);
6143
6156
  continue;
6144
6157
  }
@@ -6153,6 +6166,9 @@ function processXExtras(node, attrs, opName, startIdx, px) {
6153
6166
  }
6154
6167
  return node;
6155
6168
  }
6169
+ function maybeDeprecateBareXDirective(px, opName, name) {
6170
+ px.onDeprecatedSyntax("bare-x-directive", { op: opName, name });
6171
+ }
6156
6172
  function wrap(node, px, wrappers) {
6157
6173
  if (wrappers) {
6158
6174
  for (let i = wrappers.length - 1;i >= 0; i--) {
@@ -6181,6 +6197,29 @@ function dynRenderStep(comp, name, key) {
6181
6197
  return null;
6182
6198
  return key === undefined ? new DynStep(p.producerCompId, p.producerSteps) : new DynEachStep(p.producerCompId, p.producerSteps, key);
6183
6199
  }
6200
+ function parseRenderEach(px, value, as, attrs) {
6201
+ const seqVal = parseXOpVal("render-each", value, px, vp.parseSequence);
6202
+ if (seqVal === null)
6203
+ return null;
6204
+ const renderIt = px.addNodeIf(RenderItNode, vp.bindValIt, as);
6205
+ const attrParser = getAttrParser(px);
6206
+ const eachAttr = attrParser.eachAttr = attrParser.pushWrapper("each", value, seqVal);
6207
+ const when = attrs.getNamedItem("@when") ?? attrs.getNamedItem("when");
6208
+ if (when) {
6209
+ if (when.name.charCodeAt(0) !== 64)
6210
+ maybeDeprecateBareXDirective(px, "render-each", "when");
6211
+ attrParser._parseWhen(when.value);
6212
+ }
6213
+ const lWith = attrs.getNamedItem("loop-with");
6214
+ if (lWith)
6215
+ attrParser._parseLoopWith(lWith.value);
6216
+ const each = px.addNodeIf(EachNode, seqVal);
6217
+ each.iterInfo.whenVal = eachAttr.whenVal ?? null;
6218
+ each.iterInfo.loopWithVal = eachAttr.loopWithVal ?? null;
6219
+ each.fromRenderEach = true;
6220
+ each.wrapNode(renderIt);
6221
+ return each;
6222
+ }
6184
6223
 
6185
6224
  class IterInfo {
6186
6225
  constructor(val, whenVal, loopWithVal, enrichWithVal) {
@@ -6202,13 +6241,13 @@ class IterInfo {
6202
6241
  const binds = { key, value };
6203
6242
  if (enricher) {
6204
6243
  const { iterData } = unpackLoopResult(loopWith.call(stack.it, seq, makeLoopCtx(stack, filter)), seq);
6205
- enricher.call(stack.it, binds, key, value, iterData);
6244
+ callEnricher(enricher, stack.it, binds, key, value, iterData);
6206
6245
  }
6207
6246
  return binds;
6208
6247
  }
6209
6248
  }
6210
- function xOp(consumed = [], { wrappable = false, wrapper = null } = {}) {
6211
- return { consumed: new Set(consumed), wrappable, wrapper };
6249
+ function xOp(consumed = [], { wrappable = false, wrapper = null, ignoresChildren = false } = {}) {
6250
+ return { consumed: new Set(consumed), wrappable, wrapper, ignoresChildren };
6212
6251
  }
6213
6252
 
6214
6253
  class ParseContext {
@@ -6256,9 +6295,9 @@ class ParseContext {
6256
6295
  const anySlot = [];
6257
6296
  const slots = { _: new FragmentNode(anySlot) };
6258
6297
  for (const child of childs)
6259
- if (child instanceof SlotNode)
6298
+ if (child.isSlotNode)
6260
6299
  slots[child.val.val] = child.node;
6261
- else if (!(child instanceof TextNode) || !child.isWhiteSpace())
6300
+ else if (!child.isWhiteSpace())
6262
6301
  anySlot.push(child);
6263
6302
  const node = new MacroNode(macroName, mAttrs, slots, this);
6264
6303
  this.macroNodes.push(node);
@@ -6282,6 +6321,7 @@ class ParseContext {
6282
6321
  onParseIssue(kind, info) {
6283
6322
  console.warn(`tutuca parse issue [${kind}]`, info);
6284
6323
  }
6324
+ onDeprecatedSyntax(_kind, _info) {}
6285
6325
  }
6286
6326
  function trimEdgeWhite(node) {
6287
6327
  if (!node.isWhiteSpace?.())
@@ -6361,10 +6401,10 @@ function compileModifiers(eventName, names) {
6361
6401
  return w(this, f, args, ctx);
6362
6402
  };
6363
6403
  }
6364
- var TextNode, CommentNode, ChildsNode, DomNode, FragmentNode, maybeFragment = (xs) => xs.length === 1 ? xs[0] : new FragmentNode(xs), VALID_NODE_RE, ANode, MacroNode, RenderViewId, RenderNode, RenderItNode, RenderEachNode, RenderTextNode, RenderOnceNode, WrapperNode, ShowNode, HideNode, PushViewNameNode, SlotNode, ScopeNode, EachNode, X_OPS, WRAPPER_NODES, _htmlBlockTags = "ADDRESS,ARTICLE,ASIDE,BLOCKQUOTE,CAPTION,COL,COLGROUP,DETAILS,DIALOG,DIV,DD,DL,DT,FIELDSET,FIGCAPTION,FIGURE,FOOTER,FORM,H1,H2,H3,H4,H5,H6,HEADER,HGROUP,HR,LEGEND,LI,MAIN,MENU,NAV,OL,P,PRE,SECTION,SUMMARY,TABLE,TBODY,TD,TFOOT,TH,THEAD,TR,UL", HTML_BLOCK_TAGS, isBlockDomNode = (n) => {
6404
+ var TextNode, CommentNode, ChildsNode, DomNode, FragmentNode, maybeFragment = (xs) => xs.length === 1 ? xs[0] : new FragmentNode(xs), VALID_NODE_RE, ANode, MacroNode, RenderViewId, RenderNode, RenderItNode, RenderTextNode, RenderOnceNode, WrapperNode, ShowNode, HideNode, PushViewNameNode, SlotNode, ScopeNode, EachNode, X_OPS, WRAPPER_NODES, _htmlBlockTags = "ADDRESS,ARTICLE,ASIDE,BLOCKQUOTE,CAPTION,COL,COLGROUP,DETAILS,DIALOG,DIV,DD,DL,DT,FIELDSET,FIGCAPTION,FIGURE,FOOTER,FORM,H1,H2,H3,H4,H5,H6,HEADER,HGROUP,HR,LEGEND,LI,MAIN,MENU,NAV,OL,P,PRE,SECTION,SUMMARY,TABLE,TBODY,TD,TFOOT,TH,THEAD,TR,UL", HTML_BLOCK_TAGS, isBlockDomNode = (n) => {
6365
6405
  const node = n instanceof FragmentNode ? n.childs[0] : n;
6366
6406
  return node instanceof DomNode && HTML_BLOCK_TAGS.has(node.tagName);
6367
- }, isEmptyText = (c) => c instanceof TextNode && c.val === "", fwdIfCtxPred = (pred) => (w) => (that, f, args, ctx) => pred(ctx) ? w(that, f, args, ctx) : that, fwdIfKey = (keyName) => fwdIfCtxPred((ctx) => ctx.e.key === keyName), fwdCtrl, fwdMeta, fwdAlt, metaWraps, MOD_WRAPPERS_BY_EVENT, identityModifierWrapper = (f, _ctx) => f;
6407
+ }, isEmptyText = (c) => c instanceof TextNode && c.val === "", isIgnorableXChild = (c) => c instanceof CommentNode || (c.isWhiteSpace?.() ?? false), hasMeaningfulChilds = (childs) => childs.some((c) => !isIgnorableXChild(c)), fwdIfCtxPred = (pred) => (w) => (that, f, args, ctx) => pred(ctx) ? w(that, f, args, ctx) : that, fwdIfKey = (keyName) => fwdIfCtxPred((ctx) => ctx.e.key === keyName), fwdCtrl, fwdMeta, fwdAlt, metaWraps, MOD_WRAPPERS_BY_EVENT, identityModifierWrapper = (f, _ctx) => f;
6368
6408
  var init_anode = __esm(() => {
6369
6409
  init_attribute();
6370
6410
  init_path();
@@ -6596,36 +6636,6 @@ var init_anode = __esm(() => {
6596
6636
  return null;
6597
6637
  }
6598
6638
  };
6599
- RenderEachNode = class RenderEachNode extends RenderViewId {
6600
- constructor(nodeId, val, viewVal) {
6601
- super(nodeId, val, viewVal);
6602
- this.iterInfo = new IterInfo(val, null, null, null);
6603
- }
6604
- render(stack, rx) {
6605
- return rx.renderEach(stack, this.iterInfo, this, this.evalViewName(stack));
6606
- }
6607
- toPathStep(ctx) {
6608
- if (this.val instanceof DynVal)
6609
- return ctx.hasKey ? dynRenderStep(ctx.comp, this.val.name, ctx.key) : null;
6610
- return super.toPathStep(ctx);
6611
- }
6612
- static parse(px, vp2, s, as, attrs) {
6613
- const node = px.addNodeIf(RenderEachNode, parseXOpVal("render-each", s, px, vp2.parseSequence), as);
6614
- if (node !== null) {
6615
- const attrParser = getAttrParser(px);
6616
- attrParser.eachAttr = attrParser.pushWrapper("each", s, node.val);
6617
- const when = attrs.getNamedItem("when");
6618
- if (when)
6619
- attrParser._parseWhen(when.value);
6620
- const lWith = attrs.getNamedItem("loop-with");
6621
- if (lWith)
6622
- attrParser._parseLoopWith(lWith.value);
6623
- node.iterInfo.whenVal = attrParser.eachAttr.whenVal ?? null;
6624
- node.iterInfo.loopWithVal = attrParser.eachAttr.loopWithVal ?? null;
6625
- }
6626
- return node;
6627
- }
6628
- };
6629
6639
  RenderTextNode = class RenderTextNode extends ANode {
6630
6640
  render(stack, _rx) {
6631
6641
  return this.val.eval(stack);
@@ -6678,6 +6688,7 @@ var init_anode = __esm(() => {
6678
6688
  }
6679
6689
  };
6680
6690
  SlotNode = class SlotNode extends WrapperNode {
6691
+ isSlotNode = true;
6681
6692
  optimize() {
6682
6693
  this.node.optimize();
6683
6694
  }
@@ -6712,15 +6723,17 @@ var init_anode = __esm(() => {
6712
6723
  };
6713
6724
  X_OPS = {
6714
6725
  slot: xOp(),
6715
- text: xOp([], { wrappable: true }),
6716
- render: xOp(["as"], { wrappable: true }),
6717
- "render-it": xOp(["as"], { wrappable: true }),
6718
- "render-each": xOp(["as", "when", "loop-with"], { wrappable: true }),
6726
+ text: xOp([], { wrappable: true, ignoresChildren: true }),
6727
+ render: xOp(["as"], { wrappable: true, ignoresChildren: true }),
6728
+ "render-it": xOp(["as"], { wrappable: true, ignoresChildren: true }),
6729
+ "render-each": xOp(["as", "when", "loop-with", "@when"], {
6730
+ wrappable: true,
6731
+ ignoresChildren: true
6732
+ }),
6719
6733
  show: xOp([], { wrapper: ShowNode }),
6720
6734
  hide: xOp([], { wrapper: HideNode })
6721
6735
  };
6722
6736
  WRAPPER_NODES = {
6723
- slot: SlotNode,
6724
6737
  show: ShowNode,
6725
6738
  hide: HideNode,
6726
6739
  each: EachNode,
@@ -8990,14 +9003,17 @@ function closestName(name, candidates, maxDistance = 2) {
8990
9003
 
8991
9004
  // tools/core/lint-check.js
8992
9005
  function protoHasMethod(proto, name) {
9006
+ return protoMethodValue(proto, name) !== null;
9007
+ }
9008
+ function protoMethodValue(proto, name) {
8993
9009
  let cursor = proto;
8994
9010
  while (cursor && cursor !== Object.prototype) {
8995
9011
  const desc = Object.getOwnPropertyDescriptor(cursor, name);
8996
9012
  if (desc !== undefined)
8997
- return typeof desc.value === "function";
9013
+ return typeof desc.value === "function" ? desc.value : null;
8998
9014
  cursor = Object.getPrototypeOf(cursor);
8999
9015
  }
9000
- return false;
9016
+ return null;
9001
9017
  }
9002
9018
  function collectProtoMethodNames(proto) {
9003
9019
  const out = [];
@@ -9041,6 +9057,8 @@ function classifyBadValue(value) {
9041
9057
  return "logical";
9042
9058
  if (/^[.$][A-Za-z_]\w*\s+\S/.test(s))
9043
9059
  return "call-with-args";
9060
+ if (/^\.[A-Za-z_]\w*\??(\.[A-Za-z_]\w*\??)+$/.test(s))
9061
+ return "field-path";
9044
9062
  return null;
9045
9063
  }
9046
9064
  function checkComponent(Comp, lx = new LintContext, { wellKnownExtras = EMPTY_SET2 } = {}) {
@@ -9069,6 +9087,7 @@ function checkComponent(Comp, lx = new LintContext, { wellKnownExtras = EMPTY_SE
9069
9087
  function checkView(lx, view, Comp, referencedAlters, referencedDynamics) {
9070
9088
  checkParseIssues(lx, view);
9071
9089
  checkRenderItInLoop(lx, view);
9090
+ checkEnrichProjection(lx, view, Comp);
9072
9091
  checkEventModifiers(lx, view);
9073
9092
  checkKnownHandlerNames(lx, view, Comp, referencedAlters, referencedDynamics);
9074
9093
  checkMacroCallArgs(lx, view, Comp);
@@ -9084,11 +9103,31 @@ function checkParseIssues(lx, view) {
9084
9103
  if (!issues)
9085
9104
  return;
9086
9105
  for (const { kind, info } of issues) {
9106
+ if (kind === "deprecated:bare-x-directive") {
9107
+ lx.warn(DEPRECATED_BARE_X_DIRECTIVE, info, {
9108
+ kind: "add-prefix",
9109
+ from: info.name,
9110
+ to: `@${info.name}`
9111
+ });
9112
+ continue;
9113
+ }
9114
+ if (kind === "x-op-ignores-children") {
9115
+ lx.warn(X_OP_IGNORES_CHILDREN, info, { kind: "remove", what: "the ignored children" });
9116
+ continue;
9117
+ }
9087
9118
  const rule = PARSE_ISSUES[kind];
9088
9119
  if (!rule)
9089
9120
  continue;
9090
9121
  const id = rule.id;
9091
9122
  if (kind === "bad-value") {
9123
+ if (typeof info.value === "string" && BINDING_MEMBER_TOO_DEEP_RE.test(info.value.trim())) {
9124
+ lx.error(BINDING_MEMBER_TOO_DEEP, info, {
9125
+ kind: "rephrase",
9126
+ from: info.value,
9127
+ text: "Read one level off the binding and compute the rest in a method or an '@enrich-with' handler."
9128
+ });
9129
+ continue;
9130
+ }
9092
9131
  const detected = classifyBadValue(info.value);
9093
9132
  if (detected) {
9094
9133
  lx.error(UNSUPPORTED_EXPR_SYNTAX, { ...info, detected }, { kind: "rephrase", from: info.value, text: UNSUPPORTED_EXPR_GUIDANCE[detected] });
@@ -9135,6 +9174,54 @@ function checkRenderItInLoop(lx, view) {
9135
9174
  return;
9136
9175
  walkForRenderIt(lx, view.anode, 0);
9137
9176
  }
9177
+ function pureProjectionBinds(fn) {
9178
+ const src = Function.prototype.toString.call(fn);
9179
+ const head = src.match(/^[^(]*\(([^)]*)\)\s*(?:=>)?\s*/);
9180
+ if (!head)
9181
+ return null;
9182
+ const params = head[1].split(",").map((p) => p.trim());
9183
+ const [bindsName, , valueName] = params;
9184
+ if (!/^\w+$/.test(bindsName ?? "") || !/^\w+$/.test(valueName ?? ""))
9185
+ return null;
9186
+ let body = src.slice(head[0].length).trim();
9187
+ if (body.startsWith("{")) {
9188
+ if (!body.endsWith("}"))
9189
+ return null;
9190
+ body = body.slice(1, -1);
9191
+ }
9192
+ const statements = body.split(/[;\n]/).map((s) => s.trim()).filter((s) => s !== "" && !s.startsWith("//"));
9193
+ if (statements.length === 0)
9194
+ return null;
9195
+ const projectionRe = new RegExp(`^${bindsName}\\.(\\w+)\\s*=\\s*${valueName}(?:\\.(\\w+)|\\.get\\((['"])(\\w+)\\3\\))$`);
9196
+ const members = [];
9197
+ for (const statement of statements) {
9198
+ const m = statement.match(projectionRe);
9199
+ if (!m)
9200
+ return null;
9201
+ members.push({ bind: m[1], member: m[2] ?? m[4] });
9202
+ }
9203
+ return members;
9204
+ }
9205
+ function checkEnrichProjection(lx, view, Comp) {
9206
+ for (const node of view.ctx.nodes) {
9207
+ if (node.constructor.name !== "EachNode")
9208
+ continue;
9209
+ const enrich = node.iterInfo?.enrichWithVal;
9210
+ if (!enrich?.name)
9211
+ continue;
9212
+ const fn = enrich.constructor.name === "MethodVal" ? protoMethodValue(Comp.Class?.prototype, enrich.name) : Comp.alter?.[enrich.name];
9213
+ if (typeof fn !== "function")
9214
+ continue;
9215
+ const members = pureProjectionBinds(fn);
9216
+ if (members === null)
9217
+ continue;
9218
+ const rewrites = members.map(({ bind, member }) => `'@${bind}' -> '@value.${member}'`);
9219
+ lx.hint(SUGGEST_BINDING_MEMBER, { name: enrich.name, members }, {
9220
+ kind: "rephrase",
9221
+ text: `Replace ${rewrites.join(", ")} in the view, then remove the '@enrich-with' and the '${enrich.name}' alter handler.`
9222
+ });
9223
+ }
9224
+ }
9138
9225
  function walkForRenderIt(lx, node, loopDepth) {
9139
9226
  if (node === null || node === undefined)
9140
9227
  return;
@@ -9382,7 +9469,7 @@ function checkConsistentAttrs(lx, Comp, referencedAlters, referencedDynamics) {
9382
9469
  if (node.val) {
9383
9470
  checkConsistentAttrVal(lx, node.val, env, false, baseCtx);
9384
9471
  }
9385
- if (nodeKind === "RenderEachNode") {
9472
+ if (nodeKind === "EachNode" && node.fromRenderEach) {
9386
9473
  const iter = node.iterInfo;
9387
9474
  if (iter.whenVal)
9388
9475
  checkConsistentAttrVal(lx, iter.whenVal, env, false, {
@@ -9609,7 +9696,7 @@ class LintContext {
9609
9696
  this.reports.push({ id, info, level, context: { ...this.frame }, suggestion });
9610
9697
  }
9611
9698
  }
9612
- var KNOWN_COMPONENT_SPEC_KEYS, EMPTY_SET2, FRAMEWORK_WELL_KNOWN_EXTRAS, KNOWN_DIRECTIVE_NAMES, ALT_HANDLER_NOT_DEFINED = "ALT_HANDLER_NOT_DEFINED", ALT_HANDLER_NOT_REFERENCED = "ALT_HANDLER_NOT_REFERENCED", DYN_VAL_NOT_DEFINED = "DYN_VAL_NOT_DEFINED", DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED", PROVIDE_NOT_ADDRESSABLE = "PROVIDE_NOT_ADDRESSABLE", LOOKUP_BAD_SHAPE = "LOOKUP_BAD_SHAPE", LOOKUP_TARGET_MALFORMED = "LOOKUP_TARGET_MALFORMED", RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP", UNKNOWN_EVENT_MODIFIER = "UNKNOWN_EVENT_MODIFIER", UNKNOWN_HANDLER_ARG_NAME = "UNKNOWN_HANDLER_ARG_NAME", INPUT_HANDLER_NOT_IMPLEMENTED = "INPUT_HANDLER_NOT_IMPLEMENTED", INPUT_HANDLER_NOT_REFERENCED = "INPUT_HANDLER_NOT_REFERENCED", INPUT_HANDLER_METHOD_NOT_IMPLEMENTED = "INPUT_HANDLER_METHOD_NOT_IMPLEMENTED", INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD = "INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD", INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER = "INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER", FIELD_VAL_NOT_DEFINED = "FIELD_VAL_NOT_DEFINED", FIELD_VAL_IS_METHOD = "FIELD_VAL_IS_METHOD", METHOD_VAL_NOT_DEFINED = "METHOD_VAL_NOT_DEFINED", METHOD_VAL_IS_FIELD = "METHOD_VAL_IS_FIELD", DUPLICATE_ATTR_DEFINITION = "DUPLICATE_ATTR_DEFINITION", IF_NO_BRANCH_SET = "IF_NO_BRANCH_SET", UNKNOWN_REQUEST_NAME = "UNKNOWN_REQUEST_NAME", UNKNOWN_COMPONENT_NAME = "UNKNOWN_COMPONENT_NAME", UNKNOWN_MACRO_ARG = "UNKNOWN_MACRO_ARG", UNKNOWN_DIRECTIVE = "UNKNOWN_DIRECTIVE", UNKNOWN_X_OP = "UNKNOWN_X_OP", UNKNOWN_X_ATTR = "UNKNOWN_X_ATTR", MAYBE_DROP_AT_PREFIX = "MAYBE_DROP_AT_PREFIX", MAYBE_ADD_AT_PREFIX = "MAYBE_ADD_AT_PREFIX", BAD_VALUE = "BAD_VALUE", UNSUPPORTED_EXPR_SYNTAX = "UNSUPPORTED_EXPR_SYNTAX", REDUNDANT_TEMPLATE_STRING = "REDUNDANT_TEMPLATE_STRING", PLACEHOLDERLESS_TEMPLATE_STRING = "PLACEHOLDERLESS_TEMPLATE_STRING", UNKNOWN_COMPONENT_SPEC_KEY = "UNKNOWN_COMPONENT_SPEC_KEY", COMP_FIELD_BAD_SHAPE = "COMP_FIELD_BAD_SHAPE", ASYNC_HANDLER = "ASYNC_HANDLER", TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE = "TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE", GLOBAL_SELECTOR_IN_SCOPED_STYLE = "GLOBAL_SELECTOR_IN_SCOPED_STYLE", FIELD_NAME_RESERVED_BY_RECORD = "FIELD_NAME_RESERVED_BY_RECORD", RECORD_FIELD_NAME_COLLISIONS, X_KNOWN_OP_NAMES, X_KNOWN_ATTR_NAMES, HOST_DIRECTIVE_ONLY_NAMES, LEVEL_WARN2 = "warn", LEVEL_ERROR2 = "error", LEVEL_HINT = "hint", PARSE_ISSUES, UNSUPPORTED_EXPR_GUIDANCE, HTML_LINT_OPTS, NO_WRAPPERS, KNOWN_HANDLER_NAMES, fixTo = (from, to) => ({ kind: "rewrite", from, to }), ATTR_VAL_CHECKERS, NODE_KIND_TO_CTX, HANDLER_CHANNELS, ASYNC_HANDLER_HELP, NON_NESTABLE_AT_RULE, GLOBAL_LEADING_SELECTOR, IGNORE_DIRECTIVE, blankRun = (m) => m.replace(/[^\n]/g, " "), STYLE_TO_GLOBAL_HELP, KNOWN_LOOKUP_KEYS, LintParseContext;
9699
+ var KNOWN_COMPONENT_SPEC_KEYS, EMPTY_SET2, FRAMEWORK_WELL_KNOWN_EXTRAS, KNOWN_DIRECTIVE_NAMES, ALT_HANDLER_NOT_DEFINED = "ALT_HANDLER_NOT_DEFINED", ALT_HANDLER_NOT_REFERENCED = "ALT_HANDLER_NOT_REFERENCED", DYN_VAL_NOT_DEFINED = "DYN_VAL_NOT_DEFINED", DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED", PROVIDE_NOT_ADDRESSABLE = "PROVIDE_NOT_ADDRESSABLE", LOOKUP_BAD_SHAPE = "LOOKUP_BAD_SHAPE", LOOKUP_TARGET_MALFORMED = "LOOKUP_TARGET_MALFORMED", RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP", UNKNOWN_EVENT_MODIFIER = "UNKNOWN_EVENT_MODIFIER", UNKNOWN_HANDLER_ARG_NAME = "UNKNOWN_HANDLER_ARG_NAME", INPUT_HANDLER_NOT_IMPLEMENTED = "INPUT_HANDLER_NOT_IMPLEMENTED", INPUT_HANDLER_NOT_REFERENCED = "INPUT_HANDLER_NOT_REFERENCED", INPUT_HANDLER_METHOD_NOT_IMPLEMENTED = "INPUT_HANDLER_METHOD_NOT_IMPLEMENTED", INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD = "INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD", INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER = "INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER", FIELD_VAL_NOT_DEFINED = "FIELD_VAL_NOT_DEFINED", FIELD_VAL_IS_METHOD = "FIELD_VAL_IS_METHOD", METHOD_VAL_NOT_DEFINED = "METHOD_VAL_NOT_DEFINED", METHOD_VAL_IS_FIELD = "METHOD_VAL_IS_FIELD", DUPLICATE_ATTR_DEFINITION = "DUPLICATE_ATTR_DEFINITION", IF_NO_BRANCH_SET = "IF_NO_BRANCH_SET", UNKNOWN_COMPONENT_NAME = "UNKNOWN_COMPONENT_NAME", UNKNOWN_MACRO_ARG = "UNKNOWN_MACRO_ARG", UNKNOWN_DIRECTIVE = "UNKNOWN_DIRECTIVE", UNKNOWN_X_OP = "UNKNOWN_X_OP", UNKNOWN_X_ATTR = "UNKNOWN_X_ATTR", X_OP_IGNORES_CHILDREN = "X_OP_IGNORES_CHILDREN", MAYBE_DROP_AT_PREFIX = "MAYBE_DROP_AT_PREFIX", MAYBE_ADD_AT_PREFIX = "MAYBE_ADD_AT_PREFIX", DEPRECATED_BARE_X_DIRECTIVE = "DEPRECATED_BARE_X_DIRECTIVE", BAD_VALUE = "BAD_VALUE", UNSUPPORTED_EXPR_SYNTAX = "UNSUPPORTED_EXPR_SYNTAX", BINDING_MEMBER_TOO_DEEP = "BINDING_MEMBER_TOO_DEEP", SUGGEST_BINDING_MEMBER = "SUGGEST_BINDING_MEMBER", REDUNDANT_TEMPLATE_STRING = "REDUNDANT_TEMPLATE_STRING", PLACEHOLDERLESS_TEMPLATE_STRING = "PLACEHOLDERLESS_TEMPLATE_STRING", CONSTANT_CONDITION = "CONSTANT_CONDITION", UNKNOWN_COMPONENT_SPEC_KEY = "UNKNOWN_COMPONENT_SPEC_KEY", COMP_FIELD_BAD_SHAPE = "COMP_FIELD_BAD_SHAPE", ASYNC_HANDLER = "ASYNC_HANDLER", TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE = "TOP_LEVEL_AT_RULE_IN_SCOPED_STYLE", GLOBAL_SELECTOR_IN_SCOPED_STYLE = "GLOBAL_SELECTOR_IN_SCOPED_STYLE", FIELD_NAME_RESERVED_BY_RECORD = "FIELD_NAME_RESERVED_BY_RECORD", RECORD_FIELD_NAME_COLLISIONS, X_KNOWN_OP_NAMES, X_KNOWN_ATTR_NAMES, HOST_DIRECTIVE_ONLY_NAMES, LEVEL_WARN2 = "warn", LEVEL_ERROR2 = "error", LEVEL_HINT = "hint", PARSE_ISSUES, BINDING_MEMBER_TOO_DEEP_RE, UNSUPPORTED_EXPR_GUIDANCE, HTML_LINT_OPTS, NO_WRAPPERS, KNOWN_HANDLER_NAMES, fixTo = (from, to) => ({ kind: "rewrite", from, to }), BOOL_CONDITION_ORIGINS, isBoolConditionCtx = (errCtx) => errCtx != null && (BOOL_CONDITION_ORIGINS.has(errCtx.originAttr) || errCtx.branch === "@if"), ATTR_VAL_CHECKERS, NODE_KIND_TO_CTX, HANDLER_CHANNELS, ASYNC_HANDLER_HELP, NON_NESTABLE_AT_RULE, GLOBAL_LEADING_SELECTOR, IGNORE_DIRECTIVE, blankRun = (m) => m.replace(/[^\n]/g, " "), STYLE_TO_GLOBAL_HELP, KNOWN_LOOKUP_KEYS, LintParseContext;
9613
9700
  var init_lint_check = __esm(() => {
9614
9701
  init_anode();
9615
9702
  init_htmllinter();
@@ -9652,11 +9739,13 @@ var init_lint_check = __esm(() => {
9652
9739
  },
9653
9740
  "bad-value": { id: BAD_VALUE }
9654
9741
  };
9742
+ BINDING_MEMBER_TOO_DEEP_RE = /^@[a-zA-Z]\w*\??(\.[a-zA-Z]\w*\??){2,}$/;
9655
9743
  UNSUPPORTED_EXPR_GUIDANCE = {
9656
9744
  ternary: "Ternary expressions aren't supported in dynamic attributes. Define a method or computed field on the component that returns the value, then reference it as '$methodName'.",
9657
9745
  comparison: "Comparisons aren't supported in dynamic attributes. Define a method like 'isFooSelected' that returns the boolean, then reference it as '$isFooSelected'.",
9658
9746
  logical: "Logical operators aren't supported in dynamic attributes. Combine the conditions in a method on the component and reference it as '$methodName'.",
9659
- "call-with-args": "Method calls with arguments aren't supported here. Reference a no-arg method ('$methodName') and read what you need from component state, or split into per-case methods."
9747
+ "call-with-args": "Method calls with arguments aren't supported here. Reference a no-arg method ('$methodName') and read what you need from component state, or split into per-case methods.",
9748
+ "field-path": "Fields can't be read through a dotted path. Define a method that returns the value, render a child component for the nested data, or — inside a loop — read one member off a binding ('@value.member')."
9660
9749
  };
9661
9750
  HTML_LINT_OPTS = {
9662
9751
  fragmentContext: "template",
@@ -9683,7 +9772,12 @@ var init_lint_check = __esm(() => {
9683
9772
  "ctx",
9684
9773
  "dragInfo"
9685
9774
  ]);
9775
+ BOOL_CONDITION_ORIGINS = new Set(["@show", "@hide", "<x show>", "<x hide>"]);
9686
9776
  ATTR_VAL_CHECKERS = {
9777
+ ConstVal({ lx, val, errCtx }) {
9778
+ if (isBoolConditionCtx(errCtx) && typeof val.val !== "boolean")
9779
+ lx.warn(CONSTANT_CONDITION, { ...errCtx, literal: String(val) });
9780
+ },
9687
9781
  FieldVal({ lx, val, env, errCtx }) {
9688
9782
  const { fields, proto } = env;
9689
9783
  const { name } = val;
@@ -9711,10 +9805,6 @@ var init_lint_check = __esm(() => {
9711
9805
  recurse(val.seqVal);
9712
9806
  recurse(val.keyVal);
9713
9807
  },
9714
- RequestVal({ lx, val, env, errCtx }) {
9715
- if (env.scope.lookupRequest(val.name) === null)
9716
- reportUnknownName(lx, UNKNOWN_REQUEST_NAME, val.name, scopeKeysAlong(env.scope, "reqsByName"), errCtx);
9717
- },
9718
9808
  TypeVal({ lx, val, env, errCtx }) {
9719
9809
  if (env.scope.lookupComponent(val.name) === null)
9720
9810
  reportUnknownName(lx, UNKNOWN_COMPONENT_NAME, val.name, scopeKeysAlong(env.scope, "byName"), errCtx);
@@ -9727,7 +9817,11 @@ var init_lint_check = __esm(() => {
9727
9817
  const vs = val.vals;
9728
9818
  const literal = val.toLiteralSource();
9729
9819
  if (literal !== null) {
9730
- lx.hint(PLACEHOLDERLESS_TEMPLATE_STRING, { ...errCtx, literal }, fixTo(`$${literal}`, literal));
9820
+ if (isBoolConditionCtx(errCtx))
9821
+ lx.warn(CONSTANT_CONDITION, { ...errCtx, literal });
9822
+ else
9823
+ lx.hint(PLACEHOLDERLESS_TEMPLATE_STRING, { ...errCtx, literal }, fixTo(`$${literal}`, literal));
9824
+ return;
9731
9825
  } else if (vs.length === 1) {
9732
9826
  const simpler = String(vs[0]);
9733
9827
  lx.warn(REDUNDANT_TEMPLATE_STRING, { ...errCtx, simpler }, fixTo(`$'{${simpler}}'`, simpler));
@@ -9754,7 +9848,6 @@ var init_lint_check = __esm(() => {
9754
9848
  RenderTextNode: { originAttr: "<x text>" },
9755
9849
  RenderNode: { originAttr: "<x render>" },
9756
9850
  RenderItNode: { originAttr: "<x render-it>" },
9757
- RenderEachNode: { originAttr: "<x render-each>" },
9758
9851
  ShowNode: { originAttr: "<x show>" },
9759
9852
  HideNode: { originAttr: "<x hide>" },
9760
9853
  PushViewNameNode: { originAttr: "<x push-view>" }
@@ -9779,6 +9872,13 @@ var init_lint_check = __esm(() => {
9779
9872
  const tag = this.currentTag;
9780
9873
  this.parseIssues.push({ kind, info: tag && info.tag === undefined ? { ...info, tag } : info });
9781
9874
  }
9875
+ onDeprecatedSyntax(kind, info) {
9876
+ const tag = this.currentTag;
9877
+ this.parseIssues.push({
9878
+ kind: `deprecated:${kind}`,
9879
+ info: tag && info.tag === undefined ? { ...info, tag } : info
9880
+ });
9881
+ }
9782
9882
  };
9783
9883
  });
9784
9884
 
@@ -9867,6 +9967,12 @@ var init_lint_rules = __esm(() => {
9867
9967
  group: "Iteration helpers (`alter`)",
9868
9968
  summary: "`alter` entry is defined but never used."
9869
9969
  },
9970
+ {
9971
+ code: SUGGEST_BINDING_MEMBER,
9972
+ level: "hint",
9973
+ group: "Iteration helpers (`alter`)",
9974
+ summary: "`@enrich-with` handler only copies members of the loop value — read them directly as `@value.member` and drop the enrich."
9975
+ },
9870
9976
  {
9871
9977
  code: DYN_VAL_NOT_DEFINED,
9872
9978
  level: "error",
@@ -9945,6 +10051,12 @@ var init_lint_rules = __esm(() => {
9945
10051
  group: "Templates / events",
9946
10052
  summary: "Extra attribute on `<x op>` not consumed by the op and not a known wrapper."
9947
10053
  },
10054
+ {
10055
+ code: X_OP_IGNORES_CHILDREN,
10056
+ level: "warn",
10057
+ group: "Templates / events",
10058
+ summary: "`<x text/render/render-it/render-each>` has child content the op silently drops."
10059
+ },
9948
10060
  {
9949
10061
  code: MAYBE_DROP_AT_PREFIX,
9950
10062
  level: "hint",
@@ -9957,6 +10069,12 @@ var init_lint_rules = __esm(() => {
9957
10069
  group: "Templates / events",
9958
10070
  summary: "A directive name (`when`/`enrich-with`/`loop-with`/`show`/`hide`) was written as a plain attribute on a host element — add the `@` prefix."
9959
10071
  },
10072
+ {
10073
+ code: DEPRECATED_BARE_X_DIRECTIVE,
10074
+ level: "warn",
10075
+ group: "Templates / events",
10076
+ summary: "Bare `show`/`hide`/`when` on an `<x>` op is deprecated — use the `@`-prefixed directive (`@show`/`@hide`/`@when`)."
10077
+ },
9960
10078
  {
9961
10079
  code: BAD_VALUE,
9962
10080
  level: "error",
@@ -9967,7 +10085,13 @@ var init_lint_rules = __esm(() => {
9967
10085
  code: UNSUPPORTED_EXPR_SYNTAX,
9968
10086
  level: "error",
9969
10087
  group: "Value expressions",
9970
- summary: "Value uses JS-style syntax (ternary, comparison, logical, call-with-args) tutuca does not support — move the logic into a method."
10088
+ summary: "Value uses JS-style syntax (ternary, comparison, logical, call-with-args, dotted field path) tutuca does not support — move the logic into a method."
10089
+ },
10090
+ {
10091
+ code: BINDING_MEMBER_TOO_DEEP,
10092
+ level: "error",
10093
+ group: "Value expressions",
10094
+ summary: "A binding member read goes more than one level deep (`@x.a.b`) — only one member is allowed."
9971
10095
  },
9972
10096
  {
9973
10097
  code: REDUNDANT_TEMPLATE_STRING,
@@ -9982,10 +10106,10 @@ var init_lint_rules = __esm(() => {
9982
10106
  summary: "`$'…'` template has no `{}` placeholder — use a plain string literal."
9983
10107
  },
9984
10108
  {
9985
- code: UNKNOWN_REQUEST_NAME,
9986
- level: "error",
9987
- group: "Names registered with the app",
9988
- summary: "`!name` references a request handler not registered with the app."
10109
+ code: CONSTANT_CONDITION,
10110
+ level: "warn",
10111
+ group: "Value expressions",
10112
+ summary: "A non-boolean literal in a boolean condition (`@show`, `@hide`, `@if`) never changes — reference a field or method instead."
9989
10113
  },
9990
10114
  {
9991
10115
  code: UNKNOWN_COMPONENT_NAME,
@@ -10158,8 +10282,6 @@ function lintIdToMessage(id, info) {
10158
10282
  }
10159
10283
  case "IF_NO_BRANCH_SET":
10160
10284
  return `'@if.${info.attr}' has no '@then' or '@else' branch — add '@then="…"' or '@else="…"' (or both)${fmtTagSuffix(info)}`;
10161
- case "UNKNOWN_REQUEST_NAME":
10162
- return `Unknown request '!${info.name}'${fmtOriginSuffix(info)}`;
10163
10285
  case "UNKNOWN_COMPONENT_NAME":
10164
10286
  return `Unknown component '${info.name}'${fmtOriginSuffix(info)}`;
10165
10287
  case "ALT_HANDLER_NOT_DEFINED":
@@ -10184,20 +10306,32 @@ function lintIdToMessage(id, info) {
10184
10306
  return `Unknown <x> op '${info.name}=${JSON.stringify(info.value)}'${fmtTagSuffix(info)}`;
10185
10307
  case "UNKNOWN_X_ATTR":
10186
10308
  return `Unknown attribute '${info.name}=${JSON.stringify(info.value)}' on <x ${info.op}>${fmtTagSuffix(info)}`;
10309
+ case "X_OP_IGNORES_CHILDREN":
10310
+ return `<x ${info.op}> has child content, but the '${info.op}' op ignores its children — they are silently dropped when the template is parsed; remove them${fmtTagSuffix(info)}`;
10187
10311
  case "MAYBE_DROP_AT_PREFIX": {
10188
10312
  const written = info.value !== undefined ? `${info.name}=${JSON.stringify(info.value)}` : info.name;
10189
10313
  return `'${written}' on <x> looks like a directive but is actually an x op/attr written with a leading '@'`;
10190
10314
  }
10191
10315
  case "MAYBE_ADD_AT_PREFIX":
10192
10316
  return `'${info.name}' on <${(info.tag ?? "").toLowerCase()}> is a plain attribute, but '@${info.name}' is a directive — add the leading '@'`;
10317
+ case "DEPRECATED_BARE_X_DIRECTIVE":
10318
+ return `'${info.name}' on <x ${info.op}> is deprecated — write '@${info.name}' instead${fmtTagSuffix(info)}`;
10193
10319
  case "BAD_VALUE":
10194
10320
  return `${badValueMessage(info)}${fmtTagSuffix(info)}`;
10195
10321
  case "UNSUPPORTED_EXPR_SYNTAX":
10196
10322
  return `${unsupportedExprMessage(info)}${fmtTagSuffix(info)}`;
10323
+ case "BINDING_MEMBER_TOO_DEEP":
10324
+ return `Binding member read ${JSON.stringify(info.value)} goes more than one level deep — only one member is allowed (like '@value.title')${fmtTagSuffix(info)}`;
10325
+ case "SUGGEST_BINDING_MEMBER": {
10326
+ const reads = (info.members ?? []).map(({ member }) => `'@value.${member}'`).join(", ");
10327
+ return `Enrich handler '${info.name}' only copies members of the loop value — read them directly${reads ? ` (${reads})` : ""} and remove the '@enrich-with'`;
10328
+ }
10197
10329
  case "REDUNDANT_TEMPLATE_STRING":
10198
10330
  return `Redundant template string — '{${info.simpler}}' should be just '${info.simpler}'${fmtOriginSuffix(info)}`;
10199
10331
  case "PLACEHOLDERLESS_TEMPLATE_STRING":
10200
10332
  return `Template string has no dynamic parts — use the string literal ${info.literal} instead${fmtOriginSuffix(info)}`;
10333
+ case "CONSTANT_CONDITION":
10334
+ return `Condition is the constant literal ${info.literal} — it never changes; reference a field ('.name') or a method ('$name') instead${fmtOriginSuffix(info)}`;
10201
10335
  case "UNKNOWN_COMPONENT_SPEC_KEY":
10202
10336
  return `Unknown component spec key '${info.key}' — value will be ignored at runtime`;
10203
10337
  case "COMP_FIELD_BAD_SHAPE":
@@ -10300,7 +10434,8 @@ var init_lint = __esm(() => {
10300
10434
  ternary: "ternary expression",
10301
10435
  comparison: "comparison",
10302
10436
  logical: "logical expression",
10303
- "call-with-args": "method call with arguments"
10437
+ "call-with-args": "method call with arguments",
10438
+ "field-path": "dotted field path"
10304
10439
  };
10305
10440
  });
10306
10441
 
@@ -15108,9 +15243,6 @@ class Stack2 {
15108
15243
  getHandlerFor(name, key) {
15109
15244
  return this.comps.getHandlerFor(this.it, name, key);
15110
15245
  }
15111
- lookupRequest(name) {
15112
- return this.comps.getRequestFor(this.it, name);
15113
- }
15114
15246
  lookupBestView(views, defaultViewName) {
15115
15247
  let n = this.views;
15116
15248
  while (n !== null) {
@@ -15154,12 +15286,47 @@ class Transactor {
15154
15286
  this.transactions = [];
15155
15287
  this.state = new State2(rootValue);
15156
15288
  this.onTransactionPushed = () => {};
15289
+ this._observers = [];
15157
15290
  this._inflight = new Set;
15158
15291
  }
15159
15292
  pushTransaction(t) {
15160
15293
  this.transactions.push(t);
15161
15294
  this.onTransactionPushed(t);
15162
15295
  }
15296
+ observe(cb) {
15297
+ this._observers.push(cb);
15298
+ return () => {
15299
+ const i = this._observers.indexOf(cb);
15300
+ if (i !== -1)
15301
+ this._observers.splice(i, 1);
15302
+ };
15303
+ }
15304
+ _emit(record) {
15305
+ for (const cb of this._observers)
15306
+ cb(record);
15307
+ }
15308
+ _emitTransaction(transaction, root) {
15309
+ if (this._observers.length === 0)
15310
+ return;
15311
+ if (transaction._resolvedHandler === undefined)
15312
+ return;
15313
+ const path = transaction.getTransactionPath().pinKeys(root);
15314
+ this._emit({
15315
+ kind: transaction.observeKind,
15316
+ name: transaction.observeName,
15317
+ args: transaction.args ?? null,
15318
+ path,
15319
+ pathKeys: path.toKeys(),
15320
+ targetPath: transaction.targetPath ?? transaction.path,
15321
+ handler: transaction._resolvedHandler ?? null,
15322
+ handlerName: transaction._resolvedHandler?.name || null,
15323
+ matched: transaction._matched ?? null,
15324
+ before: transaction._before,
15325
+ after: transaction._after,
15326
+ parent: transaction.parentTransaction,
15327
+ timestamp: Date.now()
15328
+ });
15329
+ }
15163
15330
  _link(child, parent) {
15164
15331
  if (parent) {
15165
15332
  const release = parent.completion.track();
@@ -15210,7 +15377,26 @@ class Transactor {
15210
15377
  const curRoot = this.state.val;
15211
15378
  const txnPath = path.toTransactionPath();
15212
15379
  const curLeaf = txnPath.lookup(curRoot);
15213
- const handler = this.comps.getRequestFor(curLeaf, name) ?? mkReq404(name);
15380
+ const found = this.comps.getRequestFor(curLeaf, name);
15381
+ const handler = found ?? mkReq404(name);
15382
+ if (this._observers.length > 0) {
15383
+ const reqPath = txnPath.pinKeys(curRoot);
15384
+ this._emit({
15385
+ kind: "request",
15386
+ name,
15387
+ args,
15388
+ path: reqPath,
15389
+ pathKeys: reqPath.toKeys(),
15390
+ targetPath: path,
15391
+ handler: found?.fn ?? null,
15392
+ handlerName: found?.fn?.name || name,
15393
+ matched: found ? "exact" : "none",
15394
+ before: curLeaf,
15395
+ after: undefined,
15396
+ parent,
15397
+ timestamp: Date.now()
15398
+ });
15399
+ }
15214
15400
  const reqCtx = new RequestContext(path, this, parent, curRoot);
15215
15401
  const resHandlerName = opts?.onResName ?? name;
15216
15402
  const resPath = opts?.livePath ? null : txnPath.pinKeys(curRoot);
@@ -15245,6 +15431,7 @@ class Transactor {
15245
15431
  if (newState !== undefined) {
15246
15432
  this.state.set(newState, { transaction });
15247
15433
  transaction.afterTransaction();
15434
+ this._emitTransaction(transaction, curState);
15248
15435
  } else
15249
15436
  console.warn("undefined new state", { curState, transaction });
15250
15437
  } finally {
@@ -15293,8 +15480,15 @@ class Transaction {
15293
15480
  buildStack(root, comps) {
15294
15481
  return this.path.toTransactionPath().buildStack(this.buildRootStack(root, comps));
15295
15482
  }
15483
+ get observeKind() {
15484
+ return null;
15485
+ }
15486
+ get observeName() {
15487
+ return null;
15488
+ }
15296
15489
  callHandler(root, instance, comps) {
15297
15490
  const [handler, args] = this.getHandlerAndArgs(root, instance, comps);
15491
+ this._resolvedHandler = handler;
15298
15492
  return handler.apply(instance, args);
15299
15493
  }
15300
15494
  getHandlerAndArgs(_root, _instance, _comps) {
@@ -15307,6 +15501,8 @@ class Transaction {
15307
15501
  const txnPath = this.getTransactionPath();
15308
15502
  const curLeaf = txnPath.lookup(curRoot);
15309
15503
  const newLeaf = this.callHandler(curRoot, curLeaf, comps);
15504
+ this._before = curLeaf;
15505
+ this._after = newLeaf;
15310
15506
  this._completion?.markSelfSettled({ value: newLeaf, old: curLeaf });
15311
15507
  return curLeaf !== newLeaf ? txnPath.setValue(curRoot, newLeaf) : curRoot;
15312
15508
  }
@@ -15442,6 +15638,12 @@ var init_transactor = __esm(() => {
15442
15638
  this._dispatchPath ??= this.path.compact();
15443
15639
  return this._dispatchPath;
15444
15640
  }
15641
+ get observeKind() {
15642
+ return "input";
15643
+ }
15644
+ get observeName() {
15645
+ return this.e?.type ?? null;
15646
+ }
15445
15647
  buildRootStack(root, comps) {
15446
15648
  return Stack2.root(comps, root, this);
15447
15649
  }
@@ -15449,13 +15651,6 @@ var init_transactor = __esm(() => {
15449
15651
  const stack = this.buildStack(root, comps);
15450
15652
  const [handler, args] = this.handler.getHandlerAndArgs(stack, this);
15451
15653
  const path = this.dispatchPath;
15452
- let dispatcher;
15453
- for (let i = 0;i < args.length; i++) {
15454
- if (args[i]?.toHandlerArg) {
15455
- dispatcher ??= new Dispatcher(path, this.transactor, this);
15456
- args[i] = args[i].toHandlerArg(dispatcher);
15457
- }
15458
- }
15459
15654
  args.push(new EventContext(path, this.transactor, this));
15460
15655
  return [handler, args];
15461
15656
  }
@@ -15510,9 +15705,26 @@ var init_transactor = __esm(() => {
15510
15705
  this.targetPath = path;
15511
15706
  }
15512
15707
  handlerProp = null;
15708
+ get observeKind() {
15709
+ return this.handlerProp;
15710
+ }
15711
+ get observeName() {
15712
+ return this.name;
15713
+ }
15513
15714
  getHandlerForName(comp) {
15514
15715
  const handlers = comp?.[this.handlerProp];
15515
- return handlers?.[this.name] ?? handlers?.$unknown ?? nullHandler;
15716
+ const exact = handlers?.[this.name];
15717
+ if (exact) {
15718
+ this._matched = "exact";
15719
+ return exact;
15720
+ }
15721
+ const unknown = handlers?.$unknown;
15722
+ if (unknown) {
15723
+ this._matched = "unknown";
15724
+ return unknown;
15725
+ }
15726
+ this._matched = "none";
15727
+ return nullHandler;
15516
15728
  }
15517
15729
  getHandlerAndArgs(_root, instance, comps) {
15518
15730
  const handler = this.getHandlerForName(comps.getCompFor(instance));
@@ -15722,6 +15934,9 @@ class App {
15722
15934
  onChange(callback) {
15723
15935
  this.transactor.state.onChange(callback);
15724
15936
  }
15937
+ observe(callback) {
15938
+ return this.transactor.observe(callback);
15939
+ }
15725
15940
  compile() {
15726
15941
  for (const Comp of this.comps.byId.values()) {
15727
15942
  Comp.compile(this.ParseContext);