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.
- package/README.md +16 -16
- package/dist/immer.js +1261 -0
- package/dist/tutuca-cli.js +2056 -4798
- package/dist/tutuca-components.js +206 -442
- package/dist/tutuca-dev.ext.js +878 -944
- package/dist/tutuca-dev.js +1679 -5298
- package/dist/tutuca-dev.min.js +5 -5
- package/dist/tutuca-extra.ext.js +740 -695
- package/dist/tutuca-extra.js +1403 -4925
- package/dist/tutuca-extra.min.js +3 -3
- package/dist/tutuca-storybook.js +226 -210
- package/dist/tutuca.ext.js +740 -695
- package/dist/tutuca.js +1403 -4925
- package/dist/tutuca.min.js +3 -3
- package/package.json +7 -11
- package/skill/tutuca/SKILL.md +4 -8
- package/skill/tutuca/advanced.md +13 -5
- package/skill/tutuca/cli.md +8 -9
- package/skill/tutuca/component-design.md +50 -47
- package/skill/tutuca/core.md +134 -117
- package/skill/tutuca/iteration.md +2 -2
- package/skill/tutuca/macros.md +1 -1
- package/skill/tutuca/messages-and-intents.md +399 -0
- package/skill/tutuca/patterns/README.md +1 -1
- package/skill/tutuca/patterns/add-a-story.md +2 -2
- package/skill/tutuca/patterns/bind-text-and-attributes.md +1 -1
- package/skill/tutuca/patterns/conditional-attribute-value.md +1 -1
- package/skill/tutuca/patterns/coordinate-components.md +41 -18
- package/skill/tutuca/patterns/file-input.md +8 -8
- package/skill/tutuca/patterns/filter-and-paginate.md +1 -1
- package/skill/tutuca/patterns/handle-events.md +11 -9
- package/skill/tutuca/patterns/switch-between-views.md +1 -1
- package/skill/tutuca/patterns/tabbed-interface.md +10 -5
- package/skill/tutuca/semantics.md +41 -35
- package/skill/tutuca/storybook.md +34 -27
- package/skill/tutuca/testing.md +75 -64
- package/skill/tutuca-source/tutuca.ext.js +740 -695
- package/dist/immutable.js +0 -4331
- package/skill/immutable-js/SKILL.md +0 -78
- package/skill/immutable-js/references/collection.md +0 -346
- package/skill/immutable-js/references/conversions.md +0 -99
- package/skill/immutable-js/references/deep-updates.md +0 -172
- package/skill/immutable-js/references/equality.md +0 -95
- package/skill/immutable-js/references/list.md +0 -266
- package/skill/immutable-js/references/map.md +0 -300
- package/skill/immutable-js/references/predicates.md +0 -93
- package/skill/immutable-js/references/range-repeat.md +0 -55
- package/skill/immutable-js/references/record.md +0 -196
- package/skill/immutable-js/references/seq.md +0 -248
- package/skill/immutable-js/references/set.md +0 -270
- package/skill/immutable-js/references/shallow-functional.md +0 -99
- package/skill/immutable-js/references/stack.md +0 -210
- package/skill/tutuca/request-response.md +0 -341
package/dist/tutuca-extra.ext.js
CHANGED
|
@@ -1,8 +1,80 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
|
151
|
+
return readKey(v, this.field, dval);
|
|
78
152
|
}
|
|
79
153
|
setValue(root, v) {
|
|
80
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
104
|
-
|
|
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
|
|
121
|
-
const key = v
|
|
122
|
-
return key !== NONE && seq
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
-
|
|
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
|
|
619
|
-
return _parseHandler(s, px, "
|
|
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 &
|
|
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
|
|
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)
|
|
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 =
|
|
1249
|
+
const r = parseReceiveHandler(s, px);
|
|
1163
1250
|
return r === null ? null : new _EventHandler(r.handlerVal, r.args);
|
|
1164
1251
|
}
|
|
1165
1252
|
};
|
|
1166
|
-
var
|
|
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)
|
|
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) =>
|
|
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
|
|
1730
|
-
const attrName =
|
|
1731
|
-
for (const key of keys) visit(key, seq
|
|
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
|
|
1738
|
-
const [s, e] = normalizeRange(start, end, seq
|
|
1739
|
-
for (let i = s; i < e; i++) visit(i, seq
|
|
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
|
|
1742
|
-
const [s, e] = normalizeRange(start, end, seq
|
|
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
|
|
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
|
|
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
|
-
|
|
2538
|
-
|
|
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.
|
|
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
|
-
|
|
2584
|
-
|
|
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
|
-
|
|
2587
|
-
|
|
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
|
|
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.
|
|
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
|
|
2846
|
+
let has = false;
|
|
2752
2847
|
for (const k in provide) {
|
|
2753
2848
|
dynObj[provide[k].symbol] = provide[k].val.eval(this);
|
|
2754
|
-
|
|
2849
|
+
has = true;
|
|
2755
2850
|
}
|
|
2756
|
-
if (!
|
|
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
|
|
2860
|
-
//
|
|
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
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
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
|
|
2927
|
-
|
|
2928
|
-
|
|
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 =
|
|
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 "
|
|
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
|
-
|
|
3221
|
-
|
|
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
|
|
3225
|
-
if (
|
|
3226
|
-
|
|
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
|
|
3230
|
-
handlerProp = "
|
|
3231
|
-
constructor(path, transactor,
|
|
3232
|
-
super(path, transactor, name, args, parent,
|
|
3233
|
-
this.
|
|
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
|
-
|
|
3236
|
-
this.
|
|
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
|
|
3240
|
-
|
|
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
|
-
|
|
3331
|
-
|
|
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
|
-
|
|
3334
|
-
return this.transactor.
|
|
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
|
-
|
|
3351
|
-
|
|
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
|
|
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
|
-
|
|
3365
|
-
return this.
|
|
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", "
|
|
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 "
|
|
3782
|
-
dispatcher.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
};
|