tutuca 0.9.115 → 0.11.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.
Files changed (53) hide show
  1. package/README.md +16 -16
  2. package/dist/immer.js +1261 -0
  3. package/dist/tutuca-cli.js +2056 -4798
  4. package/dist/tutuca-components.js +206 -442
  5. package/dist/tutuca-dev.ext.js +878 -944
  6. package/dist/tutuca-dev.js +1679 -5298
  7. package/dist/tutuca-dev.min.js +5 -5
  8. package/dist/tutuca-extra.ext.js +740 -695
  9. package/dist/tutuca-extra.js +1403 -4925
  10. package/dist/tutuca-extra.min.js +3 -3
  11. package/dist/tutuca-storybook.js +226 -210
  12. package/dist/tutuca.ext.js +740 -695
  13. package/dist/tutuca.js +1403 -4925
  14. package/dist/tutuca.min.js +3 -3
  15. package/package.json +7 -11
  16. package/skill/tutuca/SKILL.md +4 -8
  17. package/skill/tutuca/advanced.md +13 -5
  18. package/skill/tutuca/cli.md +8 -9
  19. package/skill/tutuca/component-design.md +50 -47
  20. package/skill/tutuca/core.md +134 -117
  21. package/skill/tutuca/iteration.md +2 -2
  22. package/skill/tutuca/macros.md +1 -1
  23. package/skill/tutuca/messages-and-intents.md +399 -0
  24. package/skill/tutuca/patterns/README.md +1 -1
  25. package/skill/tutuca/patterns/add-a-story.md +2 -2
  26. package/skill/tutuca/patterns/bind-text-and-attributes.md +1 -1
  27. package/skill/tutuca/patterns/conditional-attribute-value.md +1 -1
  28. package/skill/tutuca/patterns/coordinate-components.md +41 -18
  29. package/skill/tutuca/patterns/file-input.md +8 -8
  30. package/skill/tutuca/patterns/filter-and-paginate.md +1 -1
  31. package/skill/tutuca/patterns/handle-events.md +11 -9
  32. package/skill/tutuca/patterns/switch-between-views.md +1 -1
  33. package/skill/tutuca/patterns/tabbed-interface.md +10 -5
  34. package/skill/tutuca/semantics.md +41 -35
  35. package/skill/tutuca/storybook.md +34 -27
  36. package/skill/tutuca/testing.md +75 -64
  37. package/skill/tutuca-source/tutuca.ext.js +740 -695
  38. package/dist/immutable.js +0 -4331
  39. package/skill/immutable-js/SKILL.md +0 -78
  40. package/skill/immutable-js/references/collection.md +0 -346
  41. package/skill/immutable-js/references/conversions.md +0 -99
  42. package/skill/immutable-js/references/deep-updates.md +0 -172
  43. package/skill/immutable-js/references/equality.md +0 -95
  44. package/skill/immutable-js/references/list.md +0 -266
  45. package/skill/immutable-js/references/map.md +0 -300
  46. package/skill/immutable-js/references/predicates.md +0 -93
  47. package/skill/immutable-js/references/range-repeat.md +0 -55
  48. package/skill/immutable-js/references/record.md +0 -196
  49. package/skill/immutable-js/references/seq.md +0 -248
  50. package/skill/immutable-js/references/set.md +0 -270
  51. package/skill/immutable-js/references/shallow-functional.md +0 -99
  52. package/skill/immutable-js/references/stack.md +0 -210
  53. package/skill/tutuca/request-response.md +0 -341
@@ -1,8 +1,80 @@
1
- // src/value.js
2
- import { is } from "immutable";
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __copyProps = (to, from, except, desc) => {
6
+ if (from && typeof from === "object" || typeof from === "function") {
7
+ for (let key of __getOwnPropNames(from))
8
+ if (!__hasOwnProp.call(to, key) && key !== except)
9
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
10
+ }
11
+ return to;
12
+ };
13
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
14
+
15
+ // src/collection.js
16
+ var isPlainObject = (value) => {
17
+ if (value === null || typeof value !== "object") return false;
18
+ const proto = Object.getPrototypeOf(value);
19
+ return proto === Object.prototype || proto === null;
20
+ };
21
+ var seqSize = (seq) => {
22
+ if (seq == null) return 0;
23
+ if (typeof seq.length === "number") return seq.length;
24
+ if (typeof seq.size === "number") return seq.size;
25
+ return isPlainObject(seq) ? Object.keys(seq).length : 0;
26
+ };
27
+ var seqGet = (seq, key, dval = null) => {
28
+ if (seq == null) return dval;
29
+ if (seq instanceof Map) return seq.has(key) ? seq.get(key) : dval;
30
+ if (seq instanceof Set) return seq.has(key) ? key : dval;
31
+ if (Object.hasOwn(seq, key)) return seq[key];
32
+ if (typeof seq.get === "function") return seq.get(key, dval);
33
+ return dval;
34
+ };
35
+ var isIndexedSeq = (seq) => Array.isArray(seq);
36
+ var isKeyedSeq = (seq) => seq instanceof Map || isPlainObject(seq);
37
+ var isSetSeq = (seq) => seq instanceof Set;
38
+ function* seqEntries(seq) {
39
+ if (seq instanceof Map) yield* seq.entries();
40
+ else if (seq instanceof Set) for (const value of seq) yield [value, value];
41
+ else if (isPlainObject(seq)) yield* Object.entries(seq);
42
+ else if (Array.isArray(seq)) for (let i = 0; i < seq.length; i++) yield [i, seq[i]];
43
+ }
44
+
45
+ // src/immer.js
46
+ var immer_exports = {};
47
+ __reExport(immer_exports, immer_star);
48
+ import { enableMapSet } from "immer";
49
+ import * as immer_star from "immer";
50
+ enableMapSet();
3
51
 
4
52
  // src/path.js
5
53
  var NONE = /* @__PURE__ */ Symbol("NONE");
54
+ var readKey = (value, key, dval = null) => {
55
+ if (value == null) return dval;
56
+ if (value instanceof Map) return value.has(key) ? value.get(key) : dval;
57
+ if (value instanceof Set) return value.has(key) ? key : dval;
58
+ return Object.hasOwn(value, key) ? value[key] : dval;
59
+ };
60
+ var writeKey = (value, key, next) => {
61
+ if (value instanceof Map) value.set(key, next);
62
+ else if (value instanceof Set) {
63
+ value.delete(key);
64
+ value.add(next);
65
+ } else value[key] = next;
66
+ };
67
+ var readSeqKey = (value, key, dval = null) => {
68
+ const direct = readKey(value, key, NONE);
69
+ if (direct !== NONE) return direct;
70
+ return typeof value?.get === "function" ? value.get(key, dval) : dval;
71
+ };
72
+ var writeSeqKey = (value, key, next) => {
73
+ if (value instanceof Map || value instanceof Set || Array.isArray(value) || Object.hasOwn(value, key))
74
+ writeKey(value, key, next);
75
+ else if (typeof value?.set === "function") value.set(key, next);
76
+ else writeKey(value, key, next);
77
+ };
6
78
  var Step = class {
7
79
  lookup(_v, dval = null) {
8
80
  return dval;
@@ -10,6 +82,8 @@ var Step = class {
10
82
  setValue(root, _v) {
11
83
  return root;
12
84
  }
85
+ setDraftValue(_root, _v) {
86
+ }
13
87
  enterFrame(stack, _prev, next) {
14
88
  return stack.enter(next, {}, true);
15
89
  }
@@ -74,10 +148,13 @@ var FieldStep = class extends Step {
74
148
  this.field = field;
75
149
  }
76
150
  lookup(v, dval = null) {
77
- return v?.get ? v.get(this.field, dval) : dval;
151
+ return readKey(v, this.field, dval);
78
152
  }
79
153
  setValue(root, v) {
80
- return root.set(this.field, v);
154
+ return (0, immer_exports.produce)(root, (draft) => this.setDraftValue(draft, v));
155
+ }
156
+ setDraftValue(root, v) {
157
+ writeKey(root, this.field, v);
81
158
  }
82
159
  withIndex(i) {
83
160
  return new SeqStep(this.field, i);
@@ -96,12 +173,14 @@ var SeqStep = class extends Step {
96
173
  this.key = key;
97
174
  }
98
175
  lookup(v, dval = null) {
99
- const o = v?.get(this.field, null);
100
- return o?.get ? o.get(this.key, dval) : dval;
176
+ return readSeqKey(readKey(v, this.field, null), this.key, dval);
101
177
  }
102
178
  setValue(root, v) {
103
- const seq = root?.get(this.field, null);
104
- return seq ? root.set(this.field, seq.set(this.key, v)) : root;
179
+ return (0, immer_exports.produce)(root, (draft) => this.setDraftValue(draft, v));
180
+ }
181
+ setDraftValue(root, v) {
182
+ const seq = readKey(root, this.field, null);
183
+ if (seq != null) writeSeqKey(seq, this.key, v);
105
184
  }
106
185
  enterFrame(stack, _prev, next) {
107
186
  return stack.enter(next, { key: this.key }, true);
@@ -117,19 +196,22 @@ var SeqAccessStep = class extends Step {
117
196
  this.keyField = keyField;
118
197
  }
119
198
  lookup(v, dval = null) {
120
- const seq = v?.get(this.seqField, NONE);
121
- const key = v?.get(this.keyField, NONE);
122
- return key !== NONE && seq?.get ? seq.get(key, dval) : dval;
199
+ const seq = readKey(v, this.seqField, NONE);
200
+ const key = readKey(v, this.keyField, NONE);
201
+ return key !== NONE && seq !== NONE ? readSeqKey(seq, key, dval) : dval;
123
202
  }
124
203
  setValue(root, v) {
125
- const seq = root?.get(this.seqField, NONE);
126
- const key = root?.get(this.keyField, NONE);
127
- return seq === NONE || key === NONE ? root : root.set(this.seqField, seq.set(key, v));
204
+ return (0, immer_exports.produce)(root, (draft) => this.setDraftValue(draft, v));
205
+ }
206
+ setDraftValue(root, v) {
207
+ const seq = readKey(root, this.seqField, NONE);
208
+ const key = readKey(root, this.keyField, NONE);
209
+ if (seq !== NONE && key !== NONE) writeSeqKey(seq, key, v);
128
210
  }
129
211
  // Resolve `keyField` against `v` now and freeze it as a literal-key `SeqStep`, so a
130
212
  // later lookup/setValue lands on this same item even if `keyField` changes meanwhile.
131
213
  pinKey(v) {
132
- const key = v?.get(this.keyField, NONE);
214
+ const key = readKey(v, this.keyField, NONE);
133
215
  return key === NONE ? this : new SeqStep(this.seqField, key);
134
216
  }
135
217
  // The key is a *field reference* resolved live, so it is unknown without a value;
@@ -310,19 +392,15 @@ var Path = class _Path {
310
392
  return out;
311
393
  }
312
394
  setValue(root, v) {
313
- const intermediates = new Array(this.steps.length);
314
- let curVal = root;
315
- for (let i = 0; i < this.steps.length; i++) {
316
- intermediates[i] = curVal;
317
- curVal = this.steps[i].lookup(curVal, NONE);
318
- if (curVal === NONE) return root;
319
- }
320
- let newVal = v;
321
- for (let i = this.steps.length - 1; i >= 0; i--) {
322
- newVal = this.steps[i].setValue(intermediates[i], newVal);
323
- intermediates[i] = newVal;
324
- }
325
- return newVal;
395
+ if (this.steps.length === 0) return v;
396
+ return (0, immer_exports.produce)(root, (draft) => {
397
+ let parent = draft;
398
+ for (let i = 0; i < this.steps.length - 1; i++) {
399
+ parent = this.steps[i].lookup(parent, NONE);
400
+ if (parent === NONE) return;
401
+ }
402
+ this.steps.at(-1).setDraftValue(parent, v);
403
+ });
326
404
  }
327
405
  buildStack(stack) {
328
406
  let prev = stack.it;
@@ -519,7 +597,8 @@ function sizeOf(v) {
519
597
  const s = v.size;
520
598
  if (typeof s === "number") return s;
521
599
  const l = v.length;
522
- return typeof l === "number" ? l : null;
600
+ if (typeof l === "number") return l;
601
+ return isPlainObject(v) ? Object.keys(v).length : null;
523
602
  }
524
603
  var predTruthy = (v) => {
525
604
  const n = sizeOf(v);
@@ -530,7 +609,7 @@ var PREDICATES = {
530
609
  "truthy?": { name: "truthy?", arity: 1, fn: predTruthy },
531
610
  "falsy?": { name: "falsy?", arity: 1, fn: (v) => !predTruthy(v) },
532
611
  "null?": { name: "null?", arity: 1, fn: (v) => v == null },
533
- "equals?": { name: "equals?", arity: 2, fn: (a, b) => is(a, b) }
612
+ "equals?": { name: "equals?", arity: 2, fn: (a, b) => Object.is(a, b) }
534
613
  };
535
614
  function parseToken(s, px) {
536
615
  const c0 = s.charCodeAt(0);
@@ -615,21 +694,29 @@ function parseProvide(s, px) {
615
694
  function parseMacroAttr(s, px) {
616
695
  return _parseSingle(s, px, G_ALL);
617
696
  }
618
- function parseInputHandler(s, px) {
619
- return _parseHandler(s, px, "input", true, true);
697
+ function parseReceiveHandler(s, px) {
698
+ return _parseHandler(s, px, "receive", true, true, false);
620
699
  }
621
700
  function parseAlterHandler(s, px) {
622
- const r = _parseHandler(s, px, "alter", false, false);
701
+ const r = _parseHandler(s, px, "alter", false, false, true);
623
702
  return r === null ? null : r.handlerVal;
624
703
  }
625
- function _parseHandler(s, px, namespace, allowArgs, report) {
704
+ function _parseHandler(s, px, namespace, allowArgs, report, allowMethod) {
626
705
  const tokens = tokenizeValue(s.trim());
627
706
  const headTok = tokens[0] ?? "";
628
707
  const head = headTok === "" ? null : parseToken(headTok, px);
629
708
  const hk = kindOf(head);
630
709
  let handlerVal;
631
- if (hk & K_METHOD) handlerVal = head;
632
- else if (hk & K_NAME) handlerVal = new HandlerNameVal(head.name, namespace);
710
+ if (hk & K_METHOD && allowMethod) handlerVal = head;
711
+ else if (hk & K_METHOD) {
712
+ if (report)
713
+ px.onParseIssue("event-method-handler", {
714
+ name: head.name,
715
+ role: "handler-name",
716
+ value: headTok
717
+ });
718
+ return null;
719
+ } else if (hk & K_NAME) handlerVal = new HandlerNameVal(head.name, namespace);
633
720
  else {
634
721
  if (report) px.onParseIssue("bad-value", { role: "handler-name", value: headTok });
635
722
  return null;
@@ -843,7 +930,7 @@ var BindMemberVal = class extends BindVal {
843
930
  }
844
931
  eval(stack) {
845
932
  const v = stack.lookupBind(this.name);
846
- return typeof v?.get === "function" ? v.get(this.member, null) : v?.[this.member] ?? null;
933
+ return seqGet(v, this.member, null);
847
934
  }
848
935
  toString() {
849
936
  return `@${this.name}.${this.member}`;
@@ -890,7 +977,7 @@ var SeqAccessVal = class extends RenderVal {
890
977
  }
891
978
  eval(stack) {
892
979
  const key = this.keyVal.eval(stack);
893
- return this.seqVal.eval(stack)?.get(key, null);
980
+ return seqGet(this.seqVal.eval(stack), key, null);
894
981
  }
895
982
  toString() {
896
983
  return `${this.seqVal}[${this.keyVal}]`;
@@ -1159,20 +1246,17 @@ var EventHandler = class _EventHandler {
1159
1246
  return [this.handlerVal.evalAsHandler(stack), argValues];
1160
1247
  }
1161
1248
  static parse(s, px) {
1162
- const r = parseInputHandler(s, px);
1249
+ const r = parseReceiveHandler(s, px);
1163
1250
  return r === null ? null : new _EventHandler(r.handlerVal, r.args);
1164
1251
  }
1165
1252
  };
1166
- var RequestHandler = class {
1253
+ var IntentHandler = class {
1167
1254
  constructor(name, fn) {
1168
1255
  this.name = name;
1169
1256
  this.fn = fn;
1170
1257
  }
1171
1258
  };
1172
1259
 
1173
- // src/renderer.js
1174
- import { isIndexed, isKeyed } from "immutable";
1175
-
1176
1260
  // src/cache.js
1177
1261
  var isWeakKey = (k) => k !== null && (typeof k === "object" || typeof k === "function");
1178
1262
  var NullDomCache = class {
@@ -1669,7 +1753,7 @@ var Renderer = class {
1669
1753
  this.cache.set(cachePath, cacheKey, dom);
1670
1754
  }
1671
1755
  };
1672
- if (keys) imKeysIter(seq, renderOne, keys);
1756
+ if (keys) keysIter(seq, renderOne, keys);
1673
1757
  else
1674
1758
  getSeqInfo(seq)(
1675
1759
  seq,
@@ -1703,7 +1787,7 @@ var Renderer = class {
1703
1787
  return new VFragment([this._renderMetadata({ $: "Scope", nid }), dom]);
1704
1788
  }
1705
1789
  };
1706
- var getSeqInfo = (seq) => isIndexed(seq) ? imIndexedIter : isKeyed(seq) ? imKeyedIter : seq?.[SEQ_INFO] ?? unkIter;
1790
+ var getSeqInfo = (seq) => isIndexedSeq(seq) ? nativeIndexedIter : isKeyedSeq(seq) || isSetSeq(seq) ? nativeKeyedIter : seq?.[SEQ_INFO] ?? unkIter;
1707
1791
  var normalizeRange = (start, end, size) => {
1708
1792
  let s = start == null ? 0 : start < 0 ? size + start : start;
1709
1793
  let e = end == null ? size : end < 0 ? size + end : end;
@@ -1726,22 +1810,22 @@ var unpackLoopResult = (result, seq) => {
1726
1810
  const r = result ?? {};
1727
1811
  return { iterData: r.iterData ?? { seq }, start: r.start, end: r.end, keys: r.keys };
1728
1812
  };
1729
- var imKeysIter = (seq, visit, keys) => {
1730
- const attrName = isIndexed(seq) ? "si" : "sk";
1731
- for (const key of keys) visit(key, seq.get(key), attrName);
1813
+ var keysIter = (seq, visit, keys) => {
1814
+ const attrName = isIndexedSeq(seq) ? "si" : "sk";
1815
+ for (const key of keys) visit(key, seqGet(seq, key), attrName);
1732
1816
  };
1733
1817
  var makeLoopCtx = (stack, filter) => ({
1734
1818
  lookup: (name) => stack.lookupBind(name),
1735
1819
  filter: (key, value, iterData) => filter.call(stack.it, key, value, iterData)
1736
1820
  });
1737
- var imIndexedIter = (seq, visit, start, end) => {
1738
- const [s, e] = normalizeRange(start, end, seq.size);
1739
- for (let i = s; i < e; i++) visit(i, seq.get(i), "si");
1821
+ var nativeIndexedIter = (seq, visit, start, end) => {
1822
+ const [s, e] = normalizeRange(start, end, seqSize(seq));
1823
+ for (let i = s; i < e; i++) visit(i, seq[i], "si");
1740
1824
  };
1741
- var imKeyedIter = (seq, visit, start, end) => {
1742
- const [s, e] = normalizeRange(start, end, seq.size);
1825
+ var nativeKeyedIter = (seq, visit, start, end) => {
1826
+ const [s, e] = normalizeRange(start, end, seqSize(seq));
1743
1827
  let i = 0;
1744
- for (const [k, v] of seq.toSeq().entries()) {
1828
+ for (const [k, v] of seqEntries(seq)) {
1745
1829
  if (i >= e) break;
1746
1830
  if (i >= s) visit(k, v, "sk");
1747
1831
  i++;
@@ -2261,7 +2345,7 @@ var IterInfo = class {
2261
2345
  // { key, value }, then run @enrich-with (with @loop-with's iterData) if any.
2262
2346
  enrichBinds(stack, key) {
2263
2347
  const { seq, filter, loopWith, enricher } = this.eval(stack);
2264
- const value = seq?.get ? seq.get(key, null) : null;
2348
+ const value = seqGet(seq, key, null);
2265
2349
  const binds = { key, value };
2266
2350
  if (enricher) {
2267
2351
  const { iterData } = unpackLoopResult(
@@ -2534,8 +2618,11 @@ var Components = class {
2534
2618
  getHandlerFor(v, name, key) {
2535
2619
  return this.getCompFor(v)?.[key][name] ?? null;
2536
2620
  }
2537
- getRequestFor(v, name) {
2538
- return this.getCompFor(v)?.scope.lookupRequest(name) ?? null;
2621
+ // The `lex` leg of an intent's route: the handlers registered on the scope chain of
2622
+ // the component that raised it, innermost first. A list, not a first match — see
2623
+ // ComponentStack.lookupIntentChain.
2624
+ getIntentChainFor(v, name) {
2625
+ return this.getCompFor(v)?.scope.lookupIntentChain(name) ?? [];
2539
2626
  }
2540
2627
  compileStyles() {
2541
2628
  const styles = [];
@@ -2548,7 +2635,7 @@ var ComponentStack = class _ComponentStack {
2548
2635
  this.comps = comps;
2549
2636
  this.parent = parent;
2550
2637
  this.byName = {};
2551
- this.reqsByName = {};
2638
+ this.intentsByName = {};
2552
2639
  this.macros = {};
2553
2640
  }
2554
2641
  enter() {
@@ -2580,11 +2667,21 @@ var ComponentStack = class _ComponentStack {
2580
2667
  getCompFor(v) {
2581
2668
  return this.comps.getCompFor(v);
2582
2669
  }
2583
- registerRequestHandlers(handlers) {
2584
- for (const name in handlers) this.reqsByName[name] = new RequestHandler(name, handlers[name]);
2670
+ // Handlers that answer an intent's `lex` leg. A name maps to a LIST, because the leg
2671
+ // walks: a handler that returns PASS declines and hands the intent to the next one.
2672
+ // A bare function is accepted as a one-element list.
2673
+ registerIntentHandlers(handlers) {
2674
+ for (const name in handlers) {
2675
+ const fns = Array.isArray(handlers[name]) ? handlers[name] : [handlers[name]];
2676
+ this.intentsByName[name] = fns.map((fn) => new IntentHandler(name, fn));
2677
+ }
2585
2678
  }
2586
- lookupRequest(name) {
2587
- return this.reqsByName[name] ?? this.parent?.lookupRequest(name) ?? null;
2679
+ // Innermost scope first, then outward. Concatenated rather than first-match, so a
2680
+ // declining handler can hand the intent to one further up.
2681
+ lookupIntentChain(name) {
2682
+ const here = this.intentsByName[name] ?? [];
2683
+ const up = this.parent?.lookupIntentChain(name) ?? [];
2684
+ return up.length === 0 ? here : here.concat(up);
2588
2685
  }
2589
2686
  lookupComponent(name) {
2590
2687
  return this.byName[name] ?? this.parent?.lookupComponent(name) ?? null;
@@ -2615,7 +2712,7 @@ var LookupInfo = class {
2615
2712
  }
2616
2713
  };
2617
2714
  var isString = (v) => typeof v === "string";
2618
- var _rawSpecKeys = "name view style commonStyle globalStyle input receive bubble response alter views provide lookup fields methods statics";
2715
+ var _rawSpecKeys = "name view style commonStyle globalStyle receive intent alter views provide lookup fields methods statics";
2619
2716
  var KNOWN_SPEC_KEYS = new Set(_rawSpecKeys.split(" "));
2620
2717
  var _compId = 0;
2621
2718
  var Component = class {
@@ -2626,10 +2723,8 @@ var Component = class {
2626
2723
  this.views = { main: new View("main", o.view, o.style) };
2627
2724
  this.commonStyle = o.commonStyle ?? "";
2628
2725
  this.globalStyle = o.globalStyle ?? "";
2629
- this.input = o.input ?? {};
2630
2726
  this.receive = o.receive ?? {};
2631
- this.bubble = o.bubble ?? {};
2632
- this.response = o.response ?? {};
2727
+ this.intent = o.intent ?? {};
2633
2728
  this.alter = o.alter ?? {};
2634
2729
  for (const name in o.views ?? {}) {
2635
2730
  const v = o.views[name];
@@ -2748,12 +2843,12 @@ var Stack = class _Stack {
2748
2843
  const provide = this.comps.getCompFor(this.it)?.provide;
2749
2844
  if (provide == null) return this;
2750
2845
  const dynObj = {};
2751
- let has2 = false;
2846
+ let has = false;
2752
2847
  for (const k in provide) {
2753
2848
  dynObj[provide[k].symbol] = provide[k].val.eval(this);
2754
- has2 = true;
2849
+ has = true;
2755
2850
  }
2756
- if (!has2) return this;
2851
+ if (!has) return this;
2757
2852
  const newDynBinds = [new ObjectFrame(dynObj), this.dynBinds];
2758
2853
  const { comps, it, binds, views, viewsId, ctx } = this;
2759
2854
  return new _Stack(comps, it, binds, newDynBinds, views, viewsId, ctx);
@@ -2824,6 +2919,316 @@ var Stack = class _Stack {
2824
2919
  }
2825
2920
  };
2826
2921
 
2922
+ // src/oo.js
2923
+ var BAD_VALUE = /* @__PURE__ */ Symbol("BadValue");
2924
+ var nullCoercer = (v) => v;
2925
+ var isPlainObject2 = (v) => {
2926
+ if (v === null || typeof v !== "object") return false;
2927
+ const proto = Object.getPrototypeOf(v);
2928
+ return proto === Object.prototype || proto === null;
2929
+ };
2930
+ function defaultToData(v) {
2931
+ if (v instanceof Map) return [...v.entries()];
2932
+ if (v instanceof Set) return [...v.values()];
2933
+ return v;
2934
+ }
2935
+ var Field = class {
2936
+ constructor(type, name, typeCheck, coercer, defaultValue = null) {
2937
+ this.type = type;
2938
+ this.name = name;
2939
+ this.typeCheck = typeCheck;
2940
+ this.coercer = coercer;
2941
+ this.checks = [];
2942
+ this.defaultValue = defaultValue;
2943
+ }
2944
+ toDataDef() {
2945
+ return { type: this.type, defaultValue: defaultToData(this.defaultValue) };
2946
+ }
2947
+ getFirstFailingCheck(v) {
2948
+ if (!this.typeCheck.isValid(v)) return this.typeCheck;
2949
+ for (const check3 of this.checks) if (!check3.isValid(v)) return check3;
2950
+ return null;
2951
+ }
2952
+ isValid(v) {
2953
+ return this.getFirstFailingCheck(v) === null;
2954
+ }
2955
+ addCheck(check3) {
2956
+ this.checks.push(check3);
2957
+ return this;
2958
+ }
2959
+ coerceOr(v, defaultValue = null) {
2960
+ if (this.isValid(v)) return v;
2961
+ const v1 = this.coercer(v);
2962
+ return this.isValid(v1) ? v1 : defaultValue;
2963
+ }
2964
+ coerceOrDefault(v) {
2965
+ return this.coerceOr(v, this.defaultValue);
2966
+ }
2967
+ };
2968
+ var Check = class {
2969
+ isValid(_v) {
2970
+ return true;
2971
+ }
2972
+ getMessage(_v) {
2973
+ return "Invalid";
2974
+ }
2975
+ };
2976
+ var CheckTypeAny = class extends Check {
2977
+ };
2978
+ var CHECK_TYPE_ANY = new CheckTypeAny();
2979
+ var FnCheck = class extends Check {
2980
+ constructor(isValidFn, getMessageFn) {
2981
+ super();
2982
+ this._isValid = isValidFn;
2983
+ this._getMessage = getMessageFn;
2984
+ }
2985
+ isValid(v) {
2986
+ return this._isValid(v);
2987
+ }
2988
+ getMessage(v) {
2989
+ return this._getMessage(v);
2990
+ }
2991
+ };
2992
+ var check = (fn, message) => new FnCheck(fn, () => message);
2993
+ var CHECK_TYPE_INT = check(Number.isInteger, "Integer expected");
2994
+ var CHECK_TYPE_FLOAT = check(Number.isFinite, "Float expected");
2995
+ var CHECK_TYPE_BOOL = check((v) => typeof v === "boolean", "Boolean expected");
2996
+ var CHECK_TYPE_STRING = check((v) => typeof v === "string", "String expected");
2997
+ var CHECK_TYPE_LIST = check(Array.isArray, "Array expected");
2998
+ var CHECK_TYPE_OBJECT = check(isPlainObject2, "Plain object expected");
2999
+ var CHECK_TYPE_MAP = check((v) => v instanceof Map, "Map expected");
3000
+ var CHECK_TYPE_SET = check((v) => v instanceof Set, "Set expected");
3001
+ var FieldBool = class extends Field {
3002
+ constructor(name, defaultValue = false) {
3003
+ super("bool", name, CHECK_TYPE_BOOL, (v) => !!v, defaultValue);
3004
+ }
3005
+ };
3006
+ var FieldAny = class extends Field {
3007
+ constructor(name, defaultValue = null) {
3008
+ super("any", name, CHECK_TYPE_ANY, nullCoercer, defaultValue);
3009
+ }
3010
+ toDataDef() {
3011
+ return { type: getTypeName(this.defaultValue) ?? "any", defaultValue: this.defaultValue };
3012
+ }
3013
+ };
3014
+ var FieldString = class extends Field {
3015
+ constructor(name, defaultValue = "") {
3016
+ super("text", name, CHECK_TYPE_STRING, (v) => v?.toString?.() ?? "", defaultValue);
3017
+ }
3018
+ };
3019
+ var FieldInt = class extends Field {
3020
+ constructor(name, defaultValue = 0) {
3021
+ super(
3022
+ "int",
3023
+ name,
3024
+ CHECK_TYPE_INT,
3025
+ (v) => Number.isFinite(v) ? Math.trunc(v) : null,
3026
+ defaultValue
3027
+ );
3028
+ }
3029
+ };
3030
+ var FieldFloat = class extends Field {
3031
+ constructor(name, defaultValue = 0) {
3032
+ super("float", name, CHECK_TYPE_FLOAT, (_) => null, defaultValue);
3033
+ }
3034
+ };
3035
+ var getTypeName = (v) => v?.constructor?.getMetaClass?.()?.name;
3036
+ var CheckTypeName = class {
3037
+ constructor(typeName) {
3038
+ this.typeName = typeName;
3039
+ }
3040
+ isValid(v) {
3041
+ return getTypeName(v) === this.typeName;
3042
+ }
3043
+ getMessage(v) {
3044
+ return `Expected "${this.typeName}", got "${getTypeName(v)}"`;
3045
+ }
3046
+ };
3047
+ var FieldComp = class extends Field {
3048
+ constructor(type, name, args) {
3049
+ super(type, name, new CheckTypeName(type), nullCoercer, null);
3050
+ this.args = args;
3051
+ }
3052
+ toDataDef() {
3053
+ return { component: this.type, args: this.args };
3054
+ }
3055
+ };
3056
+ var FieldList = class extends Field {
3057
+ constructor(name, defaultValue = []) {
3058
+ super("list", name, CHECK_TYPE_LIST, (v) => Array.isArray(v) ? [...v] : null, defaultValue);
3059
+ }
3060
+ };
3061
+ var FieldObject = class extends Field {
3062
+ constructor(name, defaultValue = {}) {
3063
+ super(
3064
+ "object",
3065
+ name,
3066
+ CHECK_TYPE_OBJECT,
3067
+ (v) => isPlainObject2(v) ? { ...v } : null,
3068
+ defaultValue
3069
+ );
3070
+ }
3071
+ };
3072
+ var FieldMap = class extends Field {
3073
+ constructor(name, defaultValue = /* @__PURE__ */ new Map()) {
3074
+ super(
3075
+ "map",
3076
+ name,
3077
+ CHECK_TYPE_MAP,
3078
+ (v) => {
3079
+ if (v instanceof Map) return new Map(v);
3080
+ if (Array.isArray(v) || isPlainObject2(v))
3081
+ return new Map(Array.isArray(v) ? v : Object.entries(v));
3082
+ return null;
3083
+ },
3084
+ defaultValue
3085
+ );
3086
+ }
3087
+ };
3088
+ var FieldSet = class extends Field {
3089
+ constructor(name, defaultValue = /* @__PURE__ */ new Set()) {
3090
+ super(
3091
+ "set",
3092
+ name,
3093
+ CHECK_TYPE_SET,
3094
+ (v) => v instanceof Set || Array.isArray(v) ? new Set(v) : null,
3095
+ defaultValue
3096
+ );
3097
+ }
3098
+ };
3099
+ function mkCompField(field, scope, args) {
3100
+ const Comp = scope?.lookupComponent(field.type) ?? null;
3101
+ if (Comp === null)
3102
+ console.warn(
3103
+ scope ? `component field "${field.name}": component "${field.type}" not found in scope` : `component field "${field.name}": cannot resolve component "${field.type}" — built without a registered scope (use ${field.type}.make({}) as the default, or build via a registered component)`
3104
+ );
3105
+ return Comp?.make({ ...field.args, ...args }, { scope }) ?? null;
3106
+ }
3107
+ var ClassBuilder = class {
3108
+ constructor(name) {
3109
+ this.name = name;
3110
+ this.fields = {};
3111
+ this.compFields = /* @__PURE__ */ new Set();
3112
+ this._methods = {};
3113
+ this._statics = {};
3114
+ }
3115
+ build() {
3116
+ const { name, fields, compFields, _methods } = this;
3117
+ const defaults = Object.fromEntries(
3118
+ Object.entries(fields).map(([fieldName, field]) => [fieldName, field.defaultValue])
3119
+ );
3120
+ const Class = {
3121
+ [name]: class {
3122
+ constructor(values = {}) {
3123
+ Object.assign(this, defaults, values);
3124
+ }
3125
+ }
3126
+ }[name];
3127
+ Class[immer_exports.immerable] = true;
3128
+ Object.assign(Class.prototype, _methods);
3129
+ const metaClass = { fields, name, methods: _methods };
3130
+ Object.assign(
3131
+ Class,
3132
+ {
3133
+ getMetaClass: () => metaClass,
3134
+ make(inArgs = {}, opts = {}) {
3135
+ const args = {};
3136
+ const scope = opts.scope ?? this.scope;
3137
+ for (const key in inArgs) {
3138
+ const field = fields[key];
3139
+ if (compFields.has(key)) args[key] = mkCompField(field, scope, inArgs[key]);
3140
+ else if (field === void 0)
3141
+ console.warn("extra argument to constructor:", name, key, inArgs);
3142
+ else args[key] = field.coerceOrDefault(inArgs[key]);
3143
+ }
3144
+ for (const key of compFields)
3145
+ if (args[key] === void 0) args[key] = mkCompField(fields[key], scope, inArgs[key]);
3146
+ return (0, immer_exports.freeze)(new this(args), true);
3147
+ }
3148
+ },
3149
+ this._statics
3150
+ );
3151
+ return Class;
3152
+ }
3153
+ methods(proto) {
3154
+ for (const k in proto) this._methods[k] = proto[k];
3155
+ }
3156
+ statics(proto) {
3157
+ for (const k in proto) this._statics[k] = proto[k];
3158
+ }
3159
+ addField(name, dval, FieldCls) {
3160
+ const field = new FieldCls(name, dval);
3161
+ this.fields[name] = field;
3162
+ return field;
3163
+ }
3164
+ addCompField(name, type, args) {
3165
+ const field = new FieldComp(type, name, args);
3166
+ this.compFields.add(name);
3167
+ this.fields[name] = field;
3168
+ return field;
3169
+ }
3170
+ };
3171
+ var FIELD_CLASS = /* @__PURE__ */ Symbol.for("tutuca.fieldClass");
3172
+ var fieldsByTypeName = {
3173
+ text: FieldString,
3174
+ int: FieldInt,
3175
+ float: FieldFloat,
3176
+ bool: FieldBool,
3177
+ list: FieldList,
3178
+ object: FieldObject,
3179
+ map: FieldMap,
3180
+ set: FieldSet,
3181
+ any: FieldAny
3182
+ };
3183
+ function fieldFromDescriptor(name, value) {
3184
+ const FieldCls = fieldsByTypeName[value.type] ?? FieldAny;
3185
+ const probe = new FieldCls(name);
3186
+ return [FieldCls, probe.coerceOr(value.defaultValue, probe.defaultValue)];
3187
+ }
3188
+ function classFromData(name, { fields = {}, methods, statics }) {
3189
+ const b = new ClassBuilder(name);
3190
+ for (const field in fields) {
3191
+ const value = fields[field];
3192
+ const type = typeof value;
3193
+ if (type === "string") b.addField(field, value, FieldString);
3194
+ else if (type === "number")
3195
+ b.addField(field, value, Number.isInteger(value) ? FieldInt : FieldFloat);
3196
+ else if (type === "boolean") b.addField(field, value, FieldBool);
3197
+ else if (Array.isArray(value)) b.addField(field, [...value], FieldList);
3198
+ else if (value instanceof Set) b.addField(field, new Set(value), FieldSet);
3199
+ else if (value instanceof Map) b.addField(field, new Map(value), FieldMap);
3200
+ else if (value?.type && Object.hasOwn(value, "defaultValue")) {
3201
+ const [FieldCls, dval] = fieldFromDescriptor(field, value);
3202
+ b.addField(field, dval, FieldCls);
3203
+ } else if (value?.component && value?.args !== void 0)
3204
+ b.addCompField(field, value.component, value.args);
3205
+ else if (isPlainObject2(value)) b.addField(field, { ...value }, FieldObject);
3206
+ else {
3207
+ const FieldCls = value?.[FIELD_CLASS] ?? FieldAny;
3208
+ b.addField(field, value, FieldCls);
3209
+ }
3210
+ }
3211
+ if (methods) b.methods(methods);
3212
+ if (statics) b.statics(statics);
3213
+ return b.build();
3214
+ }
3215
+ function validateDraftFields(current, draft) {
3216
+ const meta = current?.constructor?.getMetaClass?.();
3217
+ if (!meta) return;
3218
+ for (const [name, field] of Object.entries(meta.fields)) {
3219
+ const value = draft[name];
3220
+ if (field.isValid(value)) continue;
3221
+ const coerced = field.coerceOr(value, BAD_VALUE);
3222
+ if (coerced !== BAD_VALUE) draft[name] = coerced;
3223
+ else {
3224
+ console.warn(`invalid value for ${meta.name}.${name}`, value);
3225
+ draft[name] = current[name];
3226
+ }
3227
+ }
3228
+ }
3229
+ Component.fromSpec = (opts) => new Component(classFromData(opts.name, opts), opts);
3230
+ var component = (opts) => Component.fromSpec(opts);
3231
+
2827
3232
  // src/transactor.js
2828
3233
  var State = class {
2829
3234
  constructor(val) {
@@ -2856,8 +3261,11 @@ var Transactor = class {
2856
3261
  this.transactions.push(t);
2857
3262
  this.onTransactionPushed(t);
2858
3263
  }
2859
- // Subscribe to a normalized record for every handler invocation (send/receive,
2860
- // bubble, request/response, input). Returns an unsubscribe function. Records
3264
+ // Subscribe to a normalized record for every handler invocation. `kind` is the
3265
+ // handler bucket (`receive` / `intent`), plus `answer` — an observation-only kind for
3266
+ // a message the runtime wrote to answer an intent. The BUCKET does not know about
3267
+ // that distinction, and must not: a handler cannot tell an answer from a message its
3268
+ // parent sent. Returns an unsubscribe function. Records
2861
3269
  // carry: kind, name, args, path, pathKeys, targetPath, handler, handlerName,
2862
3270
  // matched, before, after, parent, timestamp. Purely observational.
2863
3271
  observe(cb) {
@@ -2910,23 +3318,14 @@ var Transactor = class {
2910
3318
  this.pushTransaction(t);
2911
3319
  return this._link(t, parent);
2912
3320
  }
2913
- pushInput(path, name, args = [], opts = {}, parent = null) {
2914
- const t = new InputDispatchEvent(path, this, name, args, parent, opts);
2915
- this.pushTransaction(t);
2916
- return this._link(t, parent);
2917
- }
2918
- pushBubble(path, name, args = [], opts = {}, parent = null, targetPath = null) {
2919
- const newOpts = opts.skipSelf ? { ...opts, skipSelf: false } : opts;
2920
- const t = new BubbleEvent(path, this, name, args, parent, newOpts, targetPath);
2921
- this.pushTransaction(t);
2922
- return this._link(t, parent);
2923
- }
2924
- pushRequest(path, name, args = [], opts = {}, parent = null) {
3321
+ // Raise an intent: a job the sender did not address, walked along a route until
3322
+ // something answers. `opts.route` is a list of legs (see DEFAULT_ROUTE) and
3323
+ // `opts.livePath` opts out of pinning the answer's destination keys.
3324
+ pushIntent(path, name, args = [], opts = {}, parent = null) {
2925
3325
  const release = parent ? parent.completion.track() : null;
2926
- const p = this._runRequest(path, name, args, opts, parent, release);
2927
- this._inflight.add(p);
2928
- p.finally(() => this._inflight.delete(p));
2929
- return p;
3326
+ const walk = new IntentWalk(this, path, name, args, opts, parent, release);
3327
+ walk.advance();
3328
+ return walk;
2930
3329
  }
2931
3330
  // Drain queued transactions and await in-flight requests until quiescent. Each
2932
3331
  // awaited request enqueues a ResponseEvent, which may dispatch more work, so we
@@ -2937,57 +3336,6 @@ var Transactor = class {
2937
3336
  if (this._inflight.size) await Promise.allSettled([...this._inflight]);
2938
3337
  }
2939
3338
  }
2940
- async _runRequest(path, name, args = [], opts = {}, parent = null, release = null) {
2941
- let released = false;
2942
- const transfer = (t) => {
2943
- if (release) {
2944
- released = true;
2945
- t.completion.whenSubtreeSettled().then(release);
2946
- }
2947
- };
2948
- try {
2949
- const curRoot = this.state.val;
2950
- const txnPath = path.toTransactionPath();
2951
- const curLeaf = txnPath.lookup(curRoot);
2952
- const found = this.comps.getRequestFor(curLeaf, name);
2953
- const handler = found ?? mkReq404(name);
2954
- if (this._observers.length > 0) {
2955
- const reqPath = txnPath.pinKeys(curRoot);
2956
- this._emit({
2957
- kind: "request",
2958
- name,
2959
- args,
2960
- path: reqPath,
2961
- pathKeys: reqPath.toKeys(),
2962
- targetPath: path,
2963
- handler: found?.fn ?? null,
2964
- handlerName: found?.fn?.name || name,
2965
- matched: found ? "exact" : "none",
2966
- before: curLeaf,
2967
- after: void 0,
2968
- parent,
2969
- timestamp: Date.now()
2970
- });
2971
- }
2972
- const reqCtx = new RequestContext(path, this, parent, curRoot);
2973
- const resHandlerName = opts?.onResName ?? name;
2974
- const resPath = opts?.livePath ? null : txnPath.pinKeys(curRoot);
2975
- const push = (specificName, baseName, singleArg, result, error) => {
2976
- const resArgs = specificName ? [singleArg] : [result, error];
2977
- const t = new ResponseEvent(path, this, specificName ?? baseName, resArgs, parent, resPath);
2978
- transfer(t);
2979
- this.pushTransaction(t);
2980
- };
2981
- try {
2982
- const result = await handler.fn.apply(null, [...args, reqCtx]);
2983
- push(opts?.onOkName, resHandlerName, result, result, null);
2984
- } catch (error) {
2985
- push(opts?.onErrorName, resHandlerName, error, null, error);
2986
- }
2987
- } finally {
2988
- if (release && !released) release();
2989
- }
2990
- }
2991
3339
  get hasPendingTransactions() {
2992
3340
  return this.transactions.length > 0;
2993
3341
  }
@@ -3004,6 +3352,7 @@ var Transactor = class {
3004
3352
  this._emitTransaction(transaction, curState);
3005
3353
  } else console.warn("undefined new state", { curState, transaction });
3006
3354
  } finally {
3355
+ transaction.ensureWalkAdvanced?.();
3007
3356
  transaction._completion?.ensureSelfSettled();
3008
3357
  transaction._completion?.releaseSelf();
3009
3358
  }
@@ -3012,12 +3361,6 @@ var Transactor = class {
3012
3361
  this.transact(new InputEvent(path, event, eventHandler, this, dragInfo));
3013
3362
  }
3014
3363
  };
3015
- function mkReq404(name) {
3016
- const fn = () => {
3017
- throw new Error(`Request not found: ${name}`);
3018
- };
3019
- return { fn };
3020
- }
3021
3364
  function nullHandler() {
3022
3365
  return this;
3023
3366
  }
@@ -3048,6 +3391,15 @@ var Transaction = class {
3048
3391
  }
3049
3392
  afterTransaction() {
3050
3393
  }
3394
+ // Ending a walk and forwarding belong to the two dispatch buckets; the base warns
3395
+ // rather than throwing, so a stray call from the wrong place is a message and not a
3396
+ // crash. `walk` is undefined here, which is how ctx.reply/ctx.fail tell the two apart.
3397
+ stop() {
3398
+ console.warn('ctx.stop() is only meaningful in an "intent" handler - ignored');
3399
+ }
3400
+ forward(_opts) {
3401
+ console.warn('ctx.forward() needs a "receive" or "intent" handler - ignored');
3402
+ }
3051
3403
  buildRootStack(root, comps) {
3052
3404
  return Stack.root(comps, root);
3053
3405
  }
@@ -3064,10 +3416,10 @@ var Transaction = class {
3064
3416
  get observeName() {
3065
3417
  return null;
3066
3418
  }
3067
- callHandler(root, instance, comps) {
3419
+ callHandler(root, instance, draft, comps) {
3068
3420
  const [handler, args] = this.getHandlerAndArgs(root, instance, comps);
3069
3421
  this._resolvedHandler = handler;
3070
- return handler.apply(instance, args);
3422
+ return handler.apply(instance, [draft, ...args]);
3071
3423
  }
3072
3424
  getHandlerAndArgs(_root, _instance, _comps) {
3073
3425
  return null;
@@ -3076,12 +3428,16 @@ var Transaction = class {
3076
3428
  // the data's real location (the dispatch `this.path` keeps intermediates). A subclass
3077
3429
  // may override to supply a pre-resolved path (see ResponseEvent's pinned keys).
3078
3430
  getTransactionPath() {
3079
- return this.path.toTransactionPath();
3431
+ return this.path.toTransactionPath().compact();
3080
3432
  }
3081
3433
  updateRootValue(curRoot, comps) {
3082
3434
  const txnPath = this.getTransactionPath();
3083
3435
  const curLeaf = txnPath.lookup(curRoot);
3084
- const newLeaf = this.callHandler(curRoot, curLeaf, comps);
3436
+ const newLeaf = (0, immer_exports.produce)(curLeaf, (draft) => {
3437
+ const result = this.callHandler(curRoot, curLeaf, draft, comps);
3438
+ if (result === void 0 || result === draft) validateDraftFields(curLeaf, draft);
3439
+ return result;
3440
+ });
3085
3441
  this._before = curLeaf;
3086
3442
  this._after = newLeaf;
3087
3443
  this._completion?.markSelfSettled({ value: newLeaf, old: curLeaf });
@@ -3109,18 +3465,40 @@ var InputEvent = class extends Transaction {
3109
3465
  this._dispatchPath ??= this.path.compact();
3110
3466
  return this._dispatchPath;
3111
3467
  }
3468
+ // A DOM event is an ADDRESSED message like any other — it reaches the component that
3469
+ // owns the view and stops. It keeps its own class only because it resolves its handler
3470
+ // from the compiled view and runs synchronously, not because it is a different channel.
3112
3471
  get observeKind() {
3113
- return "input";
3472
+ return "receive";
3114
3473
  }
3115
3474
  get observeName() {
3116
3475
  return this.e?.type ?? null;
3117
3476
  }
3477
+ // A view's name is a message. `forward` is where that name can LEAVE the component:
3478
+ // it becomes an intent with the same name and the same arguments, so a view that says
3479
+ // `@on.click="saveDraft .text"` never has to change when an ancestor takes the job over.
3480
+ // (A `$method` handler has a name too, so forwarding one raises an intent under it.)
3481
+ forward(opts) {
3482
+ this._forward = opts ?? {};
3483
+ }
3484
+ afterTransaction() {
3485
+ const f = this._forward;
3486
+ if (f === void 0) return;
3487
+ this._forward = void 0;
3488
+ const { args = this._handlerArgs ?? [], ...rest } = f;
3489
+ const hv = this.handler?.handlerCall?.handlerVal ?? this.handler?.handlerVal;
3490
+ const name = hv?.name;
3491
+ if (name === void 0)
3492
+ return console.warn("ctx.forward() from a handler with no name - ignored");
3493
+ this.transactor.pushIntent(this.dispatchPath, name, args, rest, this);
3494
+ }
3118
3495
  buildRootStack(root, comps) {
3119
3496
  return Stack.root(comps, root, this);
3120
3497
  }
3121
3498
  getHandlerAndArgs(root, _instance, comps) {
3122
3499
  const stack = this.buildStack(root, comps);
3123
3500
  const [handler, args] = this.handler.getHandlerAndArgs(stack, this);
3501
+ this._handlerArgs = [...args];
3124
3502
  const path = this.dispatchPath;
3125
3503
  args.push(new EventContext(path, this.transactor, this));
3126
3504
  return [handler, args];
@@ -3205,39 +3583,190 @@ var NameArgsTransaction = class extends Transaction {
3205
3583
  return [handler, [...this.args, new EventContext(this.path, this.transactor, this)]];
3206
3584
  }
3207
3585
  };
3208
- var ResponseEvent = class extends NameArgsTransaction {
3209
- handlerProp = "response";
3210
- constructor(path, transactor, name, args, parent, txnPath = null) {
3211
- super(path, transactor, name, args, parent);
3212
- this._txnPath = txnPath;
3213
- }
3214
- getTransactionPath() {
3215
- return this._txnPath ?? super.getTransactionPath();
3216
- }
3217
- };
3218
3586
  var SendEvent = class extends NameArgsTransaction {
3219
3587
  handlerProp = "receive";
3220
- run(rootVal, comps) {
3221
- return this.opts.skipSelf ? rootVal : this.updateRootValue(rootVal, comps);
3588
+ // `receive` is the ADDRESSED bucket, and it is the only one: a view's `@on.click`,
3589
+ // a parent's ctx.send, the host's sendAtRoot and an answer to an intent all arrive
3590
+ // here, and a handler cannot tell them apart. Splitting on where a message came from
3591
+ // would let the view's name through and turn the parent's away, though the two say
3592
+ // the same thing — and neither a test nor a parent could drive the component.
3593
+ get observeKind() {
3594
+ return this._isAnswer ? "answer" : "receive";
3595
+ }
3596
+ // `forward` in a RECEIVE body starts a walk: the message that arrived becomes an
3597
+ // intent, keeping its name and payload. This is what lets a view's name leave the
3598
+ // component without the view changing.
3599
+ forward(opts) {
3600
+ this._forward = opts ?? {};
3222
3601
  }
3223
3602
  afterTransaction() {
3224
- const { path, name, args, opts, targetPath } = this;
3225
- if (opts.bubbles && path.steps.length > 0)
3226
- this.transactor.pushBubble(path.popStep(), name, args, opts, this, targetPath);
3603
+ const f = this._forward;
3604
+ if (f === void 0) return;
3605
+ this._forward = void 0;
3606
+ const { args = this.args, ...rest } = f;
3607
+ this.transactor.pushIntent(this.path, this.name, args, rest, this);
3227
3608
  }
3228
3609
  };
3229
- var BubbleEvent = class extends SendEvent {
3230
- handlerProp = "bubble";
3231
- constructor(path, transactor, name, args, parent, opts, targetPath) {
3232
- super(path, transactor, name, args, parent, opts);
3233
- this.targetPath = targetPath ?? path;
3610
+ var IntentEvent = class extends NameArgsTransaction {
3611
+ handlerProp = "intent";
3612
+ constructor(path, transactor, walk) {
3613
+ super(path, transactor, walk.name, walk.args, walk.parent, {});
3614
+ this.walk = walk;
3615
+ this.targetPath = walk.origin;
3616
+ }
3617
+ // `forward` in an INTENT body amends the hop about to be pushed — it does not push
3618
+ // one itself, because a walk advances on its own.
3619
+ forward(opts) {
3620
+ this.walk.amend(opts, this.path);
3234
3621
  }
3235
- stopPropagation() {
3236
- this.opts.bubbles = false;
3622
+ stop() {
3623
+ this.walk.finish(null, null);
3624
+ }
3625
+ afterTransaction() {
3626
+ this.ensureWalkAdvanced();
3627
+ }
3628
+ // Idempotent, because both afterTransaction and the transactor's `finally` call it —
3629
+ // whichever gets there first advances the walk exactly once.
3630
+ ensureWalkAdvanced() {
3631
+ if (this._advanced) return;
3632
+ this._advanced = true;
3633
+ this.walk.advance();
3237
3634
  }
3238
3635
  };
3239
- var InputDispatchEvent = class extends NameArgsTransaction {
3240
- handlerProp = "input";
3636
+ var PASS = /* @__PURE__ */ Symbol.for("tutuca.intent.pass");
3637
+ var DEFAULT_ROUTE = ["dyn", "lex"];
3638
+ var INTENT_DEPTH = 64;
3639
+ var IntentWalk = class {
3640
+ constructor(transactor, path, name, args, opts, parent, release) {
3641
+ this.transactor = transactor;
3642
+ this.name = name;
3643
+ this.args = args;
3644
+ this.route = opts?.route ?? DEFAULT_ROUTE;
3645
+ this.parent = parent;
3646
+ this.release = release;
3647
+ this.origin = path;
3648
+ this.answerPath = opts?.livePath ? null : path.toTransactionPath().pinKeys(transactor.state.val);
3649
+ this.legIndex = 0;
3650
+ this.dynAt = path;
3651
+ this.hops = 0;
3652
+ this.ended = false;
3653
+ }
3654
+ // Offer the intent to the next hop. Called once at dispatch, and again by every hop
3655
+ // that ran without answering.
3656
+ advance() {
3657
+ if (this.ended) return;
3658
+ if (this.hops++ >= INTENT_DEPTH) return this.exhaust("intentDepth");
3659
+ while (this.legIndex < this.route.length) {
3660
+ const leg = this.route[this.legIndex];
3661
+ if (leg === "dyn") {
3662
+ if (this.dynAt.steps.length === 0) {
3663
+ this.legIndex++;
3664
+ continue;
3665
+ }
3666
+ this.dynAt = this.dynAt.popStep();
3667
+ this.transactor.pushTransaction(new IntentEvent(this.dynAt, this.transactor, this));
3668
+ return;
3669
+ }
3670
+ if (leg === "lex") {
3671
+ this.legIndex++;
3672
+ return this._tryLex();
3673
+ }
3674
+ console.warn("unknown intent route leg", leg, '- expected "dyn" or "lex"');
3675
+ this.legIndex++;
3676
+ }
3677
+ this.exhaust("noHandler");
3678
+ }
3679
+ // The `lex` leg: the handlers registered on the scope chain of the component that
3680
+ // raised the intent, in order.
3681
+ _tryLex() {
3682
+ const root = this.transactor.state.val;
3683
+ const txnPath = this.origin.toTransactionPath();
3684
+ const leaf = txnPath.lookup(root);
3685
+ const chain = this.transactor.comps.getIntentChainFor(leaf, this.name);
3686
+ if (this.transactor._observers.length > 0) {
3687
+ const path = txnPath.pinKeys(root);
3688
+ this.transactor._emit({
3689
+ kind: "intent",
3690
+ name: this.name,
3691
+ args: this.args,
3692
+ path,
3693
+ pathKeys: path.toKeys(),
3694
+ targetPath: this.origin,
3695
+ handler: chain[0]?.fn ?? null,
3696
+ handlerName: chain[0]?.fn?.name || this.name,
3697
+ matched: chain.length > 0 ? "exact" : "none",
3698
+ before: leaf,
3699
+ after: void 0,
3700
+ parent: this.parent,
3701
+ timestamp: Date.now()
3702
+ });
3703
+ }
3704
+ if (chain.length === 0) return this.advance();
3705
+ const p = this._runLex(chain, 0);
3706
+ this.transactor._inflight.add(p);
3707
+ p.finally(() => this.transactor._inflight.delete(p));
3708
+ }
3709
+ async _runLex(chain, i) {
3710
+ if (this.ended) return;
3711
+ if (i >= chain.length) return this.advance();
3712
+ const ctx = new IntentContext(this.origin, this.transactor, this.parent);
3713
+ try {
3714
+ const res = await chain[i].fn.apply(null, [...this.args, ctx]);
3715
+ if (res === PASS) return this._runLex(chain, i + 1);
3716
+ this.answer("Ok", res);
3717
+ } catch (error) {
3718
+ this.answer("Error", error);
3719
+ }
3720
+ }
3721
+ // A hop answered. Ends the walk.
3722
+ answer(suffix, value) {
3723
+ this.finish(`${this.name}${suffix}`, [value]);
3724
+ }
3725
+ // `forward` from an intent body: keep walking, optionally with new arguments or a
3726
+ // narrowed route.
3727
+ amend(opts, from) {
3728
+ if (opts?.args !== void 0) this.args = opts.args;
3729
+ if (opts?.route !== void 0) {
3730
+ this.route = opts.route;
3731
+ this.legIndex = 0;
3732
+ this.dynAt = from ?? this.dynAt;
3733
+ }
3734
+ }
3735
+ // The route ran out. What the sender hears depends only on what it DECLARES, which is
3736
+ // how "a sender expects an answer if and only if it declares an answer arm" becomes
3737
+ // code — nobody writes it down twice, and there is no schema to keep in step.
3738
+ exhaust(reason) {
3739
+ if (this.ended) return;
3740
+ const { name, args } = this;
3741
+ const comp = this.transactor.comps.getCompFor(
3742
+ this.origin.toTransactionPath().lookup(this.transactor.state.val)
3743
+ );
3744
+ const declares = (n) => comp?.receive?.[n] !== void 0;
3745
+ if (declares(`${name}Unhandled`)) {
3746
+ this.finish(`${name}Unhandled`, args);
3747
+ } else if (declares(`${name}Error`)) {
3748
+ this.finish(`${name}Error`, [reason]);
3749
+ } else {
3750
+ if (declares(`${name}Ok`))
3751
+ console.warn(
3752
+ `intent "${name}" was not answered (${reason}) and this component declares only "${name}Ok" - add "${name}Unhandled" or "${name}Error" to handle it`
3753
+ );
3754
+ this.finish(null, null);
3755
+ }
3756
+ }
3757
+ // End the walk. `name === null` means no answer goes out, which is what `stop` and a
3758
+ // fire-and-forget notification both are — the tracked unit is released here instead
3759
+ // of following an answer's subtree.
3760
+ finish(name, args) {
3761
+ if (this.ended) return;
3762
+ this.ended = true;
3763
+ if (name === null) return this.release?.();
3764
+ const path = this.answerPath ?? this.origin;
3765
+ const t = new SendEvent(path, this.transactor, name, args, this.parent, {});
3766
+ t._isAnswer = true;
3767
+ this.transactor.pushTransaction(t);
3768
+ if (this.release) t.completion.whenSubtreeSettled().then(this.release);
3769
+ }
3241
3770
  };
3242
3771
  var Completion = class {
3243
3772
  constructor() {
@@ -3318,23 +3847,21 @@ var Dispatcher = class {
3318
3847
  get at() {
3319
3848
  return new PathChanges(this);
3320
3849
  }
3850
+ // A message is ADDRESSED: it names one component and stops there.
3321
3851
  send(name, args, opts) {
3322
3852
  return this.sendAtPath(this.path, name, args, opts);
3323
3853
  }
3324
- bubble(name, args, opts) {
3325
- return this.send(name, args, { skipSelf: true, bubbles: true, ...opts });
3326
- }
3327
3854
  sendAtPath(path, name, args, opts) {
3328
3855
  return this.transactor.pushSend(path, name, args, opts, this.parent);
3329
3856
  }
3330
- request(name, args, opts) {
3331
- return this.requestAtPath(this.path, name, args, opts);
3857
+ // An intent is ROUTED: it names a job and walks until something answers. The verb
3858
+ // does not decide which scope answers — `opts.route` does, written here at the call
3859
+ // site, which is where the decision actually is.
3860
+ intent(name, args, opts) {
3861
+ return this.intentAtPath(this.path, name, args, opts);
3332
3862
  }
3333
- requestAtPath(path, name, args, opts) {
3334
- return this.transactor.pushRequest(path, name, args, opts, this.parent);
3335
- }
3336
- inputAtPath(path, name, args, opts) {
3337
- return this.transactor.pushInput(path, name, args, opts, this.parent);
3863
+ intentAtPath(path, name, args, opts) {
3864
+ return this.transactor.pushIntent(path, name, args, opts, this.parent);
3338
3865
  }
3339
3866
  lookupTypeFor(name, inst) {
3340
3867
  return this.transactor.comps.getCompFor(inst).scope.lookupComponent(name);
@@ -3347,12 +3874,30 @@ var EventContext = class extends Dispatcher {
3347
3874
  get targetPath() {
3348
3875
  return this.parent.targetPath;
3349
3876
  }
3350
- stopPropagation() {
3351
- return this.parent.stopPropagation();
3877
+ // Answer the intent being handled, with a result or with an error. Either ENDS the
3878
+ // walk. Legal only in an `intent` handler: a message has no sender waiting on it.
3879
+ reply(value) {
3880
+ return this.parent.walk === void 0 ? warnNotIntent("reply") : this.parent.walk.answer("Ok", value);
3881
+ }
3882
+ fail(error) {
3883
+ return this.parent.walk === void 0 ? warnNotIntent("fail") : this.parent.walk.answer("Error", error);
3884
+ }
3885
+ // End the walk answering nothing — "served, and no answer".
3886
+ stop() {
3887
+ return this.parent.stop();
3888
+ }
3889
+ // From an `intent` body: hand the intent to the next hop, optionally amending it.
3890
+ // From a `receive` body: turn the message that arrived into an intent. One word from
3891
+ // both ends of a walk; which one you get depends on which bucket you are in.
3892
+ forward(opts) {
3893
+ return this.parent.forward(opts);
3352
3894
  }
3353
3895
  };
3354
- var RequestContext = class extends Dispatcher {
3896
+ var IntentContext = class extends Dispatcher {
3355
3897
  };
3898
+ function warnNotIntent(verb) {
3899
+ console.warn(`ctx.${verb}() is only meaningful in an "intent" handler - ignored`);
3900
+ }
3356
3901
  var PathChanges = class extends PathBuilder {
3357
3902
  constructor(dispatcher) {
3358
3903
  super();
@@ -3361,8 +3906,8 @@ var PathChanges = class extends PathBuilder {
3361
3906
  send(name, args, opts) {
3362
3907
  return this.dispatcher.sendAtPath(this.buildPath(), name, args, opts);
3363
3908
  }
3364
- bubble(name, args, opts) {
3365
- return this.send(name, args, { skipSelf: true, bubbles: true, ...opts });
3909
+ intent(name, args, opts) {
3910
+ return this.dispatcher.intentAtPath(this.buildPath(), name, args, opts);
3366
3911
  }
3367
3912
  buildPath() {
3368
3913
  return this.dispatcher.path.concat(this.pathChanges);
@@ -3699,63 +4244,8 @@ function collectAppClassesInSet(app) {
3699
4244
  return classes;
3700
4245
  }
3701
4246
 
3702
- // index.js
3703
- import {
3704
- Collection,
3705
- fromJS,
3706
- get,
3707
- getIn,
3708
- has,
3709
- hash,
3710
- hasIn,
3711
- is as is2,
3712
- isAssociative,
3713
- isCollection,
3714
- isImmutable,
3715
- isIndexed as isIndexed2,
3716
- isKeyed as isKeyed2,
3717
- isList,
3718
- isMap,
3719
- isMap as isMap2,
3720
- isOrdered,
3721
- isOrderedMap,
3722
- isOrderedMap as isOrderedMap2,
3723
- isOrderedSet,
3724
- isPlainObject,
3725
- isRecord,
3726
- isSeq,
3727
- isSet,
3728
- isStack,
3729
- isValueObject,
3730
- List as List2,
3731
- Map as Map2,
3732
- Map as Map3,
3733
- merge,
3734
- mergeDeep,
3735
- mergeDeepWith,
3736
- mergeWith,
3737
- OrderedMap as OrderedMap2,
3738
- OrderedMap as OrderedMap3,
3739
- OrderedSet,
3740
- PairSorting,
3741
- Range,
3742
- Record as Record2,
3743
- Repeat,
3744
- remove,
3745
- removeIn,
3746
- Seq,
3747
- Set as Set2,
3748
- Set as Set3,
3749
- Stack as Stack2,
3750
- set,
3751
- setIn,
3752
- update,
3753
- updateIn,
3754
- version
3755
- } from "immutable";
3756
-
3757
4247
  // src/on.js
3758
- var OP_KINDS = ["send", "bubble", "request", "input"];
4248
+ var OP_KINDS = ["send", "intent"];
3759
4249
  function phaseOps(phase) {
3760
4250
  const ops = [];
3761
4251
  for (const type of OP_KINDS) for (const a of phase[type] ?? []) ops.push({ type, ...a });
@@ -3765,11 +4255,6 @@ function phaseOps(phase) {
3765
4255
  function resolveArgs(args, self) {
3766
4256
  return typeof args === "function" ? args(self) ?? [] : args ?? [];
3767
4257
  }
3768
- function phaseHasBubble(phase) {
3769
- if (!phase) return false;
3770
- if (phase.bubble?.length) return true;
3771
- return (phase.do ?? []).some((op) => op.type === "bubble");
3772
- }
3773
4258
  function dispatchPhase(dispatcher, targetPath, phase, self) {
3774
4259
  if (!phase) return;
3775
4260
  for (const op of phaseOps(phase)) {
@@ -3778,407 +4263,18 @@ function dispatchPhase(dispatcher, targetPath, phase, self) {
3778
4263
  case "send":
3779
4264
  dispatcher.sendAtPath(targetPath, op.name, args, op.opts);
3780
4265
  break;
3781
- case "bubble":
3782
- dispatcher.sendAtPath(targetPath, op.name, args, {
3783
- skipSelf: true,
3784
- bubbles: true,
3785
- ...op.opts
3786
- });
3787
- break;
3788
- case "request":
3789
- dispatcher.requestAtPath(targetPath, op.name, args, op.opts);
3790
- break;
3791
- case "input":
3792
- dispatcher.inputAtPath(targetPath, op.name, args, op.opts);
4266
+ case "intent":
4267
+ dispatcher.intentAtPath(targetPath, op.name, args, op.opts);
3793
4268
  break;
3794
4269
  }
3795
4270
  }
3796
4271
  }
3797
4272
 
3798
- // src/oo.js
3799
- import { Map as IMap, Set as ISet, List, OrderedMap, Record } from "immutable";
3800
- var BAD_VALUE = /* @__PURE__ */ Symbol("BadValue");
3801
- var nullCoercer = (v) => v;
3802
- var Field = class {
3803
- constructor(type, name, typeCheck, coercer, defaultValue = null) {
3804
- this.type = type;
3805
- this.name = name;
3806
- this.typeCheck = typeCheck;
3807
- this.coercer = coercer;
3808
- this.checks = [];
3809
- this.defaultValue = defaultValue;
3810
- }
3811
- toDataDef() {
3812
- const { type, defaultValue: dv } = this;
3813
- return { type, defaultValue: dv?.toJS ? dv.toJS() : dv };
3814
- }
3815
- getFirstFailingCheck(v) {
3816
- if (!this.typeCheck.isValid(v)) return this.typeCheck;
3817
- for (const check2 of this.checks) if (!check2.isValid(v)) return check2;
3818
- return null;
3819
- }
3820
- isValid(v) {
3821
- return this.getFirstFailingCheck(v) === null;
3822
- }
3823
- addCheck(check2) {
3824
- this.checks.push(check2);
3825
- return this;
3826
- }
3827
- coerceOr(v, defaultValue = null) {
3828
- if (this.isValid(v)) return v;
3829
- const v1 = this.coercer(v);
3830
- return this.isValid(v1) ? v1 : defaultValue;
3831
- }
3832
- coerceOrDefault(v) {
3833
- return this.coerceOr(v, this.defaultValue);
3834
- }
3835
- extendProtoForType(_proto, _uname) {
3836
- }
3837
- extendProto(proto) {
3838
- const { name } = this;
3839
- const uname = name[0].toUpperCase() + name.slice(1);
3840
- const setName = `set${uname}`;
3841
- const that = this;
3842
- proto[setName] = function(v) {
3843
- const v1 = that.coerceOr(v, BAD_VALUE);
3844
- if (v1 === BAD_VALUE) {
3845
- console.warn("invalid value", v);
3846
- return this;
3847
- }
3848
- return this.set(name, v1);
3849
- };
3850
- proto[`update${uname}`] = function(fn) {
3851
- return this[setName](fn(this.get(name)));
3852
- };
3853
- proto[`reset${uname}`] = function() {
3854
- return this.set(name, that.defaultValue);
3855
- };
3856
- this.extendProtoForType(proto, uname);
3857
- }
3858
- };
3859
- var Check = class {
3860
- isValid(_v) {
3861
- return true;
3862
- }
3863
- getMessage(_v) {
3864
- return "Invalid";
3865
- }
3866
- };
3867
- var CheckTypeAny = class extends Check {
3868
- };
3869
- var CHECK_TYPE_ANY = new CheckTypeAny();
3870
- var FnCheck = class extends Check {
3871
- constructor(isValidFn, getMessageFn) {
3872
- super();
3873
- this._isValid = isValidFn;
3874
- this._getMessage = getMessageFn;
3875
- }
3876
- isValid(v) {
3877
- return this._isValid(v);
3878
- }
3879
- getMessage(v) {
3880
- return this._getMessage(v);
3881
- }
3882
- };
3883
- var CHECK_TYPE_INT = new FnCheck(
3884
- (v) => Number.isInteger(v),
3885
- () => "Integer expected"
3886
- );
3887
- var CHECK_TYPE_FLOAT = new FnCheck(
3888
- (v) => Number.isFinite(v),
3889
- () => "Float expected"
3890
- );
3891
- var CHECK_TYPE_BOOL = new FnCheck(
3892
- (v) => typeof v === "boolean",
3893
- () => "Boolean expected"
3894
- );
3895
- var CHECK_TYPE_STRING = new FnCheck(
3896
- (v) => typeof v === "string",
3897
- () => "String expected"
3898
- );
3899
- var CHECK_TYPE_LIST = new FnCheck(
3900
- (v) => List.isList(v),
3901
- () => "List expected"
3902
- );
3903
- var CHECK_TYPE_MAP = new FnCheck(
3904
- (v) => IMap.isMap(v),
3905
- () => "Map expected"
3906
- );
3907
- var CHECK_TYPE_OMAP = new FnCheck(
3908
- (v) => OrderedMap.isOrderedMap(v),
3909
- () => "OrderedMap expected"
3910
- );
3911
- var CHECK_TYPE_SET = new FnCheck(
3912
- (v) => ISet.isSet(v),
3913
- () => "Set expected"
3914
- );
3915
- var boolCoercer = (v) => !!v;
3916
- var FieldBool = class extends Field {
3917
- constructor(name, defaultValue = false) {
3918
- super("bool", name, CHECK_TYPE_BOOL, boolCoercer, defaultValue);
3919
- }
3920
- extendProtoForType(proto, uname) {
3921
- const { name } = this;
3922
- proto[`toggle${uname}`] = function() {
3923
- return this.set(name, !this.get(name, false));
3924
- };
3925
- proto[`set${uname}`] = function(v) {
3926
- return this.set(name, !!v);
3927
- };
3928
- }
3929
- };
3930
- var FieldAny = class extends Field {
3931
- constructor(name, defaultValue = null) {
3932
- super("any", name, CHECK_TYPE_ANY, nullCoercer, defaultValue);
3933
- }
3934
- toDataDef() {
3935
- const { defaultValue: dv } = this;
3936
- const type = getTypeName(dv) ?? "any";
3937
- return { type, defaultValue: dv?.toJS ? dv.toJS() : dv };
3938
- }
3939
- };
3940
- var stringCoercer = (v) => v?.toString?.() ?? "";
3941
- var FieldString = class extends Field {
3942
- constructor(name, defaultValue = "") {
3943
- super("text", name, CHECK_TYPE_STRING, stringCoercer, defaultValue);
3944
- }
3945
- extendProtoForType(proto, _uname) {
3946
- extendProtoSized(proto, this.name, "", "length");
3947
- }
3948
- };
3949
- var intCoercer = (v) => Number.isFinite(v) ? Math.trunc(v) : null;
3950
- var FieldInt = class extends Field {
3951
- constructor(name, defaultValue = 0) {
3952
- super("int", name, CHECK_TYPE_INT, intCoercer, defaultValue);
3953
- }
3954
- };
3955
- var floatCoercer = (_) => null;
3956
- var FieldFloat = class extends Field {
3957
- constructor(name, defaultValue = 0) {
3958
- super("float", name, CHECK_TYPE_FLOAT, floatCoercer, defaultValue);
3959
- }
3960
- };
3961
- var getTypeName = (v) => v?.constructor?.getMetaClass?.()?.name;
3962
- var CheckTypeName = class {
3963
- constructor(typeName) {
3964
- this.typeName = typeName;
3965
- }
3966
- isValid(v) {
3967
- return getTypeName(v) === this.typeName;
3968
- }
3969
- getMessage(v) {
3970
- const got = getTypeName(v);
3971
- return `Expected "${this.typeName}", got "${got}"`;
3972
- }
3973
- };
3974
- var FieldComp = class extends Field {
3975
- constructor(type, name, args) {
3976
- super(type, name, new CheckTypeName(type), nullCoercer, null);
3977
- this.args = args;
3978
- }
3979
- toDataDef() {
3980
- return { component: this.typeName, args: this.args };
3981
- }
3982
- };
3983
- var NONE2 = /* @__PURE__ */ Symbol("NONE");
3984
- function extendProtoForKeyed(proto, name, uname) {
3985
- extendProtoSized(proto, name, EMPTY_LIST);
3986
- proto[`setIn${uname}At`] = function(i, v) {
3987
- return this.set(name, this.get(name).set(i, v));
3988
- };
3989
- proto[`getIn${uname}At`] = function(i, dval) {
3990
- return this.get(name).get(i, dval);
3991
- };
3992
- proto[`updateIn${uname}At`] = function(i, fn) {
3993
- const col = this.get(name);
3994
- const v = col.get(i, NONE2);
3995
- if (v !== NONE2) return this.set(name, col.set(i, fn(v)));
3996
- console.warn("key", i, "not found in", name, col);
3997
- return this;
3998
- };
3999
- extendDeleteInAt(proto, name, `${uname}At`);
4000
- }
4001
- function extendDeleteInAt(proto, name, uname) {
4002
- proto[`deleteIn${uname}`] = function(v) {
4003
- return this.set(name, this.get(name).delete(v));
4004
- };
4005
- proto[`removeIn${uname}`] = proto[`deleteIn${uname}`];
4006
- }
4007
- var EMPTY_LIST = List();
4008
- var listCoercer = (v) => Array.isArray(v) ? List(v) : null;
4009
- var FieldList = class extends Field {
4010
- constructor(name, defaultValue = EMPTY_LIST) {
4011
- super("list", name, CHECK_TYPE_LIST, listCoercer, defaultValue);
4012
- }
4013
- extendProtoForType(proto, uname) {
4014
- const { name } = this;
4015
- extendProtoForKeyed(proto, name, uname);
4016
- proto[`pushIn${uname}`] = function(v) {
4017
- return this.set(name, this.get(name).push(v));
4018
- };
4019
- proto[`insertIn${uname}At`] = function(i, v) {
4020
- return this.set(name, this.get(name).insert(i, v));
4021
- };
4022
- }
4023
- };
4024
- var imapCoercer = (v) => IMap(v);
4025
- var FieldMap = class extends Field {
4026
- constructor(name, defaultValue = IMap()) {
4027
- super("map", name, CHECK_TYPE_MAP, imapCoercer, defaultValue);
4028
- }
4029
- extendProtoForType(proto, uname) {
4030
- extendProtoForKeyed(proto, this.name, uname);
4031
- }
4032
- };
4033
- var omapCoercer = (v) => OrderedMap(v);
4034
- var FieldOMap = class extends Field {
4035
- constructor(name, defaultValue = OrderedMap()) {
4036
- super("omap", name, CHECK_TYPE_OMAP, omapCoercer, defaultValue);
4037
- }
4038
- extendProtoForType(proto, uname) {
4039
- extendProtoForKeyed(proto, this.name, uname);
4040
- }
4041
- };
4042
- function extendProtoSized(proto, name, defaultEmpty, propName = "size") {
4043
- proto[`${name}Len`] = function() {
4044
- return this.get(name, defaultEmpty)[propName];
4045
- };
4046
- }
4047
- var EMPTY_SET = ISet();
4048
- var isetCoercer = (v) => Array.isArray(v) ? ISet(v) : v instanceof Set ? ISet(v) : null;
4049
- var FieldSet = class extends Field {
4050
- constructor(name, defaultValue = EMPTY_SET) {
4051
- super("set", name, CHECK_TYPE_SET, isetCoercer, defaultValue);
4052
- }
4053
- extendProtoForType(proto, uname) {
4054
- const { name } = this;
4055
- extendProtoSized(proto, name, EMPTY_SET);
4056
- proto[`addIn${uname}`] = function(v) {
4057
- return this.set(name, this.get(name).add(v));
4058
- };
4059
- extendDeleteInAt(proto, name, uname);
4060
- proto[`hasIn${uname}`] = function(v) {
4061
- return this.get(name).has(v);
4062
- };
4063
- proto[`toggleIn${uname}`] = function(v) {
4064
- const current = this.get(name);
4065
- return this.set(name, current.has(v) ? current.delete(v) : current.add(v));
4066
- };
4067
- }
4068
- };
4069
- function mkCompField(field, scope, args) {
4070
- const Comp = scope?.lookupComponent(field.type) ?? null;
4071
- if (Comp === null)
4072
- console.warn(
4073
- scope ? `component field "${field.name}": component "${field.type}" not found in scope` : `component field "${field.name}": cannot resolve component "${field.type}" — built without a registered scope (use ${field.type}.make({}) as the default, or build via a registered component)`
4074
- );
4075
- return Comp?.make({ ...field.args, ...args }, { scope }) ?? null;
4076
- }
4077
- var ClassBuilder = class {
4078
- constructor(name) {
4079
- const fields = {};
4080
- const compFields = /* @__PURE__ */ new Set();
4081
- this.name = name;
4082
- this.fields = fields;
4083
- this.compFields = compFields;
4084
- this._methods = {};
4085
- this._statics = {
4086
- make: function(inArgs = {}, opts = {}) {
4087
- const args = {};
4088
- const scope = opts.scope ?? this.scope;
4089
- for (const key in inArgs) {
4090
- const field = fields[key];
4091
- if (compFields.has(key)) args[key] = mkCompField(field, scope, inArgs[key]);
4092
- else if (field === void 0)
4093
- console.warn("extra argument to constructor:", name, key, inArgs);
4094
- else args[key] = field.coerceOrDefault(inArgs[key]);
4095
- }
4096
- for (const key of compFields)
4097
- if (args[key] === void 0) args[key] = mkCompField(fields[key], scope, inArgs[key]);
4098
- return this(args);
4099
- }
4100
- };
4101
- }
4102
- build() {
4103
- const fieldVals = {};
4104
- const proto = {};
4105
- const { name, _methods, fields } = this;
4106
- for (const fieldName in fields) {
4107
- const field = fields[fieldName];
4108
- fieldVals[fieldName] = field.defaultValue;
4109
- field.extendProto(proto);
4110
- }
4111
- const Class = { [name]: Record(fieldVals, name) }[name];
4112
- Object.assign(Class.prototype, proto, _methods);
4113
- const metaClass = { fields, name, methods: _methods };
4114
- Object.assign(Class, this._statics, { getMetaClass: () => metaClass });
4115
- return Class;
4116
- }
4117
- methods(proto) {
4118
- for (const k in proto) this._methods[k] = proto[k];
4119
- }
4120
- statics(proto) {
4121
- for (const k in proto) this._statics[k] = proto[k];
4122
- }
4123
- addField(name, dval, FieldCls) {
4124
- const field = new FieldCls(name, dval);
4125
- this.fields[name] = field;
4126
- return field;
4127
- }
4128
- addCompField(name, type, args) {
4129
- const field = new FieldComp(type, name, args);
4130
- this.compFields.add(name);
4131
- this.fields[name] = field;
4132
- return field;
4133
- }
4134
- };
4135
- var FIELD_CLASS = /* @__PURE__ */ Symbol.for("tutuca.fieldClass");
4136
- var fieldsByTypeName = {
4137
- text: FieldString,
4138
- int: FieldInt,
4139
- float: FieldFloat,
4140
- bool: FieldBool,
4141
- list: FieldList,
4142
- map: FieldMap,
4143
- omap: FieldOMap,
4144
- set: FieldSet,
4145
- any: FieldAny
4146
- };
4147
- function classFromData(name, { fields = {}, methods, statics }) {
4148
- const b = new ClassBuilder(name);
4149
- for (const field in fields) {
4150
- const value = fields[field];
4151
- const type = typeof value;
4152
- if (type === "string") b.addField(field, value, FieldString);
4153
- else if (type === "number") b.addField(field, value, FieldFloat);
4154
- else if (type === "boolean") b.addField(field, value, FieldBool);
4155
- else if (List.isList(value) || Array.isArray(value)) b.addField(field, List(value), FieldList);
4156
- else if (ISet.isSet(value) || value instanceof Set) b.addField(field, ISet(value), FieldSet);
4157
- else if (OrderedMap.isOrderedMap(value)) b.addField(field, value, FieldOMap);
4158
- else if (value?.type && value?.defaultValue !== void 0) {
4159
- const Field2 = fieldsByTypeName[value.type] ?? FieldAny;
4160
- b.addField(field, new Field2().coerceOr(value.defaultValue), Field2);
4161
- } else if (value?.component && value?.args !== void 0)
4162
- b.addCompField(field, value.component, value.args);
4163
- else if (IMap.isMap(value) || value?.constructor === Object)
4164
- b.addField(field, IMap(value), FieldMap);
4165
- else {
4166
- const Field2 = value?.[FIELD_CLASS] ?? FieldAny;
4167
- b.addField(field, value, Field2);
4168
- }
4169
- }
4170
- if (methods) b.methods(methods);
4171
- if (statics) b.statics(statics);
4172
- return b.build();
4173
- }
4174
- Component.fromSpec = (opts) => new Component(classFromData(opts.name, opts), opts);
4175
- var component = (opts) => Component.fromSpec(opts);
4176
-
4177
4273
  // index.js
4178
4274
  var css = String.raw;
4179
4275
  var html = String.raw;
4180
4276
  var macro = (defaults, rawView) => new Macro(defaults, rawView);
4181
- function check(_app) {
4277
+ function check2(_app) {
4182
4278
  return { error: 0, warn: 0, hint: 0, dummyCheck: true };
4183
4279
  }
4184
4280
  async function test(_opts) {
@@ -4211,74 +4307,23 @@ async function compileClassesToStyleText(app, compileClasses, Ctx = ParseCtxClas
4211
4307
  return await compileClasses(Array.from(collectAppClassesInSet(app)));
4212
4308
  }
4213
4309
  export {
4214
- Collection,
4215
4310
  FIELD_CLASS,
4216
- Map3 as IMap,
4217
- Set3 as ISet,
4218
- List2 as List,
4219
- Map2 as Map,
4220
- OrderedMap3 as OMap,
4221
- OrderedMap2 as OrderedMap,
4222
- OrderedSet,
4223
- PairSorting,
4311
+ PASS,
4224
4312
  ParseContext,
4225
- Range,
4226
- Record2 as Record,
4227
- Repeat,
4228
4313
  SEQ_INFO,
4229
- Seq,
4230
- Set2 as Set,
4231
- Stack2 as Stack,
4232
- check,
4314
+ check2 as check,
4233
4315
  collectIterBindings,
4234
4316
  compileClassesToStyle,
4235
4317
  compileClassesToStyleText,
4236
4318
  component,
4237
4319
  css,
4238
4320
  dispatchPhase,
4239
- fromJS,
4240
- get,
4241
- getIn,
4242
- has,
4243
- hasIn,
4244
- hash,
4245
4321
  html,
4246
4322
  injectCss,
4247
- is2 as is,
4248
- isAssociative,
4249
- isCollection,
4250
- isMap2 as isIMap,
4251
- isImmutable,
4252
- isIndexed2 as isIndexed,
4253
- isKeyed2 as isKeyed,
4254
- isList,
4255
- isMap,
4256
- isOrderedMap2 as isOMap,
4257
- isOrdered,
4258
- isOrderedMap,
4259
- isOrderedSet,
4260
- isPlainObject,
4261
- isRecord,
4262
- isSeq,
4263
- isSet,
4264
- isStack,
4265
- isValueObject,
4266
4323
  macro,
4267
- merge,
4268
- mergeDeep,
4269
- mergeDeepWith,
4270
- mergeWith,
4271
- phaseHasBubble,
4272
4324
  phaseOps,
4273
- remove,
4274
- removeIn,
4275
4325
  resolveArgs,
4276
4326
  rootDispatcher,
4277
- set,
4278
- setIn,
4279
4327
  test,
4280
- tutuca,
4281
- update,
4282
- updateIn,
4283
- version
4328
+ tutuca
4284
4329
  };