@elaraai/east-ui 1.0.80 → 1.0.82
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/charts/chart/index.d.ts +4 -0
- package/dist/src/charts/chart/index.d.ts.map +1 -1
- package/dist/src/charts/spec/index.d.ts +8 -0
- package/dist/src/charts/spec/index.d.ts.map +1 -1
- package/dist/src/collections/deck/index.d.ts +8 -0
- package/dist/src/collections/deck/index.d.ts.map +1 -1
- package/dist/src/collections/index.d.ts +2 -2
- package/dist/src/collections/index.d.ts.map +1 -1
- package/dist/src/collections/index.js.map +1 -1
- package/dist/src/collections/library/index.d.ts +4 -0
- package/dist/src/collections/library/index.d.ts.map +1 -1
- package/dist/src/collections/library/types.d.ts +4 -0
- package/dist/src/collections/library/types.d.ts.map +1 -1
- package/dist/src/collections/matrix/index.d.ts +56 -19
- package/dist/src/collections/matrix/index.d.ts.map +1 -1
- package/dist/src/collections/matrix/index.js +38 -24
- package/dist/src/collections/matrix/index.js.map +1 -1
- package/dist/src/collections/plan/assemble.d.ts +655 -2113
- package/dist/src/collections/plan/assemble.d.ts.map +1 -1
- package/dist/src/collections/plan/assemble.js +159 -136
- package/dist/src/collections/plan/assemble.js.map +1 -1
- package/dist/src/collections/plan/builders.d.ts +193 -40
- package/dist/src/collections/plan/builders.d.ts.map +1 -1
- package/dist/src/collections/plan/builders.js +162 -32
- package/dist/src/collections/plan/builders.js.map +1 -1
- package/dist/src/collections/plan/factories.d.ts +290 -116
- package/dist/src/collections/plan/factories.d.ts.map +1 -1
- package/dist/src/collections/plan/factories.js +281 -156
- package/dist/src/collections/plan/factories.js.map +1 -1
- package/dist/src/collections/plan/index.d.ts +111 -58
- package/dist/src/collections/plan/index.d.ts.map +1 -1
- package/dist/src/collections/plan/index.js +62 -31
- package/dist/src/collections/plan/index.js.map +1 -1
- package/dist/src/collections/plan/ir.d.ts +759 -1769
- package/dist/src/collections/plan/ir.d.ts.map +1 -1
- package/dist/src/collections/plan/ir.js +51 -27
- package/dist/src/collections/plan/ir.js.map +1 -1
- package/dist/src/collections/plan/pick.d.ts +89 -71
- package/dist/src/collections/plan/pick.d.ts.map +1 -1
- package/dist/src/collections/plan/pick.js +101 -86
- package/dist/src/collections/plan/pick.js.map +1 -1
- package/dist/src/collections/plan/root.d.ts +164 -77
- package/dist/src/collections/plan/root.d.ts.map +1 -1
- package/dist/src/collections/plan/root.js +350 -42
- package/dist/src/collections/plan/root.js.map +1 -1
- package/dist/src/collections/plan/series.d.ts +12524 -10527
- package/dist/src/collections/plan/series.d.ts.map +1 -1
- package/dist/src/collections/plan/series.js +1596 -298
- package/dist/src/collections/plan/series.js.map +1 -1
- package/dist/src/collections/plan/types.d.ts +6115 -2802
- package/dist/src/collections/plan/types.d.ts.map +1 -1
- package/dist/src/collections/plan/types.js +575 -170
- package/dist/src/collections/plan/types.js.map +1 -1
- package/dist/src/collections/sheet/bridge.d.ts +154 -43
- package/dist/src/collections/sheet/bridge.d.ts.map +1 -1
- package/dist/src/collections/sheet/bridge.js +383 -240
- package/dist/src/collections/sheet/bridge.js.map +1 -1
- package/dist/src/collections/sheet/columns.d.ts +66 -16
- package/dist/src/collections/sheet/columns.d.ts.map +1 -1
- package/dist/src/collections/sheet/columns.js +25 -25
- package/dist/src/collections/sheet/columns.js.map +1 -1
- package/dist/src/collections/sheet/context-bridge.d.ts +48 -0
- package/dist/src/collections/sheet/context-bridge.d.ts.map +1 -0
- package/dist/src/collections/sheet/context-bridge.js +216 -0
- package/dist/src/collections/sheet/context-bridge.js.map +1 -0
- package/dist/src/collections/sheet/draft-bridge.d.ts +37 -0
- package/dist/src/collections/sheet/draft-bridge.d.ts.map +1 -0
- package/dist/src/collections/sheet/draft-bridge.js +153 -0
- package/dist/src/collections/sheet/draft-bridge.js.map +1 -0
- package/dist/src/collections/sheet/drafts.d.ts +14 -0
- package/dist/src/collections/sheet/drafts.d.ts.map +1 -0
- package/dist/src/collections/sheet/drafts.js +14 -0
- package/dist/src/collections/sheet/drafts.js.map +1 -0
- package/dist/src/collections/sheet/editing-bridge.d.ts +36 -0
- package/dist/src/collections/sheet/editing-bridge.d.ts.map +1 -0
- package/dist/src/collections/sheet/editing-bridge.js +196 -0
- package/dist/src/collections/sheet/editing-bridge.js.map +1 -0
- package/dist/src/collections/sheet/editing-types.d.ts +268 -0
- package/dist/src/collections/sheet/editing-types.d.ts.map +1 -0
- package/dist/src/collections/sheet/editing-types.js +34 -0
- package/dist/src/collections/sheet/editing-types.js.map +1 -0
- package/dist/src/collections/sheet/edits.d.ts +37 -0
- package/dist/src/collections/sheet/edits.d.ts.map +1 -0
- package/dist/src/collections/sheet/edits.js +31 -0
- package/dist/src/collections/sheet/edits.js.map +1 -0
- package/dist/src/collections/sheet/group.d.ts +165 -0
- package/dist/src/collections/sheet/group.d.ts.map +1 -0
- package/dist/src/collections/sheet/group.js +52 -0
- package/dist/src/collections/sheet/group.js.map +1 -0
- package/dist/src/collections/sheet/index.d.ts +171 -27
- package/dist/src/collections/sheet/index.d.ts.map +1 -1
- package/dist/src/collections/sheet/index.js +99 -17
- package/dist/src/collections/sheet/index.js.map +1 -1
- package/dist/src/collections/sheet/link.d.ts +22 -16
- package/dist/src/collections/sheet/link.d.ts.map +1 -1
- package/dist/src/collections/sheet/link.js +18 -12
- package/dist/src/collections/sheet/link.js.map +1 -1
- package/dist/src/collections/sheet/registers.d.ts +152 -5
- package/dist/src/collections/sheet/registers.d.ts.map +1 -1
- package/dist/src/collections/sheet/registers.js +154 -5
- package/dist/src/collections/sheet/registers.js.map +1 -1
- package/dist/src/collections/sheet/root.d.ts +175 -26
- package/dist/src/collections/sheet/root.d.ts.map +1 -1
- package/dist/src/collections/sheet/root.js +150 -26
- package/dist/src/collections/sheet/root.js.map +1 -1
- package/dist/src/collections/sheet/seed-bridge.d.ts +14 -0
- package/dist/src/collections/sheet/seed-bridge.d.ts.map +1 -0
- package/dist/src/collections/sheet/seed-bridge.js +51 -0
- package/dist/src/collections/sheet/seed-bridge.js.map +1 -0
- package/dist/src/collections/sheet/sub-rows.d.ts +249 -0
- package/dist/src/collections/sheet/sub-rows.d.ts.map +1 -0
- package/dist/src/collections/sheet/sub-rows.js +222 -0
- package/dist/src/collections/sheet/sub-rows.js.map +1 -0
- package/dist/src/collections/sheet/transactions.d.ts +76 -0
- package/dist/src/collections/sheet/transactions.d.ts.map +1 -0
- package/dist/src/collections/sheet/transactions.js +40 -0
- package/dist/src/collections/sheet/transactions.js.map +1 -0
- package/dist/src/collections/sheet/types.d.ts +12034 -3651
- package/dist/src/collections/sheet/types.d.ts.map +1 -1
- package/dist/src/collections/sheet/types.js +365 -48
- package/dist/src/collections/sheet/types.js.map +1 -1
- package/dist/src/collections/table/index.d.ts +98 -123
- package/dist/src/collections/table/index.d.ts.map +1 -1
- package/dist/src/collections/table/index.js +97 -111
- package/dist/src/collections/table/index.js.map +1 -1
- package/dist/src/collections/table/types.d.ts +87 -31
- package/dist/src/collections/table/types.d.ts.map +1 -1
- package/dist/src/collections/table/types.js +51 -26
- package/dist/src/collections/table/types.js.map +1 -1
- package/dist/src/collections/value-tree/flatten.d.ts.map +1 -1
- package/dist/src/collections/value-tree/flatten.js +4 -1
- package/dist/src/collections/value-tree/flatten.js.map +1 -1
- package/dist/src/component.d.ts +5075 -4079
- package/dist/src/component.d.ts.map +1 -1
- package/dist/src/component.js +46 -46
- package/dist/src/component.js.map +1 -1
- package/dist/src/contracts/approval.d.ts +10 -8
- package/dist/src/contracts/approval.d.ts.map +1 -1
- package/dist/src/contracts/approval.js +10 -8
- package/dist/src/contracts/approval.js.map +1 -1
- package/dist/src/contracts/drag.d.ts +11 -9
- package/dist/src/contracts/drag.d.ts.map +1 -1
- package/dist/src/contracts/drag.js +28 -21
- package/dist/src/contracts/drag.js.map +1 -1
- package/dist/src/contracts/editing.d.ts +1283 -0
- package/dist/src/contracts/editing.d.ts.map +1 -0
- package/dist/src/contracts/editing.js +711 -0
- package/dist/src/contracts/editing.js.map +1 -0
- package/dist/src/contracts/index.d.ts +2 -1
- package/dist/src/contracts/index.d.ts.map +1 -1
- package/dist/src/contracts/index.js +2 -1
- package/dist/src/contracts/index.js.map +1 -1
- package/dist/src/contracts/pick.d.ts +6 -7
- package/dist/src/contracts/pick.d.ts.map +1 -1
- package/dist/src/contracts/pick.js +6 -7
- package/dist/src/contracts/pick.js.map +1 -1
- package/dist/src/contracts/review.d.ts +3 -1
- package/dist/src/contracts/review.d.ts.map +1 -1
- package/dist/src/contracts/review.js +3 -1
- package/dist/src/contracts/review.js.map +1 -1
- package/dist/src/contracts/source.d.ts +270 -80
- package/dist/src/contracts/source.d.ts.map +1 -1
- package/dist/src/contracts/source.js +357 -58
- package/dist/src/contracts/source.js.map +1 -1
- package/dist/src/display/chip-rail/index.d.ts.map +1 -1
- package/dist/src/display/chip-rail/index.js +10 -10
- package/dist/src/display/chip-rail/index.js.map +1 -1
- package/dist/src/extension.d.ts +2 -1
- package/dist/src/extension.d.ts.map +1 -1
- package/dist/src/extension.js +2 -1
- package/dist/src/extension.js.map +1 -1
- package/dist/src/format/index.d.ts +1 -154
- package/dist/src/format/index.d.ts.map +1 -1
- package/dist/src/format/types.d.ts +35 -23
- package/dist/src/format/types.d.ts.map +1 -1
- package/dist/src/format/types.js +16 -14
- package/dist/src/format/types.js.map +1 -1
- package/dist/src/forms/field/index.js +4 -4
- package/dist/src/forms/field/index.js.map +1 -1
- package/dist/src/forms/file-upload/index.js +16 -16
- package/dist/src/forms/file-upload/index.js.map +1 -1
- package/dist/src/index.d.ts +4 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +12 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/internal.d.ts +8 -3
- package/dist/src/internal.d.ts.map +1 -1
- package/dist/src/internal.js +11 -3
- package/dist/src/internal.js.map +1 -1
- package/dist/src/layout/box/index.d.ts.map +1 -1
- package/dist/src/layout/box/index.js +47 -47
- package/dist/src/layout/box/index.js.map +1 -1
- package/dist/src/layout/flex/index.d.ts.map +1 -1
- package/dist/src/layout/flex/index.js +47 -47
- package/dist/src/layout/flex/index.js.map +1 -1
- package/dist/src/layout/grid/index.js +11 -11
- package/dist/src/layout/grid/index.js.map +1 -1
- package/dist/src/layout/scroll-area/index.d.ts.map +1 -1
- package/dist/src/layout/scroll-area/index.js +8 -8
- package/dist/src/layout/scroll-area/index.js.map +1 -1
- package/dist/src/layout/separator/index.d.ts.map +1 -1
- package/dist/src/layout/separator/index.js +6 -6
- package/dist/src/layout/separator/index.js.map +1 -1
- package/dist/src/layout/stack/index.d.ts.map +1 -1
- package/dist/src/layout/stack/index.js +46 -46
- package/dist/src/layout/stack/index.js.map +1 -1
- package/dist/src/layout/sticky/index.d.ts.map +1 -1
- package/dist/src/layout/sticky/index.js +8 -8
- package/dist/src/layout/sticky/index.js.map +1 -1
- package/dist/src/overlays/action-bar/index.d.ts.map +1 -1
- package/dist/src/overlays/action-bar/index.js +8 -8
- package/dist/src/overlays/action-bar/index.js.map +1 -1
- package/dist/src/platform/index.d.ts +1 -1
- package/dist/src/platform/index.d.ts.map +1 -1
- package/dist/src/platform/index.js +1 -1
- package/dist/src/platform/index.js.map +1 -1
- package/dist/src/platform/slice/impl.d.ts +62 -51
- package/dist/src/platform/slice/impl.d.ts.map +1 -1
- package/dist/src/platform/slice/impl.js +155 -72
- package/dist/src/platform/slice/impl.js.map +1 -1
- package/dist/src/platform/slice/index.d.ts +3744 -2226
- package/dist/src/platform/slice/index.d.ts.map +1 -1
- package/dist/src/platform/slice/index.js +43 -1
- package/dist/src/platform/slice/index.js.map +1 -1
- package/dist/src/runtime/collections/deck.d.ts +4 -0
- package/dist/src/runtime/collections/deck.d.ts.map +1 -1
- package/dist/src/runtime/collections/matrix.d.ts +10 -9
- package/dist/src/runtime/collections/matrix.d.ts.map +1 -1
- package/dist/src/runtime/collections/matrix.js +11 -6
- package/dist/src/runtime/collections/matrix.js.map +1 -1
- package/dist/src/runtime/collections/plan.d.ts +148 -65
- package/dist/src/runtime/collections/plan.d.ts.map +1 -1
- package/dist/src/runtime/collections/plan.js +155 -67
- package/dist/src/runtime/collections/plan.js.map +1 -1
- package/dist/src/runtime/collections/sheet.d.ts +56 -23
- package/dist/src/runtime/collections/sheet.d.ts.map +1 -1
- package/dist/src/runtime/collections/sheet.js +30 -22
- package/dist/src/runtime/collections/sheet.js.map +1 -1
- package/dist/src/runtime/collections/table.d.ts +18 -11
- package/dist/src/runtime/collections/table.d.ts.map +1 -1
- package/dist/src/runtime/collections/table.js +7 -4
- package/dist/src/runtime/collections/table.js.map +1 -1
- package/dist/src/shared/reify.d.ts +4 -46
- package/dist/src/shared/reify.d.ts.map +1 -1
- package/dist/src/shared/reify.js +4 -61
- package/dist/src/shared/reify.js.map +1 -1
- package/dist/src/shared/tree.d.ts +88 -0
- package/dist/src/shared/tree.d.ts.map +1 -0
- package/dist/src/shared/tree.js +134 -0
- package/dist/src/shared/tree.js.map +1 -0
- package/dist/src/slice/breakdown/index.d.ts +4 -0
- package/dist/src/slice/breakdown/index.d.ts.map +1 -1
- package/dist/src/slice/breakdown/types.d.ts +4 -0
- package/dist/src/slice/breakdown/types.d.ts.map +1 -1
- package/dist/src/slice/cohort/index.d.ts +24 -4
- package/dist/src/slice/cohort/index.d.ts.map +1 -1
- package/dist/src/slice/cohort/index.js +8 -4
- package/dist/src/slice/cohort/index.js.map +1 -1
- package/dist/src/slice/cohort/types.d.ts +11 -0
- package/dist/src/slice/cohort/types.d.ts.map +1 -1
- package/dist/src/slice/cohort/types.js +7 -0
- package/dist/src/slice/cohort/types.js.map +1 -1
- package/dist/src/slice/filter/index.d.ts +4 -0
- package/dist/src/slice/filter/index.d.ts.map +1 -1
- package/dist/src/slice/filter/types.d.ts +4 -0
- package/dist/src/slice/filter/types.d.ts.map +1 -1
- package/dist/src/slice/index.d.ts +736 -0
- package/dist/src/slice/index.d.ts.map +1 -1
- package/dist/src/slice/legend/index.d.ts +4 -0
- package/dist/src/slice/legend/index.d.ts.map +1 -1
- package/dist/src/slice/legend/types.d.ts +4 -0
- package/dist/src/slice/legend/types.d.ts.map +1 -1
- package/dist/src/slice/rail/index.d.ts +8 -4
- package/dist/src/slice/rail/index.d.ts.map +1 -1
- package/dist/src/slice/rail/index.js.map +1 -1
- package/dist/src/slice/range/index.d.ts +4 -0
- package/dist/src/slice/range/index.d.ts.map +1 -1
- package/dist/src/slice/range/types.d.ts +4 -0
- package/dist/src/slice/range/types.d.ts.map +1 -1
- package/dist/src/slice/search/index.d.ts +4 -0
- package/dist/src/slice/search/index.d.ts.map +1 -1
- package/dist/src/slice/search/types.d.ts +4 -0
- package/dist/src/slice/search/types.d.ts.map +1 -1
- package/dist/src/slice/summary/index.d.ts +4 -0
- package/dist/src/slice/summary/index.d.ts.map +1 -1
- package/dist/src/slice/summary/types.d.ts +4 -0
- package/dist/src/slice/summary/types.d.ts.map +1 -1
- package/dist/src/typography/code/index.js +1 -1
- package/dist/src/typography/code/index.js.map +1 -1
- package/dist/src/typography/code-block/index.js +7 -7
- package/dist/src/typography/code-block/index.js.map +1 -1
- package/dist/src/typography/heading/index.js +2 -2
- package/dist/src/typography/heading/index.js.map +1 -1
- package/dist/src/typography/list/index.js +1 -1
- package/dist/src/typography/list/index.js.map +1 -1
- package/dist/src/typography/mark/index.js +1 -1
- package/dist/src/typography/mark/index.js.map +1 -1
- package/dist/src/typography/note/index.js +1 -1
- package/dist/src/typography/note/index.js.map +1 -1
- package/dist/src/typography/numeric/index.d.ts +3 -308
- package/dist/src/typography/numeric/index.d.ts.map +1 -1
- package/dist/src/typography/numeric/index.js +4 -4
- package/dist/src/typography/numeric/index.js.map +1 -1
- package/dist/src/typography/numeric/types.d.ts +1 -154
- package/dist/src/typography/numeric/types.d.ts.map +1 -1
- package/dist/src/typography/text/index.js +1 -1
- package/dist/src/typography/text/index.js.map +1 -1
- package/dist/test/collections/matrix.examples.d.ts +7 -3
- package/dist/test/collections/matrix.examples.d.ts.map +1 -1
- package/dist/test/collections/matrix.examples.js +55 -24
- package/dist/test/collections/matrix.examples.js.map +1 -1
- package/dist/test/collections/paged-source.examples.d.ts +4 -0
- package/dist/test/collections/paged-source.examples.d.ts.map +1 -1
- package/dist/test/collections/paged-source.examples.js +98 -13
- package/dist/test/collections/paged-source.examples.js.map +1 -1
- package/dist/test/collections/plan.examples.d.ts +101 -26
- package/dist/test/collections/plan.examples.d.ts.map +1 -1
- package/dist/test/collections/plan.examples.js +1481 -755
- package/dist/test/collections/plan.examples.js.map +1 -1
- package/dist/test/collections/sheet-link.examples.d.ts +7 -0
- package/dist/test/collections/sheet-link.examples.d.ts.map +1 -0
- package/dist/test/collections/sheet-link.examples.js +41 -0
- package/dist/test/collections/sheet-link.examples.js.map +1 -0
- package/dist/test/collections/sheet-transactions.examples.d.ts +7 -0
- package/dist/test/collections/sheet-transactions.examples.d.ts.map +1 -0
- package/dist/test/collections/sheet-transactions.examples.js +58 -0
- package/dist/test/collections/sheet-transactions.examples.js.map +1 -0
- package/dist/test/collections/sheet.examples.d.ts +52 -5
- package/dist/test/collections/sheet.examples.d.ts.map +1 -1
- package/dist/test/collections/sheet.examples.js +500 -158
- package/dist/test/collections/sheet.examples.js.map +1 -1
- package/dist/test/collections/table.examples.d.ts +31 -3
- package/dist/test/collections/table.examples.d.ts.map +1 -1
- package/dist/test/collections/table.examples.js +188 -28
- package/dist/test/collections/table.examples.js.map +1 -1
- package/dist/test/contracts/editing.examples.d.ts +7 -0
- package/dist/test/contracts/editing.examples.d.ts.map +1 -0
- package/dist/test/contracts/editing.examples.js +58 -0
- package/dist/test/contracts/editing.examples.js.map +1 -0
- package/dist/test/layout/splitter.examples.d.ts.map +1 -1
- package/dist/test/layout/splitter.examples.js +3 -1
- package/dist/test/layout/splitter.examples.js.map +1 -1
- package/dist/test/platform/slice.examples.d.ts.map +1 -1
- package/dist/test/platform/slice.examples.js +1 -0
- package/dist/test/platform/slice.examples.js.map +1 -1
- package/dist/test/slice/slice.examples.d.ts.map +1 -1
- package/dist/test/slice/slice.examples.js +5 -2
- package/dist/test/slice/slice.examples.js.map +1 -1
- package/package.json +4 -3
- package/dist/src/collections/plan/data-forms.d.ts +0 -332
- package/dist/src/collections/plan/data-forms.d.ts.map +0 -1
- package/dist/src/collections/plan/data-forms.js +0 -212
- package/dist/src/collections/plan/data-forms.js.map +0 -1
|
@@ -0,0 +1,711 @@
|
|
|
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 editing contract (#879) — one transaction session for every editable
|
|
7
|
+
* collection. A gesture edits DRAFTS (every field missing, a value, or text
|
|
8
|
+
* that could not be read); each gesture is one undoable transaction, reported
|
|
9
|
+
* to the host as a {@link EditingPatchEventTypeFor patch event}; and Apply
|
|
10
|
+
* sends one checked, idempotent batch ({@link EditingChangeSetTypeFor}) against
|
|
11
|
+
* the base the drafts began from, which {@link applyEditing} applies to a
|
|
12
|
+
* collection atomically.
|
|
13
|
+
*
|
|
14
|
+
* The Sheet was first to speak it, and keeps its names for it —
|
|
15
|
+
* `Sheet.Types.ChangeSet` is `Editing.Types.ChangeSet`, `Sheet.apply` is
|
|
16
|
+
* `Editing.apply`. What only a sheet has (a new row's destination among a
|
|
17
|
+
* group's children, the `newRow` / `newGroup` contexts) stays the Sheet's.
|
|
18
|
+
*
|
|
19
|
+
* @packageDocumentation
|
|
20
|
+
*/
|
|
21
|
+
import { ArrayType, AsyncFunctionType, BlobType, DictType, East, EastTypeType, FunctionType, IntegerType, NullType, OptionType, PatchType, SetType, StringType, StructType, VariantType, isTypeEqual, none, some, variant, } from "@elaraai/east";
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Placement, readiness, origins, results
|
|
24
|
+
// ============================================================================
|
|
25
|
+
/**
|
|
26
|
+
* A position in an explicitly ordered collection.
|
|
27
|
+
*
|
|
28
|
+
* @property start - First
|
|
29
|
+
* @property end - Last
|
|
30
|
+
* @property before - Just before the entry with this id
|
|
31
|
+
* @property after - Just after the entry with this id
|
|
32
|
+
*/
|
|
33
|
+
export const EditingPositionType = VariantType({ start: NullType, end: NullType, before: StringType, after: StringType });
|
|
34
|
+
/**
|
|
35
|
+
* Where an entry stands: a position in an ordered collection, or the key order
|
|
36
|
+
* of a keyed one.
|
|
37
|
+
*
|
|
38
|
+
* @property ordered - A position in an Array source
|
|
39
|
+
* @property keyOrder - A keyed source's own order — its entries sort by key
|
|
40
|
+
*/
|
|
41
|
+
export const EditingPlacementType = VariantType({ ordered: EditingPositionType, keyOrder: NullType });
|
|
42
|
+
/**
|
|
43
|
+
* One issue on a field, as an author check reports it.
|
|
44
|
+
*
|
|
45
|
+
* @property field - The field
|
|
46
|
+
* @property message - What is wrong, in the author's words
|
|
47
|
+
*/
|
|
48
|
+
export const EditingFieldIssueType = StructType({ field: StringType, message: StringType });
|
|
49
|
+
/**
|
|
50
|
+
* One entry's readiness, after the structural checks and the author's.
|
|
51
|
+
*
|
|
52
|
+
* @property ready - Nothing stands in the way
|
|
53
|
+
* @property incomplete - Fields still missing
|
|
54
|
+
* @property invalid - Fields whose text could not be read, or that a check refused
|
|
55
|
+
*/
|
|
56
|
+
export const EditingReadinessType = VariantType({
|
|
57
|
+
ready: NullType, incomplete: ArrayType(EditingFieldIssueType), invalid: ArrayType(EditingFieldIssueType),
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* An issue addressed to an entry, and optionally to a child row and a field.
|
|
61
|
+
*
|
|
62
|
+
* @property entry - The entry's id
|
|
63
|
+
* @property row - The child row's index in a group's children, when it is one
|
|
64
|
+
* @property field - The field, when the issue has one
|
|
65
|
+
* @property message - What is wrong
|
|
66
|
+
*/
|
|
67
|
+
export const EditingIssueType = StructType({
|
|
68
|
+
entry: StringType, row: OptionType(IntegerType), field: OptionType(StringType), message: StringType,
|
|
69
|
+
});
|
|
70
|
+
/**
|
|
71
|
+
* The readiness of a whole batch — every draft's issues together.
|
|
72
|
+
*
|
|
73
|
+
* @property ready - The batch may be applied
|
|
74
|
+
* @property incomplete - Fields still missing
|
|
75
|
+
* @property invalid - Fields whose text could not be read, or that a check refused
|
|
76
|
+
*/
|
|
77
|
+
export const EditingBatchReadinessType = VariantType({
|
|
78
|
+
ready: NullType, incomplete: ArrayType(EditingIssueType), invalid: ArrayType(EditingIssueType),
|
|
79
|
+
});
|
|
80
|
+
/**
|
|
81
|
+
* The gesture that made one transaction — how a host tells typing from a
|
|
82
|
+
* paste, a copilot's fill from a planner's drag, and a history step from both.
|
|
83
|
+
*
|
|
84
|
+
* @property typed - Typed into a cell
|
|
85
|
+
* @property pasted - Pasted
|
|
86
|
+
* @property fill - A copilot's fill taken
|
|
87
|
+
* @property row - A copilot's row fill taken
|
|
88
|
+
* @property pattern - A proposed row taken
|
|
89
|
+
* @property insert - An entry inserted
|
|
90
|
+
* @property move - An entry moved
|
|
91
|
+
* @property remove - An entry removed
|
|
92
|
+
* @property resize - An element's extent changed (#879 — a Plan run's start or end)
|
|
93
|
+
* @property drop - Something dropped onto the collection from elsewhere (#879)
|
|
94
|
+
* @property verdict - A review verdict (#879 — Approve, Reject, and their "all" forms)
|
|
95
|
+
* @property undo - Undo
|
|
96
|
+
* @property redo - Redo
|
|
97
|
+
* @property discard - Drafts discarded
|
|
98
|
+
*/
|
|
99
|
+
export const EditingOriginType = VariantType({
|
|
100
|
+
typed: NullType, pasted: NullType, fill: NullType, row: NullType, pattern: NullType,
|
|
101
|
+
insert: NullType, move: NullType, remove: NullType, resize: NullType, drop: NullType, verdict: NullType,
|
|
102
|
+
undo: NullType, redo: NullType, discard: NullType,
|
|
103
|
+
});
|
|
104
|
+
/**
|
|
105
|
+
* The source's authoritative answer to one idempotent request.
|
|
106
|
+
*
|
|
107
|
+
* @property applied - Applied; a mutable source names the revision it committed
|
|
108
|
+
* @property rejected - Refused as asked — revise the drafts before applying again
|
|
109
|
+
* @property conflict - The source moved on — review or discard the drafts
|
|
110
|
+
*/
|
|
111
|
+
export const EditingApplyResultType = VariantType({
|
|
112
|
+
applied: StructType({ revision: OptionType(StringType) }),
|
|
113
|
+
rejected: ArrayType(EditingIssueType), conflict: ArrayType(EditingIssueType),
|
|
114
|
+
});
|
|
115
|
+
// ============================================================================
|
|
116
|
+
// Drafts
|
|
117
|
+
// ============================================================================
|
|
118
|
+
/**
|
|
119
|
+
* One field of a draft — missing, a value of the field's type, or the text a
|
|
120
|
+
* planner typed that could not be read, kept as typed.
|
|
121
|
+
*
|
|
122
|
+
* @typeParam T - The field's type
|
|
123
|
+
* @param type - The field's type
|
|
124
|
+
* @returns The field's draft variant
|
|
125
|
+
*/
|
|
126
|
+
export function EditingDraftFieldType(type) {
|
|
127
|
+
return VariantType({ missing: NullType, value: type, invalid: StringType });
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Constructs the field-by-field draft type of a struct, including the fields
|
|
131
|
+
* no column shows.
|
|
132
|
+
*
|
|
133
|
+
* @typeParam R - The domain row type
|
|
134
|
+
* @param rowType - The complete domain struct
|
|
135
|
+
* @returns The draft struct; an optional domain field still starts missing
|
|
136
|
+
*/
|
|
137
|
+
export function EditingDraftTypeFor(rowType) {
|
|
138
|
+
return StructType(Object.fromEntries(Object.entries(rowType.fields).map(([k, t]) => [k, EditingDraftFieldType(t)])));
|
|
139
|
+
}
|
|
140
|
+
/** The declared child field of a group-or-row entry type (never inferred from arbitrary row fields). */
|
|
141
|
+
const children = new WeakMap();
|
|
142
|
+
/**
|
|
143
|
+
* Constructs a group's draft type, with drafts in its child array.
|
|
144
|
+
*
|
|
145
|
+
* @typeParam G - The complete group struct
|
|
146
|
+
* @typeParam F - Its Array-of-structs child field
|
|
147
|
+
* @param groupType - The group type
|
|
148
|
+
* @param field - The child array field
|
|
149
|
+
* @returns The group's draft struct
|
|
150
|
+
* @throws {Error} When the named field is not an Array of structs
|
|
151
|
+
*/
|
|
152
|
+
export function EditingDraftGroupTypeFor(groupType, field) {
|
|
153
|
+
const t = groupType.fields[field];
|
|
154
|
+
if (t?.type !== "Array" || t.value.type !== "Struct")
|
|
155
|
+
throw new Error(`Editing: "${field}" must hold an Array of row structs`);
|
|
156
|
+
const rowDraft = EditingDraftTypeFor(t.value);
|
|
157
|
+
return StructType(Object.fromEntries(Object.entries(groupType.fields).map(([key, type]) => [key,
|
|
158
|
+
key === field ? ArrayType(rowDraft) : EditingDraftFieldType(type),
|
|
159
|
+
])));
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Constructs the union of groups carrying child rows and ungrouped rows. The
|
|
163
|
+
* child field is recorded with the type, so {@link EditingDraftEntryTypeFor}
|
|
164
|
+
* and {@link EditingPatchEventTypeFor} find it without being told again.
|
|
165
|
+
*
|
|
166
|
+
* @remarks
|
|
167
|
+
* The Sheet names it `Sheet.Types.Entry`; the example below uses that name.
|
|
168
|
+
*
|
|
169
|
+
* @typeParam G - The group struct
|
|
170
|
+
* @typeParam F - Its Array-of-structs child field
|
|
171
|
+
* @param groupType - The group type
|
|
172
|
+
* @param field - The child array field
|
|
173
|
+
* @returns The group-or-row entry type
|
|
174
|
+
* @throws {Error} When the child field is not an Array of structs
|
|
175
|
+
* @example
|
|
176
|
+
* ```ts
|
|
177
|
+
* import { ArrayType, East, OptionType, StringType, StructType, none, some, variant } from "@elaraai/east";
|
|
178
|
+
* import { Sheet } from "@elaraai/east-ui";
|
|
179
|
+
*
|
|
180
|
+
* const reordered = East.function([], ArrayType(StringType), ($) => {
|
|
181
|
+
* const TaskType = StructType({ id: StringType, task: StringType });
|
|
182
|
+
* const PackageType = StructType({ id: StringType, name: StringType, tasks: ArrayType(TaskType) });
|
|
183
|
+
* const Entry = Sheet.Types.Entry(PackageType, "tasks");
|
|
184
|
+
* const before = $.const(variant("group", { id: "p1", name: "P-40 roughing", tasks: [
|
|
185
|
+
* { id: "t1", task: "Machine blanks" }, { id: "t2", task: "Inspect lots" },
|
|
186
|
+
* ] }), Entry);
|
|
187
|
+
* const after = $.const(variant("group", { id: "p1", name: "P-40 roughing", tasks: [
|
|
188
|
+
* { id: "t2", task: "Inspect lots" }, { id: "t1", task: "Machine blanks" },
|
|
189
|
+
* ] }), Entry);
|
|
190
|
+
* const loose = $.const(variant("row", { id: "t9", task: "Pack for shipping" }), Entry);
|
|
191
|
+
* const entries = $.const([before, loose], ArrayType(Entry));
|
|
192
|
+
* const oldEntry = $.const(some(before), OptionType(Entry));
|
|
193
|
+
* const newEntry = $.const(some(after), OptionType(Entry));
|
|
194
|
+
* const batch = $.const({
|
|
195
|
+
* requestId: "reorder-roughing", base: variant("snapshot", entries), label: "Move a task",
|
|
196
|
+
* changes: [{ id: "p1", patch: East.diff(oldEntry, newEntry), place: none }],
|
|
197
|
+
* }, Sheet.Types.ChangeSet(Entry));
|
|
198
|
+
* const apply = $.const(Sheet.apply(Entry, "id"));
|
|
199
|
+
* const applied = $.const(apply(entries, batch, none).unwrap("applied"));
|
|
200
|
+
* return applied.map((_$, entry) => entry.match({
|
|
201
|
+
* group: (_$2, p) => East.str`${p.name}: ${p.tasks.map((_$3, t) => t.task).stringJoin(" → ")}`,
|
|
202
|
+
* row: (_$2, t) => t.task,
|
|
203
|
+
* }));
|
|
204
|
+
* });
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
export function EditingEntryTypeFor(groupType, field) {
|
|
208
|
+
EditingDraftGroupTypeFor(groupType, field);
|
|
209
|
+
const row = groupType.fields[field].value;
|
|
210
|
+
const entry = VariantType({ group: groupType, row });
|
|
211
|
+
children.set(entry, field);
|
|
212
|
+
return entry;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Constructs the draft equivalent of a source entry.
|
|
216
|
+
*
|
|
217
|
+
* @typeParam E - The entry schema
|
|
218
|
+
* @typeParam F - A groups-only source's child field
|
|
219
|
+
* @param entryType - A flat struct or a group-or-row union
|
|
220
|
+
* @param field - The child field for a groups-only source
|
|
221
|
+
* @returns The draft entry schema
|
|
222
|
+
* @throws {Error} When a group union has no unambiguous child field
|
|
223
|
+
*/
|
|
224
|
+
export function EditingDraftEntryTypeFor(entryType, field) {
|
|
225
|
+
if (entryType.type === "Struct") {
|
|
226
|
+
return (field === undefined ? EditingDraftTypeFor(entryType) : EditingDraftGroupTypeFor(entryType, field));
|
|
227
|
+
}
|
|
228
|
+
if (entryType.type === "Variant" && entryType.cases.group?.type === "Struct" && entryType.cases.row?.type === "Struct") {
|
|
229
|
+
const group = entryType.cases.group;
|
|
230
|
+
const row = entryType.cases.row;
|
|
231
|
+
const candidates = Object.entries(group.fields).filter(([, t]) => t.type === "Array" && isTypeEqual(t.value, row));
|
|
232
|
+
const childField = field ?? children.get(entryType) ?? (candidates.length === 1 ? candidates[0][0] : undefined);
|
|
233
|
+
if (childField === undefined)
|
|
234
|
+
throw new Error("Editing: declare the child field with Entry(GroupType, field) — Sheet.Types.Entry / Editing.Types.Entry");
|
|
235
|
+
return VariantType({ group: EditingDraftGroupTypeFor(group, childField), row: EditingDraftTypeFor(row) });
|
|
236
|
+
}
|
|
237
|
+
throw new Error("Editing: entries must be row structs or a group/row variant of structs");
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Constructs the checked base of a batch — the revision a mutable source
|
|
241
|
+
* committed, or the whole inline collection the drafts began from.
|
|
242
|
+
*
|
|
243
|
+
* @typeParam E - The source entry type
|
|
244
|
+
* @typeParam K - A keyed source's key type; omitted for an Array
|
|
245
|
+
* @param entryType - The source entry type
|
|
246
|
+
* @param keyType - A keyed source's key type
|
|
247
|
+
* @returns A revision token or the complete collection snapshot
|
|
248
|
+
*/
|
|
249
|
+
export function EditingBaseTypeFor(entryType, keyType) {
|
|
250
|
+
const snapshot = keyType === undefined ? ArrayType(entryType) : DictType(keyType, entryType);
|
|
251
|
+
return VariantType({ revision: StringType, snapshot });
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Constructs one composed entry patch and its placement.
|
|
255
|
+
*
|
|
256
|
+
* @typeParam E - The source entry type
|
|
257
|
+
* @param entryType - The source entry type
|
|
258
|
+
* @returns An entry-addressed East patch; `none` stands for an absent entry
|
|
259
|
+
*/
|
|
260
|
+
export function EditingChangeTypeFor(entryType) {
|
|
261
|
+
return StructType({ id: StringType, patch: PatchType(OptionType(entryType)), place: OptionType(EditingPlacementType) });
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Constructs an atomic request against a checked base — what `onApply`
|
|
265
|
+
* receives.
|
|
266
|
+
*
|
|
267
|
+
* @typeParam E - The source entry type
|
|
268
|
+
* @typeParam K - A keyed source's key type; omitted for an Array
|
|
269
|
+
* @param entryType - The source entry type
|
|
270
|
+
* @param keyType - A keyed source's key type
|
|
271
|
+
* @returns The batch type
|
|
272
|
+
*/
|
|
273
|
+
export function EditingChangeSetTypeFor(entryType, keyType) {
|
|
274
|
+
return StructType({
|
|
275
|
+
requestId: StringType, base: EditingBaseTypeFor(entryType, keyType), label: StringType,
|
|
276
|
+
changes: ArrayType(EditingChangeTypeFor(entryType)),
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Constructs the result of {@link applyEditing}.
|
|
281
|
+
*
|
|
282
|
+
* @typeParam E - The source entry type
|
|
283
|
+
* @typeParam K - A keyed source's key type; omitted for an Array
|
|
284
|
+
* @param entryType - The source entry type
|
|
285
|
+
* @param keyType - A keyed source's key type
|
|
286
|
+
* @returns The complete applied collection, or the conflict's issues
|
|
287
|
+
*/
|
|
288
|
+
export function EditingAppliedTypeFor(entryType, keyType) {
|
|
289
|
+
const applied = keyType === undefined ? ArrayType(entryType) : DictType(keyType, entryType);
|
|
290
|
+
return VariantType({ applied, conflict: ArrayType(EditingIssueType) });
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Constructs one gesture's typed, draft-aware patch event — what `onPatch`
|
|
294
|
+
* receives, incomplete drafts included.
|
|
295
|
+
*
|
|
296
|
+
* @typeParam E - The source entry type
|
|
297
|
+
* @typeParam F - A groups-only source's child field
|
|
298
|
+
* @param entryType - The domain entry schema
|
|
299
|
+
* @param field - The child field for a groups-only source
|
|
300
|
+
* @returns The event: its gesture, the draft changes, the domain changes once every draft is complete, and the batch's readiness
|
|
301
|
+
*/
|
|
302
|
+
export function EditingPatchEventTypeFor(entryType, field) {
|
|
303
|
+
return EditingPatchEventTypeWith(entryType, EditingDraftEntryTypeFor(entryType, field));
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Constructs the patch event of a collection whose drafts take another shape
|
|
307
|
+
* than a field-by-field one — the Plan drafts a whole entry at once
|
|
308
|
+
* (`DraftField(E)`, #880), since every gesture on it writes a complete entry.
|
|
309
|
+
* {@link EditingPatchEventTypeFor} is this over the field-by-field draft.
|
|
310
|
+
*
|
|
311
|
+
* @typeParam E - The source entry type
|
|
312
|
+
* @typeParam D - The entry's draft type
|
|
313
|
+
* @param entryType - The domain entry schema
|
|
314
|
+
* @param draftType - The entry's draft schema
|
|
315
|
+
* @returns The event: its gesture, the draft changes, the domain changes once every draft is complete, and the batch's readiness
|
|
316
|
+
*/
|
|
317
|
+
export function EditingPatchEventTypeWith(entryType, draftType) {
|
|
318
|
+
return StructType({
|
|
319
|
+
transactionId: StringType, origin: EditingOriginType, label: StringType,
|
|
320
|
+
draftChanges: ArrayType(EditingChangeTypeFor(draftType)),
|
|
321
|
+
domainChanges: OptionType(ArrayType(EditingChangeTypeFor(entryType))),
|
|
322
|
+
readiness: EditingBatchReadinessType,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Constructs an entry-addressed draft patch, placement included.
|
|
327
|
+
*
|
|
328
|
+
* @typeParam E - The source entry schema
|
|
329
|
+
* @typeParam F - A groups-only source's child field
|
|
330
|
+
* @param entryType - The source entry type
|
|
331
|
+
* @param field - The child field for a groups-only source
|
|
332
|
+
* @returns One change to the local draft collection
|
|
333
|
+
*/
|
|
334
|
+
export function EditingDraftChangeTypeFor(entryType, field) {
|
|
335
|
+
return EditingChangeTypeFor(EditingDraftEntryTypeFor(entryType, field));
|
|
336
|
+
}
|
|
337
|
+
export function applyEditing(type, idField) {
|
|
338
|
+
if (idField !== undefined)
|
|
339
|
+
return applyOrdered(type, idField);
|
|
340
|
+
if (type.type !== "Dict") {
|
|
341
|
+
throw new Error("Editing.apply: an Array source names its entries' identity field — Editing.apply(E, \"id\"); a keyed source passes its Dict type — Editing.apply(DictType(K, E))");
|
|
342
|
+
}
|
|
343
|
+
return applyKeyed(type);
|
|
344
|
+
}
|
|
345
|
+
/** {@link applyEditing} over an Array, its entries addressed by their identity field. */
|
|
346
|
+
function applyOrdered(entryType, idField) {
|
|
347
|
+
const identity = (type) => {
|
|
348
|
+
if (type.type === "Struct" && type.fields[idField]?.type === "String") {
|
|
349
|
+
return East.function([type], StringType, (_$, row) => row[idField]);
|
|
350
|
+
}
|
|
351
|
+
if (type.type === "Variant") {
|
|
352
|
+
const arms = Object.fromEntries(Object.entries(type.cases).map(([tag, t]) => [tag, identity(t)]));
|
|
353
|
+
return East.function([type], StringType, ($, entry) => {
|
|
354
|
+
const fns = Object.fromEntries(Object.entries(arms).map(([tag, fn]) => [tag, $.const(fn)]));
|
|
355
|
+
return entry.match(Object.fromEntries(Object.entries(fns).map(([tag, fn]) => [tag, (_$, row) => fn(row)])));
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
throw new Error(`Editing.apply: "${idField}" must be a String field on every entry — pass the source identity field`);
|
|
359
|
+
};
|
|
360
|
+
const idOf = identity(entryType);
|
|
361
|
+
// The algorithm treats entries opaquely. Erase their TS fields here to
|
|
362
|
+
// avoid expanding recursive PatchTypeOf<EastType>; the runtime East type
|
|
363
|
+
// remains the exact author type, including every nested field and arm.
|
|
364
|
+
const opaqueEntry = entryType;
|
|
365
|
+
const rowsType = ArrayType(opaqueEntry);
|
|
366
|
+
const batchType = EditingChangeSetTypeFor(opaqueEntry);
|
|
367
|
+
const resultType = EditingAppliedTypeFor(opaqueEntry);
|
|
368
|
+
return East.function([rowsType, batchType, OptionType(StringType)], resultType, ($, current, batch, revision) => {
|
|
369
|
+
const id = $.const(idOf);
|
|
370
|
+
const issues = $.let([], ArrayType(EditingIssueType));
|
|
371
|
+
const result = $.let(variant("conflict", issues), resultType);
|
|
372
|
+
const activeId = $.let("");
|
|
373
|
+
$.try(($) => {
|
|
374
|
+
const valid = $.const(batch.base.match({
|
|
375
|
+
snapshot: (_$, snapshot) => East.equal(current, snapshot),
|
|
376
|
+
revision: (_$, expected) => East.equal(revision, some(expected)),
|
|
377
|
+
}));
|
|
378
|
+
$.if(valid.not(), ($) => { $.error("The source changed since this batch began — discard or review against the new source"); });
|
|
379
|
+
const seen = $.let(new Set(), SetType(StringType));
|
|
380
|
+
$.for(current, ($, row) => {
|
|
381
|
+
const key = $.const(id(row));
|
|
382
|
+
$.if(seen.has(key), ($) => { $.error("The source contains duplicate entry identities"); });
|
|
383
|
+
$(seen.insert(key));
|
|
384
|
+
});
|
|
385
|
+
// Detach nested mutable values: even a conflict after an earlier
|
|
386
|
+
// patch leaves the caller's complete collection untouched.
|
|
387
|
+
const next = $.let(East.Blob.encodeBeast(current, "v2").decodeBeast(rowsType, "v2"));
|
|
388
|
+
const changed = $.let(new Set(), SetType(StringType));
|
|
389
|
+
$.for(batch.changes, ($, change) => {
|
|
390
|
+
$.assign(activeId, change.id);
|
|
391
|
+
$.if(changed.has(change.id), ($) => { $.error("Compose each entry into one change before applying a batch"); });
|
|
392
|
+
$(changed.insert(change.id));
|
|
393
|
+
const before = $.const(next.firstMap((_$, row) => id(row).equal(change.id).ifElse(() => some(row), () => none)));
|
|
394
|
+
const after = $.const(East.applyPatch(before, change.patch));
|
|
395
|
+
$.if(after.hasTag("some"), ($) => {
|
|
396
|
+
const row = $.const(after.unwrap("some"));
|
|
397
|
+
$.if(id(row).notEqual(change.id), ($) => { $.error("An entry patch cannot change its identity"); });
|
|
398
|
+
$.if(before.hasTag("none").and(() => change.place.hasTag("none")), ($) => { $.error("A new entry needs an explicit placement"); });
|
|
399
|
+
$.if(before.hasTag("some"), ($) => {
|
|
400
|
+
$.assign(next, next.map((_$, old) => id(old).equal(change.id).ifElse(() => row, () => old)));
|
|
401
|
+
}).else(($) => { $.assign(next, next.concat([row])); });
|
|
402
|
+
}).else(($) => {
|
|
403
|
+
$.if(change.place.hasTag("some"), ($) => { $.error("A removed entry cannot have a placement"); });
|
|
404
|
+
$.assign(next, next.filter((_$, row) => id(row).notEqual(change.id)));
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
// All newly created identities exist before resolving anchors.
|
|
408
|
+
$.for(batch.changes, ($, change) => {
|
|
409
|
+
$.assign(activeId, change.id);
|
|
410
|
+
$.if(change.place.hasTag("some"), ($) => {
|
|
411
|
+
const placement = $.const(change.place.unwrap("some"));
|
|
412
|
+
$.if(placement.hasTag("keyOrder"), ($) => { $.error("An inline Array requires an ordered placement; keyOrder belongs to a keyed source"); });
|
|
413
|
+
const position = $.const(placement.unwrap("ordered"));
|
|
414
|
+
const row = $.const(next.firstMap((_$, row) => id(row).equal(change.id).ifElse(() => some(row), () => none)).unwrap("some"));
|
|
415
|
+
const rest = $.const(next.filter((_$, row) => id(row).notEqual(change.id)));
|
|
416
|
+
const anchored = $.const(East.function([StringType], rowsType, ($, anchor) => {
|
|
417
|
+
$.if(anchor.equal(change.id).or(() => rest.filter((_$, r) => id(r).equal(anchor)).size().equal(0n)), ($) => {
|
|
418
|
+
$.error("The placement anchor is missing or names the moved entry");
|
|
419
|
+
});
|
|
420
|
+
return rest.flatMap((_$, other) => id(other).equal(anchor).ifElse(() => position.hasTag("before").ifElse(() => [row, other], () => [other, row]), () => [other]));
|
|
421
|
+
}));
|
|
422
|
+
$.assign(next, position.match({
|
|
423
|
+
start: () => East.value([row], rowsType).concat(rest),
|
|
424
|
+
end: () => rest.concat([row]),
|
|
425
|
+
before: (_$, anchor) => anchored(anchor),
|
|
426
|
+
after: (_$, anchor) => anchored(anchor),
|
|
427
|
+
}));
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
$.assign(result, variant("applied", next));
|
|
431
|
+
}).catch(($, message) => {
|
|
432
|
+
$(issues.pushLast({ entry: activeId, row: none, field: none, message }));
|
|
433
|
+
$.assign(result, variant("conflict", issues));
|
|
434
|
+
});
|
|
435
|
+
return result;
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
/** {@link applyEditing} over a keyed Dict, its entries addressed by key. */
|
|
439
|
+
function applyKeyed(sourceType) {
|
|
440
|
+
const keyType = sourceType.key;
|
|
441
|
+
// Entries are opaque here, as in the ordered form; the runtime types are exact.
|
|
442
|
+
const opaqueEntry = sourceType.value;
|
|
443
|
+
const entriesType = DictType(keyType, opaqueEntry);
|
|
444
|
+
const batchType = EditingChangeSetTypeFor(opaqueEntry, keyType);
|
|
445
|
+
const resultType = EditingAppliedTypeFor(opaqueEntry, keyType);
|
|
446
|
+
// A change names its entry by the key's text: a String key is its own
|
|
447
|
+
// text, any other key its `.east` printing, read back here.
|
|
448
|
+
const keyOf = keyType.type === "String"
|
|
449
|
+
? East.function([StringType], StringType, (_$, id) => id)
|
|
450
|
+
: East.function([StringType], keyType, (_$, id) => id.parse(keyType));
|
|
451
|
+
return East.function([entriesType, batchType, OptionType(StringType)], resultType, ($, current, batch, revision) => {
|
|
452
|
+
const keyFrom = $.const(keyOf);
|
|
453
|
+
const issues = $.let([], ArrayType(EditingIssueType));
|
|
454
|
+
const result = $.let(variant("conflict", issues), resultType);
|
|
455
|
+
const activeId = $.let("");
|
|
456
|
+
$.try(($) => {
|
|
457
|
+
const valid = $.const(batch.base.match({
|
|
458
|
+
snapshot: (_$, snapshot) => East.equal(current, snapshot),
|
|
459
|
+
revision: (_$, expected) => East.equal(revision, some(expected)),
|
|
460
|
+
}));
|
|
461
|
+
$.if(valid.not(), ($) => { $.error("The source changed since this batch began — discard or review against the new source"); });
|
|
462
|
+
// Detached, as in the ordered form: a conflict leaves the caller's collection untouched.
|
|
463
|
+
const next = $.let(East.Blob.encodeBeast(current, "v2").decodeBeast(entriesType, "v2"));
|
|
464
|
+
const changed = $.let(new Set(), SetType(StringType));
|
|
465
|
+
$.for(batch.changes, ($, change) => {
|
|
466
|
+
$.assign(activeId, change.id);
|
|
467
|
+
$.if(changed.has(change.id), ($) => { $.error("Compose each entry into one change before applying a batch"); });
|
|
468
|
+
$(changed.insert(change.id));
|
|
469
|
+
const key = $.const(keyFrom(change.id));
|
|
470
|
+
$.if(change.place.hasTag("some").and(() => change.place.unwrap("some").hasTag("ordered")), ($) => {
|
|
471
|
+
$.error("A keyed source orders its entries by key — its placement is keyOrder");
|
|
472
|
+
});
|
|
473
|
+
const before = $.const(next.tryGet(key));
|
|
474
|
+
const after = $.const(East.applyPatch(before, change.patch));
|
|
475
|
+
$.if(after.hasTag("some"), ($) => {
|
|
476
|
+
$.if(before.hasTag("none").and(() => change.place.hasTag("none")), ($) => { $.error("A new entry needs an explicit placement"); });
|
|
477
|
+
$(next.insertOrUpdate(key, after.unwrap("some"), (_$, _old, value) => value));
|
|
478
|
+
}).else(($) => {
|
|
479
|
+
$.if(change.place.hasTag("some"), ($) => { $.error("A removed entry cannot have a placement"); });
|
|
480
|
+
$.if(before.hasTag("some"), ($) => { $(next.delete(key)); });
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
$.assign(result, variant("applied", next));
|
|
484
|
+
}).catch(($, message) => {
|
|
485
|
+
$(issues.pushLast({ entry: activeId, row: none, field: none, message }));
|
|
486
|
+
$.assign(result, variant("conflict", issues));
|
|
487
|
+
});
|
|
488
|
+
return result;
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
// ============================================================================
|
|
492
|
+
// The wire — what a collection hands the renderer's session
|
|
493
|
+
// ============================================================================
|
|
494
|
+
/**
|
|
495
|
+
* A synchronous or asynchronous authoritative apply callback, over the batch's
|
|
496
|
+
* bytes.
|
|
497
|
+
*
|
|
498
|
+
* @internal
|
|
499
|
+
* @property sync - A function
|
|
500
|
+
* @property async - An async function
|
|
501
|
+
*/
|
|
502
|
+
export const EditingWireApplyType = VariantType({
|
|
503
|
+
sync: FunctionType([BlobType], EditingApplyResultType),
|
|
504
|
+
async: AsyncFunctionType([BlobType], EditingApplyResultType),
|
|
505
|
+
});
|
|
506
|
+
/**
|
|
507
|
+
* When a ready batch goes: on Apply, or as soon as it is ready. Both use the
|
|
508
|
+
* same serial checked protocol.
|
|
509
|
+
*
|
|
510
|
+
* @property batch - On Apply
|
|
511
|
+
* @property auto - As soon as the batch is ready
|
|
512
|
+
*/
|
|
513
|
+
export const EditingApplyModeType = VariantType({ batch: NullType, auto: NullType });
|
|
514
|
+
/**
|
|
515
|
+
* The fields every source-bound editing session carries to the renderer's
|
|
516
|
+
* shared session — a collection's own editing declaration spreads them beside
|
|
517
|
+
* its own (`SheetEditingType`). Blobs carry values at the exact schemas named,
|
|
518
|
+
* so no hidden domain field is ever lost.
|
|
519
|
+
*
|
|
520
|
+
* @internal
|
|
521
|
+
*/
|
|
522
|
+
export const EditingSessionFields = {
|
|
523
|
+
/** The source's identity — one session, and one unresolved request, per source. */
|
|
524
|
+
sourceId: StringType,
|
|
525
|
+
/** The entry schema. */
|
|
526
|
+
entryType: EastTypeType,
|
|
527
|
+
/** An Array source's String identity field. */
|
|
528
|
+
idField: OptionType(StringType),
|
|
529
|
+
/** The entry's draft schema. */
|
|
530
|
+
draftType: EastTypeType,
|
|
531
|
+
/** A group entry's child field. */
|
|
532
|
+
children: OptionType(StringType),
|
|
533
|
+
/**
|
|
534
|
+
* A keyed source's key type — its entries sort by key, a change names its
|
|
535
|
+
* entry by the key's text (a String key as it is, any other key its
|
|
536
|
+
* `.east` text), its batches are `ChangeSet(E, K)` and an inline snapshot
|
|
537
|
+
* is a `Dict` of it; `none` for an Array source.
|
|
538
|
+
*/
|
|
539
|
+
keyType: OptionType(EastTypeType),
|
|
540
|
+
/** An inline source's whole collection — the base its batches check. */
|
|
541
|
+
snapshot: OptionType(BlobType),
|
|
542
|
+
/** One entry by id at its source offset, for a paged source's reads. */
|
|
543
|
+
readEntry: FunctionType([StringType, IntegerType], OptionType(BlobType)),
|
|
544
|
+
/** The author's patch observer, over the event's bytes. */
|
|
545
|
+
onPatch: OptionType(FunctionType([BlobType], NullType)),
|
|
546
|
+
/** The authoritative apply. */
|
|
547
|
+
onApply: OptionType(EditingWireApplyType),
|
|
548
|
+
/** When a ready batch goes. */
|
|
549
|
+
mode: EditingApplyModeType,
|
|
550
|
+
};
|
|
551
|
+
/**
|
|
552
|
+
* The shared session's wire declaration — {@link EditingSessionFields} alone.
|
|
553
|
+
*
|
|
554
|
+
* @internal
|
|
555
|
+
*/
|
|
556
|
+
export const EditingType = StructType(EditingSessionFields);
|
|
557
|
+
// ============================================================================
|
|
558
|
+
// The inline adapter — idempotent writes through a live handle
|
|
559
|
+
// ============================================================================
|
|
560
|
+
/**
|
|
561
|
+
* The inline adapter's request ledger. Unlike reactive state, it must outlive
|
|
562
|
+
* a collection disappearing from a render; the browser implementation scopes
|
|
563
|
+
* it to the current UI store's lifetime.
|
|
564
|
+
*
|
|
565
|
+
* @internal
|
|
566
|
+
*/
|
|
567
|
+
export const EditingRequestStore = {
|
|
568
|
+
read: East.platform("editing_requests_read", [StringType], OptionType(BlobType), { optional: true }),
|
|
569
|
+
write: East.platform("editing_requests_write", [StringType, BlobType], NullType, { optional: true }),
|
|
570
|
+
};
|
|
571
|
+
/** A request retains its payload before writing, and the target of an uncertain write. */
|
|
572
|
+
const RequestType = StructType({
|
|
573
|
+
payload: BlobType,
|
|
574
|
+
result: OptionType(EditingApplyResultType),
|
|
575
|
+
expected: OptionType(BlobType),
|
|
576
|
+
});
|
|
577
|
+
const RequestsType = DictType(StringType, RequestType);
|
|
578
|
+
/**
|
|
579
|
+
* Builds the inline `onUpdate` adapter over a live Array handle.
|
|
580
|
+
*
|
|
581
|
+
* A confirmed request replays before any stale-base check. A throwing write
|
|
582
|
+
* leaves an unresolved request in the store: retries confirm the target by
|
|
583
|
+
* reading it, and never repeat an uncertain mutation. The request table lives
|
|
584
|
+
* in the same browser state store as the binding, surviving remounts. It
|
|
585
|
+
* holds the source's latest request alone — a new one is taken only once
|
|
586
|
+
* every earlier one is resolved, so none of those is retried again — and a
|
|
587
|
+
* confirmed request keeps no copy of the collection it wrote.
|
|
588
|
+
*
|
|
589
|
+
* @internal
|
|
590
|
+
* @param entryType - The exact domain schema
|
|
591
|
+
* @param idField - The entry identity field
|
|
592
|
+
* @param sourceId - Stable identity of the captured binding
|
|
593
|
+
* @param read - Invocation-time collection reader
|
|
594
|
+
* @param write - The author's whole-collection writer
|
|
595
|
+
* @returns A closed Blob transport function for the typed apply callback
|
|
596
|
+
*/
|
|
597
|
+
export function buildInlineApply(entryType, idField, sourceId, read, write) {
|
|
598
|
+
const entry = entryType;
|
|
599
|
+
return inlineApply(ArrayType(entry), EditingChangeSetTypeFor(entry), applyEditing(entry, idField), sourceId, read, write);
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Builds the inline `onUpdate` adapter over a live keyed handle — the
|
|
603
|
+
* {@link buildInlineApply} protocol over a `Dict`, its batches applied by key
|
|
604
|
+
* (`Editing.apply(DictType(K, E))`, #880).
|
|
605
|
+
*
|
|
606
|
+
* @internal
|
|
607
|
+
* @param sourceType - The source's Dict type
|
|
608
|
+
* @param sourceId - Stable identity of the captured binding
|
|
609
|
+
* @param read - Invocation-time collection reader
|
|
610
|
+
* @param write - The author's whole-collection writer
|
|
611
|
+
* @returns A closed Blob transport function for the typed apply callback
|
|
612
|
+
*/
|
|
613
|
+
export function buildKeyedInlineApply(sourceType, sourceId, read, write) {
|
|
614
|
+
const entry = sourceType.value;
|
|
615
|
+
return inlineApply(DictType(sourceType.key, entry), EditingChangeSetTypeFor(entry, sourceType.key), applyEditing(DictType(sourceType.key, entry)), sourceId, read, write);
|
|
616
|
+
}
|
|
617
|
+
/** The inline adapter's protocol over one collection type — see {@link buildInlineApply}. */
|
|
618
|
+
function inlineApply(collectionType, batchType, apply, sourceId, read, write) {
|
|
619
|
+
// The algorithm treats the collection opaquely; its runtime type is exact.
|
|
620
|
+
const _rowsType = collectionType;
|
|
621
|
+
const typedBatch = batchType;
|
|
622
|
+
return East.function([BlobType], EditingApplyResultType, ($, payload) => {
|
|
623
|
+
const reader = $.const(read);
|
|
624
|
+
const writer = $.const(write);
|
|
625
|
+
const transform = $.const(apply);
|
|
626
|
+
const key = $.const(East.str `editing.requests:${sourceId}`);
|
|
627
|
+
const saved = $.const(EditingRequestStore.read(key));
|
|
628
|
+
const requests = $.let(saved.match({ none: ($) => $.const(new Map(), RequestsType), some: (_$, blob) => blob.decodeBeast(RequestsType, "v2") }));
|
|
629
|
+
const batch = $.const(payload.decodeBeast(typedBatch, "v2"));
|
|
630
|
+
const recorded = $.const(requests.tryGet(batch.requestId));
|
|
631
|
+
return recorded.match({
|
|
632
|
+
some: ($, request) => East.equal(request.payload, payload).ifElse((_$) => request.result.match({
|
|
633
|
+
some: (_$, result) => result,
|
|
634
|
+
none: ($) => {
|
|
635
|
+
// An uncertain writer is never invoked twice. Only
|
|
636
|
+
// the exact target establishes successful application.
|
|
637
|
+
const current = $.const(East.Blob.encodeBeast(reader(), "v2"));
|
|
638
|
+
return East.equal(request.expected, some(current)).ifElse(($) => {
|
|
639
|
+
const confirmed = $.const(variant("applied", { revision: none }), EditingApplyResultType);
|
|
640
|
+
// Confirmed: the target it was recovered by is no longer needed.
|
|
641
|
+
$(requests.insertOrUpdate(batch.requestId, { payload, result: some(confirmed), expected: none }, (_$, _old, next) => next));
|
|
642
|
+
$(EditingRequestStore.write(key, East.Blob.encodeBeast(requests, "v2")));
|
|
643
|
+
return confirmed;
|
|
644
|
+
}, ($) => $.error("The previous write has an unknown outcome — retry after its target is confirmed; it cannot be safely issued twice"));
|
|
645
|
+
},
|
|
646
|
+
}), ($) => $.const(variant("conflict", [{ entry: "", row: none, field: none, message: "This request id already names a different batch" }]), EditingApplyResultType)),
|
|
647
|
+
none: ($) => {
|
|
648
|
+
const unresolved = $.const(requests.toArray((_$, request) => request.result.hasTag("none")).filter((_$, pending) => pending).size());
|
|
649
|
+
$.if(unresolved.greater(0n), ($) => {
|
|
650
|
+
$.error("This source already has an unresolved write — recover its original request before submitting another batch");
|
|
651
|
+
});
|
|
652
|
+
// Every earlier request is resolved, so none is retried again:
|
|
653
|
+
// the ledger keeps this one alone.
|
|
654
|
+
const ledger = $.let(new Map(), RequestsType);
|
|
655
|
+
const current = $.const(reader());
|
|
656
|
+
const applied = $.const(transform(current, batch, none));
|
|
657
|
+
return applied.match({
|
|
658
|
+
conflict: ($, issues) => {
|
|
659
|
+
const result = $.const(variant("conflict", issues), EditingApplyResultType);
|
|
660
|
+
$(ledger.insert(batch.requestId, { payload, result: some(result), expected: none }));
|
|
661
|
+
$(EditingRequestStore.write(key, East.Blob.encodeBeast(ledger, "v2")));
|
|
662
|
+
return result;
|
|
663
|
+
},
|
|
664
|
+
applied: ($, rows) => {
|
|
665
|
+
const expected = $.const(some(East.Blob.encodeBeast(rows, "v2")), OptionType(BlobType));
|
|
666
|
+
$(ledger.insert(batch.requestId, { payload, result: none, expected }));
|
|
667
|
+
$(EditingRequestStore.write(key, East.Blob.encodeBeast(ledger, "v2")));
|
|
668
|
+
$(writer(rows));
|
|
669
|
+
const result = $.const(variant("applied", { revision: none }), EditingApplyResultType);
|
|
670
|
+
// Confirmed: the target is kept only while the outcome is unknown.
|
|
671
|
+
$(ledger.insertOrUpdate(batch.requestId, { payload, result: some(result), expected: none }, (_$, _old, next) => next));
|
|
672
|
+
$(EditingRequestStore.write(key, East.Blob.encodeBeast(ledger, "v2")));
|
|
673
|
+
return result;
|
|
674
|
+
},
|
|
675
|
+
});
|
|
676
|
+
},
|
|
677
|
+
});
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* The editing contract (#879) — the one transaction session every editable
|
|
682
|
+
* collection speaks: drafts, one undoable transaction per gesture reported as a
|
|
683
|
+
* `PatchEvent`, and Apply as one checked, idempotent `ChangeSet` applied with
|
|
684
|
+
* `Editing.apply`. The Sheet keeps its names for it (`Sheet.Types.ChangeSet`,
|
|
685
|
+
* `Sheet.apply`, …) — the same values.
|
|
686
|
+
*/
|
|
687
|
+
export const Editing = {
|
|
688
|
+
apply: applyEditing,
|
|
689
|
+
Types: {
|
|
690
|
+
DraftField: EditingDraftFieldType,
|
|
691
|
+
Draft: EditingDraftTypeFor,
|
|
692
|
+
DraftGroup: EditingDraftGroupTypeFor,
|
|
693
|
+
Entry: EditingEntryTypeFor,
|
|
694
|
+
DraftEntry: EditingDraftEntryTypeFor,
|
|
695
|
+
DraftChange: EditingDraftChangeTypeFor,
|
|
696
|
+
Change: EditingChangeTypeFor,
|
|
697
|
+
ChangeSet: EditingChangeSetTypeFor,
|
|
698
|
+
Base: EditingBaseTypeFor,
|
|
699
|
+
Applied: EditingAppliedTypeFor,
|
|
700
|
+
ApplyResult: EditingApplyResultType,
|
|
701
|
+
Issue: EditingIssueType,
|
|
702
|
+
FieldIssue: EditingFieldIssueType,
|
|
703
|
+
Readiness: EditingReadinessType,
|
|
704
|
+
BatchReadiness: EditingBatchReadinessType,
|
|
705
|
+
Origin: EditingOriginType,
|
|
706
|
+
PatchEvent: EditingPatchEventTypeFor,
|
|
707
|
+
Placement: EditingPlacementType,
|
|
708
|
+
Position: EditingPositionType,
|
|
709
|
+
},
|
|
710
|
+
};
|
|
711
|
+
//# sourceMappingURL=editing.js.map
|