@elaraai/east-ui 1.0.39 → 1.0.41
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/dist/src/collections/calendar/index.d.ts +275 -62
- package/dist/src/collections/calendar/index.d.ts.map +1 -1
- package/dist/src/collections/calendar/index.js +168 -51
- package/dist/src/collections/calendar/index.js.map +1 -1
- package/dist/src/collections/calendar/types.d.ts +228 -22
- package/dist/src/collections/calendar/types.d.ts.map +1 -1
- package/dist/src/collections/calendar/types.js +132 -18
- package/dist/src/collections/calendar/types.js.map +1 -1
- package/dist/src/collections/deck/index.d.ts +2229 -0
- package/dist/src/collections/deck/index.d.ts.map +1 -0
- package/dist/src/collections/deck/index.js +461 -0
- package/dist/src/collections/deck/index.js.map +1 -0
- package/dist/src/collections/deck/types.d.ts +327 -0
- package/dist/src/collections/deck/types.d.ts.map +1 -0
- package/dist/src/collections/deck/types.js +206 -0
- package/dist/src/collections/deck/types.js.map +1 -0
- package/dist/src/collections/index.d.ts +2 -0
- package/dist/src/collections/index.d.ts.map +1 -1
- package/dist/src/collections/index.js +2 -0
- package/dist/src/collections/index.js.map +1 -1
- package/dist/src/collections/value-tree/index.d.ts +380 -0
- package/dist/src/collections/value-tree/index.d.ts.map +1 -0
- package/dist/src/collections/value-tree/index.js +688 -0
- package/dist/src/collections/value-tree/index.js.map +1 -0
- package/dist/src/collections/value-tree/materialize.d.ts +65 -0
- package/dist/src/collections/value-tree/materialize.d.ts.map +1 -0
- package/dist/src/collections/value-tree/materialize.js +238 -0
- package/dist/src/collections/value-tree/materialize.js.map +1 -0
- package/dist/src/collections/value-tree/types.d.ts +324 -0
- package/dist/src/collections/value-tree/types.d.ts.map +1 -0
- package/dist/src/collections/value-tree/types.js +168 -0
- package/dist/src/collections/value-tree/types.js.map +1 -0
- package/dist/src/component.d.ts +1047 -61
- package/dist/src/component.d.ts.map +1 -1
- package/dist/src/component.js +97 -3
- package/dist/src/component.js.map +1 -1
- package/dist/src/index.d.ts +3 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/internal.d.ts +4 -3
- package/dist/src/internal.d.ts.map +1 -1
- package/dist/src/internal.js +3 -3
- package/dist/src/internal.js.map +1 -1
- package/dist/src/layout/splitter/index.d.ts +1 -0
- package/dist/src/layout/splitter/index.d.ts.map +1 -1
- package/dist/src/layout/splitter/index.js +2 -1
- package/dist/src/layout/splitter/index.js.map +1 -1
- package/dist/src/layout/splitter/types.d.ts +9 -0
- package/dist/src/layout/splitter/types.d.ts.map +1 -1
- package/dist/src/layout/splitter/types.js +4 -0
- package/dist/src/layout/splitter/types.js.map +1 -1
- package/dist/src/navigation/app/index.d.ts +135 -0
- package/dist/src/navigation/app/index.d.ts.map +1 -0
- package/dist/src/navigation/app/index.js +191 -0
- package/dist/src/navigation/app/index.js.map +1 -0
- package/dist/src/navigation/index.d.ts +1 -0
- package/dist/src/navigation/index.d.ts.map +1 -1
- package/dist/src/navigation/index.js +1 -0
- package/dist/src/navigation/index.js.map +1 -1
- package/dist/src/navigation/pages/types.d.ts +15 -1
- package/dist/src/navigation/pages/types.d.ts.map +1 -1
- package/dist/src/navigation/pages/types.js.map +1 -1
- package/dist/src/platform/slice/impl.js +2 -2
- package/dist/src/platform/slice/impl.js.map +1 -1
- package/dist/src/runtime/collections/calendar.d.ts +10 -5
- package/dist/src/runtime/collections/calendar.d.ts.map +1 -1
- package/dist/src/runtime/collections/calendar.js +10 -5
- package/dist/src/runtime/collections/calendar.js.map +1 -1
- package/dist/src/runtime/collections/deck.d.ts +1066 -0
- package/dist/src/runtime/collections/deck.d.ts.map +1 -0
- package/dist/src/runtime/collections/deck.js +54 -0
- package/dist/src/runtime/collections/deck.js.map +1 -0
- package/dist/src/runtime/collections/index.d.ts +2 -0
- package/dist/src/runtime/collections/index.d.ts.map +1 -1
- package/dist/src/runtime/collections/index.js +2 -0
- package/dist/src/runtime/collections/index.js.map +1 -1
- package/dist/src/runtime/collections/value-tree.d.ts +202 -0
- package/dist/src/runtime/collections/value-tree.d.ts.map +1 -0
- package/dist/src/runtime/collections/value-tree.js +49 -0
- package/dist/src/runtime/collections/value-tree.js.map +1 -0
- package/dist/src/runtime/navigation/app.d.ts +50 -0
- package/dist/src/runtime/navigation/app.d.ts.map +1 -0
- package/dist/src/runtime/navigation/app.js +51 -0
- package/dist/src/runtime/navigation/app.js.map +1 -0
- package/dist/src/runtime/navigation/index.d.ts +1 -0
- package/dist/src/runtime/navigation/index.d.ts.map +1 -1
- package/dist/src/runtime/navigation/index.js +1 -0
- package/dist/src/runtime/navigation/index.js.map +1 -1
- package/dist/test/collections/calendar.examples.d.ts +2 -0
- package/dist/test/collections/calendar.examples.d.ts.map +1 -1
- package/dist/test/collections/calendar.examples.js +74 -42
- package/dist/test/collections/calendar.examples.js.map +1 -1
- package/dist/test/collections/deck.examples.d.ts +8 -0
- package/dist/test/collections/deck.examples.d.ts.map +1 -0
- package/dist/test/collections/deck.examples.js +169 -0
- package/dist/test/collections/deck.examples.js.map +1 -0
- package/dist/test/collections/planner.examples.js +2 -2
- package/dist/test/collections/value-tree.examples.d.ts +10 -0
- package/dist/test/collections/value-tree.examples.d.ts.map +1 -0
- package/dist/test/collections/value-tree.examples.js +263 -0
- package/dist/test/collections/value-tree.examples.js.map +1 -0
- package/dist/test/display/image.examples.js +1 -1
- package/dist/test/display/image.examples.js.map +1 -1
- package/dist/test/layout/aligned-stack.examples.js +4 -4
- package/dist/test/layout/aligned-stack.examples.js.map +1 -1
- package/dist/test/layout/dock.examples.js +6 -6
- package/dist/test/layout/dock.examples.js.map +1 -1
- package/dist/test/layout/splitter.examples.d.ts +1 -0
- package/dist/test/layout/splitter.examples.d.ts.map +1 -1
- package/dist/test/layout/splitter.examples.js +11 -0
- package/dist/test/layout/splitter.examples.js.map +1 -1
- package/dist/test/navigation/app.examples.d.ts +22 -0
- package/dist/test/navigation/app.examples.d.ts.map +1 -0
- package/dist/test/navigation/app.examples.js +116 -0
- package/dist/test/navigation/app.examples.js.map +1 -0
- package/dist/test/navigation/nav-list.examples.js +2 -2
- package/dist/test/navigation/nav-list.examples.js.map +1 -1
- package/dist/test/overlays/drawer.examples.js +2 -2
- package/dist/test/overlays/drawer.examples.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,688 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* ValueTree — the editable value-driven tree (#360).
|
|
7
|
+
*
|
|
8
|
+
* `ValueTree.Root(value, opts)` walks the STATIC East type of `value` at
|
|
9
|
+
* authoring time and materializes it into the fixed recursive
|
|
10
|
+
* {@link ValueTreeNodeType} IR — automatic tree formation from ANY East
|
|
11
|
+
* type, leaf-type-aware editing, and compound/collection editing
|
|
12
|
+
* (add/remove array rows and dict entries, variant tag switching,
|
|
13
|
+
* option toggling).
|
|
14
|
+
*
|
|
15
|
+
* Two edit surfaces, one contract:
|
|
16
|
+
*
|
|
17
|
+
* - **Value handlers** (the default): `onUpdate` receives the whole
|
|
18
|
+
* value with the edit applied; `at: [ValueTree.at(Type, p => …, fn)]`
|
|
19
|
+
* scopes receive just the rebuilt SUBTREE at their path. Every edit
|
|
20
|
+
* dispatches to the deepest scope whose path prefixes the edit path
|
|
21
|
+
* and bubbles up to `onUpdate` when no scope matches. The rebuild is
|
|
22
|
+
* an authoring-time macro over the static type — hosts never
|
|
23
|
+
* hand-apply paths.
|
|
24
|
+
* - **Path callbacks** (the escape hatch): `onEdit` / `onInsert` /
|
|
25
|
+
* `onRemove` / `onTag` receive the raw typed path and payload for
|
|
26
|
+
* hosts that own a finer-grained store. A raw callback overrides the
|
|
27
|
+
* generated handler for its event.
|
|
28
|
+
*
|
|
29
|
+
* The host owns the data either way: write the new value and let the
|
|
30
|
+
* Reactive re-materialization refresh the tree (the same contract as
|
|
31
|
+
* every collection surface).
|
|
32
|
+
*/
|
|
33
|
+
import { ArrayType, East, Expr, FunctionType, NullType, RecursiveExpr, StringType, StructType, defaultValue, some, none, variant, } from "@elaraai/east";
|
|
34
|
+
import { UIComponentType } from "../../component.js";
|
|
35
|
+
import { ValueTreeStepType, ValueTreePathType, ValueTreeLeafType, ValueTreeNodeType, ValueTreeStyleType, ValueTreeRootType, } from "./types.js";
|
|
36
|
+
export { ValueTreeStepType, ValueTreePathType, ValueTreeLeafType, ValueTreeNodeType, ValueTreeStyleType, ValueTreeRootType, } from "./types.js";
|
|
37
|
+
import { materialize, applyEdit } from "./materialize.js";
|
|
38
|
+
export { materialize, applyEdit, } from "./materialize.js";
|
|
39
|
+
/** Beyond this many unrollings of a RecursiveType the subtree prints as
|
|
40
|
+
* an `opaque` node (a lazy "expand further" is the #360 follow-up). */
|
|
41
|
+
const MAX_RECURSION_DEPTH = 6;
|
|
42
|
+
/** Total materializer expansions per tree — caps generated IR size for
|
|
43
|
+
* wide recursive types, where a pure depth limit still explodes
|
|
44
|
+
* (branching^depth). Exhausted subtrees print as `opaque`. */
|
|
45
|
+
const MAX_MATERIALIZED_NODES = 4096;
|
|
46
|
+
/** Expansion budget for each generated edit-application wrapper —
|
|
47
|
+
* the rebuild macro mirrors the materializer's recursion, so it gets
|
|
48
|
+
* the same cap; exhausted subtrees pass through unchanged (the
|
|
49
|
+
* materializer shows them as read-only `opaque` anyway). */
|
|
50
|
+
const MAX_REBUILD_NODES = 4096;
|
|
51
|
+
/** Longest opaque summary before the printed value is clamped. */
|
|
52
|
+
const MAX_OPAQUE_PRINT = 80n;
|
|
53
|
+
/** The dict-entry element of a dict node. */
|
|
54
|
+
const DictEntryType = StructType({ key: StringType, node: ValueTreeNodeType });
|
|
55
|
+
/** The struct-field element of a struct node. */
|
|
56
|
+
const StructFieldType = StructType({ name: StringType, node: ValueTreeNodeType });
|
|
57
|
+
/** True when a Variant type is exactly the Option shape (`some` + `none`). */
|
|
58
|
+
function isOptionType(t) {
|
|
59
|
+
if (t.type !== "Variant")
|
|
60
|
+
return false;
|
|
61
|
+
const tags = Object.keys(t.cases);
|
|
62
|
+
return tags.length === 2 && tags.includes("some") && tags.includes("none");
|
|
63
|
+
}
|
|
64
|
+
/** Clamps a printed value for opaque summaries (large collections would
|
|
65
|
+
* otherwise dump their whole print into a single row). */
|
|
66
|
+
const clampPrint = East.function([StringType], StringType, (_$, p) => p.length().greater(MAX_OPAQUE_PRINT).ifElse(() => p.substring(0n, MAX_OPAQUE_PRINT).concat("…"), () => p));
|
|
67
|
+
/** Kind-aware opaque summary — "Set · 12 items" beats an unbounded print. */
|
|
68
|
+
function opaqueSummary(t, v) {
|
|
69
|
+
switch (t.type) {
|
|
70
|
+
case "Set":
|
|
71
|
+
return East.str `Set · ${v.size()} items`;
|
|
72
|
+
case "Blob":
|
|
73
|
+
return East.str `Blob · ${v.size()} bytes`;
|
|
74
|
+
case "Vector":
|
|
75
|
+
return East.str `Vector · ${v.length()} values`;
|
|
76
|
+
case "Matrix": {
|
|
77
|
+
const m = v;
|
|
78
|
+
return East.str `Matrix · ${m.rows()}×${m.cols()}`;
|
|
79
|
+
}
|
|
80
|
+
case "Ref":
|
|
81
|
+
return East.str `Ref · ${clampPrint(East.print(v.get()))}`;
|
|
82
|
+
case "Function":
|
|
83
|
+
case "AsyncFunction":
|
|
84
|
+
return East.value("Function", StringType);
|
|
85
|
+
default:
|
|
86
|
+
return clampPrint(East.print(v));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Materialize `v` (typed `t`) into a {@link ValueTreeNodeType} expression.
|
|
91
|
+
* Recursion is over the STATIC type — the generated IR walks the value
|
|
92
|
+
* eagerly (the `mapRows` discipline: expressions expand once per type
|
|
93
|
+
* shape, user data flows through East collection ops).
|
|
94
|
+
*/
|
|
95
|
+
function nodeOf(t, v, depth, state) {
|
|
96
|
+
state.budget -= 1;
|
|
97
|
+
if (depth > MAX_RECURSION_DEPTH || state.budget <= 0) {
|
|
98
|
+
return East.value(variant("opaque", clampPrint(East.print(v))), ValueTreeNodeType);
|
|
99
|
+
}
|
|
100
|
+
switch (t.type) {
|
|
101
|
+
case "Null":
|
|
102
|
+
return East.value(variant("leaf", variant("null", null)), ValueTreeNodeType);
|
|
103
|
+
case "Boolean":
|
|
104
|
+
return East.value(variant("leaf", variant("boolean", v)), ValueTreeNodeType);
|
|
105
|
+
case "Integer":
|
|
106
|
+
return East.value(variant("leaf", variant("integer", v)), ValueTreeNodeType);
|
|
107
|
+
case "Float":
|
|
108
|
+
return East.value(variant("leaf", variant("float", v)), ValueTreeNodeType);
|
|
109
|
+
case "String":
|
|
110
|
+
return East.value(variant("leaf", variant("string", v)), ValueTreeNodeType);
|
|
111
|
+
case "DateTime":
|
|
112
|
+
return East.value(variant("leaf", variant("datetime", v)), ValueTreeNodeType);
|
|
113
|
+
case "Struct": {
|
|
114
|
+
const fields = t.fields;
|
|
115
|
+
const fieldNodes = Object.entries(fields).map(([name, ft]) => ({
|
|
116
|
+
name,
|
|
117
|
+
node: nodeOf(ft, v[name], depth, state),
|
|
118
|
+
}));
|
|
119
|
+
return East.value(variant("struct", {
|
|
120
|
+
fields: East.value(fieldNodes, ArrayType(StructFieldType)),
|
|
121
|
+
}), ValueTreeNodeType);
|
|
122
|
+
}
|
|
123
|
+
case "Array": {
|
|
124
|
+
const elem = t.value;
|
|
125
|
+
const fn = East.function([elem], ValueTreeNodeType, (_$, x) => nodeOf(elem, x, depth, state));
|
|
126
|
+
const items = v
|
|
127
|
+
.map((_$, x) => fn(x));
|
|
128
|
+
return East.value(variant("array", { items }), ValueTreeNodeType);
|
|
129
|
+
}
|
|
130
|
+
case "Dict": {
|
|
131
|
+
const { key, value } = t;
|
|
132
|
+
const editable = key.type === "String";
|
|
133
|
+
const fn = East.function([value, key], DictEntryType, (_$, x, k) => ({
|
|
134
|
+
key: editable
|
|
135
|
+
? k
|
|
136
|
+
: East.print(k),
|
|
137
|
+
node: nodeOf(value, x, depth, state),
|
|
138
|
+
}));
|
|
139
|
+
const entries = v
|
|
140
|
+
.toArray((_$, x, k) => fn(x, k));
|
|
141
|
+
return East.value(variant("dict", { entries, editable }), ValueTreeNodeType);
|
|
142
|
+
}
|
|
143
|
+
case "Variant": {
|
|
144
|
+
const cases = t.cases;
|
|
145
|
+
const tags = Object.keys(cases);
|
|
146
|
+
if (isOptionType(t)) {
|
|
147
|
+
const someType = cases["some"];
|
|
148
|
+
return v.match({
|
|
149
|
+
some: (_$, payload) => East.value(variant("option", {
|
|
150
|
+
value: some(nodeOf(someType, payload, depth, state)),
|
|
151
|
+
}), ValueTreeNodeType),
|
|
152
|
+
none: () => East.value(variant("option", {
|
|
153
|
+
value: none,
|
|
154
|
+
}), ValueTreeNodeType),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
const handlers = {};
|
|
158
|
+
for (const tag of tags) {
|
|
159
|
+
handlers[tag] = (_$, payload) => East.value(variant("variant", {
|
|
160
|
+
tag,
|
|
161
|
+
tags: East.value(tags, ArrayType(StringType)),
|
|
162
|
+
value: nodeOf(cases[tag], payload, depth, state),
|
|
163
|
+
}), ValueTreeNodeType);
|
|
164
|
+
}
|
|
165
|
+
return v
|
|
166
|
+
.match(handlers);
|
|
167
|
+
}
|
|
168
|
+
case "Recursive":
|
|
169
|
+
// A recursive-typed expression only exposes unwrap()/wrap —
|
|
170
|
+
// unwrap to the inner node type before descending.
|
|
171
|
+
return nodeOf(t.node, v.unwrap(), depth + 1, state);
|
|
172
|
+
default:
|
|
173
|
+
// Sets, blobs, vectors, matrices, refs, functions — summarized.
|
|
174
|
+
return East.value(variant("opaque", opaqueSummary(t, v)), ValueTreeNodeType);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/** Leaf case tag of {@link ValueTreeLeafType} for a primitive type. */
|
|
178
|
+
function leafTag(t) {
|
|
179
|
+
switch (t.type) {
|
|
180
|
+
case "String": return "string";
|
|
181
|
+
case "Integer": return "integer";
|
|
182
|
+
case "Float": return "float";
|
|
183
|
+
case "Boolean": return "boolean";
|
|
184
|
+
case "DateTime": return "datetime";
|
|
185
|
+
default: return undefined;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/** Zero value as an East expression, or undefined where no zero exists
|
|
189
|
+
* (functions, matrices) — the corresponding insert/tag branch then
|
|
190
|
+
* passes the value through unchanged. */
|
|
191
|
+
function tryZeroExpr(t) {
|
|
192
|
+
try {
|
|
193
|
+
return East.value(zeroValue(t), t);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Rebuild `v` (typed `t`) with `op` applied at `path` — the write-side
|
|
201
|
+
* mirror of {@link nodeOf}. Expands per static type shape; `si` is the
|
|
202
|
+
* number of path steps already consumed (static: one per nesting level),
|
|
203
|
+
* `depth` counts RecursiveType unrollings against the same cap as the
|
|
204
|
+
* materializer (edits cannot originate beyond what materialized).
|
|
205
|
+
*
|
|
206
|
+
* Op semantics at the addressed node:
|
|
207
|
+
* - `edit`: the path ends AT a primitive leaf — its typed payload replaces it.
|
|
208
|
+
* - `insert`: the path ends with `append` (array) or the NEW `key` (dict).
|
|
209
|
+
* - `remove`: the path ends with the element `index` / entry `key`.
|
|
210
|
+
* - `tag`: the path ends AT an option (tag "some"/"none") or variant node;
|
|
211
|
+
* the payload becomes the target case's zero value.
|
|
212
|
+
*/
|
|
213
|
+
function rebuild(t, v, path, si, depth, state, op) {
|
|
214
|
+
state.budget -= 1;
|
|
215
|
+
if (depth > MAX_RECURSION_DEPTH || state.budget <= 0) {
|
|
216
|
+
return v;
|
|
217
|
+
}
|
|
218
|
+
const p = path;
|
|
219
|
+
const step = () => p.get(BigInt(si));
|
|
220
|
+
const atEnd = (n) => p.size().equals(BigInt(n));
|
|
221
|
+
switch (t.type) {
|
|
222
|
+
case "Null":
|
|
223
|
+
return v;
|
|
224
|
+
case "Boolean":
|
|
225
|
+
case "Integer":
|
|
226
|
+
case "Float":
|
|
227
|
+
case "String":
|
|
228
|
+
case "DateTime": {
|
|
229
|
+
if (op.kind !== "edit")
|
|
230
|
+
return v;
|
|
231
|
+
const tag = leafTag(t);
|
|
232
|
+
return op.leaf.unwrap(tag);
|
|
233
|
+
}
|
|
234
|
+
case "Struct": {
|
|
235
|
+
const fields = Object.entries(t.fields);
|
|
236
|
+
if (fields.length === 0)
|
|
237
|
+
return v;
|
|
238
|
+
const vs = v;
|
|
239
|
+
let chain = v;
|
|
240
|
+
for (const [name, ft] of [...fields].reverse()) {
|
|
241
|
+
const rebuiltFields = {};
|
|
242
|
+
for (const [n] of fields) {
|
|
243
|
+
rebuiltFields[n] = n === name
|
|
244
|
+
? rebuild(ft, vs[n], path, si + 1, depth, state, op)
|
|
245
|
+
: vs[n];
|
|
246
|
+
}
|
|
247
|
+
const prev = chain;
|
|
248
|
+
chain = step().hasTag("field")
|
|
249
|
+
.and(() => step().unwrap("field").equals(name))
|
|
250
|
+
.ifElse(() => East.value(rebuiltFields, t), () => prev);
|
|
251
|
+
}
|
|
252
|
+
return chain;
|
|
253
|
+
}
|
|
254
|
+
case "Array": {
|
|
255
|
+
const elem = t.value;
|
|
256
|
+
const av = v;
|
|
257
|
+
const descend = () => av.map((_$, x, i) => i.equals(step().unwrap("index"))
|
|
258
|
+
.ifElse(() => rebuild(elem, x, path, si + 1, depth, state, op), () => x));
|
|
259
|
+
if (op.kind === "insert") {
|
|
260
|
+
const zero = tryZeroExpr(elem);
|
|
261
|
+
if (zero === undefined)
|
|
262
|
+
return descend();
|
|
263
|
+
return atEnd(si + 1)
|
|
264
|
+
.and(() => step().hasTag("append"))
|
|
265
|
+
.ifElse(() => av.concat(East.value([zero], ArrayType(elem))), descend);
|
|
266
|
+
}
|
|
267
|
+
if (op.kind === "remove") {
|
|
268
|
+
return atEnd(si + 1).ifElse(() => av.filter((_$, _x, i) => i.notEquals(step().unwrap("index"))), descend);
|
|
269
|
+
}
|
|
270
|
+
return descend();
|
|
271
|
+
}
|
|
272
|
+
case "Dict": {
|
|
273
|
+
const { key: kt, value: vt } = t;
|
|
274
|
+
if (kt.type !== "String")
|
|
275
|
+
return v;
|
|
276
|
+
const dv = v;
|
|
277
|
+
const descend = () => dv.map((_$, val, k) => k.equals(step().unwrap("key"))
|
|
278
|
+
.ifElse(() => rebuild(vt, val, path, si + 1, depth, state, op), () => val));
|
|
279
|
+
if (op.kind === "insert") {
|
|
280
|
+
const zero = tryZeroExpr(vt);
|
|
281
|
+
if (zero === undefined)
|
|
282
|
+
return descend();
|
|
283
|
+
// Tail-return style (no `$.return`): these run inside the
|
|
284
|
+
// ASYNC generated wrappers, where a Return's control-flow
|
|
285
|
+
// exception would escape the sync call boundary.
|
|
286
|
+
const insertFn = East.function([t, StringType], t, ($, d, k) => {
|
|
287
|
+
const next = $.let(d.copy());
|
|
288
|
+
$(next
|
|
289
|
+
.insertOrUpdate(k, zero));
|
|
290
|
+
return next;
|
|
291
|
+
});
|
|
292
|
+
return atEnd(si + 1)
|
|
293
|
+
.and(() => step().hasTag("key"))
|
|
294
|
+
.ifElse(() => insertFn(v, step().unwrap("key")), descend);
|
|
295
|
+
}
|
|
296
|
+
if (op.kind === "remove") {
|
|
297
|
+
const removeFn = East.function([t, StringType], t, ($, d, k) => {
|
|
298
|
+
const next = $.let(d.copy());
|
|
299
|
+
$(next
|
|
300
|
+
.tryDelete(k));
|
|
301
|
+
return next;
|
|
302
|
+
});
|
|
303
|
+
return atEnd(si + 1)
|
|
304
|
+
.and(() => step().hasTag("key"))
|
|
305
|
+
.ifElse(() => removeFn(v, step().unwrap("key")), descend);
|
|
306
|
+
}
|
|
307
|
+
return descend();
|
|
308
|
+
}
|
|
309
|
+
case "Variant": {
|
|
310
|
+
const cases = t.cases;
|
|
311
|
+
const mv = v;
|
|
312
|
+
if (isOptionType(t)) {
|
|
313
|
+
const inner = cases["some"];
|
|
314
|
+
const descend = () => mv.match({
|
|
315
|
+
some: (_$, payload) => East.value(some(rebuild(inner, payload, path, si + 1, depth, state, op)), t),
|
|
316
|
+
none: () => East.value(none, t),
|
|
317
|
+
});
|
|
318
|
+
if (op.kind === "tag") {
|
|
319
|
+
const zero = tryZeroExpr(inner);
|
|
320
|
+
const toSome = zero !== undefined
|
|
321
|
+
? East.value(some(zero), t)
|
|
322
|
+
: v;
|
|
323
|
+
return atEnd(si).ifElse(() => op.tag.equals("some")
|
|
324
|
+
.ifElse(() => toSome, () => East.value(none, t)), descend);
|
|
325
|
+
}
|
|
326
|
+
return descend();
|
|
327
|
+
}
|
|
328
|
+
const tags = Object.keys(cases);
|
|
329
|
+
const descend = () => {
|
|
330
|
+
const handlers = {};
|
|
331
|
+
for (const tag of tags) {
|
|
332
|
+
handlers[tag] = (_$, payload) => East.value(variant(tag, rebuild(cases[tag], payload, path, si + 1, depth, state, op)), t);
|
|
333
|
+
}
|
|
334
|
+
return mv.match(handlers);
|
|
335
|
+
};
|
|
336
|
+
if (op.kind === "tag") {
|
|
337
|
+
let chain = v;
|
|
338
|
+
for (const tag of [...tags].reverse()) {
|
|
339
|
+
const zero = tryZeroExpr(cases[tag]);
|
|
340
|
+
if (zero === undefined)
|
|
341
|
+
continue;
|
|
342
|
+
const target = East.value(variant(tag, zero), t);
|
|
343
|
+
const prev = chain;
|
|
344
|
+
chain = op.tag.equals(tag)
|
|
345
|
+
.ifElse(() => target, () => prev);
|
|
346
|
+
}
|
|
347
|
+
const tagChain = chain;
|
|
348
|
+
return atEnd(si).ifElse(() => tagChain, descend);
|
|
349
|
+
}
|
|
350
|
+
return descend();
|
|
351
|
+
}
|
|
352
|
+
case "Recursive": {
|
|
353
|
+
const inner = rebuild(t.node, v.unwrap(), path, si, depth + 1, state, op);
|
|
354
|
+
// Field/element sites require the exact wrapped type — re-wrap
|
|
355
|
+
// the rebuilt inner value (subtype transparency does not apply
|
|
356
|
+
// to heap slots).
|
|
357
|
+
return RecursiveExpr.wrap(inner, t, Expr.fromAst);
|
|
358
|
+
}
|
|
359
|
+
default:
|
|
360
|
+
// Sets, blobs, vectors, matrices, refs, functions — read-only.
|
|
361
|
+
return v;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/** Runtime probe: records navigation steps against the static type.
|
|
365
|
+
* Members are lazy (getters / closures) so recursive types terminate. */
|
|
366
|
+
function probeOf(t, steps) {
|
|
367
|
+
const probe = {};
|
|
368
|
+
Object.defineProperty(probe, "__vtSubtype", { value: t, enumerable: false });
|
|
369
|
+
Object.defineProperty(probe, "__vtSteps", { value: steps, enumerable: false });
|
|
370
|
+
let ct = t;
|
|
371
|
+
while (ct.type === "Recursive")
|
|
372
|
+
ct = ct.node;
|
|
373
|
+
if (ct.type === "Struct") {
|
|
374
|
+
for (const [name, ft] of Object.entries(ct.fields)) {
|
|
375
|
+
Object.defineProperty(probe, name, {
|
|
376
|
+
get: () => probeOf(ft, [...steps, { kind: "field", name }]),
|
|
377
|
+
enumerable: true,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
else if (ct.type === "Array") {
|
|
382
|
+
const elem = ct.value;
|
|
383
|
+
probe["item"] = (index) => probeOf(elem, [...steps, { kind: "index", index: BigInt(index) }]);
|
|
384
|
+
}
|
|
385
|
+
else if (ct.type === "Dict") {
|
|
386
|
+
const { key, value } = ct;
|
|
387
|
+
if (key.type === "String") {
|
|
388
|
+
probe["entry"] = (k) => probeOf(value, [...steps, { kind: "key", key: k }]);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
else if (isOptionType(ct)) {
|
|
392
|
+
const inner = ct.cases["some"];
|
|
393
|
+
probe["some"] = () => probeOf(inner, [...steps, { kind: "some" }]);
|
|
394
|
+
}
|
|
395
|
+
return probe;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Builds a scoped subtree handler for {@link ValueTree.Root}'s `at`
|
|
399
|
+
* option: navigate the root type with the typed probe, and receive the
|
|
400
|
+
* rebuilt SUBTREE at that path whenever an edit lands inside it.
|
|
401
|
+
*
|
|
402
|
+
* @typeParam T - The root value's East type
|
|
403
|
+
* @typeParam S - The subtree type at the accessor's path (inferred)
|
|
404
|
+
* @param root - The root value's East type (drives probe navigation)
|
|
405
|
+
* @param path - Accessor over the typed probe selecting the subtree
|
|
406
|
+
* @param onUpdate - Handler receiving the rebuilt subtree value
|
|
407
|
+
* @returns An opaque scope for the `at` option
|
|
408
|
+
*
|
|
409
|
+
* @example
|
|
410
|
+
* ```ts
|
|
411
|
+
* import { East, DictType, StringType, FloatType, NullType } from "@elaraai/east";
|
|
412
|
+
* import { ValueTree } from "@elaraai/east-ui";
|
|
413
|
+
*
|
|
414
|
+
* const RatesType = DictType(StringType, FloatType);
|
|
415
|
+
* const scope = ValueTree.at(RatesType, p => p.entry("base"),
|
|
416
|
+
* East.function([FloatType], NullType, (_$, _next) => null));
|
|
417
|
+
* ```
|
|
418
|
+
*/
|
|
419
|
+
function scopeAt(root, path, onUpdate) {
|
|
420
|
+
const target = path(probeOf(root, []));
|
|
421
|
+
if (target?.__vtSteps === undefined) {
|
|
422
|
+
throw new Error("ValueTree.at: the path accessor must return a probe position (e.g. p => p.machines)");
|
|
423
|
+
}
|
|
424
|
+
return { steps: target.__vtSteps, subtype: target.__vtSubtype, handler: onUpdate };
|
|
425
|
+
}
|
|
426
|
+
/** Walks the static type along scope steps (RecursiveType transparent);
|
|
427
|
+
* throws when a step does not fit the type — a scope built against a
|
|
428
|
+
* different root type than the tree's value. */
|
|
429
|
+
function navigateType(t, steps) {
|
|
430
|
+
let ct = t;
|
|
431
|
+
for (const st of steps) {
|
|
432
|
+
while (ct.type === "Recursive")
|
|
433
|
+
ct = ct.node;
|
|
434
|
+
if (st.kind === "field") {
|
|
435
|
+
const ft = ct.fields?.[st.name];
|
|
436
|
+
if (ct.type !== "Struct" || ft === undefined) {
|
|
437
|
+
throw new Error(`ValueTree.at: no struct field '${st.name}' at scope path`);
|
|
438
|
+
}
|
|
439
|
+
ct = ft;
|
|
440
|
+
}
|
|
441
|
+
else if (st.kind === "index") {
|
|
442
|
+
if (ct.type !== "Array")
|
|
443
|
+
throw new Error("ValueTree.at: 'item' step on a non-array at scope path");
|
|
444
|
+
ct = ct.value;
|
|
445
|
+
}
|
|
446
|
+
else if (st.kind === "key") {
|
|
447
|
+
if (ct.type !== "Dict")
|
|
448
|
+
throw new Error("ValueTree.at: 'entry' step on a non-dict at scope path");
|
|
449
|
+
ct = ct.value;
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
if (!isOptionType(ct))
|
|
453
|
+
throw new Error("ValueTree.at: 'some' step on a non-option at scope path");
|
|
454
|
+
ct = ct.cases["some"];
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return ct;
|
|
458
|
+
}
|
|
459
|
+
/** Navigates a value expression along scope steps (RecursiveType
|
|
460
|
+
* unwrapped as consumed; the terminal value keeps its wrapper). */
|
|
461
|
+
function navigateExpr(root, t, steps) {
|
|
462
|
+
let e = root;
|
|
463
|
+
let ct = t;
|
|
464
|
+
for (const st of steps) {
|
|
465
|
+
while (ct.type === "Recursive") {
|
|
466
|
+
e = e.unwrap();
|
|
467
|
+
ct = ct.node;
|
|
468
|
+
}
|
|
469
|
+
if (st.kind === "field") {
|
|
470
|
+
e = e[st.name];
|
|
471
|
+
ct = ct.fields[st.name];
|
|
472
|
+
}
|
|
473
|
+
else if (st.kind === "index") {
|
|
474
|
+
e = e.get(st.index);
|
|
475
|
+
ct = ct.value;
|
|
476
|
+
}
|
|
477
|
+
else if (st.kind === "key") {
|
|
478
|
+
e = e.get(st.key);
|
|
479
|
+
ct = ct.value;
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
e = e.unwrap("some");
|
|
483
|
+
ct = ct.cases["some"];
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return e;
|
|
487
|
+
}
|
|
488
|
+
/** Runtime prefix-match condition for a scope against an edit path.
|
|
489
|
+
* `extra` is 1 for container ops (insert/remove: the affected element's
|
|
490
|
+
* container must sit strictly below the scope root — removing the
|
|
491
|
+
* scope's own subtree dispatches to its PARENT scope, whose value
|
|
492
|
+
* still exists) and 0 for edit/tag. */
|
|
493
|
+
function scopeMatch(path, scope, extra) {
|
|
494
|
+
const p = path;
|
|
495
|
+
let cond = p.size().greaterEqual(BigInt(scope.steps.length + extra));
|
|
496
|
+
scope.steps.forEach((st, i) => {
|
|
497
|
+
const stepAt = () => p.get(BigInt(i));
|
|
498
|
+
if (st.kind === "field") {
|
|
499
|
+
cond = cond.and(() => stepAt().hasTag("field")
|
|
500
|
+
.and(() => stepAt().unwrap("field").equals(st.name)));
|
|
501
|
+
}
|
|
502
|
+
else if (st.kind === "index") {
|
|
503
|
+
cond = cond.and(() => stepAt().hasTag("index")
|
|
504
|
+
.and(() => stepAt().unwrap("index").equals(st.index)));
|
|
505
|
+
}
|
|
506
|
+
else if (st.kind === "key") {
|
|
507
|
+
cond = cond.and(() => stepAt().hasTag("key")
|
|
508
|
+
.and(() => stepAt().unwrap("key").equals(st.key)));
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
cond = cond.and(() => stepAt().hasTag("some"));
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
return cond;
|
|
515
|
+
}
|
|
516
|
+
/** Invokes a user handler expression (`FunctionType` value) with one arg. */
|
|
517
|
+
function callHandler(handler, arg) {
|
|
518
|
+
const fn = East.value(handler);
|
|
519
|
+
return fn.call(arg);
|
|
520
|
+
}
|
|
521
|
+
// ============================================================================
|
|
522
|
+
// Factory
|
|
523
|
+
// ============================================================================
|
|
524
|
+
/**
|
|
525
|
+
* Creates a ValueTree — the editable tree of any East value.
|
|
526
|
+
*
|
|
527
|
+
* @typeParam T - The value's East type (captured statically)
|
|
528
|
+
* @param value - The value to materialize (any East type)
|
|
529
|
+
* @param options - Value handlers, raw path callbacks + style
|
|
530
|
+
* ({@link ValueTreeOptions})
|
|
531
|
+
* @returns An East expression of `UIComponentType`
|
|
532
|
+
*
|
|
533
|
+
* @example
|
|
534
|
+
* ```ts
|
|
535
|
+
* import { East, DictType, StringType, StructType, FloatType } from "@elaraai/east";
|
|
536
|
+
* import { ValueTree, UIComponentType } from "@elaraai/east-ui";
|
|
537
|
+
*
|
|
538
|
+
* const ConfigType = DictType(StringType, StructType({ rate: FloatType, label: StringType }));
|
|
539
|
+
* const view = East.function([ConfigType], UIComponentType, (_$, config) =>
|
|
540
|
+
* ValueTree.Root(config),
|
|
541
|
+
* );
|
|
542
|
+
* ```
|
|
543
|
+
*/
|
|
544
|
+
function createValueTree(value, options) {
|
|
545
|
+
const v = East.value(value);
|
|
546
|
+
const t = Expr.type(v);
|
|
547
|
+
const root = nodeOf(t, v, 0, { budget: MAX_MATERIALIZED_NODES });
|
|
548
|
+
const scopes = options?.at ?? [];
|
|
549
|
+
for (const scope of scopes) {
|
|
550
|
+
// Fail at authoring time when a scope was built against a
|
|
551
|
+
// different root type than the tree's value.
|
|
552
|
+
navigateType(t, scope.steps);
|
|
553
|
+
}
|
|
554
|
+
const wantsGenerated = options?.onUpdate !== undefined || scopes.length > 0;
|
|
555
|
+
/** Deepest-first dispatch: scoped handlers, then `onUpdate`. */
|
|
556
|
+
const emitDispatch = ($, path, newRoot, extra) => {
|
|
557
|
+
const ordered = [...scopes].sort((a, b) => b.steps.length - a.steps.length);
|
|
558
|
+
const emit = ($b, remaining) => {
|
|
559
|
+
const scope = remaining[0];
|
|
560
|
+
if (scope === undefined) {
|
|
561
|
+
if (options?.onUpdate !== undefined) {
|
|
562
|
+
$b(callHandler(options.onUpdate, newRoot));
|
|
563
|
+
}
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
const subtree = navigateExpr(newRoot, t, scope.steps);
|
|
567
|
+
$b.if(scopeMatch(path, scope, extra), ($t) => {
|
|
568
|
+
$t(callHandler(scope.handler, subtree));
|
|
569
|
+
}).else(($e) => {
|
|
570
|
+
emit($e, remaining.slice(1));
|
|
571
|
+
});
|
|
572
|
+
};
|
|
573
|
+
emit($, ordered);
|
|
574
|
+
};
|
|
575
|
+
// Generated wrappers are ASYNC: user handlers routinely write State
|
|
576
|
+
// (an async platform effect), and sync handlers are subtypes.
|
|
577
|
+
const genOnEdit = () => East.asyncFunction([ValueTreePathType, ValueTreeLeafType], NullType, ($, p, leaf) => {
|
|
578
|
+
const next = $.let(rebuild(t, v, p, 0, 0, { budget: MAX_REBUILD_NODES }, { kind: "edit", leaf }));
|
|
579
|
+
emitDispatch($, p, next, 0);
|
|
580
|
+
});
|
|
581
|
+
const genOnInsert = () => East.asyncFunction([ValueTreePathType], NullType, ($, p) => {
|
|
582
|
+
const next = $.let(rebuild(t, v, p, 0, 0, { budget: MAX_REBUILD_NODES }, { kind: "insert" }));
|
|
583
|
+
emitDispatch($, p, next, 1);
|
|
584
|
+
});
|
|
585
|
+
const genOnRemove = () => East.asyncFunction([ValueTreePathType], NullType, ($, p) => {
|
|
586
|
+
const next = $.let(rebuild(t, v, p, 0, 0, { budget: MAX_REBUILD_NODES }, { kind: "remove" }));
|
|
587
|
+
emitDispatch($, p, next, 1);
|
|
588
|
+
});
|
|
589
|
+
const genOnTag = () => East.asyncFunction([ValueTreePathType, StringType], NullType, ($, p, tag) => {
|
|
590
|
+
const next = $.let(rebuild(t, v, p, 0, 0, { budget: MAX_REBUILD_NODES }, { kind: "tag", tag }));
|
|
591
|
+
emitDispatch($, p, next, 0);
|
|
592
|
+
});
|
|
593
|
+
const onEdit = options?.onEdit ?? (wantsGenerated ? genOnEdit() : undefined);
|
|
594
|
+
const onInsert = options?.onInsert ?? (wantsGenerated ? genOnInsert() : undefined);
|
|
595
|
+
const onRemove = options?.onRemove ?? (wantsGenerated ? genOnRemove() : undefined);
|
|
596
|
+
const onTag = options?.onTag ?? (wantsGenerated ? genOnTag() : undefined);
|
|
597
|
+
const style = options?.style;
|
|
598
|
+
// Sync raw callbacks widen into the async slots at runtime
|
|
599
|
+
// (FunctionType <: AsyncFunctionType) — the casts only quiet TS.
|
|
600
|
+
return East.value(variant("ValueTree", {
|
|
601
|
+
root,
|
|
602
|
+
onEdit: onEdit !== undefined ? some(onEdit) : none,
|
|
603
|
+
onInsert: onInsert !== undefined ? some(onInsert) : none,
|
|
604
|
+
onRemove: onRemove !== undefined ? some(onRemove) : none,
|
|
605
|
+
onTag: onTag !== undefined ? some(onTag) : none,
|
|
606
|
+
style: style !== undefined ? some(East.value({
|
|
607
|
+
height: style.height !== undefined ? some(style.height) : none,
|
|
608
|
+
maxHeight: style.maxHeight !== undefined ? some(style.maxHeight) : none,
|
|
609
|
+
}, ValueTreeStyleType)) : none,
|
|
610
|
+
}), UIComponentType);
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* A host-side zero value for any East data type — the default payload
|
|
614
|
+
* for inserted elements, toggled options and switched variant cases.
|
|
615
|
+
*
|
|
616
|
+
* @param t - The East type to zero
|
|
617
|
+
* @returns The TypeScript `ValueTypeOf` zero value
|
|
618
|
+
* @throws When the type has no default (functions; recursion below the
|
|
619
|
+
* top level)
|
|
620
|
+
*
|
|
621
|
+
* @remarks
|
|
622
|
+
* Delegates to east's {@link defaultValue} (option variants default to
|
|
623
|
+
* `none` — their sorted first case), unrolling a top-level
|
|
624
|
+
* RecursiveType wrapper first.
|
|
625
|
+
*/
|
|
626
|
+
function zeroValue(t) {
|
|
627
|
+
let ct = t;
|
|
628
|
+
while (ct.type === "Recursive")
|
|
629
|
+
ct = ct.node;
|
|
630
|
+
return defaultValue(ct);
|
|
631
|
+
}
|
|
632
|
+
// ============================================================================
|
|
633
|
+
// Namespace export
|
|
634
|
+
// ============================================================================
|
|
635
|
+
/**
|
|
636
|
+
* ValueTree component namespace.
|
|
637
|
+
*
|
|
638
|
+
* @remarks
|
|
639
|
+
* `ValueTree.Root(value, options)` materializes any East value into the
|
|
640
|
+
* editable tree; `ValueTree.at(Type, p => …, handler)` builds a scoped
|
|
641
|
+
* subtree handler for the `at` option; `ValueTree.zero(Type)` builds
|
|
642
|
+
* the host-side default element for raw `onInsert` handlers.
|
|
643
|
+
*/
|
|
644
|
+
export const ValueTree = {
|
|
645
|
+
/**
|
|
646
|
+
* Creates a ValueTree — the editable tree of any East value.
|
|
647
|
+
*
|
|
648
|
+
* @param value - The value to materialize (any East type)
|
|
649
|
+
* @param options - Value handlers, raw callbacks + style
|
|
650
|
+
* ({@link ValueTreeOptions})
|
|
651
|
+
* @returns An East expression of `UIComponentType`
|
|
652
|
+
*/
|
|
653
|
+
Root: createValueTree,
|
|
654
|
+
/**
|
|
655
|
+
* Builds a scoped subtree handler for the `at` option — the deepest
|
|
656
|
+
* scope whose path prefixes an edit's path receives the rebuilt
|
|
657
|
+
* subtree; unmatched edits bubble to `onUpdate`.
|
|
658
|
+
*/
|
|
659
|
+
at: scopeAt,
|
|
660
|
+
/** Host-side zero value for any East type (for raw `onInsert` handlers). */
|
|
661
|
+
zero: zeroValue,
|
|
662
|
+
/**
|
|
663
|
+
* Host-side materializer — a DECODED value + its type into the node tree
|
|
664
|
+
* the renderer consumes (for callers holding a value, not an `Expr`; the
|
|
665
|
+
* East-side {@link ValueTree.Root} materializes an expression instead).
|
|
666
|
+
*/
|
|
667
|
+
materialize,
|
|
668
|
+
/**
|
|
669
|
+
* Host-side edit application — apply one reported ValueTree edit (leaf /
|
|
670
|
+
* insert / remove / tag) to a decoded value, returning the new value.
|
|
671
|
+
*/
|
|
672
|
+
applyEdit,
|
|
673
|
+
Types: {
|
|
674
|
+
/** The full ValueTree payload. */
|
|
675
|
+
Root: ValueTreeRootType,
|
|
676
|
+
/** The materialized node tree. */
|
|
677
|
+
Node: ValueTreeNodeType,
|
|
678
|
+
/** A node path. */
|
|
679
|
+
Path: ValueTreePathType,
|
|
680
|
+
/** One path step. */
|
|
681
|
+
Step: ValueTreeStepType,
|
|
682
|
+
/** A primitive leaf value / leaf edit. */
|
|
683
|
+
Leaf: ValueTreeLeafType,
|
|
684
|
+
/** Container style. */
|
|
685
|
+
Style: ValueTreeStyleType,
|
|
686
|
+
},
|
|
687
|
+
};
|
|
688
|
+
//# sourceMappingURL=index.js.map
|