@elaraai/e3-ui 1.0.102 → 1.0.104
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/allocation/functions.d.ts +2 -1
- package/dist/src/allocation/functions.d.ts.map +1 -1
- package/dist/src/allocation/functions.js +3 -2
- package/dist/src/allocation/functions.js.map +1 -1
- package/dist/src/allocation/index.d.ts +120 -1
- package/dist/src/allocation/index.d.ts.map +1 -1
- package/dist/src/allocation/index.js +24 -4
- package/dist/src/allocation/index.js.map +1 -1
- package/dist/src/bind/data-indexed.d.ts +25 -4
- package/dist/src/bind/data-indexed.d.ts.map +1 -1
- package/dist/src/bind/data-indexed.js +58 -7
- package/dist/src/bind/data-indexed.js.map +1 -1
- package/dist/src/bind/data.d.ts +12 -14
- package/dist/src/bind/data.d.ts.map +1 -1
- package/dist/src/bind/data.js +11 -11
- package/dist/src/bind/data.js.map +1 -1
- package/dist/src/bind/func.d.ts +7 -2
- package/dist/src/bind/func.d.ts.map +1 -1
- package/dist/src/bind/func.js +10 -3
- package/dist/src/bind/func.js.map +1 -1
- package/dist/src/bind/record.d.ts +6 -6
- package/dist/src/bind/record.d.ts.map +1 -1
- package/dist/src/bind/record.js.map +1 -1
- package/dist/src/calendar/index.d.ts +148 -1
- package/dist/src/calendar/index.d.ts.map +1 -1
- package/dist/src/calendar/index.js +55 -5
- package/dist/src/calendar/index.js.map +1 -1
- package/dist/src/decision/bind.d.ts.map +1 -1
- package/dist/src/decision/index.d.ts +218 -218
- package/dist/src/decision/index.d.ts.map +1 -1
- package/dist/src/decision/types.d.ts.map +1 -1
- package/dist/src/flowchart/canvas.d.ts +32 -14
- package/dist/src/flowchart/canvas.d.ts.map +1 -1
- package/dist/src/flowchart/canvas.js +21 -6
- package/dist/src/flowchart/canvas.js.map +1 -1
- package/dist/src/flowchart/details.d.ts +469 -0
- package/dist/src/flowchart/details.d.ts.map +1 -0
- package/dist/src/flowchart/details.js +1035 -0
- package/dist/src/flowchart/details.js.map +1 -0
- package/dist/src/flowchart/flowchart.d.ts +16 -72
- package/dist/src/flowchart/flowchart.d.ts.map +1 -1
- package/dist/src/flowchart/flowchart.js +10 -1
- package/dist/src/flowchart/flowchart.js.map +1 -1
- package/dist/src/flowchart/index.d.ts +282 -251
- package/dist/src/flowchart/index.d.ts.map +1 -1
- package/dist/src/flowchart/index.js +234 -220
- package/dist/src/flowchart/index.js.map +1 -1
- package/dist/src/flowchart/kinds.d.ts +438 -0
- package/dist/src/flowchart/kinds.d.ts.map +1 -0
- package/dist/src/flowchart/kinds.js +799 -0
- package/dist/src/flowchart/kinds.js.map +1 -0
- package/dist/src/flowchart/library.d.ts +155 -740
- package/dist/src/flowchart/library.d.ts.map +1 -1
- package/dist/src/flowchart/library.js +191 -673
- package/dist/src/flowchart/library.js.map +1 -1
- package/dist/src/flowchart/payload.d.ts +3654 -7845
- package/dist/src/flowchart/payload.d.ts.map +1 -1
- package/dist/src/flowchart/payload.js +331 -469
- package/dist/src/flowchart/payload.js.map +1 -1
- package/dist/src/flowchart/templates.d.ts +657 -0
- package/dist/src/flowchart/templates.d.ts.map +1 -0
- package/dist/src/flowchart/templates.js +658 -0
- package/dist/src/flowchart/templates.js.map +1 -0
- package/dist/src/flowchart/types.d.ts +2258 -214
- package/dist/src/flowchart/types.d.ts.map +1 -1
- package/dist/src/flowchart/types.js +374 -124
- package/dist/src/flowchart/types.js.map +1 -1
- package/dist/src/index.d.ts +13 -15
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +24 -29
- package/dist/src/index.js.map +1 -1
- package/dist/src/internal.d.ts +5 -7
- package/dist/src/internal.d.ts.map +1 -1
- package/dist/src/internal.js +12 -19
- package/dist/src/internal.js.map +1 -1
- package/dist/src/ontology/index.d.ts +44 -15
- package/dist/src/ontology/index.d.ts.map +1 -1
- package/dist/src/ontology/index.js +19 -11
- package/dist/src/ontology/index.js.map +1 -1
- package/dist/src/plan/builders.d.ts +26 -26
- package/dist/src/plan/builders.d.ts.map +1 -1
- package/dist/src/plan/builders.js.map +1 -1
- package/dist/src/plan/event-rows.d.ts +59 -58
- package/dist/src/plan/event-rows.d.ts.map +1 -1
- package/dist/src/plan/event-rows.js +6 -4
- package/dist/src/plan/event-rows.js.map +1 -1
- package/dist/src/plan/factories.js.map +1 -1
- package/dist/src/plan/ir.d.ts +1 -0
- package/dist/src/plan/ir.d.ts.map +1 -1
- package/dist/src/plan/library.d.ts.map +1 -1
- package/dist/src/plan/nested-checks.js.map +1 -1
- package/dist/src/plan/plan.d.ts +567 -423
- package/dist/src/plan/plan.d.ts.map +1 -1
- package/dist/src/plan/plan.js +67 -3
- package/dist/src/plan/plan.js.map +1 -1
- package/dist/src/plan/series.d.ts.map +1 -1
- package/dist/src/plan/series.js.map +1 -1
- package/dist/src/plan/types.d.ts +173 -172
- package/dist/src/plan/types.d.ts.map +1 -1
- package/dist/src/query/builder.d.ts +368 -321
- package/dist/src/query/builder.d.ts.map +1 -1
- package/dist/src/query/builder.js +12 -1
- package/dist/src/query/builder.js.map +1 -1
- package/dist/src/query/index.d.ts +5 -1
- package/dist/src/query/index.d.ts.map +1 -1
- package/dist/src/query/index.js +3 -1
- package/dist/src/query/index.js.map +1 -1
- package/dist/src/query/library.d.ts +6 -6
- package/dist/src/query/queries.d.ts +3 -3
- package/dist/src/roster/fit.d.ts +5 -2
- package/dist/src/roster/fit.d.ts.map +1 -1
- package/dist/src/roster/fit.js +5 -3
- package/dist/src/roster/fit.js.map +1 -1
- package/dist/src/roster/index.d.ts +103 -2
- package/dist/src/roster/index.d.ts.map +1 -1
- package/dist/src/roster/index.js +45 -6
- package/dist/src/roster/index.js.map +1 -1
- package/dist/src/runtime/index.d.ts +1 -3
- package/dist/src/runtime/index.d.ts.map +1 -1
- package/dist/src/runtime/index.js +1 -3
- package/dist/src/runtime/index.js.map +1 -1
- package/dist/src/schedule/fields.js.map +1 -1
- package/dist/src/schedule/library.js.map +1 -1
- package/dist/src/schedule/record-window.d.ts +27 -26
- package/dist/src/schedule/record-window.d.ts.map +1 -1
- package/dist/src/schedule/types.d.ts +196 -196
- package/dist/src/schematic/index.d.ts +1 -1
- package/dist/src/schematic/index.d.ts.map +1 -1
- package/dist/src/schematic/index.js +1 -1
- package/dist/src/schematic/index.js.map +1 -1
- package/dist/src/schematic/kinds.d.ts +4 -24
- package/dist/src/schematic/kinds.d.ts.map +1 -1
- package/dist/src/schematic/kinds.js +8 -212
- package/dist/src/schematic/kinds.js.map +1 -1
- package/dist/src/schematic/library.d.ts.map +1 -1
- package/dist/src/schematic/payload.d.ts +1688 -1549
- package/dist/src/schematic/payload.d.ts.map +1 -1
- package/dist/src/schematic/payload.js +38 -1
- package/dist/src/schematic/payload.js.map +1 -1
- package/dist/src/schematic/shapes.d.ts.map +1 -1
- package/dist/src/schematic/types.d.ts +978 -978
- package/dist/src/schematic/types.d.ts.map +1 -1
- package/dist/src/schematic/types.js +4 -36
- package/dist/src/schematic/types.js.map +1 -1
- package/dist/src/schematic/window.d.ts +9 -42
- package/dist/src/schematic/window.d.ts.map +1 -1
- package/dist/src/schematic/window.js +10 -115
- package/dist/src/schematic/window.js.map +1 -1
- package/dist/src/sheet/actions.d.ts +388 -0
- package/dist/src/sheet/actions.d.ts.map +1 -0
- package/dist/src/sheet/actions.js +594 -0
- package/dist/src/sheet/actions.js.map +1 -0
- package/dist/src/sheet/bridge.d.ts +12 -1
- package/dist/src/sheet/bridge.d.ts.map +1 -1
- package/dist/src/sheet/bridge.js +30 -6
- package/dist/src/sheet/bridge.js.map +1 -1
- package/dist/src/sheet/columns.d.ts +5 -2
- package/dist/src/sheet/columns.d.ts.map +1 -1
- package/dist/src/sheet/columns.js.map +1 -1
- package/dist/src/sheet/draft-bridge.d.ts +9 -4
- package/dist/src/sheet/draft-bridge.d.ts.map +1 -1
- package/dist/src/sheet/draft-bridge.js +22 -7
- package/dist/src/sheet/draft-bridge.js.map +1 -1
- package/dist/src/sheet/editing-types.d.ts +71 -71
- package/dist/src/sheet/fields.d.ts +13 -0
- package/dist/src/sheet/fields.d.ts.map +1 -1
- package/dist/src/sheet/fields.js +3 -0
- package/dist/src/sheet/fields.js.map +1 -1
- package/dist/src/sheet/group.js.map +1 -1
- package/dist/src/sheet/index.d.ts +32 -2
- package/dist/src/sheet/index.d.ts.map +1 -1
- package/dist/src/sheet/index.js +18 -3
- package/dist/src/sheet/index.js.map +1 -1
- package/dist/src/sheet/library.d.ts +8 -0
- package/dist/src/sheet/library.d.ts.map +1 -1
- package/dist/src/sheet/library.js.map +1 -1
- package/dist/src/sheet/link.d.ts.map +1 -1
- package/dist/src/sheet/record.js.map +1 -1
- package/dist/src/sheet/root.d.ts +23 -1
- package/dist/src/sheet/root.d.ts.map +1 -1
- package/dist/src/sheet/root.js +40 -5
- package/dist/src/sheet/root.js.map +1 -1
- package/dist/src/sheet/sheet.d.ts +9332 -5621
- package/dist/src/sheet/sheet.d.ts.map +1 -1
- package/dist/src/sheet/sheet.js +41 -2
- package/dist/src/sheet/sheet.js.map +1 -1
- package/dist/src/sheet/templates.d.ts +4 -0
- package/dist/src/sheet/templates.d.ts.map +1 -1
- package/dist/src/sheet/types.d.ts +13020 -5839
- package/dist/src/sheet/types.d.ts.map +1 -1
- package/dist/src/sheet/types.js +114 -2
- package/dist/src/sheet/types.js.map +1 -1
- package/dist/src/studio/builder.d.ts +6 -6
- package/dist/src/studio/library.d.ts +9 -9
- package/dist/src/studio/pages.d.ts +6 -6
- package/dist/src/studio/publish.d.ts +3 -3
- package/dist/src/utils/derive.d.ts +1 -1
- package/dist/src/utils/derive.js +1 -1
- package/dist/src/utils/derive.js.map +1 -1
- package/dist/src/utils/key-visibility.d.ts +1 -1
- package/dist/src/utils/record-kind.d.ts +625 -0
- package/dist/src/utils/record-kind.d.ts.map +1 -0
- package/dist/src/utils/record-kind.js +450 -0
- package/dist/src/utils/record-kind.js.map +1 -0
- package/dist/src/utils/views.d.ts +55 -0
- package/dist/src/utils/views.d.ts.map +1 -0
- package/dist/src/utils/views.js +72 -0
- package/dist/src/utils/views.js.map +1 -0
- package/dist/src/utils/vocabulary.d.ts +48 -0
- package/dist/src/utils/vocabulary.d.ts.map +1 -0
- package/dist/src/utils/vocabulary.js +47 -0
- package/dist/src/utils/vocabulary.js.map +1 -0
- package/dist/test/allocation/allocation.examples.d.ts +3 -1
- package/dist/test/allocation/allocation.examples.d.ts.map +1 -1
- package/dist/test/allocation/allocation.examples.js +81 -2
- package/dist/test/allocation/allocation.examples.js.map +1 -1
- package/dist/test/bind/data/data.examples.d.ts +1 -1
- package/dist/test/bind/func/func.examples.d.ts +19 -1
- package/dist/test/bind/func/func.examples.d.ts.map +1 -1
- package/dist/test/bind/func/func.examples.js +36 -1
- package/dist/test/bind/func/func.examples.js.map +1 -1
- package/dist/test/calendar/calendar.examples.d.ts +2 -0
- package/dist/test/calendar/calendar.examples.d.ts.map +1 -1
- package/dist/test/calendar/calendar.examples.js +80 -2
- package/dist/test/calendar/calendar.examples.js.map +1 -1
- package/dist/test/decision/queue.examples.d.ts +1 -0
- package/dist/test/decision/queue.examples.d.ts.map +1 -1
- package/dist/test/decision/queue.examples.js +12 -0
- package/dist/test/decision/queue.examples.js.map +1 -1
- package/dist/test/examples-package.d.ts.map +1 -1
- package/dist/test/examples-package.js +5 -4
- package/dist/test/examples-package.js.map +1 -1
- package/dist/test/flowchart/flowchart.examples.d.ts +1250 -4605
- package/dist/test/flowchart/flowchart.examples.d.ts.map +1 -1
- package/dist/test/flowchart/flowchart.examples.js +861 -512
- package/dist/test/flowchart/flowchart.examples.js.map +1 -1
- package/dist/test/ontology/ontology.examples.d.ts +2 -3
- package/dist/test/ontology/ontology.examples.d.ts.map +1 -1
- package/dist/test/ontology/ontology.examples.js +32 -21
- package/dist/test/ontology/ontology.examples.js.map +1 -1
- package/dist/test/plan/plan-events.examples.d.ts +1 -0
- package/dist/test/plan/plan-events.examples.d.ts.map +1 -1
- package/dist/test/plan/plan-events.examples.js +44 -0
- package/dist/test/plan/plan-events.examples.js.map +1 -1
- package/dist/test/plan/plan-nested.examples.d.ts +15 -15
- package/dist/test/plan/plan-nested.examples.d.ts.map +1 -1
- package/dist/test/plan/plan-nested.examples.js +5 -5
- package/dist/test/plan/plan-nested.examples.js.map +1 -1
- package/dist/test/plan/plan-records.examples.d.ts.map +1 -1
- package/dist/test/plan/plan-records.examples.js +1 -1
- package/dist/test/plan/plan-records.examples.js.map +1 -1
- package/dist/test/plan/plan.examples.js.map +1 -1
- package/dist/test/query/query.examples.d.ts +2 -0
- package/dist/test/query/query.examples.d.ts.map +1 -1
- package/dist/test/query/query.examples.js +37 -1
- package/dist/test/query/query.examples.js.map +1 -1
- package/dist/test/roster/domain.examples.d.ts.map +1 -1
- package/dist/test/roster/roster.examples.d.ts +2 -0
- package/dist/test/roster/roster.examples.d.ts.map +1 -1
- package/dist/test/roster/roster.examples.js +76 -15
- package/dist/test/roster/roster.examples.js.map +1 -1
- package/dist/test/schematic/schematic-records.examples.d.ts.map +1 -1
- package/dist/test/schematic/schematic-records.examples.js +6 -4
- package/dist/test/schematic/schematic-records.examples.js.map +1 -1
- package/dist/test/schematic/schematic.examples.d.ts +1 -0
- package/dist/test/schematic/schematic.examples.d.ts.map +1 -1
- package/dist/test/schematic/schematic.examples.js +144 -0
- package/dist/test/schematic/schematic.examples.js.map +1 -1
- package/dist/test/sheet/sheet-actions.examples.d.ts +1264 -0
- package/dist/test/sheet/sheet-actions.examples.d.ts.map +1 -0
- package/dist/test/sheet/sheet-actions.examples.js +394 -0
- package/dist/test/sheet/sheet-actions.examples.js.map +1 -0
- package/dist/test/sheet/sheet-records.examples.d.ts +1 -1
- package/dist/test/sheet/sheet.examples.d.ts +1 -0
- package/dist/test/sheet/sheet.examples.d.ts.map +1 -1
- package/dist/test/sheet/sheet.examples.js +77 -0
- package/dist/test/sheet/sheet.examples.js.map +1 -1
- package/dist/test/studio/studio.examples.d.ts +1 -0
- package/dist/test/studio/studio.examples.d.ts.map +1 -1
- package/dist/test/studio/studio.examples.js +18 -8
- package/dist/test/studio/studio.examples.js.map +1 -1
- package/package.json +14 -13
- package/dist/src/diff/index.d.ts +0 -407
- package/dist/src/diff/index.d.ts.map +0 -1
- package/dist/src/diff/index.js +0 -203
- package/dist/src/diff/index.js.map +0 -1
- package/dist/src/experiment/index.d.ts +0 -1033
- package/dist/src/experiment/index.d.ts.map +0 -1
- package/dist/src/experiment/index.js +0 -197
- package/dist/src/experiment/index.js.map +0 -1
- package/dist/src/experiment/types.d.ts +0 -1093
- package/dist/src/experiment/types.d.ts.map +0 -1
- package/dist/src/experiment/types.js +0 -423
- package/dist/src/experiment/types.js.map +0 -1
- package/dist/src/flowchart/over.d.ts +0 -227
- package/dist/src/flowchart/over.d.ts.map +0 -1
- package/dist/src/flowchart/over.js +0 -213
- package/dist/src/flowchart/over.js.map +0 -1
- package/dist/src/flowchart/patch.d.ts +0 -64
- package/dist/src/flowchart/patch.d.ts.map +0 -1
- package/dist/src/flowchart/patch.js +0 -67
- package/dist/src/flowchart/patch.js.map +0 -1
- package/dist/src/flowchart/values.d.ts +0 -271
- package/dist/src/flowchart/values.d.ts.map +0 -1
- package/dist/src/flowchart/values.js +0 -220
- package/dist/src/flowchart/values.js.map +0 -1
- package/dist/src/flowchart/window.d.ts +0 -306
- package/dist/src/flowchart/window.d.ts.map +0 -1
- package/dist/src/flowchart/window.js +0 -63
- package/dist/src/flowchart/window.js.map +0 -1
- package/dist/src/runtime/diff.d.ts +0 -36
- package/dist/src/runtime/diff.d.ts.map +0 -1
- package/dist/src/runtime/diff.js +0 -34
- package/dist/src/runtime/diff.js.map +0 -1
- package/dist/src/runtime/experiment.d.ts +0 -48
- package/dist/src/runtime/experiment.d.ts.map +0 -1
- package/dist/src/runtime/experiment.js +0 -10
- package/dist/src/runtime/experiment.js.map +0 -1
- package/dist/test/diff/diff.examples.d.ts +0 -313
- package/dist/test/diff/diff.examples.d.ts.map +0 -1
- package/dist/test/diff/diff.examples.js +0 -452
- package/dist/test/diff/diff.examples.js.map +0 -1
- package/dist/test/experiment/experiment.examples.d.ts +0 -1512
- package/dist/test/experiment/experiment.examples.d.ts.map +0 -1
- package/dist/test/experiment/experiment.examples.js +0 -609
- package/dist/test/experiment/experiment.examples.js.map +0 -1
|
@@ -0,0 +1,799 @@
|
|
|
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
|
+
* The Flowchart's kinds (#1500, `Flowchart Records Spec.md` §2, §3, R1–R4):
|
|
7
|
+
* `Flowchart.states`, `transitions`, `lanes` and `decisions`. Each takes a
|
|
8
|
+
* record of the app's and maps its fields to the Flowchart's roles, as the
|
|
9
|
+
* Schematic's kinds do (`schematic/kinds.ts`): the Flowchart stores nothing in
|
|
10
|
+
* a shape of its own, and no record of the app's holds a type of the
|
|
11
|
+
* Flowchart's.
|
|
12
|
+
*
|
|
13
|
+
* - **A role is a field of the row where a gesture writes it or it names
|
|
14
|
+
* another row's key** (R2): a state's `label`, `lane` and `order`; a
|
|
15
|
+
* transition's `from`, `to` and `decision`; a lane's `label` and `order`; a
|
|
16
|
+
* decision's `queue`. Each is checked against the row's type when the code
|
|
17
|
+
* compiles — a field of another type is a type error naming the fields that
|
|
18
|
+
* fit — and again at build, in the kind's words. Every other role is an
|
|
19
|
+
* accessor over the row and its key, reified once and called per row: a
|
|
20
|
+
* state's `members`; a transition's `kind` and `evidence`; a decision's
|
|
21
|
+
* `label`, `letter`, `owner` and `outcomes`.
|
|
22
|
+
* - **A gesture writes only its roles' fields** (R8): every other field of the
|
|
23
|
+
* row reads back as it was, and a new row takes a card's values, or its
|
|
24
|
+
* type's defaults.
|
|
25
|
+
* - **How a kind is bound follows how it is read** (R3): a state or a
|
|
26
|
+
* transition `Record.bind(record)`, editable — read whole, or through its
|
|
27
|
+
* source of the slice with `entries: Data.bindPaged(record)` beside it —
|
|
28
|
+
* `Data.bind(record)`, read only and whole, or `Data.bindPaged(record)`
|
|
29
|
+
* alone, read only, through its source; a lane or a decision `Record.bind`
|
|
30
|
+
* or `Data.bind`, read whole. Every other form is refused, naming the one to
|
|
31
|
+
* use.
|
|
32
|
+
*
|
|
33
|
+
* A kind closes its record behind East functions over beast2 bytes (§3, "What
|
|
34
|
+
* the payload carries") — its stored rows a read holds, each one's roles
|
|
35
|
+
* beside its bytes; a drafted row's roles; a gesture's write and a new row;
|
|
36
|
+
* the fields an author's card sets; the shared editing session over the
|
|
37
|
+
* record with its patch door; the record read by key; its history, its form,
|
|
38
|
+
* its own Details and the app's check — through the machinery the Schematic's
|
|
39
|
+
* kinds share (`utils/record-kind.ts`), so the payload is one type whatever
|
|
40
|
+
* the rows hold, and the renderer needs no row type.
|
|
41
|
+
*
|
|
42
|
+
* @packageDocumentation
|
|
43
|
+
*/
|
|
44
|
+
import { ArrayType, AsyncFunctionType, BlobType, East, Expr, FloatType, IntegerType, OptionType, PatchType, SetType, StringType, defaultValue, isTypeEqual, none, printType, some, variant, } from "@elaraai/east";
|
|
45
|
+
import { resolveRowSource } from "@elaraai/east-ui/internal";
|
|
46
|
+
import { RecordOutcomeType } from "../bind/record.js";
|
|
47
|
+
import { fieldOf } from "../schedule/fields.js";
|
|
48
|
+
import { checkEntries, typeText } from "../schedule/window.js";
|
|
49
|
+
import { accessorRole, called, fieldTypeOf, kindSourceRows, kindWholeRows, readOf, recordSide, roleField, rowWith, rowsReadOf, storedRows, valuesTypeOf, } from "../utils/record-kind.js";
|
|
50
|
+
import { isFlowchartDetails } from "./details.js";
|
|
51
|
+
import { checkedFlowchartTemplates } from "./templates.js";
|
|
52
|
+
import { FlowchartDecisionEntryType, FlowchartDecisionKindType, FlowchartDecisionRolesType, FlowchartEvidenceType, FlowchartLaneEntryType, FlowchartLaneKindType, FlowchartLaneRolesType, FlowchartLaneWriteType, FlowchartLinkKindType, FlowchartStateEntryType, FlowchartStateKindType, FlowchartStateRolesType, FlowchartStateWriteType, FlowchartTemplateType, FlowchartTransitionEntryType, FlowchartTransitionKindType, FlowchartTransitionRolesType, FlowchartTransitionWriteType, } from "./types.js";
|
|
53
|
+
/**
|
|
54
|
+
* Tells a kind's binding by its handle's East struct (`bind/sources.ts`'
|
|
55
|
+
* rule): a `page` field is `Data.bindPaged`'s, a `mutate` field
|
|
56
|
+
* `Record.bind`'s — checked for its patch door — and any other `Data.bind`'s.
|
|
57
|
+
*
|
|
58
|
+
* @param record - What the kind was given
|
|
59
|
+
* @param where - The kind, as a refusal names it
|
|
60
|
+
* @param noun - What its rows are, as a refusal names them (`states`)
|
|
61
|
+
* @param whole - Whether the kind is read whole only: a lane or a decision
|
|
62
|
+
* @returns The binding
|
|
63
|
+
* @throws {Error} Naming the forms to use: anything but a binding of a record of structs keyed by String — a paged read
|
|
64
|
+
* for a kind read whole, one paging an index's window, and one naming no snapshot among them — and a `Record.bind`
|
|
65
|
+
* without its patch door
|
|
66
|
+
*/
|
|
67
|
+
function boundOf(record, where, noun, whole) {
|
|
68
|
+
const forms = whole
|
|
69
|
+
? "bind Record.bind(record) to edit them, or Data.bind(record) to read them"
|
|
70
|
+
: "bind Record.bind(record) to edit them, Data.bind(record) to read them whole, or Data.bindPaged(record) to read them through their source";
|
|
71
|
+
const refuse = (why) => { throw new Error(`${where}: the ${noun} are a record of the app's${whole ? ", read whole" : ""}: ${forms}${why}`); };
|
|
72
|
+
if (!(record instanceof Expr))
|
|
73
|
+
return refuse("");
|
|
74
|
+
const type = Expr.type(record);
|
|
75
|
+
const fields = type.type === "Struct" ? type.fields : undefined;
|
|
76
|
+
if (fields === undefined)
|
|
77
|
+
return refuse(` — and this is ${printType(type)}`);
|
|
78
|
+
let form;
|
|
79
|
+
let recordType;
|
|
80
|
+
if (fields["page"] !== undefined) {
|
|
81
|
+
if (whole)
|
|
82
|
+
return refuse(" — and this is Data.bindPaged(record), which reads a record through its source of the slice");
|
|
83
|
+
form = "paged";
|
|
84
|
+
if (fields["indexes"] !== undefined) {
|
|
85
|
+
return refuse(` — and this paged read reads the record through its indexes: bind its own entries, Data.bindPaged(record), and name the indexed read ` +
|
|
86
|
+
`as the ${noun}' source of the slice`);
|
|
87
|
+
}
|
|
88
|
+
const resolved = resolveRowSource(record, where);
|
|
89
|
+
if (resolved.kind !== "paged")
|
|
90
|
+
return refuse(" — and this paged read pages an index's window: bind the record's own entries, Data.bindPaged(record)");
|
|
91
|
+
recordType = resolved.collectionType;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
form = fields["mutate"] !== undefined ? "record" : "data";
|
|
95
|
+
const read = fields["read"];
|
|
96
|
+
if (read === undefined || read.type !== "Function" || read.inputs.length !== 0 || fields["binding"] === undefined)
|
|
97
|
+
return refuse("");
|
|
98
|
+
recordType = read.output;
|
|
99
|
+
}
|
|
100
|
+
if (recordType.type !== "Dict" || !isTypeEqual(recordType.key, StringType) || recordType.value.type !== "Struct") {
|
|
101
|
+
throw new Error(`${where}: the ${noun}' record holds its rows by String key, each a struct — and this one holds ${typeText(recordType)}`);
|
|
102
|
+
}
|
|
103
|
+
const rowType = recordType.value;
|
|
104
|
+
// A paged read of the record's own entries, at the snapshot every read of it is at.
|
|
105
|
+
if (form === "paged")
|
|
106
|
+
checkEntries(record, StringType, rowType, `${where}: Data.bindPaged(record)`);
|
|
107
|
+
if (form === "record") {
|
|
108
|
+
// The patch door: the record's mutation.patch(), the mutation named `patch` taking the record's patch.
|
|
109
|
+
const commit = fields["commit"];
|
|
110
|
+
const door = commit?.type === "Struct" ? commit.fields["patch"] : undefined;
|
|
111
|
+
if (door === undefined || !isTypeEqual(door, AsyncFunctionType([StringType, PatchType(recordType)], RecordOutcomeType))) {
|
|
112
|
+
throw new Error(`${where}: the ${noun}' record is bound without its patch door — its record declares patch: mutation.patch(), ` +
|
|
113
|
+
"which Save writes each change through");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return { form, handle: record, rowType };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Checks a kind's `entries` where it declares them (R3): for a record bound to
|
|
120
|
+
* be edited, a paged read of its own entries.
|
|
121
|
+
*
|
|
122
|
+
* @param bound - The kind's binding
|
|
123
|
+
* @param entries - What the kind was given
|
|
124
|
+
* @param where - The kind, as a refusal names it
|
|
125
|
+
* @param noun - What its rows are, as a refusal names them
|
|
126
|
+
* @returns The paged read, checked; `undefined` for none
|
|
127
|
+
* @throws {Error} When a read-only binding is given `entries`, or they are no paged read of the record's own entries
|
|
128
|
+
*/
|
|
129
|
+
function entriesOf(bound, entries, where, noun) {
|
|
130
|
+
if (entries === undefined)
|
|
131
|
+
return undefined;
|
|
132
|
+
if (bound.form !== "record") {
|
|
133
|
+
throw new Error(`${where}: entries reads beside Record.bind: bind the ${noun} with Record.bind(record), ` +
|
|
134
|
+
"or bind Data.bindPaged(record) alone to read them through their source, read only");
|
|
135
|
+
}
|
|
136
|
+
return checkEntries(entries, StringType, bound.rowType, `${where}: \`entries\``);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* A state's or a transition's reads, as its binding and its source of the
|
|
140
|
+
* slice decide them (R3, R4): whole, or complete through its source; and the
|
|
141
|
+
* record read by key.
|
|
142
|
+
*
|
|
143
|
+
* @param bound - The kind's binding
|
|
144
|
+
* @param entries - Its `entries`, checked
|
|
145
|
+
* @param part - Its source of the slice, named after it, when the slice has one
|
|
146
|
+
* @param where - The kind, as a refusal names it
|
|
147
|
+
* @param noun - The kind, as the slice's source is named after it
|
|
148
|
+
* @param every - Whether, read through its source, it reads every row: under `narrowed="dim"` (R6)
|
|
149
|
+
* @returns `()` → its rows, by key, and the paged read its rows are read by key through, if any
|
|
150
|
+
* @throws {Error} Naming the form to use, for each binding its read does not take
|
|
151
|
+
*/
|
|
152
|
+
function readsOf(bound, entries, part, where, noun, every) {
|
|
153
|
+
const source = `give a source named ${noun}, Data.bindPaged(record, { indexes }) over the ${noun}' record`;
|
|
154
|
+
if (part === undefined) {
|
|
155
|
+
if (bound.form === "paged") {
|
|
156
|
+
throw new Error(`${where}: Data.bindPaged(record) reads the ${noun} through their source of the slice: ${source} — ` +
|
|
157
|
+
"or bind Data.bind(record) to read them whole");
|
|
158
|
+
}
|
|
159
|
+
if (entries !== undefined)
|
|
160
|
+
throw new Error(`${where}: the ${noun} are read through their source of the slice: ${source}, or drop entries`);
|
|
161
|
+
return { rows: kindWholeRows(bound.handle, bound.rowType), byKey: undefined };
|
|
162
|
+
}
|
|
163
|
+
if (bound.form === "data") {
|
|
164
|
+
throw new Error(`${where}: Data.bind(record) reads the ${noun} whole, and the slice's source "${noun}" reads them through it — ` +
|
|
165
|
+
"bind Data.bindPaged(record) to read them through their source, read only, or Record.bind(record) with entries: Data.bindPaged(record) to edit them");
|
|
166
|
+
}
|
|
167
|
+
if (bound.form === "record" && entries === undefined) {
|
|
168
|
+
throw new Error(`${where}: the slice's source "${noun}" reads only its narrowing, and a row is read by its key — the inspector's, ` +
|
|
169
|
+
`a gesture's original and Save's confirmation: give entries: Data.bindPaged(record) beside Record.bind, the ${noun}' record's own entries`);
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
rows: kindSourceRows(part, noun, bound.rowType, where, every, `name it "${noun}", over the ${noun}' own record`),
|
|
173
|
+
byKey: bound.form === "paged" ? bound.handle : entries,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Checks a text role: a String field, or an Option of one.
|
|
178
|
+
*
|
|
179
|
+
* @param rowType - The kind's row type
|
|
180
|
+
* @param where - The kind, as a refusal names it
|
|
181
|
+
* @param role - The role, as the refusal names it
|
|
182
|
+
* @param name - What it was given
|
|
183
|
+
* @returns The field, checked
|
|
184
|
+
* @throws {Error} When it names no field of the row, or one of another type
|
|
185
|
+
*/
|
|
186
|
+
function textField(rowType, where, role, name) {
|
|
187
|
+
if (typeof name !== "string")
|
|
188
|
+
throw new Error(`${where}: \`${role}\` names a String field of the row, or an Option of one`);
|
|
189
|
+
const type = fieldTypeOf(rowType, where)(role, name);
|
|
190
|
+
if (isTypeEqual(type, StringType))
|
|
191
|
+
return { field: name, optional: false };
|
|
192
|
+
if (isTypeEqual(type, OptionType(StringType)))
|
|
193
|
+
return { field: name, optional: true };
|
|
194
|
+
throw new Error(`${where}: \`${role}\` names a String field, or an Option of one — "${name}" holds ${typeText(type)}`);
|
|
195
|
+
}
|
|
196
|
+
/** A text field's value as a String: an Option field holding none is empty. */
|
|
197
|
+
function textOf(row, text) {
|
|
198
|
+
const value = fieldOf(row, text.field);
|
|
199
|
+
return text.optional
|
|
200
|
+
? value.match({ none: (_$) => East.value("", StringType), some: (_$, t) => t })
|
|
201
|
+
: value;
|
|
202
|
+
}
|
|
203
|
+
/** A String written to a text field: into an Option field, an empty one is none. */
|
|
204
|
+
function textValue(text, value) {
|
|
205
|
+
if (!text.optional)
|
|
206
|
+
return value;
|
|
207
|
+
return East.equal(value, "").ifElse((_$) => East.value(none, OptionType(StringType)), (_$) => East.value(some(value), OptionType(StringType)));
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* A field written from a gesture's Option, `none` keeping it as it was.
|
|
211
|
+
*
|
|
212
|
+
* @param given - The gesture's value for the field
|
|
213
|
+
* @param source - The row it is written over
|
|
214
|
+
* @param field - The field
|
|
215
|
+
* @param value - What a given value writes
|
|
216
|
+
* @returns The field's value
|
|
217
|
+
*/
|
|
218
|
+
function kept(given, source, field, value) {
|
|
219
|
+
return given.match({
|
|
220
|
+
none: (_$) => fieldOf(source, field),
|
|
221
|
+
some: (_$, v) => value(v),
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
/** Checks a kind's `name` or `icon`: a string when given, else its default. */
|
|
225
|
+
function wordOf(where, prop, given, otherwise) {
|
|
226
|
+
if (given === undefined)
|
|
227
|
+
return otherwise;
|
|
228
|
+
if (typeof given !== "string" || given === "") {
|
|
229
|
+
throw new Error(`${where}: \`${prop}\` is ${prop === "name" ? "what one is called, a string" : "a Font Awesome solid icon's name"}`);
|
|
230
|
+
}
|
|
231
|
+
return given;
|
|
232
|
+
}
|
|
233
|
+
/** Each field's type default, for the fields a new row takes from a gesture rather than from a card's values. */
|
|
234
|
+
function defaultsOf(rowType, placed) {
|
|
235
|
+
const fields = rowType.fields;
|
|
236
|
+
return Object.fromEntries(placed.map((name) => [name, East.value(defaultValue(fields[name]), fields[name])]));
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* A new row's source: a card's values, or the values' type defaults, with the
|
|
240
|
+
* fields a drop writes at their type defaults — what a gesture then writes its
|
|
241
|
+
* roles over.
|
|
242
|
+
*
|
|
243
|
+
* @param rowType - The record's row type
|
|
244
|
+
* @param valuesType - The values' type: the row's fields less those `placed` names
|
|
245
|
+
* @param placed - The fields a drop writes
|
|
246
|
+
* @returns `(values)` → the row, from a card's values' bytes, if any
|
|
247
|
+
*/
|
|
248
|
+
function sourceOf(rowType, valuesType, placed) {
|
|
249
|
+
return East.function([OptionType(BlobType)], rowType, ($, values) => {
|
|
250
|
+
const given = $.const(values.match({
|
|
251
|
+
some: (_$2, bytes) => bytes.decodeBeast(valuesType, "v2"),
|
|
252
|
+
none: (_$2) => East.value(defaultValue(valuesType), valuesType),
|
|
253
|
+
}));
|
|
254
|
+
return rowWith(rowType, given, defaultsOf(rowType, placed));
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
/** The key a Flowchart's declarations carry their sort under. */
|
|
258
|
+
export const FLOWCHART_DEF = Symbol("Flowchart.def");
|
|
259
|
+
/**
|
|
260
|
+
* Whether something is a Flowchart kind of the sort given.
|
|
261
|
+
*
|
|
262
|
+
* @param value - What a prop holds
|
|
263
|
+
* @param sort - The sort the prop takes
|
|
264
|
+
* @returns Whether it is one
|
|
265
|
+
*/
|
|
266
|
+
export function isFlowchartKind(value, sort) {
|
|
267
|
+
return typeof value === "object" && value !== null && FLOWCHART_DEF in value && value[FLOWCHART_DEF] === sort;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* A state's or a transition's details, checked where the kind declares them
|
|
271
|
+
* (#1507, R21): `Flowchart.details`', and on a transition what its label draws.
|
|
272
|
+
*
|
|
273
|
+
* @param details - What the kind's `details` holds
|
|
274
|
+
* @param where - The kind, as a refusal names it
|
|
275
|
+
* @param noun - The kind's rows
|
|
276
|
+
* @returns The details; `undefined` for none
|
|
277
|
+
* @throws {Error} When they are not `Flowchart.details`', and on a transition, a tags or a histogram read on the canvas
|
|
278
|
+
* and lines past three
|
|
279
|
+
*/
|
|
280
|
+
function detailsOf(details, where, noun) {
|
|
281
|
+
if (details === undefined)
|
|
282
|
+
return undefined;
|
|
283
|
+
if (!isFlowchartDetails(details)) {
|
|
284
|
+
throw new Error(`${where}: \`details\` takes Flowchart.details(binding, { details: [ … ] }) — the reads over the app's rows, keyed as the ${noun} are`);
|
|
285
|
+
}
|
|
286
|
+
details.check(noun, where);
|
|
287
|
+
return details;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Declares the states (see the module docs, R1–R3).
|
|
291
|
+
*
|
|
292
|
+
* @typeParam R - The record's row type
|
|
293
|
+
* @typeParam L - The `lane` field
|
|
294
|
+
* @typeParam O - The `order` field
|
|
295
|
+
* @param binding - The app's record of states: `Record.bind(record)`, its record declaring `patch: mutation.patch()`, to edit
|
|
296
|
+
* them — read whole, or through the slice's `states` source with `entries: Data.bindPaged(record)` — or `Data.bind(record)`,
|
|
297
|
+
* read whole, or `Data.bindPaged(record)`, read through their source, to read them only
|
|
298
|
+
* @param spec - What one is called, its icon, the fields its roles are, its accessors, and its cards, form, Details and check
|
|
299
|
+
* @returns The states, for the Flowchart's `states`
|
|
300
|
+
* @throws {Error} Naming the role or the binding (R2, R3): a role naming a field the row lacks, or one of another type;
|
|
301
|
+
* `label` and `lane` naming one field; an accessor returning another type; cards written in the code, or giving other
|
|
302
|
+
* fields than every field but `lane` and `order`; and a record that is no binding of a record of structs keyed by
|
|
303
|
+
* String, a `Record.bind` without its patch door, or `entries` beside a read-only binding
|
|
304
|
+
*
|
|
305
|
+
* @example
|
|
306
|
+
* ```tsx
|
|
307
|
+
* // .tsx file with the `@jsxImportSource @elaraai/e3-ui` pragma
|
|
308
|
+
* import { DateTimeType, DictType, East, FloatType, IntegerType, NullType, OptionType, StringType, StructType, VariantType, none, some, variant } from "@elaraai/east";
|
|
309
|
+
* import { Box, Reactive, UIComponentType } from "@elaraai/east-ui";
|
|
310
|
+
* import { Flowchart, Record } from "@elaraai/e3-ui";
|
|
311
|
+
* import e3 from "@elaraai/e3";
|
|
312
|
+
*
|
|
313
|
+
* // The depot's four records, each declaring its patch door: Save writes each through its own.
|
|
314
|
+
* export const DepotState = StructType({
|
|
315
|
+
* name: StringType, // what its card calls it
|
|
316
|
+
* phase: StringType, // the lane it stands in, by the lane's key
|
|
317
|
+
* seq: FloatType, // its place in its lane
|
|
318
|
+
* slots: OptionType(IntegerType), // a class of interchangeable states: the ×N badge
|
|
319
|
+
* flow: StringType, // the flow it belongs to: one record holds the depot's flows
|
|
320
|
+
* });
|
|
321
|
+
* export const DepotTransition = StructType({
|
|
322
|
+
* src: StringType, // the state it leaves, by its key
|
|
323
|
+
* dst: StringType, // the state it enters
|
|
324
|
+
* kind: VariantType({ planned: NullType, observed: NullType }),
|
|
325
|
+
* trigger: OptionType(StringType), // the decision that governs it, by its key
|
|
326
|
+
* volume: OptionType(FloatType), // the parcels measured on it
|
|
327
|
+
* n: OptionType(IntegerType), // the moves behind them
|
|
328
|
+
* at: OptionType(DateTimeType), // when they were measured
|
|
329
|
+
* flow: StringType,
|
|
330
|
+
* });
|
|
331
|
+
* export const DepotLane = StructType({ label: StringType, seq: FloatType });
|
|
332
|
+
* export const DepotDecision = StructType({ name: StringType, owner: StringType });
|
|
333
|
+
* export const depotStates = e3.record("flowchart_depot_states", DictType(StringType, DepotState), {
|
|
334
|
+
* initial: new Map([
|
|
335
|
+
* ["ARV", { name: "Arrived", phase: "intake", seq: 1, slots: none, flow: "Parcels" }],
|
|
336
|
+
* ["SCN", { name: "Scanned", phase: "intake", seq: 2, slots: none, flow: "Parcels" }],
|
|
337
|
+
* ["IND", { name: "Inducted", phase: "induct", seq: 1, slots: none, flow: "Parcels" }],
|
|
338
|
+
* ["LBL", { name: "Relabelled", phase: "induct", seq: 2, slots: none, flow: "Parcels" }],
|
|
339
|
+
* ["CH*", { name: "Sort chutes (class)", phase: "sort", seq: 1, slots: some(14n), flow: "Parcels" }],
|
|
340
|
+
* ["SRD", { name: "Sorted", phase: "sort", seq: 2, slots: none, flow: "Parcels" }],
|
|
341
|
+
* ["HLD", { name: "Held", phase: "hold", seq: 1, slots: none, flow: "Parcels" }],
|
|
342
|
+
* ["CLR", { name: "Cleared", phase: "hold", seq: 2, slots: none, flow: "Parcels" }],
|
|
343
|
+
* ["LDD", { name: "Loaded", phase: "dispatch", seq: 1, slots: none, flow: "Parcels" }],
|
|
344
|
+
* ["DSP", { name: "Dispatched", phase: "dispatch", seq: 2, slots: none, flow: "Parcels" }],
|
|
345
|
+
* // The returns flow's states, beside the parcels'.
|
|
346
|
+
* ["RCV", { name: "Received", phase: "intake", seq: 3, slots: none, flow: "Returns" }],
|
|
347
|
+
* ["INS", { name: "Inspected", phase: "hold", seq: 3, slots: none, flow: "Returns" }],
|
|
348
|
+
* ["RSD", { name: "Resent", phase: "dispatch", seq: 3, slots: none, flow: "Returns" }],
|
|
349
|
+
* ]),
|
|
350
|
+
* mutations: (mutation) => ({ patch: mutation.patch() }),
|
|
351
|
+
* indexes: (index) => ({
|
|
352
|
+
* flowchart_states_by_flow: index.on(["flow"]),
|
|
353
|
+
* flowchart_states_by_lane: index.on(["flow", "phase"]),
|
|
354
|
+
* }),
|
|
355
|
+
* });
|
|
356
|
+
* export const depotTransitions = e3.record("flowchart_depot_transitions", DictType(StringType, DepotTransition), {
|
|
357
|
+
* initial: new Map([
|
|
358
|
+
* ["l1", { src: "ARV", dst: "SCN", kind: variant("planned", null), trigger: none, volume: some(18460), n: some(412n), at: some(new Date("2026-06-30T00:00:00Z")), flow: "Parcels" }],
|
|
359
|
+
* ["l2", { src: "SCN", dst: "IND", kind: variant("planned", null), trigger: none, volume: some(18390), n: some(409n), at: some(new Date("2026-06-30T00:00:00Z")), flow: "Parcels" }],
|
|
360
|
+
* // In place: parcels inducted again, counted but not weighed.
|
|
361
|
+
* ["l3", { src: "IND", dst: "IND", kind: variant("observed", null), trigger: none, volume: none, n: some(2n), at: some(new Date("2026-06-30T00:00:00Z")), flow: "Parcels" }],
|
|
362
|
+
* ["l4", { src: "IND", dst: "CH*", kind: variant("planned", null), trigger: some("route"), volume: some(17350), n: some(386n), at: some(new Date("2026-06-30T00:00:00Z")), flow: "Parcels" }],
|
|
363
|
+
* ["l5", { src: "CH*", dst: "SRD", kind: variant("planned", null), trigger: none, volume: some(18210), n: some(405n), at: some(new Date("2026-06-30T00:00:00Z")), flow: "Parcels" }],
|
|
364
|
+
* ["l6", { src: "SRD", dst: "HLD", kind: variant("planned", null), trigger: none, volume: some(8420), n: some(188n), at: some(new Date("2026-06-30T00:00:00Z")), flow: "Parcels" }],
|
|
365
|
+
* ["l7", { src: "HLD", dst: "CLR", kind: variant("planned", null), trigger: some("customs"), volume: some(8350), n: some(186n), at: some(new Date("2026-06-30T00:00:00Z")), flow: "Parcels" }],
|
|
366
|
+
* ["l8", { src: "CLR", dst: "LDD", kind: variant("planned", null), trigger: none, volume: some(8240), n: some(183n), at: some(new Date("2026-06-30T00:00:00Z")), flow: "Parcels" }],
|
|
367
|
+
* ["l9", { src: "LDD", dst: "DSP", kind: variant("planned", null), trigger: none, volume: some(17030), n: some(378n), at: some(new Date("2026-06-30T00:00:00Z")), flow: "Parcels" }],
|
|
368
|
+
* // Its far end names a state no row is keyed by: drawn as a ghost, and listed in Issues.
|
|
369
|
+
* ["l10", { src: "DSP", dst: "DLV", kind: variant("planned", null), trigger: none, volume: none, n: none, at: none, flow: "Parcels" }],
|
|
370
|
+
* // Observed, not planned: parcels relabelled before they are sorted.
|
|
371
|
+
* ["l11", { src: "IND", dst: "LBL", kind: variant("observed", null), trigger: none, volume: some(980), n: some(22n), at: some(new Date("2026-06-30T00:00:00Z")), flow: "Parcels" }],
|
|
372
|
+
* ["l12", { src: "LBL", dst: "CH*", kind: variant("observed", null), trigger: none, volume: some(975), n: some(22n), at: some(new Date("2026-06-30T00:00:00Z")), flow: "Parcels" }],
|
|
373
|
+
* ["l13", { src: "SRD", dst: "LDD", kind: variant("planned", null), trigger: none, volume: some(8790), n: some(195n), at: some(new Date("2026-06-30T00:00:00Z")), flow: "Parcels" }],
|
|
374
|
+
* ["r1", { src: "RCV", dst: "INS", kind: variant("planned", null), trigger: none, volume: some(640), n: some(15n), at: some(new Date("2026-06-30T00:00:00Z")), flow: "Returns" }],
|
|
375
|
+
* ["r2", { src: "INS", dst: "RSD", kind: variant("planned", null), trigger: none, volume: some(610), n: some(14n), at: some(new Date("2026-06-30T00:00:00Z")), flow: "Returns" }],
|
|
376
|
+
* ]),
|
|
377
|
+
* mutations: (mutation) => ({ patch: mutation.patch() }),
|
|
378
|
+
* indexes: (index) => ({
|
|
379
|
+
* flowchart_transitions_by_flow: index.on(["flow"]),
|
|
380
|
+
* }),
|
|
381
|
+
* });
|
|
382
|
+
* export const depotLanes = e3.record("flowchart_depot_lanes", DictType(StringType, DepotLane), {
|
|
383
|
+
* initial: new Map([
|
|
384
|
+
* ["intake", { label: "Intake", seq: 1 }], ["induct", { label: "Induct", seq: 2 }], ["sort", { label: "Sort", seq: 3 }],
|
|
385
|
+
* ["hold", { label: "Hold", seq: 4 }], ["dispatch", { label: "Dispatch", seq: 5 }],
|
|
386
|
+
* ]),
|
|
387
|
+
* mutations: (mutation) => ({ patch: mutation.patch() }),
|
|
388
|
+
* });
|
|
389
|
+
* export const depotDecisions = e3.record("flowchart_depot_decisions", DictType(StringType, DepotDecision), {
|
|
390
|
+
* initial: new Map([["route", { name: "route", owner: "sort-planner" }], ["customs", { name: "customs", owner: "customs-desk" }]]),
|
|
391
|
+
* mutations: (mutation) => ({ patch: mutation.patch() }),
|
|
392
|
+
* });
|
|
393
|
+
*
|
|
394
|
+
* const flowchartDepotEditing = East.function([], UIComponentType, (_$) => (
|
|
395
|
+
* <Reactive>{$ => {
|
|
396
|
+
* const states = $.let(Record.bind(depotStates));
|
|
397
|
+
* const transitions = $.let(Record.bind(depotTransitions));
|
|
398
|
+
* const lanes = $.let(Record.bind(depotLanes));
|
|
399
|
+
* const decisions = $.let(Record.bind(depotDecisions));
|
|
400
|
+
* return (
|
|
401
|
+
* <Box height="600px">
|
|
402
|
+
* <Flowchart
|
|
403
|
+
* states={Flowchart.states(states, {
|
|
404
|
+
* label: "name", lane: "phase", order: "seq", members: (s) => s.slots,
|
|
405
|
+
* // The app's check on a drafted state: Save waits until it names its flow, and Issues says why.
|
|
406
|
+
* ready: (s) => s.flow.equal("").ifElse(
|
|
407
|
+
* () => variant("incomplete", [{ field: "flow", message: "Give the state its flow" }]),
|
|
408
|
+
* () => variant("ready", null),
|
|
409
|
+
* ),
|
|
410
|
+
* })}
|
|
411
|
+
* transitions={Flowchart.transitions(transitions, { from: "src", to: "dst", decision: "trigger", kind: (t) => t.kind })}
|
|
412
|
+
* lanes={Flowchart.lanes(lanes, { label: "label", order: "seq" })}
|
|
413
|
+
* decisions={Flowchart.decisions(decisions, { label: (d) => d.name, owner: (d) => some(d.owner) })}
|
|
414
|
+
* name="depotEditing"
|
|
415
|
+
* />
|
|
416
|
+
* </Box>
|
|
417
|
+
* );
|
|
418
|
+
* }}</Reactive>
|
|
419
|
+
* ));
|
|
420
|
+
* ```
|
|
421
|
+
*/
|
|
422
|
+
export function flowchartStates(binding, spec) {
|
|
423
|
+
const where = "Flowchart.states";
|
|
424
|
+
const bound = boundOf(binding, where, "states", false);
|
|
425
|
+
const rowType = bound.rowType;
|
|
426
|
+
const role = roleField(rowType, where);
|
|
427
|
+
const entries = entriesOf(bound, spec.entries, where, "states");
|
|
428
|
+
const name = wordOf(where, "name", spec.name, "State");
|
|
429
|
+
const icon = wordOf(where, "icon", spec.icon, "square");
|
|
430
|
+
// The roles that are fields.
|
|
431
|
+
const label = textField(rowType, where, "label", spec.label);
|
|
432
|
+
const laneField = role("lane", spec.lane, StringType, "a String field holding its lane's key");
|
|
433
|
+
const orderField = role("order", spec.order, FloatType, "a Float field, the state's place in its lane");
|
|
434
|
+
if (laneField !== undefined && laneField === label.field) {
|
|
435
|
+
throw new Error(`${where}: \`label\` and \`lane\` both name "${laneField}" — a state's name and its lane are two fields`);
|
|
436
|
+
}
|
|
437
|
+
// The role the app computes.
|
|
438
|
+
const membersOf = accessorRole(rowType, where, "members", OptionType(IntegerType), "an Option of an Integer", spec.members);
|
|
439
|
+
// Its details, read as the states are (#1507).
|
|
440
|
+
const details = detailsOf(spec.details, where, "states");
|
|
441
|
+
// The cards: every field but the lane and the order.
|
|
442
|
+
const placed = [laneField, orderField].filter((field) => field !== undefined);
|
|
443
|
+
const ValuesType = valuesTypeOf(rowType, placed);
|
|
444
|
+
const templates = checkedFlowchartTemplates(spec.templates, ValuesType, placed, where);
|
|
445
|
+
// One state's roles, from its row and its key.
|
|
446
|
+
const rolesOf = East.function([rowType, StringType], FlowchartStateRolesType, ($, row, key) => East.value({
|
|
447
|
+
label: textOf(row, label),
|
|
448
|
+
lane: laneField === undefined ? none : some(fieldOf(row, laneField)),
|
|
449
|
+
order: orderField === undefined ? none : some(fieldOf(row, orderField)),
|
|
450
|
+
members: called($, membersOf, row, key, (m) => m, none),
|
|
451
|
+
}, FlowchartStateRolesType));
|
|
452
|
+
// The row with a gesture's roles written over `source`, each where given, every other field as it was (R8).
|
|
453
|
+
const written = (source, w) => ({
|
|
454
|
+
[label.field]: kept(w.label, source, label.field, (t) => textValue(label, t)),
|
|
455
|
+
...(laneField === undefined ? {} : { [laneField]: kept(w.lane, source, laneField, (k) => k) }),
|
|
456
|
+
...(orderField === undefined ? {} : { [orderField]: kept(w.order, source, orderField, (o) => o) }),
|
|
457
|
+
});
|
|
458
|
+
const write = East.function([BlobType, FlowchartStateWriteType], OptionType(BlobType), ($, bytes, w) => {
|
|
459
|
+
const row = $.const(bytes.decodeBeast(rowType, "v2"));
|
|
460
|
+
return East.value(some(East.Blob.encodeBeast(rowWith(rowType, row, written(row, w)), "v2")), OptionType(BlobType));
|
|
461
|
+
});
|
|
462
|
+
// A new state: its roles over a card's values, or its type's defaults.
|
|
463
|
+
const source = sourceOf(rowType, ValuesType, placed);
|
|
464
|
+
const create = East.function([FlowchartStateWriteType, OptionType(BlobType)], OptionType(BlobType), ($, w, values) => {
|
|
465
|
+
const from = $.const(source);
|
|
466
|
+
const row = $.const(from(values));
|
|
467
|
+
return East.value(some(East.Blob.encodeBeast(rowWith(rowType, row, written(row, w)), "v2")), OptionType(BlobType));
|
|
468
|
+
});
|
|
469
|
+
return {
|
|
470
|
+
[FLOWCHART_DEF]: "states",
|
|
471
|
+
rowType,
|
|
472
|
+
name,
|
|
473
|
+
form: bound.form,
|
|
474
|
+
lane: laneField !== undefined,
|
|
475
|
+
inspectorReads: details?.inspectorReads ?? false,
|
|
476
|
+
weighedBy: details?.source === undefined ? undefined : { source: details.source, rowType: details.rowType },
|
|
477
|
+
hasTemplates: templates !== undefined,
|
|
478
|
+
templatesSource: templates?.source,
|
|
479
|
+
build(part, every) {
|
|
480
|
+
const { rows, byKey } = readsOf(bound, entries, part, where, "states", every);
|
|
481
|
+
const side = recordSide(bound, entries, byKey, spec, [label.field, laneField, orderField], where);
|
|
482
|
+
return East.value({
|
|
483
|
+
...side,
|
|
484
|
+
name,
|
|
485
|
+
icon,
|
|
486
|
+
rows: storedRows(rows, rolesOf, FlowchartStateEntryType),
|
|
487
|
+
// Rows a reader read by key through the states' source — a hidden end's, Find's — made stored states (R6, R17).
|
|
488
|
+
rowsOf: rowsReadOf(rowType, rolesOf, FlowchartStateEntryType),
|
|
489
|
+
read: readOf(rowType, rolesOf, FlowchartStateRolesType),
|
|
490
|
+
write,
|
|
491
|
+
create,
|
|
492
|
+
templates: templates?.items ?? East.value([], ArrayType(FlowchartTemplateType)),
|
|
493
|
+
templatesSource: templates?.source === undefined ? none : some(templates.source),
|
|
494
|
+
// The field naming a state's lane: a conflict on it named by the lane's label (#1503).
|
|
495
|
+
laneField: laneField === undefined ? none : some(laneField),
|
|
496
|
+
// Its details, read as the states are: whole, or by the drawn rows' keys (#1507, R19).
|
|
497
|
+
details: details === undefined ? none : some(details.build("states", where, part !== undefined)),
|
|
498
|
+
}, FlowchartStateKindType);
|
|
499
|
+
},
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Declares the transitions (see the module docs, R1–R3).
|
|
504
|
+
*
|
|
505
|
+
* @typeParam R - The record's row type
|
|
506
|
+
* @typeParam F - The `from` field
|
|
507
|
+
* @typeParam T - The `to` field
|
|
508
|
+
* @typeParam D - The `decision` field
|
|
509
|
+
* @param binding - The app's record of transitions, bound as `Flowchart.states`' is
|
|
510
|
+
* @param spec - What one is called, its icon, its ends and its decision, its accessors, and its cards, form, Details and check
|
|
511
|
+
* @returns The transitions, for the Flowchart's `transitions`
|
|
512
|
+
* @throws {Error} Naming the role or the binding, as `Flowchart.states` does, and `from`, `to` and `decision` naming one field
|
|
513
|
+
*/
|
|
514
|
+
export function flowchartTransitions(binding, spec) {
|
|
515
|
+
const where = "Flowchart.transitions";
|
|
516
|
+
const bound = boundOf(binding, where, "transitions", false);
|
|
517
|
+
const rowType = bound.rowType;
|
|
518
|
+
const role = roleField(rowType, where);
|
|
519
|
+
const entries = entriesOf(bound, spec.entries, where, "transitions");
|
|
520
|
+
const name = wordOf(where, "name", spec.name, "Transition");
|
|
521
|
+
const icon = wordOf(where, "icon", spec.icon, "arrow-right");
|
|
522
|
+
// The ends, each a String field naming a state by its key, and the decision.
|
|
523
|
+
const end = (prop, given) => {
|
|
524
|
+
if (typeof given !== "string")
|
|
525
|
+
throw new Error(`${where}: \`${prop}\` names the String field holding a state's key`);
|
|
526
|
+
return role(prop, given, StringType, "a state by its key, a String field");
|
|
527
|
+
};
|
|
528
|
+
const fromField = end("from", spec.from);
|
|
529
|
+
const toField = end("to", spec.to);
|
|
530
|
+
if (fromField === toField)
|
|
531
|
+
throw new Error(`${where}: \`from\` and \`to\` both name "${fromField}" — a transition's ends are two fields`);
|
|
532
|
+
const decision = spec.decision === undefined ? undefined : textField(rowType, where, "decision", spec.decision);
|
|
533
|
+
if (decision !== undefined && (decision.field === fromField || decision.field === toField)) {
|
|
534
|
+
throw new Error(`${where}: \`decision\` and \`${decision.field === fromField ? "from" : "to"}\` both name "${decision.field}" — a transition's decision and its ends are three fields`);
|
|
535
|
+
}
|
|
536
|
+
// The roles the app computes.
|
|
537
|
+
const kindOf = accessorRole(rowType, where, "kind", FlowchartLinkKindType, "a Flowchart.Types.Kind, planned or observed", spec.kind);
|
|
538
|
+
const evidenceOf = accessorRole(rowType, where, "evidence", OptionType(FlowchartEvidenceType), "an Option of a Flowchart.Types.Evidence", spec.evidence);
|
|
539
|
+
// Its details, read as the transitions are (#1507): what its label draws, checked.
|
|
540
|
+
const details = detailsOf(spec.details, where, "transitions");
|
|
541
|
+
// The cards: every field but the ends.
|
|
542
|
+
const placed = [fromField, toField];
|
|
543
|
+
const ValuesType = valuesTypeOf(rowType, placed);
|
|
544
|
+
const templates = checkedFlowchartTemplates(spec.templates, ValuesType, placed, where);
|
|
545
|
+
// One transition's roles, from its row and its key: a decision's String field, empty, names none.
|
|
546
|
+
const rolesOf = East.function([rowType, StringType], FlowchartTransitionRolesType, ($, row, key) => {
|
|
547
|
+
let named = none;
|
|
548
|
+
if (decision !== undefined) {
|
|
549
|
+
const held = $.const(fieldOf(row, decision.field));
|
|
550
|
+
named = decision.optional ? held : East.equal(held, "").ifElse((_$2) => East.value(none, OptionType(StringType)), (_$2) => East.value(some(held), OptionType(StringType)));
|
|
551
|
+
}
|
|
552
|
+
return East.value({
|
|
553
|
+
from: fieldOf(row, fromField),
|
|
554
|
+
to: fieldOf(row, toField),
|
|
555
|
+
decision: named,
|
|
556
|
+
kind: called($, kindOf, row, key, (k) => k, East.value(variant("planned", null), FlowchartLinkKindType)),
|
|
557
|
+
evidence: called($, evidenceOf, row, key, (e) => e, none),
|
|
558
|
+
}, FlowchartTransitionRolesType);
|
|
559
|
+
});
|
|
560
|
+
// The row with a gesture's ends and decision written over `source`, each where given (R8): a decision cleared is
|
|
561
|
+
// an empty String field, or an Option field holding none.
|
|
562
|
+
const written = (source, w) => ({
|
|
563
|
+
[fromField]: kept(w.from, source, fromField, (k) => k),
|
|
564
|
+
[toField]: kept(w.to, source, toField, (k) => k),
|
|
565
|
+
...(decision === undefined ? {} : {
|
|
566
|
+
[decision.field]: kept(w.decision, source, decision.field, (d) => decision.optional ? d
|
|
567
|
+
: d.match({ none: (_$) => East.value("", StringType), some: (_$, k) => k })),
|
|
568
|
+
}),
|
|
569
|
+
});
|
|
570
|
+
const write = East.function([BlobType, FlowchartTransitionWriteType], OptionType(BlobType), ($, bytes, w) => {
|
|
571
|
+
const row = $.const(bytes.decodeBeast(rowType, "v2"));
|
|
572
|
+
return East.value(some(East.Blob.encodeBeast(rowWith(rowType, row, written(row, w)), "v2")), OptionType(BlobType));
|
|
573
|
+
});
|
|
574
|
+
// A new transition — Connect — its ends over a card's values, or its type's defaults.
|
|
575
|
+
const source = sourceOf(rowType, ValuesType, placed);
|
|
576
|
+
const create = East.function([FlowchartTransitionWriteType, OptionType(BlobType)], OptionType(BlobType), ($, w, values) => {
|
|
577
|
+
const from = $.const(source);
|
|
578
|
+
const row = $.const(from(values));
|
|
579
|
+
return East.value(some(East.Blob.encodeBeast(rowWith(rowType, row, written(row, w)), "v2")), OptionType(BlobType));
|
|
580
|
+
});
|
|
581
|
+
return {
|
|
582
|
+
[FLOWCHART_DEF]: "transitions",
|
|
583
|
+
rowType,
|
|
584
|
+
name,
|
|
585
|
+
form: bound.form,
|
|
586
|
+
decision: decision !== undefined,
|
|
587
|
+
inspectorReads: details?.inspectorReads ?? false,
|
|
588
|
+
weighedBy: details?.source === undefined ? undefined : { source: details.source, rowType: details.rowType },
|
|
589
|
+
hasTemplates: templates !== undefined,
|
|
590
|
+
templatesSource: templates?.source,
|
|
591
|
+
build(part, every) {
|
|
592
|
+
const { rows, byKey } = readsOf(bound, entries, part, where, "transitions", every);
|
|
593
|
+
const side = recordSide(bound, entries, byKey, spec, [fromField, toField, decision?.field], where);
|
|
594
|
+
return East.value({
|
|
595
|
+
...side,
|
|
596
|
+
name,
|
|
597
|
+
icon,
|
|
598
|
+
rows: storedRows(rows, rolesOf, FlowchartTransitionEntryType),
|
|
599
|
+
read: readOf(rowType, rolesOf, FlowchartTransitionRolesType),
|
|
600
|
+
write,
|
|
601
|
+
create,
|
|
602
|
+
templates: templates?.items ?? East.value([], ArrayType(FlowchartTemplateType)),
|
|
603
|
+
templatesSource: templates?.source === undefined ? none : some(templates.source),
|
|
604
|
+
// The fields naming its ends and its decision: a conflict on one named by its state's label, or the decision's (#1503).
|
|
605
|
+
fromField,
|
|
606
|
+
toField,
|
|
607
|
+
decisionField: decision === undefined ? none : some(decision.field),
|
|
608
|
+
// Its details, read as the transitions are: whole, or by the drawn rows' keys (#1507, R19).
|
|
609
|
+
details: details === undefined ? none : some(details.build("transitions", where, part !== undefined)),
|
|
610
|
+
}, FlowchartTransitionKindType);
|
|
611
|
+
},
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Declares the lanes (see the module docs, R1–R3, D5).
|
|
616
|
+
*
|
|
617
|
+
* @typeParam R - The record's row type
|
|
618
|
+
* @typeParam O - The `order` field
|
|
619
|
+
* @param binding - The app's record of lanes: `Record.bind(record)`, its record declaring `patch: mutation.patch()`, to edit
|
|
620
|
+
* them, or `Data.bind(record)` to read them — each read whole
|
|
621
|
+
* @param spec - What one is called, the fields its header and its order are, and its cards, form and Details
|
|
622
|
+
* @returns The lanes, for the Flowchart's `lanes`
|
|
623
|
+
* @throws {Error} Naming the role or the binding, as `Flowchart.states` does: a paged read, and `entries`, among them —
|
|
624
|
+
* the lanes are read whole
|
|
625
|
+
*/
|
|
626
|
+
export function flowchartLanes(binding, spec) {
|
|
627
|
+
const where = "Flowchart.lanes";
|
|
628
|
+
const bound = boundOf(binding, where, "lanes", true);
|
|
629
|
+
const rowType = bound.rowType;
|
|
630
|
+
if (spec.entries !== undefined)
|
|
631
|
+
throw new Error(`${where}: the lanes are read whole — leave entries out`);
|
|
632
|
+
const name = wordOf(where, "name", spec.name, "Lane");
|
|
633
|
+
const label = textField(rowType, where, "label", spec.label);
|
|
634
|
+
// The order: a Float field, or an Integer one, which takes a whole order alone.
|
|
635
|
+
let orderField;
|
|
636
|
+
let integer = false;
|
|
637
|
+
if (spec.order !== undefined) {
|
|
638
|
+
if (typeof spec.order !== "string")
|
|
639
|
+
throw new Error(`${where}: \`order\` names the Integer or Float field placing a lane among the lanes`);
|
|
640
|
+
const type = fieldTypeOf(rowType, where)("order", spec.order);
|
|
641
|
+
integer = isTypeEqual(type, IntegerType);
|
|
642
|
+
if (!integer && !isTypeEqual(type, FloatType)) {
|
|
643
|
+
throw new Error(`${where}: \`order\` names an Integer or a Float field, the lane's place among the lanes — "${spec.order}" holds ${typeText(type)}`);
|
|
644
|
+
}
|
|
645
|
+
orderField = spec.order;
|
|
646
|
+
}
|
|
647
|
+
// The cards: every field but the order.
|
|
648
|
+
const placed = orderField === undefined ? [] : [orderField];
|
|
649
|
+
const ValuesType = valuesTypeOf(rowType, placed);
|
|
650
|
+
const templates = checkedFlowchartTemplates(spec.templates, ValuesType, placed, where);
|
|
651
|
+
// One lane's roles, from its row and its key: an Integer order read as a Float.
|
|
652
|
+
const rolesOf = East.function([rowType, StringType], FlowchartLaneRolesType, ($, row, _key) => {
|
|
653
|
+
let order = none;
|
|
654
|
+
if (orderField !== undefined) {
|
|
655
|
+
const held = $.const(fieldOf(row, orderField));
|
|
656
|
+
order = some(integer ? held.toFloat() : held);
|
|
657
|
+
}
|
|
658
|
+
return East.value({ label: textOf(row, label), order }, FlowchartLaneRolesType);
|
|
659
|
+
});
|
|
660
|
+
// The row with a gesture's label and order written over `source`, each where given (R8); `none` for an order an
|
|
661
|
+
// Integer field can't hold, between two whole numbers — the renderer moves the lanes after it on.
|
|
662
|
+
const laned = ($, source, w) => {
|
|
663
|
+
const result = $.let(East.value(none, OptionType(BlobType)), OptionType(BlobType));
|
|
664
|
+
const fits = orderField === undefined || !integer ? East.value(true)
|
|
665
|
+
: w.order.match({ none: (_$2) => East.value(true), some: (_$2, o) => East.equal(o.remainder(1.0).abs(), 0.0) });
|
|
666
|
+
$.if(fits, ($2) => {
|
|
667
|
+
const set = {
|
|
668
|
+
[label.field]: kept(w.label, source, label.field, (t) => textValue(label, t)),
|
|
669
|
+
...(orderField === undefined ? {} : {
|
|
670
|
+
[orderField]: kept(w.order, source, orderField, (o) => (integer ? o.toInteger() : o)),
|
|
671
|
+
}),
|
|
672
|
+
};
|
|
673
|
+
$2.assign(result, East.value(some(East.Blob.encodeBeast(rowWith(rowType, source, set), "v2")), OptionType(BlobType)));
|
|
674
|
+
});
|
|
675
|
+
return result;
|
|
676
|
+
};
|
|
677
|
+
const write = East.function([BlobType, FlowchartLaneWriteType], OptionType(BlobType), ($, bytes, w) => {
|
|
678
|
+
const row = $.const(bytes.decodeBeast(rowType, "v2"));
|
|
679
|
+
return laned($, row, w);
|
|
680
|
+
});
|
|
681
|
+
// A new lane — + LANE, or a card dropped between two lanes — over a card's values, or its type's defaults.
|
|
682
|
+
const source = sourceOf(rowType, ValuesType, placed);
|
|
683
|
+
const create = East.function([FlowchartLaneWriteType, OptionType(BlobType)], OptionType(BlobType), ($, w, values) => {
|
|
684
|
+
const from = $.const(source);
|
|
685
|
+
const row = $.const(from(values));
|
|
686
|
+
return laned($, row, w);
|
|
687
|
+
});
|
|
688
|
+
return {
|
|
689
|
+
[FLOWCHART_DEF]: "lanes",
|
|
690
|
+
rowType,
|
|
691
|
+
name,
|
|
692
|
+
form: bound.form,
|
|
693
|
+
hasTemplates: templates !== undefined,
|
|
694
|
+
templatesSource: templates?.source,
|
|
695
|
+
build(_part, _every) {
|
|
696
|
+
const side = recordSide(bound, undefined, undefined, spec, [label.field, orderField], where);
|
|
697
|
+
return East.value({
|
|
698
|
+
...side,
|
|
699
|
+
name,
|
|
700
|
+
icon: "table-columns",
|
|
701
|
+
rows: storedRows(kindWholeRows(bound.handle, rowType), rolesOf, FlowchartLaneEntryType),
|
|
702
|
+
read: readOf(rowType, rolesOf, FlowchartLaneRolesType),
|
|
703
|
+
write,
|
|
704
|
+
create,
|
|
705
|
+
templates: templates?.items ?? East.value([], ArrayType(FlowchartTemplateType)),
|
|
706
|
+
templatesSource: templates?.source === undefined ? none : some(templates.source),
|
|
707
|
+
}, FlowchartLaneKindType);
|
|
708
|
+
},
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Declares the decisions (see the module docs, R1–R3).
|
|
713
|
+
*
|
|
714
|
+
* @typeParam R - The record's row type
|
|
715
|
+
* @param binding - The app's record of decisions, bound as `Flowchart.lanes`' is
|
|
716
|
+
* @param spec - What one is called, its accessors, its queue, and its form and Details
|
|
717
|
+
* @returns The decisions, for the Flowchart's `decisions`
|
|
718
|
+
* @throws {Error} Naming the role or the binding, as `Flowchart.lanes` does, and a `label` that is no accessor
|
|
719
|
+
*/
|
|
720
|
+
export function flowchartDecisions(binding, spec) {
|
|
721
|
+
const where = "Flowchart.decisions";
|
|
722
|
+
const bound = boundOf(binding, where, "decisions", true);
|
|
723
|
+
const rowType = bound.rowType;
|
|
724
|
+
if (spec.entries !== undefined)
|
|
725
|
+
throw new Error(`${where}: the decisions are read whole — leave entries out`);
|
|
726
|
+
const name = wordOf(where, "name", spec.name, "Decision");
|
|
727
|
+
if (typeof spec.label !== "function")
|
|
728
|
+
throw new Error(`${where}: \`label\` is an accessor giving a decision's name, (row, key) => …`);
|
|
729
|
+
const labelOf = accessorRole(rowType, where, "label", StringType, "a String", spec.label);
|
|
730
|
+
const letterOf = accessorRole(rowType, where, "letter", OptionType(StringType), "an Option of a String", spec.letter);
|
|
731
|
+
const ownerOf = accessorRole(rowType, where, "owner", OptionType(StringType), "an Option of a String", spec.owner);
|
|
732
|
+
const outcomesOf = accessorRole(rowType, where, "outcomes", OptionType(StringType), "an Option of a String", spec.outcomes);
|
|
733
|
+
// The queue: an Array or a Set of String keys.
|
|
734
|
+
let queue;
|
|
735
|
+
if (spec.queue !== undefined) {
|
|
736
|
+
if (typeof spec.queue !== "string")
|
|
737
|
+
throw new Error(`${where}: \`queue\` names the Array or Set of String field holding the states queued at it, by key`);
|
|
738
|
+
const type = fieldTypeOf(rowType, where)("queue", spec.queue);
|
|
739
|
+
const set = isTypeEqual(type, SetType(StringType));
|
|
740
|
+
if (!set && !isTypeEqual(type, ArrayType(StringType))) {
|
|
741
|
+
throw new Error(`${where}: \`queue\` names an Array or a Set of String field, the states queued at it by key — "${spec.queue}" holds ${typeText(type)}`);
|
|
742
|
+
}
|
|
743
|
+
queue = { field: spec.queue, set };
|
|
744
|
+
}
|
|
745
|
+
// One decision's roles, from its row and its key: a Set queue in its order.
|
|
746
|
+
const rolesOf = East.function([rowType, StringType], FlowchartDecisionRolesType, ($, row, key) => {
|
|
747
|
+
let queued = East.value([], ArrayType(StringType));
|
|
748
|
+
if (queue !== undefined) {
|
|
749
|
+
const held = $.const(fieldOf(row, queue.field));
|
|
750
|
+
queued = queue.set ? held.toArray((_$2, k) => k) : held;
|
|
751
|
+
}
|
|
752
|
+
return East.value({
|
|
753
|
+
label: called($, labelOf, row, key, (l) => l, ""),
|
|
754
|
+
letter: called($, letterOf, row, key, (l) => l, none),
|
|
755
|
+
owner: called($, ownerOf, row, key, (o) => o, none),
|
|
756
|
+
outcomes: called($, outcomesOf, row, key, (o) => o, none),
|
|
757
|
+
queue: queued,
|
|
758
|
+
}, FlowchartDecisionRolesType);
|
|
759
|
+
});
|
|
760
|
+
// A state's key taken out of the queue — a state's Del (R10): the row, or `none` where the key is not queued.
|
|
761
|
+
const unqueue = queue === undefined ? undefined : East.function([BlobType, StringType], OptionType(BlobType), ($, bytes, key) => {
|
|
762
|
+
const row = $.const(bytes.decodeBeast(rowType, "v2"));
|
|
763
|
+
const result = $.let(East.value(none, OptionType(BlobType)), OptionType(BlobType));
|
|
764
|
+
if (queue.set) {
|
|
765
|
+
const held = $.let(fieldOf(row, queue.field));
|
|
766
|
+
$.if(held.tryDelete(key), ($2) => {
|
|
767
|
+
$2.assign(result, East.value(some(East.Blob.encodeBeast(rowWith(rowType, row, { [queue.field]: held }), "v2")), OptionType(BlobType)));
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
const held = $.const(fieldOf(row, queue.field));
|
|
772
|
+
const left = $.const(held.filter((_$2, k) => East.equal(k, key).not()));
|
|
773
|
+
$.if(left.size().equal(held.size()).not(), ($2) => {
|
|
774
|
+
$2.assign(result, East.value(some(East.Blob.encodeBeast(rowWith(rowType, row, { [queue.field]: left }), "v2")), OptionType(BlobType)));
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
return result;
|
|
778
|
+
});
|
|
779
|
+
return {
|
|
780
|
+
[FLOWCHART_DEF]: "decisions",
|
|
781
|
+
rowType,
|
|
782
|
+
name,
|
|
783
|
+
form: bound.form,
|
|
784
|
+
hasTemplates: false,
|
|
785
|
+
templatesSource: undefined,
|
|
786
|
+
build(_part, _every) {
|
|
787
|
+
const side = recordSide(bound, undefined, undefined, spec, [queue?.field], where);
|
|
788
|
+
return East.value({
|
|
789
|
+
...side,
|
|
790
|
+
name,
|
|
791
|
+
icon: "diamond",
|
|
792
|
+
rows: storedRows(kindWholeRows(bound.handle, rowType), rolesOf, FlowchartDecisionEntryType),
|
|
793
|
+
read: readOf(rowType, rolesOf, FlowchartDecisionRolesType),
|
|
794
|
+
unqueue: unqueue === undefined ? none : some(unqueue),
|
|
795
|
+
}, FlowchartDecisionKindType);
|
|
796
|
+
},
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
//# sourceMappingURL=kinds.js.map
|