@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
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
* positional surface, a `Dict<String, Row>` for a keyed one (#568). east-ui
|
|
12
12
|
* declares the shape; whoever can actually fetch windows produces a value of
|
|
13
13
|
* it. `Data.bindPaged` in `@elaraai/e3-ui` is the production implementation
|
|
14
|
-
* (dataset windows over an e3 workspace) and {@link Paged.of}
|
|
15
|
-
*
|
|
14
|
+
* (dataset windows over an e3 workspace) and {@link Paged.of} /
|
|
15
|
+
* {@link Paged.pinned} are the in-memory ones; e3 is never named here, and
|
|
16
|
+
* **nothing in this file imports e3**.
|
|
16
17
|
*
|
|
17
18
|
* # Why a variant, not a narrow struct
|
|
18
19
|
*
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
*
|
|
29
30
|
* @packageDocumentation
|
|
30
31
|
*/
|
|
31
|
-
import { East, Expr, ArrayType, BooleanType, DictType, FunctionType, IntegerType, OptionType, StringType, StructType, VariantType, variant, some, none, } from "@elaraai/east";
|
|
32
|
+
import { East, Expr, ArrayType, BooleanType, DictType, FunctionType, IntegerType, NullType, OptionType, SetType, StringType, StructType, VariantType, isTypeEqual, variant, some, none, } from "@elaraai/east";
|
|
32
33
|
// ============================================================================
|
|
33
34
|
// Seek — where a key lands in a source's row space
|
|
34
35
|
// ============================================================================
|
|
@@ -123,10 +124,10 @@ export const SeekQueryType = VariantType({
|
|
|
123
124
|
*
|
|
124
125
|
* Parameterised on the COLLECTION, not on the row: a window of an `Array<R>`
|
|
125
126
|
* dataset is an `Array<R>`, a window of a `Dict<K, V>` dataset is a
|
|
126
|
-
* `Dict<K, V>`, and the Plan's canvas windows are
|
|
127
|
-
* (#
|
|
128
|
-
* `Option<T>` at the dataset's own type — so the
|
|
129
|
-
* instead of narrowing it to arrays.
|
|
127
|
+
* `Dict<K, V>`, and the Plan's canvas windows are its blocks — one
|
|
128
|
+
* `Array<PlanRow>` stream per series (#823). That is what `Data.bindPaged`
|
|
129
|
+
* already produces — it returns `Option<T>` at the dataset's own type — so the
|
|
130
|
+
* contract matches the producer instead of narrowing it to arrays.
|
|
130
131
|
*
|
|
131
132
|
* `page` and `total` follow the in-flight-is-`none` convention: a window still
|
|
132
133
|
* being fetched reads `none` and the call re-fires when it lands, so the reads
|
|
@@ -134,6 +135,28 @@ export const SeekQueryType = VariantType({
|
|
|
134
135
|
* source is exhausted at that offset — a reader that walks offsets terminates
|
|
135
136
|
* on an empty window, never on `none`.
|
|
136
137
|
*
|
|
138
|
+
* A window may also hold FEWER than `limit` elements while the source still
|
|
139
|
+
* has more: a source that bounds its pages serves what fits. e3 trims every
|
|
140
|
+
* page of a dataset to a byte budget — the same number of elements on every
|
|
141
|
+
* page of one dataset — so wide elements come back in short windows (#829).
|
|
142
|
+
* Only an EMPTY window means exhausted. Components never see a short window:
|
|
143
|
+
* {@link buildRowSource}'s derived `page` re-requests whatever a trimmed page
|
|
144
|
+
* left out, so every window it serves is whole.
|
|
145
|
+
*
|
|
146
|
+
* A window that CANNOT be read — a failed fetch, a page that does not decode —
|
|
147
|
+
* makes `page` THROW its reason; it never reads `none`, which means in flight,
|
|
148
|
+
* and a reader waiting on it would wait forever (#811). A component shows the
|
|
149
|
+
* failure where that window's rows would be and asks again when the user
|
|
150
|
+
* retries. A source may rate-limit repeat attempts (e3 relaunches a failed
|
|
151
|
+
* window on a read at least two seconds later), and an authoring error — a
|
|
152
|
+
* dataset that cannot be paged — keeps throwing. `seek` follows the same rule.
|
|
153
|
+
*
|
|
154
|
+
* A paged source names no snapshot, so a component reads each of its windows
|
|
155
|
+
* once: the same `id` serves the same rows for as long as the component holds
|
|
156
|
+
* it. A source whose content moves under one id — a dataset the view follows,
|
|
157
|
+
* a source the view writes — is a {@link PinnedSourceType}, which names the
|
|
158
|
+
* snapshot its windows, total and searches belong to.
|
|
159
|
+
*
|
|
137
160
|
* @typeParam C - The collection type one window carries.
|
|
138
161
|
* @param c - The collection type value.
|
|
139
162
|
* @returns The concrete `StructType` of a paged source over `c`.
|
|
@@ -144,11 +167,13 @@ export const SeekQueryType = VariantType({
|
|
|
144
167
|
* never re-renders when the source is swapped, and a window cache cannot key
|
|
145
168
|
* itself. Two sources with the same `id` must serve the same rows.
|
|
146
169
|
* @property page - `(offset, limit)` → that window's elements as a value of the
|
|
147
|
-
* collection type; `none` while in flight, an EMPTY collection at exhaustion
|
|
170
|
+
* collection type; `none` while in flight, an EMPTY collection at exhaustion,
|
|
171
|
+
* and possibly fewer than `limit` elements before it (see above). Throws when
|
|
172
|
+
* the window cannot be read.
|
|
148
173
|
* @property total - The source's total element count, once known; `none` until then.
|
|
149
174
|
* @property seek - The source's key-search capability ({@link SeekType}) —
|
|
150
175
|
* `none` when the source is not key-ordered (an Array-backed source cannot
|
|
151
|
-
* seek; there is nothing to binary-search).
|
|
176
|
+
* seek; there is nothing to binary-search). A search that fails throws.
|
|
152
177
|
*/
|
|
153
178
|
export const PagedSourceType = (c) => StructType({
|
|
154
179
|
id: StringType,
|
|
@@ -156,6 +181,46 @@ export const PagedSourceType = (c) => StructType({
|
|
|
156
181
|
total: FunctionType([], OptionType(IntegerType)),
|
|
157
182
|
seek: OptionType(FunctionType([SeekQueryType], OptionType(SeekRangeType))),
|
|
158
183
|
});
|
|
184
|
+
/**
|
|
185
|
+
* A {@link PagedSourceType} that names its SNAPSHOT — the contract a component
|
|
186
|
+
* follows through changes to the source's content, and the one it edits.
|
|
187
|
+
*
|
|
188
|
+
* @remarks
|
|
189
|
+
* Every window, the total and every search belong to one snapshot, the
|
|
190
|
+
* source's `revision`, so rows from two snapshots never sit side by side. When
|
|
191
|
+
* the revision moves — the data was written, `refresh` was asked for — a
|
|
192
|
+
* component reads its windows again at the new one, and the rows it had stand
|
|
193
|
+
* in until each lands: the view never empties between two snapshots of its
|
|
194
|
+
* data (#821). An edit is checked against the snapshot it was drafted on, and
|
|
195
|
+
* the drafts retire once the source reads back at the snapshot the write made
|
|
196
|
+
* (#880) — which is why the Sheet and the Plan edit a paged source only
|
|
197
|
+
* through this contract.
|
|
198
|
+
*
|
|
199
|
+
* `Data.bindPaged` in `@elaraai/e3-ui` returns one, its revision the dataset's
|
|
200
|
+
* content hash, and {@link Paged.pinned} is the in-memory one.
|
|
201
|
+
*
|
|
202
|
+
* @typeParam C - The collection type one window carries.
|
|
203
|
+
* @param c - The collection type value.
|
|
204
|
+
* @returns The concrete `StructType` of a pinned source over `c`.
|
|
205
|
+
*
|
|
206
|
+
* @property id - The logical source's identity, as {@link PagedSourceType}'s;
|
|
207
|
+
* the rows it serves are its current revision's.
|
|
208
|
+
* @property page - As {@link PagedSourceType}'s, at the current revision.
|
|
209
|
+
* @property total - As {@link PagedSourceType}'s, at the current revision.
|
|
210
|
+
* @property seek - As {@link PagedSourceType}'s, at the current revision.
|
|
211
|
+
* @property revision - The snapshot the source serves; `none` while it is
|
|
212
|
+
* being found. A reader re-fires when the source moves to another.
|
|
213
|
+
* @property refresh - Move the source: `some(hash)` to that snapshot — after a
|
|
214
|
+
* write the view confirmed — or `none` to the one the source holds now.
|
|
215
|
+
*/
|
|
216
|
+
export const PinnedSourceType = (c) => StructType({
|
|
217
|
+
id: StringType,
|
|
218
|
+
page: FunctionType([IntegerType, IntegerType], OptionType(c)),
|
|
219
|
+
total: FunctionType([], OptionType(IntegerType)),
|
|
220
|
+
seek: OptionType(FunctionType([SeekQueryType], OptionType(SeekRangeType))),
|
|
221
|
+
revision: FunctionType([], OptionType(StringType)),
|
|
222
|
+
refresh: FunctionType([OptionType(StringType)], NullType),
|
|
223
|
+
});
|
|
159
224
|
// ============================================================================
|
|
160
225
|
// The row-source variant — what components actually store
|
|
161
226
|
// ============================================================================
|
|
@@ -163,10 +228,17 @@ export const PagedSourceType = (c) => StructType({
|
|
|
163
228
|
* How a component's rows arrive: inline, or from a windowed source.
|
|
164
229
|
*
|
|
165
230
|
* @remarks
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
* `Dict<String,
|
|
169
|
-
*
|
|
231
|
+
* Every arm speaks the same COLLECTION type — inline is the whole of it, the
|
|
232
|
+
* windowed arms a window of it — so a keyed source (a Sheet's
|
|
233
|
+
* `Dict<String, Row>`), a positional one (a Table's `Array<Row>`) and a
|
|
234
|
+
* composite one (the Plan's blocks, one row stream per series) share one
|
|
235
|
+
* vocabulary without any shape leaking into the arm names (#568).
|
|
236
|
+
*
|
|
237
|
+
* The two windowed arms differ in one thing: whether the source names its
|
|
238
|
+
* snapshot. `paged` is the released arm — exported UIs carry it, so it never
|
|
239
|
+
* changes — and a component reads each of its windows once; `pinned` names the
|
|
240
|
+
* revision its windows belong to, which is what lets a component follow the
|
|
241
|
+
* source as its content changes, and edit it.
|
|
170
242
|
*
|
|
171
243
|
* @typeParam C - The collection type.
|
|
172
244
|
* @param c - The collection type value.
|
|
@@ -174,10 +246,12 @@ export const PagedSourceType = (c) => StructType({
|
|
|
174
246
|
*
|
|
175
247
|
* @property inline - The whole collection, already in hand.
|
|
176
248
|
* @property paged - A {@link PagedSourceType} fetched a window at a time.
|
|
249
|
+
* @property pinned - A {@link PinnedSourceType} — windows at a named snapshot, followed from one to the next.
|
|
177
250
|
*/
|
|
178
251
|
export const RowSourceType = (c) => VariantType({
|
|
179
252
|
inline: c,
|
|
180
253
|
paged: PagedSourceType(c),
|
|
254
|
+
pinned: PinnedSourceType(c),
|
|
181
255
|
});
|
|
182
256
|
/**
|
|
183
257
|
* The element shape an ORDERED window carries: an index entry.
|
|
@@ -236,6 +310,34 @@ function keyTypeOf(t) {
|
|
|
236
310
|
return type.key;
|
|
237
311
|
return undefined;
|
|
238
312
|
}
|
|
313
|
+
/** The members a {@link PinnedSourceType} adds to a paged source, at their types. */
|
|
314
|
+
const SNAPSHOT_MEMBERS = {
|
|
315
|
+
revision: FunctionType([], OptionType(StringType)),
|
|
316
|
+
refresh: FunctionType([OptionType(StringType)], NullType),
|
|
317
|
+
};
|
|
318
|
+
/**
|
|
319
|
+
* Whether a windowed source names its snapshot — `revision` and `refresh`,
|
|
320
|
+
* both, at {@link PinnedSourceType}'s types.
|
|
321
|
+
*
|
|
322
|
+
* @param fields - The source's struct fields
|
|
323
|
+
* @param label - Component name for the error message
|
|
324
|
+
* @returns Whether the source is pinned
|
|
325
|
+
* @throws Error when the source has one of the two without the other, or
|
|
326
|
+
* either at another type — it would build neither arm faithfully
|
|
327
|
+
*/
|
|
328
|
+
function namesSnapshot(fields, label) {
|
|
329
|
+
const revision = fields["revision"];
|
|
330
|
+
const refresh = fields["refresh"];
|
|
331
|
+
if (revision === undefined && refresh === undefined)
|
|
332
|
+
return false;
|
|
333
|
+
if (revision !== undefined && refresh !== undefined
|
|
334
|
+
&& isTypeEqual(revision, SNAPSHOT_MEMBERS.revision) && isTypeEqual(refresh, SNAPSHOT_MEMBERS.refresh)) {
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
const found = revision === undefined ? "only `refresh`" : refresh === undefined ? "only `revision`" : "them at other types";
|
|
338
|
+
throw new Error(`${label}: a paged source names its snapshot with both \`revision: () → Option<String>\` and ` +
|
|
339
|
+
`\`refresh: (Option<String>) → Null\`, as Data.bindPaged's handle and Paged.pinned do — this one has ${found}`);
|
|
340
|
+
}
|
|
239
341
|
/**
|
|
240
342
|
* Classify a component's rows prop at BUILD time — the one dispatch every
|
|
241
343
|
* collection shares, so no component re-sniffs shapes of its own.
|
|
@@ -243,7 +345,9 @@ function keyTypeOf(t) {
|
|
|
243
345
|
* Accepted shapes, in order:
|
|
244
346
|
* - a collection expression / value (`Array<R>`, `Dict<K, V>`, `Set<R>`) ⇒ `inline`;
|
|
245
347
|
* - a struct carrying `page` + `total` ⇒ `paged` (a {@link PagedSourceType},
|
|
246
|
-
* or anything structurally matching it
|
|
348
|
+
* or anything structurally matching it), marked `pinned` when it also
|
|
349
|
+
* carries `revision` + `refresh` (a {@link PinnedSourceType} —
|
|
350
|
+
* `Data.bindPaged`'s handle, `Paged.pinned`);
|
|
247
351
|
* - a struct carrying `read` ⇒ a whole-value bind handle, which resolves by
|
|
248
352
|
* CALLING `read()` and recursing. The call becomes part of the surrounding
|
|
249
353
|
* East expression, so it is evaluated inside the component's reactive render
|
|
@@ -252,7 +356,7 @@ function keyTypeOf(t) {
|
|
|
252
356
|
*
|
|
253
357
|
* @param data - The rows prop as the author passed it
|
|
254
358
|
* @param label - Component name for the error message (`"Plan"`, `"Table"`)
|
|
255
|
-
* @returns The resolved arm — see {@link ResolvedRowSource}
|
|
359
|
+
* @returns The resolved arm, retaining a live handle for invocation-time reads — see {@link ResolvedRowSource}
|
|
256
360
|
* @throws Error when the expression is none of the accepted shapes
|
|
257
361
|
*/
|
|
258
362
|
export function resolveRowSource(data, label) {
|
|
@@ -274,6 +378,8 @@ export function resolveRowSource(data, label) {
|
|
|
274
378
|
throw new Error(`${label}: the paged source's \`page\` must return \`Option<Collection>\` ` +
|
|
275
379
|
`(an Array, Dict or Set) — got ${JSON.stringify(page)}`);
|
|
276
380
|
}
|
|
381
|
+
// A source that names its snapshot builds the `pinned` arm.
|
|
382
|
+
const pinned = namesSnapshot(fields, label);
|
|
277
383
|
// An ORDERED window: positional, but every row carries its own key.
|
|
278
384
|
// Recognised by the element's shape rather than announced by a flag,
|
|
279
385
|
// so a source that serves index entries needs no second vocabulary.
|
|
@@ -281,19 +387,20 @@ export function resolveRowSource(data, label) {
|
|
|
281
387
|
if (entry !== null) {
|
|
282
388
|
return {
|
|
283
389
|
kind: "ordered", source: expr,
|
|
284
|
-
collectionType, elementType, keyType: entry.key, orderKeyType: entry.ik,
|
|
390
|
+
collectionType, elementType, keyType: entry.key, orderKeyType: entry.ik, pinned,
|
|
285
391
|
};
|
|
286
392
|
}
|
|
287
393
|
return {
|
|
288
394
|
kind: "paged", source: expr,
|
|
289
|
-
collectionType, elementType, keyType: keyTypeOf(collectionType),
|
|
395
|
+
collectionType, elementType, keyType: keyTypeOf(collectionType), pinned,
|
|
290
396
|
};
|
|
291
397
|
}
|
|
292
398
|
if (fields !== undefined && fields["read"] !== undefined) {
|
|
293
399
|
// A whole-value bind handle (`Data.bind`) — read it here, in the
|
|
294
400
|
// surrounding East expression, and resolve the result.
|
|
295
401
|
const handle = expr;
|
|
296
|
-
|
|
402
|
+
const resolved = resolveRowSource(handle.read(), label);
|
|
403
|
+
return resolved.kind === "inline" ? { ...resolved, live: expr } : resolved;
|
|
297
404
|
}
|
|
298
405
|
throw new Error(`${label}: rows must be a collection, a paged source (\`{ id, page, total }\` — e.g. Data.bindPaged), ` +
|
|
299
406
|
`or a bound value (\`{ read }\` — e.g. Data.bind); got a ${t.type}`);
|
|
@@ -314,25 +421,40 @@ export function resolveRowSource(data, label) {
|
|
|
314
421
|
* component's rows inherit the source's keys, and flattening at the boundary
|
|
315
422
|
* would throw away exactly what makes a window's rows addressable (#568). A
|
|
316
423
|
* positional component (Table) receives its `Array<Row>`; a keyed one (Plan) a
|
|
317
|
-
* `Dict<
|
|
424
|
+
* `Dict<K, R>`.
|
|
318
425
|
*
|
|
319
426
|
* @typeParam Out - The component's own row COLLECTION type.
|
|
320
427
|
* @param resolved - The output of {@link resolveRowSource}
|
|
321
428
|
* @param outType - The component's row collection type
|
|
322
429
|
* @param make - The source collection → the component's row collection
|
|
323
|
-
* @param idSuffix - Appended to the paged source's `id` — see below
|
|
324
430
|
* @returns The `RowSourceType(outType)` value to store in the IR
|
|
325
431
|
*
|
|
326
432
|
* @remarks
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
433
|
+
* The derived source keeps the handle's `id`: `make` is part of what it
|
|
434
|
+
* serves, and a component whose `make` changes (a Plan whose series list a
|
|
435
|
+
* pick narrowed) serves other rows from the same handle — which a renderer
|
|
436
|
+
* tells apart by comparing the derived source whole, closures included
|
|
437
|
+
* (`equivalentFor`, #809), not by its id (#822).
|
|
438
|
+
*
|
|
439
|
+
* The derived `page` serves WHOLE windows (#829). A handle may answer
|
|
440
|
+
* `(offset, limit)` with fewer than `limit` elements while it still has more —
|
|
441
|
+
* e3 trims every page to a byte budget — and every component addresses its
|
|
442
|
+
* windows at `w × size`, so a short window would silently drop its tail. The
|
|
443
|
+
* derived `page` therefore re-requests `(offset + served, limit − served)`
|
|
444
|
+
* until the window holds `limit` elements, the source is exhausted (an empty
|
|
445
|
+
* piece, or `offset + served` reaching a known `total()`), or a piece is still
|
|
446
|
+
* in flight — in which case the whole window reads `none` and re-fires when the
|
|
447
|
+
* piece lands. The pieces join in the collection's own order (arrays
|
|
448
|
+
* concatenate; dicts and sets union, their pieces being disjoint and ascending)
|
|
449
|
+
* and `make` runs once over the whole window. Each piece stays its own request,
|
|
450
|
+
* so a runtime caches them at the size the source chose to serve.
|
|
451
|
+
*
|
|
452
|
+
* A source that names its snapshot (`resolved.pinned`, a
|
|
453
|
+
* {@link PinnedSourceType}) builds the `pinned` arm, its `revision` and
|
|
454
|
+
* `refresh` carried through as they are; any other builds `paged`, which has
|
|
455
|
+
* neither. The derived `page` is the same either way.
|
|
334
456
|
*/
|
|
335
|
-
export function buildRowSource(resolved, outType, make
|
|
457
|
+
export function buildRowSource(resolved, outType, make) {
|
|
336
458
|
const sourceType = RowSourceType(outType);
|
|
337
459
|
if (resolved.kind === "inline") {
|
|
338
460
|
return East.value(variant("inline", make(resolved.rows)), sourceType);
|
|
@@ -340,48 +462,208 @@ export function buildRowSource(resolved, outType, make, idSuffix) {
|
|
|
340
462
|
// `ordered` and `paged` build the same value: by the time `make` has run,
|
|
341
463
|
// the rows are the component's own shape, and whatever the entry carried —
|
|
342
464
|
// its key, its order key — is in there because the component's `make` put
|
|
343
|
-
// it there. The distinction is a BUILD-time one, which is why
|
|
344
|
-
//
|
|
345
|
-
|
|
465
|
+
// it there. The distinction is a BUILD-time one, which is why `ordered`
|
|
466
|
+
// has no arm of its own for a renderer to match on. What picks the arm is
|
|
467
|
+
// whether the source names its snapshot.
|
|
468
|
+
const handle = pagedHandleOf(resolved);
|
|
346
469
|
// Erased locally: the window type is `Out`, but TS cannot see through the
|
|
347
470
|
// generic to unify `Option<Out>`'s arms — the East type is what types it.
|
|
348
471
|
const winType = outType;
|
|
472
|
+
// The WHOLE window of the source's own collection, then `make` over it
|
|
473
|
+
// (#829) — reified once, and shared with anything that must read the very
|
|
474
|
+
// windows a component reads (the Plan's editing, #880).
|
|
475
|
+
const window = buildPagedWindow(resolved);
|
|
349
476
|
const page = East.function([IntegerType, IntegerType], OptionType(winType), ($, offset, limit) => {
|
|
350
|
-
const
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
477
|
+
const read = $.const(window);
|
|
478
|
+
const result = $.let(none, OptionType(winType));
|
|
479
|
+
const whole = $.let(read(offset, limit));
|
|
480
|
+
$.match(whole, {
|
|
481
|
+
some: ($, entries) => {
|
|
482
|
+
const built = $.let(make(entries), outType);
|
|
483
|
+
$.assign(result, some(built));
|
|
356
484
|
},
|
|
357
|
-
none: (_$) => empty,
|
|
358
485
|
});
|
|
486
|
+
return result;
|
|
359
487
|
});
|
|
360
488
|
// A source predating the contract carries no `id` / `seek`; fall back to a
|
|
361
489
|
// constant identity (it still compares equal to itself) and no seek.
|
|
362
490
|
const fields = structFields(Expr.type(resolved.source)) ?? {};
|
|
363
|
-
const
|
|
364
|
-
const id = idSuffix !== undefined
|
|
365
|
-
? East.str `${baseId}#${East.value(idSuffix, StringType)}`
|
|
366
|
-
: baseId;
|
|
491
|
+
const id = fields["id"] !== undefined ? handle.id : East.value("", StringType);
|
|
367
492
|
const seek = fields["seek"] !== undefined
|
|
368
493
|
? handle.seek
|
|
369
494
|
: East.value(none, OptionType(FunctionType([SeekQueryType], OptionType(SeekRangeType))));
|
|
370
|
-
|
|
495
|
+
if (!resolved.pinned) {
|
|
496
|
+
return East.value(variant("paged", { id, page, total: handle.total, seek }), sourceType);
|
|
497
|
+
}
|
|
498
|
+
const snapshot = resolved.source;
|
|
499
|
+
return East.value(variant("pinned", { id, page, total: handle.total, seek, revision: snapshot.revision, refresh: snapshot.refresh }), sourceType);
|
|
500
|
+
}
|
|
501
|
+
/** A resolved paged source's handle, at the contract's shape. */
|
|
502
|
+
function pagedHandleOf(resolved) {
|
|
503
|
+
return resolved.source;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* The WHOLE window `(offset, limit)` of a paged source's own collection — the
|
|
507
|
+
* pieces a trimmed page left out asked for until the window holds `limit`
|
|
508
|
+
* elements, the source is exhausted, or a piece is in flight (the window then
|
|
509
|
+
* reads `none`, #829). What {@link buildRowSource}'s derived `page` maps its
|
|
510
|
+
* `make` over; a caller that must read the very windows a component reads —
|
|
511
|
+
* the Plan's editing reads its entries back (#880) — asks for them here, so
|
|
512
|
+
* each piece is the same request the component made.
|
|
513
|
+
*
|
|
514
|
+
* @param resolved - A resolved paged source ({@link resolveRowSource})
|
|
515
|
+
* @returns `(offset, limit)` → the window, `none` while a piece is in flight
|
|
516
|
+
*/
|
|
517
|
+
export function buildPagedWindow(resolved) {
|
|
518
|
+
const handle = pagedHandleOf(resolved);
|
|
519
|
+
// The source's own collection — every piece of a window is a value of it.
|
|
520
|
+
// Its size and the in-order join of two pieces are reified ONCE, outside
|
|
521
|
+
// the block, per collection kind, then CALLED. Both are pure: a piece may
|
|
522
|
+
// be the very window object a runtime holds in its cache.
|
|
523
|
+
const pieceType = resolved.collectionType;
|
|
524
|
+
const pieceKind = pieceType.type;
|
|
525
|
+
const sizeOf = pieceKind === "Array"
|
|
526
|
+
? East.function([pieceType], IntegerType, (_$, c) => c.length())
|
|
527
|
+
: pieceKind === "Dict"
|
|
528
|
+
? East.function([pieceType], IntegerType, (_$, c) => c.size())
|
|
529
|
+
: East.function([pieceType], IntegerType, (_$, c) => c.size());
|
|
530
|
+
const joinOf = pieceKind === "Array"
|
|
531
|
+
? East.function([pieceType, pieceType], pieceType, (_$, a, b) => a.concat(b))
|
|
532
|
+
: pieceKind === "Dict"
|
|
533
|
+
// Pieces are disjoint, so the conflict arm never runs.
|
|
534
|
+
? East.function([pieceType, pieceType], pieceType, (_$, a, b) => a.union(b, (_$2, _mine, theirs) => theirs))
|
|
535
|
+
: East.function([pieceType, pieceType], pieceType, (_$, a, b) => a.union(b));
|
|
536
|
+
return East.function([IntegerType, IntegerType], OptionType(pieceType), ($, offset, limit) => {
|
|
537
|
+
// Bound ONCE: the handle may be a platform call (`Data.bindPaged(…)`
|
|
538
|
+
// passed inline), and this body reads it up to three times.
|
|
539
|
+
const src = $.const(handle);
|
|
540
|
+
const size = $.const(sizeOf);
|
|
541
|
+
const join = $.const(joinOf);
|
|
542
|
+
const result = $.let(none, OptionType(pieceType));
|
|
543
|
+
const first = $.let(src.page(offset, limit));
|
|
544
|
+
$.match(first, {
|
|
545
|
+
some: ($, head) => {
|
|
546
|
+
const window = $.let(head, pieceType);
|
|
547
|
+
const served = $.let(size(head), IntegerType);
|
|
548
|
+
const inFlight = $.let(false, BooleanType);
|
|
549
|
+
// An empty first window is exhaustion. A short one — the
|
|
550
|
+
// source trimmed it — asks for the rest, piece by piece.
|
|
551
|
+
$.while(served.greater(0n).and(() => served.less(limit)), ($, label) => {
|
|
552
|
+
// A known total says where the source ends: no piece past it.
|
|
553
|
+
const total = $.let(src.total());
|
|
554
|
+
$.match(total, {
|
|
555
|
+
some: ($, t) => {
|
|
556
|
+
$.if(offset.add(served).greaterEqual(t), ($) => { $.break(label); });
|
|
557
|
+
},
|
|
558
|
+
});
|
|
559
|
+
const next = $.let(src.page(offset.add(served), limit.subtract(served)));
|
|
560
|
+
$.match(next, {
|
|
561
|
+
none: ($) => {
|
|
562
|
+
// Still in flight: the whole window waits, and the
|
|
563
|
+
// piece's landing re-fires this evaluation.
|
|
564
|
+
$.assign(inFlight, true);
|
|
565
|
+
$.break(label);
|
|
566
|
+
},
|
|
567
|
+
some: ($, piece) => {
|
|
568
|
+
const n = $.let(size(piece), IntegerType);
|
|
569
|
+
$.if(n.equal(0n), ($) => { $.break(label); });
|
|
570
|
+
$.assign(window, join(window, piece));
|
|
571
|
+
$.assign(served, served.add(n));
|
|
572
|
+
},
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
$.if(inFlight.not(), ($) => { $.assign(result, some(window)); });
|
|
576
|
+
},
|
|
577
|
+
});
|
|
578
|
+
return result;
|
|
579
|
+
});
|
|
371
580
|
}
|
|
372
581
|
function createPagedOf(id, collection, options) {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
582
|
+
return createInMemory(id, collection, options, false);
|
|
583
|
+
}
|
|
584
|
+
function createPagedPinned(id, collection, options) {
|
|
585
|
+
return createInMemory(id, collection, options, true);
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* The in-memory source over a collection — {@link Paged.of}'s, or, `pinned`,
|
|
589
|
+
* {@link Paged.pinned}'s.
|
|
590
|
+
*
|
|
591
|
+
* @param id - The source's identity
|
|
592
|
+
* @param collection - The whole collection
|
|
593
|
+
* @param options - {@link PagedOfOptions}
|
|
594
|
+
* @param pinned - Whether the source names its snapshot
|
|
595
|
+
* @returns The source, at the collection it was given
|
|
596
|
+
* @throws {Error} When `pageLimit` is not a positive integer
|
|
597
|
+
*/
|
|
598
|
+
function createInMemory(id, collection, options, pinned) {
|
|
599
|
+
const pageLimit = options?.pageLimit;
|
|
600
|
+
if (pageLimit !== undefined && !(Number.isInteger(pageLimit) && pageLimit > 0)) {
|
|
601
|
+
throw new Error(`${pinned ? "Paged.pinned" : "Paged.of"}: \`pageLimit\` must be a positive integer — the most elements one window serves (got ${pageLimit})`);
|
|
376
602
|
}
|
|
377
|
-
|
|
603
|
+
const collectionExpr = East.value(collection);
|
|
604
|
+
const collectionType = Expr.type(collectionExpr);
|
|
605
|
+
const keyed = collectionType.type === "Dict";
|
|
606
|
+
const sourceType = pinned ? PinnedSourceType(collectionType) : PagedSourceType(collectionType);
|
|
607
|
+
// Evaluate the input once when creating the source, then detach nested
|
|
608
|
+
// mutable values. Page and seek closures share that captured snapshot;
|
|
609
|
+
// passing a live read expression cannot make later pages drift. The copy
|
|
610
|
+
// is beast v2: v1 writes neither a recursive type nor a function, and an
|
|
611
|
+
// entry may be either (a Plan's entries, #822).
|
|
612
|
+
const capture = East.function([StringType, collectionType], sourceType, ($, snapshotId, input) => {
|
|
613
|
+
const snapshot = $.const(East.Blob.encodeBeast(input, "v2").decodeBeast(collectionType, "v2"), collectionType);
|
|
614
|
+
return keyed ? keyedPagedOf(snapshotId, snapshot, pageLimit, pinned)
|
|
615
|
+
: arrayPagedOf(snapshotId, snapshot, options, pinned);
|
|
616
|
+
});
|
|
617
|
+
return East.value(capture)(id, collectionExpr);
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* An in-memory source's lifecycle when it is pinned: its one snapshot is its
|
|
621
|
+
* id. Built as real `East.function`s over the id, never spliced.
|
|
622
|
+
*
|
|
623
|
+
* @param id - The source's identity — its snapshot
|
|
624
|
+
* @returns `revision` and `refresh`, at {@link PinnedSourceType}'s types
|
|
625
|
+
*/
|
|
626
|
+
function fixedSnapshot(id) {
|
|
627
|
+
const revision = East.function([], OptionType(StringType), ($) => {
|
|
628
|
+
const snapshot = $.const(id, StringType);
|
|
629
|
+
return some(snapshot);
|
|
630
|
+
});
|
|
631
|
+
const refresh = East.function([OptionType(StringType)], NullType, ($, target) => {
|
|
632
|
+
const snapshot = $.const(id, StringType);
|
|
633
|
+
$.match(target, {
|
|
634
|
+
some: ($2, hash) => {
|
|
635
|
+
$2.if(hash.notEqual(snapshot), ($3) => {
|
|
636
|
+
$3.error("Paged.pinned: an immutable snapshot — create a source with the requested snapshot id");
|
|
637
|
+
});
|
|
638
|
+
},
|
|
639
|
+
none: () => { },
|
|
640
|
+
});
|
|
641
|
+
return null;
|
|
642
|
+
});
|
|
643
|
+
return { revision, refresh };
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* The window size a source actually serves for a requested `limit` — the
|
|
647
|
+
* request itself, or `pageLimit` when that is smaller. Reified once as a real
|
|
648
|
+
* East function (the `shared/reify` rule), so a page body CALLS it instead of
|
|
649
|
+
* branching on host state. `none` ⇒ the source never trims.
|
|
650
|
+
*/
|
|
651
|
+
function servedLimitFn(pageLimit) {
|
|
652
|
+
const capValue = pageLimit === undefined ? none : some(BigInt(pageLimit));
|
|
653
|
+
return East.function([IntegerType], IntegerType, ($, limit) => {
|
|
654
|
+
const cap = $.const(capValue, OptionType(IntegerType));
|
|
655
|
+
return cap.match({
|
|
656
|
+
some: (_$, c) => limit.less(c).ifElse(() => limit, () => c),
|
|
657
|
+
none: (_$) => limit,
|
|
658
|
+
});
|
|
659
|
+
});
|
|
378
660
|
}
|
|
379
661
|
/**
|
|
380
662
|
* The KEYED in-memory source — dict windows in canonical key order, with
|
|
381
663
|
* `seek` derived from the keys themselves (a keyed collection needs no key
|
|
382
|
-
* accessor: it already is one).
|
|
664
|
+
* accessor: it already is one). `pinned` adds its {@link fixedSnapshot}.
|
|
383
665
|
*/
|
|
384
|
-
function keyedPagedOf(id, collection) {
|
|
666
|
+
function keyedPagedOf(id, collection, pageLimit, pinned) {
|
|
385
667
|
const all = collection;
|
|
386
668
|
const valueType = Expr.type(all).value;
|
|
387
669
|
const dictType = DictType(StringType, valueType);
|
|
@@ -389,14 +671,16 @@ function keyedPagedOf(id, collection) {
|
|
|
389
671
|
const entriesType = ArrayType(entryType);
|
|
390
672
|
// Built OUTSIDE every block (an East macro inside one splices per use).
|
|
391
673
|
const keyOfEntry = East.function([entryType], StringType, (_$, e) => e.key);
|
|
674
|
+
const servedLimit = servedLimitFn(pageLimit);
|
|
392
675
|
const page = East.function([IntegerType, IntegerType], OptionType(dictType), ($, offset, limit) => {
|
|
393
676
|
const src = $.const(all, dictType);
|
|
677
|
+
const served = $.const(servedLimit);
|
|
394
678
|
// `toArray` walks the dictionary in key order, so the window a given
|
|
395
679
|
// offset serves is the window a keyed dataset would serve.
|
|
396
680
|
const entries = $.let(src.toArray(($2, v, k) => $2.const({ key: k, value: v }, entryType)), entriesType);
|
|
397
681
|
const n = $.let(entries.length(), IntegerType);
|
|
398
682
|
const start = $.let(offset.less(n).ifElse(() => offset, () => n), IntegerType);
|
|
399
|
-
const rawEnd = $.let(offset.add(limit), IntegerType);
|
|
683
|
+
const rawEnd = $.let(offset.add(served(limit)), IntegerType);
|
|
400
684
|
const end = $.let(rawEnd.less(n).ifElse(() => rawEnd, () => n), IntegerType);
|
|
401
685
|
// In-memory windows are never in flight; an exhausted offset yields
|
|
402
686
|
// the EMPTY window, which is how a walking reader terminates.
|
|
@@ -473,10 +757,14 @@ function keyedPagedOf(id, collection) {
|
|
|
473
757
|
});
|
|
474
758
|
});
|
|
475
759
|
const seek = some(find);
|
|
476
|
-
|
|
760
|
+
const members = { id, page, total, seek };
|
|
761
|
+
return (pinned
|
|
762
|
+
? East.value({ ...members, ...fixedSnapshot(id) }, PinnedSourceType(dictType))
|
|
763
|
+
: East.value(members, PagedSourceType(dictType)));
|
|
477
764
|
}
|
|
478
|
-
/** The POSITIONAL in-memory source — array windows in stream order.
|
|
479
|
-
|
|
765
|
+
/** The POSITIONAL in-memory source — array windows in stream order.
|
|
766
|
+
* `pinned` adds its {@link fixedSnapshot}. */
|
|
767
|
+
function arrayPagedOf(id, rows, options, pinned) {
|
|
480
768
|
const all = rows;
|
|
481
769
|
const rowType = Expr.type(all).value;
|
|
482
770
|
const rowsType = ArrayType(rowType);
|
|
@@ -486,11 +774,13 @@ function arrayPagedOf(id, rows, options) {
|
|
|
486
774
|
const byFn = keyOf === undefined
|
|
487
775
|
? undefined
|
|
488
776
|
: East.function([rowType], StringType, (_$, r) => keyOf(r));
|
|
777
|
+
const servedLimit = servedLimitFn(options?.pageLimit);
|
|
489
778
|
const page = East.function([IntegerType, IntegerType], OptionType(rowsType), ($, offset, limit) => {
|
|
490
779
|
const src = $.const(all, rowsType);
|
|
780
|
+
const served = $.const(servedLimit);
|
|
491
781
|
const n = $.let(src.length(), IntegerType);
|
|
492
782
|
const start = $.let(offset.less(n).ifElse(() => offset, () => n), IntegerType);
|
|
493
|
-
const rawEnd = $.let(offset.add(limit), IntegerType);
|
|
783
|
+
const rawEnd = $.let(offset.add(served(limit)), IntegerType);
|
|
494
784
|
const end = $.let(rawEnd.less(n).ifElse(() => rawEnd, () => n), IntegerType);
|
|
495
785
|
// In-memory windows are never in flight; an exhausted offset yields
|
|
496
786
|
// the EMPTY window, which is how a walking reader terminates.
|
|
@@ -567,11 +857,16 @@ function arrayPagedOf(id, rows, options) {
|
|
|
567
857
|
// Two-step cast (the `Data.bindPaged` idiom): the members are built
|
|
568
858
|
// against the row type recovered from the expression, which TS sees as the
|
|
569
859
|
// erased `EastType` rather than the caller's `R`. The East-side type —
|
|
570
|
-
// `PagedSourceType(rowsType)` — is what
|
|
571
|
-
|
|
860
|
+
// `PagedSourceType(rowsType)`, or `PinnedSourceType(rowsType)` — is what
|
|
861
|
+
// actually types the value.
|
|
862
|
+
const members = { id, page, total, seek };
|
|
863
|
+
return (pinned
|
|
864
|
+
? East.value({ ...members, ...fixedSnapshot(id) }, PinnedSourceType(rowsType))
|
|
865
|
+
: East.value(members, PagedSourceType(rowsType)));
|
|
572
866
|
}
|
|
573
867
|
/**
|
|
574
|
-
* The `Paged` namespace — building a {@link PagedSourceType}
|
|
868
|
+
* The `Paged` namespace — building a {@link PagedSourceType} or a
|
|
869
|
+
* {@link PinnedSourceType} without a server.
|
|
575
870
|
*
|
|
576
871
|
* @remarks
|
|
577
872
|
* The contract itself is what components consume; this namespace is the
|
|
@@ -581,15 +876,19 @@ function arrayPagedOf(id, rows, options) {
|
|
|
581
876
|
export const Paged = {
|
|
582
877
|
/** Build an in-memory paged source over a collection already in hand. */
|
|
583
878
|
of: createPagedOf,
|
|
879
|
+
/** Build an in-memory PINNED source — `of`, naming its one snapshot, for a fixture a view edits or follows. */
|
|
880
|
+
pinned: createPagedPinned,
|
|
584
881
|
/** East types — the contract, for `$.const` / `$.let` annotations. */
|
|
585
882
|
Types: {
|
|
586
883
|
/** A windowed row source over a collection type. */
|
|
587
884
|
Source: PagedSourceType,
|
|
885
|
+
/** A windowed row source that names its snapshot, over a collection type. */
|
|
886
|
+
PinnedSource: PinnedSourceType,
|
|
588
887
|
/** Where a key query landed in a source's row order. */
|
|
589
888
|
SeekRange: SeekRangeType,
|
|
590
889
|
/** A key query — exact literal, String prefix, leading struct fields, or a range. */
|
|
591
890
|
SeekQuery: SeekQueryType,
|
|
592
|
-
/** How a component's rows arrive (inline / paged), at a collection type. */
|
|
891
|
+
/** How a component's rows arrive (inline / paged / pinned), at a collection type. */
|
|
593
892
|
RowSource: RowSourceType,
|
|
594
893
|
},
|
|
595
894
|
};
|