@elaraai/e3-ui 1.0.86 → 1.0.88
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/bind/data-indexed.d.ts +159 -0
- package/dist/src/bind/data-indexed.d.ts.map +1 -0
- package/dist/src/bind/data-indexed.js +105 -0
- package/dist/src/bind/data-indexed.js.map +1 -0
- package/dist/src/bind/data.d.ts +5 -3
- package/dist/src/bind/data.d.ts.map +1 -1
- package/dist/src/bind/data.js +6 -0
- package/dist/src/bind/data.js.map +1 -1
- package/dist/src/bind/record.d.ts +34 -23
- package/dist/src/bind/record.d.ts.map +1 -1
- package/dist/src/bind/record.js +46 -19
- package/dist/src/bind/record.js.map +1 -1
- package/dist/src/calendar/index.d.ts +2923 -0
- package/dist/src/calendar/index.d.ts.map +1 -0
- package/dist/src/calendar/index.js +135 -0
- package/dist/src/calendar/index.js.map +1 -0
- package/dist/src/decision/queue.d.ts +3 -3
- package/dist/src/decision/queue.d.ts.map +1 -1
- package/dist/src/decision/queue.js +2 -2
- package/dist/src/decision/queue.js.map +1 -1
- package/dist/src/decision/types.d.ts +4 -0
- package/dist/src/decision/types.d.ts.map +1 -1
- package/dist/src/decision/types.js +4 -0
- package/dist/src/decision/types.js.map +1 -1
- package/dist/src/flowchart/canvas.d.ts +114 -0
- package/dist/src/flowchart/canvas.d.ts.map +1 -0
- package/dist/src/flowchart/canvas.js +84 -0
- package/dist/src/flowchart/canvas.js.map +1 -0
- package/dist/src/flowchart/flowchart.d.ts +79 -0
- package/dist/src/flowchart/flowchart.d.ts.map +1 -0
- package/dist/src/flowchart/flowchart.js +9 -0
- package/dist/src/flowchart/flowchart.js.map +1 -0
- package/dist/src/flowchart/index.d.ts +305 -0
- package/dist/src/flowchart/index.d.ts.map +1 -0
- package/dist/src/flowchart/index.js +267 -0
- package/dist/src/flowchart/index.js.map +1 -0
- package/dist/src/flowchart/library.d.ts +720 -0
- package/dist/src/flowchart/library.d.ts.map +1 -0
- package/dist/src/flowchart/library.js +611 -0
- package/dist/src/flowchart/library.js.map +1 -0
- package/dist/src/flowchart/over.d.ts +234 -0
- package/dist/src/flowchart/over.d.ts.map +1 -0
- package/dist/src/flowchart/over.js +220 -0
- package/dist/src/flowchart/over.js.map +1 -0
- package/dist/src/flowchart/patch.d.ts +64 -0
- package/dist/src/flowchart/patch.d.ts.map +1 -0
- package/dist/src/flowchart/patch.js +67 -0
- package/dist/src/flowchart/patch.js.map +1 -0
- package/dist/src/flowchart/payload.d.ts +8220 -0
- package/dist/src/flowchart/payload.d.ts.map +1 -0
- package/dist/src/flowchart/payload.js +521 -0
- package/dist/src/flowchart/payload.js.map +1 -0
- package/dist/src/flowchart/types.d.ts +393 -0
- package/dist/src/flowchart/types.d.ts.map +1 -0
- package/dist/src/flowchart/types.js +216 -0
- package/dist/src/flowchart/types.js.map +1 -0
- package/dist/src/flowchart/values.d.ts +267 -0
- package/dist/src/flowchart/values.d.ts.map +1 -0
- package/dist/src/flowchart/values.js +216 -0
- package/dist/src/flowchart/values.js.map +1 -0
- package/dist/src/flowchart/window.d.ts +521 -0
- package/dist/src/flowchart/window.d.ts.map +1 -0
- package/dist/src/flowchart/window.js +69 -0
- package/dist/src/flowchart/window.js.map +1 -0
- package/dist/src/index.d.ts +25 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +48 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/internal.d.ts +35 -3
- package/dist/src/internal.d.ts.map +1 -1
- package/dist/src/internal.js +72 -3
- package/dist/src/internal.js.map +1 -1
- package/dist/src/plan/assemble.d.ts +2224 -0
- package/dist/src/plan/assemble.d.ts.map +1 -0
- package/dist/src/plan/assemble.js +210 -0
- package/dist/src/plan/assemble.js.map +1 -0
- package/dist/src/plan/builders.d.ts +790 -0
- package/dist/src/plan/builders.d.ts.map +1 -0
- package/dist/src/plan/builders.js +749 -0
- package/dist/src/plan/builders.js.map +1 -0
- package/dist/src/plan/event-rows.d.ts +2565 -0
- package/dist/src/plan/event-rows.d.ts.map +1 -0
- package/dist/src/plan/event-rows.js +689 -0
- package/dist/src/plan/event-rows.js.map +1 -0
- package/dist/src/plan/factories.d.ts +458 -0
- package/dist/src/plan/factories.d.ts.map +1 -0
- package/dist/src/plan/factories.js +484 -0
- package/dist/src/plan/factories.js.map +1 -0
- package/dist/src/plan/index.d.ts +672 -0
- package/dist/src/plan/index.d.ts.map +1 -0
- package/dist/src/plan/index.js +532 -0
- package/dist/src/plan/index.js.map +1 -0
- package/dist/src/plan/ir.d.ts +2594 -0
- package/dist/src/plan/ir.d.ts.map +1 -0
- package/dist/src/plan/ir.js +78 -0
- package/dist/src/plan/ir.js.map +1 -0
- package/dist/src/plan/library.d.ts +772 -0
- package/dist/src/plan/library.d.ts.map +1 -0
- package/dist/src/plan/library.js +424 -0
- package/dist/src/plan/library.js.map +1 -0
- package/dist/src/plan/ordered-source.d.ts +27 -0
- package/dist/src/plan/ordered-source.d.ts.map +1 -0
- package/dist/src/plan/ordered-source.js +48 -0
- package/dist/src/plan/ordered-source.js.map +1 -0
- package/dist/src/plan/over.d.ts +247 -0
- package/dist/src/plan/over.d.ts.map +1 -0
- package/dist/src/plan/over.js +266 -0
- package/dist/src/plan/over.js.map +1 -0
- package/dist/src/plan/pick.d.ts +78 -0
- package/dist/src/plan/pick.d.ts.map +1 -0
- package/dist/src/plan/pick.js +146 -0
- package/dist/src/plan/pick.js.map +1 -0
- package/dist/src/plan/plan.d.ts +11611 -0
- package/dist/src/plan/plan.d.ts.map +1 -0
- package/dist/src/plan/plan.js +428 -0
- package/dist/src/plan/plan.js.map +1 -0
- package/dist/src/plan/refs.d.ts +218 -0
- package/dist/src/plan/refs.d.ts.map +1 -0
- package/dist/src/plan/refs.js +226 -0
- package/dist/src/plan/refs.js.map +1 -0
- package/dist/src/plan/root.d.ts +290 -0
- package/dist/src/plan/root.d.ts.map +1 -0
- package/dist/src/plan/root.js +463 -0
- package/dist/src/plan/root.js.map +1 -0
- package/dist/src/plan/series.d.ts +13554 -0
- package/dist/src/plan/series.d.ts.map +1 -0
- package/dist/src/plan/series.js +1921 -0
- package/dist/src/plan/series.js.map +1 -0
- package/dist/src/plan/types.d.ts +12400 -0
- package/dist/src/plan/types.d.ts.map +1 -0
- package/dist/src/plan/types.js +1335 -0
- package/dist/src/plan/types.js.map +1 -0
- package/dist/src/roster/coverage.d.ts +240 -0
- package/dist/src/roster/coverage.d.ts.map +1 -0
- package/dist/src/roster/coverage.js +109 -0
- package/dist/src/roster/coverage.js.map +1 -0
- package/dist/src/roster/index.d.ts +4028 -0
- package/dist/src/roster/index.d.ts.map +1 -0
- package/dist/src/roster/index.js +115 -0
- package/dist/src/roster/index.js.map +1 -0
- package/dist/src/roster/library.d.ts +106 -0
- package/dist/src/roster/library.d.ts.map +1 -0
- package/dist/src/roster/library.js +78 -0
- package/dist/src/roster/library.js.map +1 -0
- package/dist/src/roster/people.d.ts +50 -0
- package/dist/src/roster/people.d.ts.map +1 -0
- package/dist/src/roster/people.js +32 -0
- package/dist/src/roster/people.js.map +1 -0
- package/dist/src/roster/rules.d.ts +275 -0
- package/dist/src/roster/rules.d.ts.map +1 -0
- package/dist/src/roster/rules.js +162 -0
- package/dist/src/roster/rules.js.map +1 -0
- package/dist/src/roster/types.d.ts +672 -0
- package/dist/src/roster/types.d.ts.map +1 -0
- package/dist/src/roster/types.js +248 -0
- package/dist/src/roster/types.js.map +1 -0
- package/dist/src/roster/visibility.d.ts +66 -0
- package/dist/src/roster/visibility.d.ts.map +1 -0
- package/dist/src/roster/visibility.js +25 -0
- package/dist/src/roster/visibility.js.map +1 -0
- package/dist/src/schedule/days.d.ts +54 -0
- package/dist/src/schedule/days.d.ts.map +1 -0
- package/dist/src/schedule/days.js +68 -0
- package/dist/src/schedule/days.js.map +1 -0
- package/dist/src/schedule/events.d.ts +433 -0
- package/dist/src/schedule/events.d.ts.map +1 -0
- package/dist/src/schedule/events.js +851 -0
- package/dist/src/schedule/events.js.map +1 -0
- package/dist/src/schedule/index.d.ts +150 -0
- package/dist/src/schedule/index.d.ts.map +1 -0
- package/dist/src/schedule/index.js +132 -0
- package/dist/src/schedule/index.js.map +1 -0
- package/dist/src/schedule/library.d.ts +70 -0
- package/dist/src/schedule/library.d.ts.map +1 -0
- package/dist/src/schedule/library.js +102 -0
- package/dist/src/schedule/library.js.map +1 -0
- package/dist/src/schedule/patch.d.ts +74 -0
- package/dist/src/schedule/patch.d.ts.map +1 -0
- package/dist/src/schedule/patch.js +68 -0
- package/dist/src/schedule/patch.js.map +1 -0
- package/dist/src/schedule/record-window.d.ts +189 -0
- package/dist/src/schedule/record-window.d.ts.map +1 -0
- package/dist/src/schedule/record-window.js +95 -0
- package/dist/src/schedule/record-window.js.map +1 -0
- package/dist/src/schedule/resources.d.ts +165 -0
- package/dist/src/schedule/resources.d.ts.map +1 -0
- package/dist/src/schedule/resources.js +234 -0
- package/dist/src/schedule/resources.js.map +1 -0
- package/dist/src/schedule/templates.d.ts +47 -0
- package/dist/src/schedule/templates.d.ts.map +1 -0
- package/dist/src/schedule/templates.js +61 -0
- package/dist/src/schedule/templates.js.map +1 -0
- package/dist/src/schedule/types.d.ts +1677 -0
- package/dist/src/schedule/types.d.ts.map +1 -0
- package/dist/src/schedule/types.js +374 -0
- package/dist/src/schedule/types.js.map +1 -0
- package/dist/src/schedule/window.d.ts +154 -0
- package/dist/src/schedule/window.d.ts.map +1 -0
- package/dist/src/schedule/window.js +363 -0
- package/dist/src/schedule/window.js.map +1 -0
- package/dist/src/sheet/bridge.d.ts +385 -0
- package/dist/src/sheet/bridge.d.ts.map +1 -0
- package/dist/src/sheet/bridge.js +809 -0
- package/dist/src/sheet/bridge.js.map +1 -0
- package/dist/src/sheet/columns.d.ts +410 -0
- package/dist/src/sheet/columns.d.ts.map +1 -0
- package/dist/src/sheet/columns.js +99 -0
- package/dist/src/sheet/columns.js.map +1 -0
- package/dist/src/sheet/context-bridge.d.ts +48 -0
- package/dist/src/sheet/context-bridge.d.ts.map +1 -0
- package/dist/src/sheet/context-bridge.js +216 -0
- package/dist/src/sheet/context-bridge.js.map +1 -0
- package/dist/src/sheet/draft-bridge.d.ts +37 -0
- package/dist/src/sheet/draft-bridge.d.ts.map +1 -0
- package/dist/src/sheet/draft-bridge.js +153 -0
- package/dist/src/sheet/draft-bridge.js.map +1 -0
- package/dist/src/sheet/drafts.d.ts +14 -0
- package/dist/src/sheet/drafts.d.ts.map +1 -0
- package/dist/src/sheet/drafts.js +14 -0
- package/dist/src/sheet/drafts.js.map +1 -0
- package/dist/src/sheet/editing-bridge.d.ts +40 -0
- package/dist/src/sheet/editing-bridge.d.ts.map +1 -0
- package/dist/src/sheet/editing-bridge.js +231 -0
- package/dist/src/sheet/editing-bridge.js.map +1 -0
- package/dist/src/sheet/editing-types.d.ts +268 -0
- package/dist/src/sheet/editing-types.d.ts.map +1 -0
- package/dist/src/sheet/editing-types.js +34 -0
- package/dist/src/sheet/editing-types.js.map +1 -0
- package/dist/src/sheet/edits.d.ts +37 -0
- package/dist/src/sheet/edits.d.ts.map +1 -0
- package/dist/src/sheet/edits.js +31 -0
- package/dist/src/sheet/edits.js.map +1 -0
- package/dist/src/sheet/fields.d.ts +693 -0
- package/dist/src/sheet/fields.d.ts.map +1 -0
- package/dist/src/sheet/fields.js +280 -0
- package/dist/src/sheet/fields.js.map +1 -0
- package/dist/src/sheet/group.d.ts +165 -0
- package/dist/src/sheet/group.d.ts.map +1 -0
- package/dist/src/sheet/group.js +52 -0
- package/dist/src/sheet/group.js.map +1 -0
- package/dist/src/sheet/index.d.ts +542 -0
- package/dist/src/sheet/index.d.ts.map +1 -0
- package/dist/src/sheet/index.js +387 -0
- package/dist/src/sheet/index.js.map +1 -0
- package/dist/src/sheet/library.d.ts +284 -0
- package/dist/src/sheet/library.d.ts.map +1 -0
- package/dist/src/sheet/library.js +259 -0
- package/dist/src/sheet/library.js.map +1 -0
- package/dist/src/sheet/link.d.ts +398 -0
- package/dist/src/sheet/link.d.ts.map +1 -0
- package/dist/src/sheet/link.js +228 -0
- package/dist/src/sheet/link.js.map +1 -0
- package/dist/src/sheet/record.d.ts +75 -0
- package/dist/src/sheet/record.d.ts.map +1 -0
- package/dist/src/sheet/record.js +128 -0
- package/dist/src/sheet/record.js.map +1 -0
- package/dist/src/sheet/registers.d.ts +681 -0
- package/dist/src/sheet/registers.d.ts.map +1 -0
- package/dist/src/sheet/registers.js +671 -0
- package/dist/src/sheet/registers.js.map +1 -0
- package/dist/src/sheet/root.d.ts +329 -0
- package/dist/src/sheet/root.d.ts.map +1 -0
- package/dist/src/sheet/root.js +511 -0
- package/dist/src/sheet/root.js.map +1 -0
- package/dist/src/sheet/seed-bridge.d.ts +14 -0
- package/dist/src/sheet/seed-bridge.d.ts.map +1 -0
- package/dist/src/sheet/seed-bridge.js +51 -0
- package/dist/src/sheet/seed-bridge.js.map +1 -0
- package/dist/src/sheet/sheet.d.ts +11819 -0
- package/dist/src/sheet/sheet.d.ts.map +1 -0
- package/dist/src/sheet/sheet.js +236 -0
- package/dist/src/sheet/sheet.js.map +1 -0
- package/dist/src/sheet/sub-rows.d.ts +299 -0
- package/dist/src/sheet/sub-rows.d.ts.map +1 -0
- package/dist/src/sheet/sub-rows.js +272 -0
- package/dist/src/sheet/sub-rows.js.map +1 -0
- package/dist/src/sheet/templates.d.ts +161 -0
- package/dist/src/sheet/templates.d.ts.map +1 -0
- package/dist/src/sheet/templates.js +122 -0
- package/dist/src/sheet/templates.js.map +1 -0
- package/dist/src/sheet/transactions.d.ts +76 -0
- package/dist/src/sheet/transactions.d.ts.map +1 -0
- package/dist/src/sheet/transactions.js +40 -0
- package/dist/src/sheet/transactions.js.map +1 -0
- package/dist/src/sheet/types.d.ts +15226 -0
- package/dist/src/sheet/types.d.ts.map +1 -0
- package/dist/src/sheet/types.js +1070 -0
- package/dist/src/sheet/types.js.map +1 -0
- package/dist/src/sheet/views.d.ts +29 -0
- package/dist/src/sheet/views.d.ts.map +1 -0
- package/dist/src/sheet/views.js +43 -0
- package/dist/src/sheet/views.js.map +1 -0
- package/dist/src/studio/component.d.ts.map +1 -1
- package/dist/src/studio/component.js +11 -8
- package/dist/src/studio/component.js.map +1 -1
- package/dist/src/utils/derive.d.ts +134 -8
- package/dist/src/utils/derive.d.ts.map +1 -1
- package/dist/src/utils/derive.js +409 -7
- package/dist/src/utils/derive.js.map +1 -1
- package/dist/src/utils/ordered-source.d.ts +38 -0
- package/dist/src/utils/ordered-source.d.ts.map +1 -0
- package/dist/src/utils/ordered-source.js +45 -0
- package/dist/src/utils/ordered-source.js.map +1 -0
- package/dist/src/utils/row-inspector.d.ts +36 -0
- package/dist/src/utils/row-inspector.d.ts.map +1 -0
- package/dist/src/utils/row-inspector.js +49 -0
- package/dist/src/utils/row-inspector.js.map +1 -0
- package/dist/test/bind/data/data.examples.d.ts +21 -19
- package/dist/test/bind/data/data.examples.d.ts.map +1 -1
- package/dist/test/bind/data/data.examples.js +122 -122
- package/dist/test/bind/data/data.examples.js.map +1 -1
- package/dist/test/bind/record/record.examples.d.ts.map +1 -1
- package/dist/test/bind/record/record.examples.js +9 -9
- package/dist/test/bind/record/record.examples.js.map +1 -1
- package/dist/test/calendar/calendar-records.examples.d.ts +228 -0
- package/dist/test/calendar/calendar-records.examples.d.ts.map +1 -0
- package/dist/test/calendar/calendar-records.examples.js +91 -0
- package/dist/test/calendar/calendar-records.examples.js.map +1 -0
- package/dist/test/calendar/calendar.examples.d.ts +776 -0
- package/dist/test/calendar/calendar.examples.d.ts.map +1 -0
- package/dist/test/calendar/calendar.examples.js +336 -0
- package/dist/test/calendar/calendar.examples.js.map +1 -0
- package/dist/test/decision/journal.examples.d.ts +28 -28
- package/dist/test/decision/journal.examples.d.ts.map +1 -1
- package/dist/test/decision/journal.examples.js +4 -4
- package/dist/test/decision/journal.examples.js.map +1 -1
- package/dist/test/decision/queue.examples.d.ts.map +1 -1
- package/dist/test/decision/queue.examples.js +5 -7
- package/dist/test/decision/queue.examples.js.map +1 -1
- package/dist/test/flowchart/flowchart.examples.d.ts +3713 -0
- package/dist/test/flowchart/flowchart.examples.d.ts.map +1 -0
- package/dist/test/flowchart/flowchart.examples.js +465 -0
- package/dist/test/flowchart/flowchart.examples.js.map +1 -0
- package/dist/test/plan/plan-events.examples.d.ts +3666 -0
- package/dist/test/plan/plan-events.examples.d.ts.map +1 -0
- package/dist/test/plan/plan-events.examples.js +773 -0
- package/dist/test/plan/plan-events.examples.js.map +1 -0
- package/dist/test/plan/plan-records.examples.d.ts +3 -0
- package/dist/test/plan/plan-records.examples.d.ts.map +1 -0
- package/dist/test/plan/plan-records.examples.js +45 -0
- package/dist/test/plan/plan-records.examples.js.map +1 -0
- package/dist/test/plan/plan-windows.examples.d.ts +588 -0
- package/dist/test/plan/plan-windows.examples.d.ts.map +1 -0
- package/dist/test/plan/plan-windows.examples.js +170 -0
- package/dist/test/plan/plan-windows.examples.js.map +1 -0
- package/dist/test/plan/plan.examples.d.ts +8095 -0
- package/dist/test/plan/plan.examples.d.ts.map +1 -0
- package/dist/test/plan/plan.examples.js +2500 -0
- package/dist/test/plan/plan.examples.js.map +1 -0
- package/dist/test/roster/domain.examples.d.ts +367 -0
- package/dist/test/roster/domain.examples.d.ts.map +1 -0
- package/dist/test/roster/domain.examples.js +72 -0
- package/dist/test/roster/domain.examples.js.map +1 -0
- package/dist/test/roster/roster-records.examples.d.ts +729 -0
- package/dist/test/roster/roster-records.examples.d.ts.map +1 -0
- package/dist/test/roster/roster-records.examples.js +86 -0
- package/dist/test/roster/roster-records.examples.js.map +1 -0
- package/dist/test/roster/roster.examples.d.ts +750 -0
- package/dist/test/roster/roster.examples.d.ts.map +1 -0
- package/dist/test/roster/roster.examples.js +198 -0
- package/dist/test/roster/roster.examples.js.map +1 -0
- package/dist/test/roster/warehouse.d.ts +87 -0
- package/dist/test/roster/warehouse.d.ts.map +1 -0
- package/dist/test/roster/warehouse.js +883 -0
- package/dist/test/roster/warehouse.js.map +1 -0
- package/dist/test/sheet/sheet-link.examples.d.ts +7 -0
- package/dist/test/sheet/sheet-link.examples.d.ts.map +1 -0
- package/dist/test/sheet/sheet-link.examples.js +41 -0
- package/dist/test/sheet/sheet-link.examples.js.map +1 -0
- package/dist/test/sheet/sheet-records.examples.d.ts +421 -0
- package/dist/test/sheet/sheet-records.examples.d.ts.map +1 -0
- package/dist/test/sheet/sheet-records.examples.js +79 -0
- package/dist/test/sheet/sheet-records.examples.js.map +1 -0
- package/dist/test/sheet/sheet-transactions.examples.d.ts +7 -0
- package/dist/test/sheet/sheet-transactions.examples.d.ts.map +1 -0
- package/dist/test/sheet/sheet-transactions.examples.js +58 -0
- package/dist/test/sheet/sheet-transactions.examples.js.map +1 -0
- package/dist/test/sheet/sheet.examples.d.ts +5562 -0
- package/dist/test/sheet/sheet.examples.d.ts.map +1 -0
- package/dist/test/sheet/sheet.examples.js +1178 -0
- package/dist/test/sheet/sheet.examples.js.map +1 -0
- package/dist/test/studio/studio.examples.d.ts.map +1 -1
- package/dist/test/studio/studio.examples.js +11 -11
- package/dist/test/studio/studio.examples.js.map +1 -1
- package/package.json +12 -9
|
@@ -0,0 +1,809 @@
|
|
|
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 typed bridge (`Sheet Spec.md` §4.8) — factory internals, both arms.
|
|
7
|
+
*
|
|
8
|
+
* Everything the author writes is typed over the host's row `R` and the
|
|
9
|
+
* driver's row `D`; everything on the wire is closed. This module joins the
|
|
10
|
+
* two with compiled East functions that capture only data, bind handles and
|
|
11
|
+
* the author's functions:
|
|
12
|
+
*
|
|
13
|
+
* - `projectRow` — `R` → the wire cells (the same projection the row source
|
|
14
|
+
* uses, so `encode` and `make` cannot disagree);
|
|
15
|
+
* - `rowById` — the REAL source row by id: the captured collection or bind
|
|
16
|
+
* handle on the inline arm, the source's `page` at the row's offset on the
|
|
17
|
+
* paged arm. Built ONCE per sheet and shared by every wrapper;
|
|
18
|
+
* - `draftDecode` — current cells over the existing field draft, preserving
|
|
19
|
+
* hidden values and retaining missing/invalid input without domain defaults;
|
|
20
|
+
* - `bridgeCtx` — the wire context to `Sheet.Types.DraftContext(R, D)`;
|
|
21
|
+
* - `bridgeReady` — a readiness batch to one `DraftContext` per check, its
|
|
22
|
+
* rows built once (#882);
|
|
23
|
+
* - the wrappers — a fill provider, a proposer, an arity rule, a member
|
|
24
|
+
* check, an options rule, a custom kind's parse / print — each the
|
|
25
|
+
* author's typed function inside its closed wire twin;
|
|
26
|
+
* - the rule cells and sub rows (#844) — a column's `level` / `actual` /
|
|
27
|
+
* `detail` accessors and the `subRows` mappers, each compiled once and
|
|
28
|
+
* called inside the row projection.
|
|
29
|
+
*
|
|
30
|
+
* On a GROUPED sheet (#740) the source row is the group `P` and the columns
|
|
31
|
+
* are declared over the line `L` its lines field holds: `projectRow` /
|
|
32
|
+
* `draftRow` / the patch run over `L`, `rowById` returns the group, and the
|
|
33
|
+
* group half of the bridge ({@link SheetGroupBridge}) projects the group's
|
|
34
|
+
* summary cells and ordered children. Internal child keys preserve identity
|
|
35
|
+
* during local editing; the author's child arrays remain positional. A
|
|
36
|
+
* source with LOOSE rows between its groups (#846) holds entries
|
|
37
|
+
* `Sheet.Types.Entry(P, "lines")`: `rowById` returns the entry, a draft is
|
|
38
|
+
* the entry's draft, and a loose row decodes as a row of `L` whose id is its
|
|
39
|
+
* own.
|
|
40
|
+
*
|
|
41
|
+
* The bridge is the one place a string ever names a field: inside the
|
|
42
|
+
* factory, against the column list it has already validated.
|
|
43
|
+
*
|
|
44
|
+
* @packageDocumentation
|
|
45
|
+
*/
|
|
46
|
+
import { East, Expr, ArrayType, AsyncFunctionType, BooleanType, DateTimeType, DictType, FloatType, FunctionType, IntegerType, NullType, OptionType, StringType, StructType, isTypeEqual, variant, some, none, } from "@elaraai/east";
|
|
47
|
+
import { SheetCellType, SheetLinkType, SheetRowType, SheetLineType, SheetContextType, SheetReadyBatchType, SheetFillType, SheetProposalType, SheetProviderType, SheetProposerType, SheetCheckContextType, SheetCheckType, SheetCountedType, SheetHalfType, SheetContextTypeFor, SheetCheckContextTypeFor, SheetPatchTypeFor, SheetProposalTypeFor, SheetGroupContextTypeFor, SheetGroupCheckContextTypeFor, SheetDateLevelType, SheetSubRowType, SheetOptionsRuleType, sheetLinesOf, sheetRuleCell, } from "./types.js";
|
|
48
|
+
import { resolveTag } from "@elaraai/east-ui/internal";
|
|
49
|
+
import { SheetMembersType, SheetRegisterMembersType, parseLink, EMPTY_LINK } from "./link.js";
|
|
50
|
+
import { buildDraftRowDecoder, buildDraftGroupDecoder, buildDraftEntryDecoder } from "./draft-bridge.js";
|
|
51
|
+
import { buildDraftContextBridge, buildDraftBase, buildReadyContexts } from "./context-bridge.js";
|
|
52
|
+
import { SheetDraftEntryTypeFor, SheetDraftGroupTypeFor } from "./drafts.js";
|
|
53
|
+
import { SheetDraftTypeFor } from "./transactions.js";
|
|
54
|
+
/** The wire cells dictionary type. */
|
|
55
|
+
export const SheetCellsType = DictType(StringType, SheetCellType);
|
|
56
|
+
/** The East type of an erased expression (`Expr.type` cannot infer through `ExprType<EastType>`). */
|
|
57
|
+
function typeOf(e) {
|
|
58
|
+
return Expr.type(e);
|
|
59
|
+
}
|
|
60
|
+
/** Whether a type is `Option<T>` — a variant of exactly `none: Null` and `some`. */
|
|
61
|
+
export function optionPayload(t) {
|
|
62
|
+
const v = t;
|
|
63
|
+
if (v.type !== "Variant" || v.cases === undefined)
|
|
64
|
+
return undefined;
|
|
65
|
+
const keys = Object.keys(v.cases);
|
|
66
|
+
if (keys.length !== 2 || v.cases["none"] === undefined || v.cases["some"] === undefined)
|
|
67
|
+
return undefined;
|
|
68
|
+
if (v.cases["none"].type !== "Null")
|
|
69
|
+
return undefined;
|
|
70
|
+
return v.cases["some"];
|
|
71
|
+
}
|
|
72
|
+
/** The cell arm a primitive payload lands in, or `undefined` for a type no cell holds. */
|
|
73
|
+
export function cellTagOf(t) {
|
|
74
|
+
if (isTypeEqual(t, SheetLinkType))
|
|
75
|
+
return "Link";
|
|
76
|
+
switch (t.type) {
|
|
77
|
+
case "String": return "String";
|
|
78
|
+
case "DateTime": return "DateTime";
|
|
79
|
+
case "Float": return "Float";
|
|
80
|
+
case "Integer": return "Integer";
|
|
81
|
+
case "Boolean": return "Boolean";
|
|
82
|
+
default: return undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const SCALAR_PAYLOAD = {
|
|
86
|
+
text: StringType, lookup: StringType, reference: StringType, enum: StringType, stamped: StringType,
|
|
87
|
+
date: DateTimeType, quantity: FloatType, integer: IntegerType,
|
|
88
|
+
};
|
|
89
|
+
/** Compile one rule accessor and check its payload — `String` or `DateTime`, or its Option. */
|
|
90
|
+
function ruleCell(key, rule, rowType, accessor, tag) {
|
|
91
|
+
const fn = East.function([rowType], undefined, (_$, r) => accessor(r));
|
|
92
|
+
const out = Expr.type(fn).output;
|
|
93
|
+
const payload = optionPayload(out);
|
|
94
|
+
const unwrapped = payload ?? out;
|
|
95
|
+
if (cellTagOf(unwrapped) !== tag) {
|
|
96
|
+
throw new Error(`Sheet: column "${key}" has a \`${rule}\` rule returning ${describeType(out)} — it must return ${tag} or Option<${tag}>`);
|
|
97
|
+
}
|
|
98
|
+
return { key: sheetRuleCell(rule, key), fn, tag, optional: payload !== undefined };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The unrendered cells a column's rules project (#844) — `detail` on any
|
|
102
|
+
* column, `level` and `actual` on a date column.
|
|
103
|
+
*/
|
|
104
|
+
function ruleCellsOf(key, kind, cfg, rowType) {
|
|
105
|
+
const out = [];
|
|
106
|
+
if (cfg.detail !== undefined)
|
|
107
|
+
out.push(ruleCell(key, "detail", rowType, cfg.detail, "String"));
|
|
108
|
+
if (kind !== "date") {
|
|
109
|
+
if (cfg.level !== undefined || cfg.actual !== undefined) {
|
|
110
|
+
throw new Error(`Sheet: column "${key}" is a ${kind} column — \`level\` and \`actual\` are date-column rules`);
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
if (cfg.level !== undefined) {
|
|
115
|
+
const level = cfg.level;
|
|
116
|
+
const fn = East.function([rowType], StringType, (_$, r) => resolveTag(level(r), SheetDateLevelType).match({
|
|
117
|
+
week: (_$2) => "week",
|
|
118
|
+
day: (_$2) => "day",
|
|
119
|
+
range: (_$2) => "range",
|
|
120
|
+
time: (_$2) => "time",
|
|
121
|
+
}));
|
|
122
|
+
out.push({ key: sheetRuleCell("level", key), fn, tag: "String", optional: false });
|
|
123
|
+
}
|
|
124
|
+
if (cfg.actual !== undefined)
|
|
125
|
+
out.push(ruleCell(key, "actual", rowType, cfg.actual, "DateTime"));
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Describe one column against the row type — the build-time refusals of
|
|
130
|
+
* §3.12 live here, every one naming the column and the remedy.
|
|
131
|
+
*
|
|
132
|
+
* @param key - The column key
|
|
133
|
+
* @param col - The built column
|
|
134
|
+
* @param rowType - The host's row type
|
|
135
|
+
* @returns The column's metadata
|
|
136
|
+
* @throws Error naming the column when the kind cannot sit on the field
|
|
137
|
+
*/
|
|
138
|
+
export function describeColumn(key, col, rowType) {
|
|
139
|
+
const meta = describeKind(key, col, rowType);
|
|
140
|
+
const ruleCells = ruleCellsOf(key, col.kind, col.config, rowType);
|
|
141
|
+
if (col.config.options !== undefined && col.kind !== "enum" && col.kind !== "lookup" && col.kind !== "link") {
|
|
142
|
+
throw new Error(`Sheet: column "${key}" is a ${col.kind} column — an \`options\` rule narrows an enum, lookup or link column's members`);
|
|
143
|
+
}
|
|
144
|
+
return ruleCells.length > 0 ? { ...meta, ruleCells } : meta;
|
|
145
|
+
}
|
|
146
|
+
/** {@link describeColumn}'s kind-by-kind half. */
|
|
147
|
+
function describeKind(key, col, rowType) {
|
|
148
|
+
const fields = rowType.fields;
|
|
149
|
+
const fieldType = fields[key];
|
|
150
|
+
if (fieldType === undefined) {
|
|
151
|
+
throw new Error(`Sheet: column "${key}" is not a field of the row type — the columns map is keyed by the row's fields (${Object.keys(fields).join(", ")})`);
|
|
152
|
+
}
|
|
153
|
+
const cfg = col.config;
|
|
154
|
+
const kind = col.kind;
|
|
155
|
+
const payloadOf = optionPayload(fieldType);
|
|
156
|
+
const optional = payloadOf !== undefined;
|
|
157
|
+
const unwrapped = payloadOf ?? fieldType;
|
|
158
|
+
// A derived read — the projection decides the payload; the field is free.
|
|
159
|
+
if (cfg.value !== undefined && kind !== "set" && kind !== "link" && kind !== "stamped" && kind !== "custom") {
|
|
160
|
+
const payloadType = SCALAR_PAYLOAD[kind];
|
|
161
|
+
const derived = East.function([rowType], undefined, (_$, r) => cfg.value(r));
|
|
162
|
+
const out = Expr.type(derived).output;
|
|
163
|
+
const outPayload = optionPayload(out);
|
|
164
|
+
const outUnwrapped = outPayload ?? out;
|
|
165
|
+
if (!isTypeEqual(outUnwrapped, payloadType)) {
|
|
166
|
+
throw new Error(`Sheet: column "${key}" is a ${kind} column whose \`value\` projection returns ${outUnwrapped.type} — a ${kind} column's projection must return ${payloadType.type} or its Option`);
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
key, field: key, kind, config: cfg, fieldType, optional, payloadType,
|
|
170
|
+
cellTag: cellTagOf(payloadType), editable: false,
|
|
171
|
+
derived, derivedOptional: outPayload !== undefined,
|
|
172
|
+
...(col.register !== undefined ? { register: col.register } : {}),
|
|
173
|
+
...(col.driverType !== undefined ? { driverType: col.driverType } : {}),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
if (kind === "set" || kind === "link") {
|
|
177
|
+
let form;
|
|
178
|
+
let ownHalf;
|
|
179
|
+
let otherField;
|
|
180
|
+
if (isTypeEqual(fieldType, SheetLinkType) || isTypeEqual(unwrapped, SheetLinkType)) {
|
|
181
|
+
form = "link";
|
|
182
|
+
}
|
|
183
|
+
else if (isTypeEqual(fieldType, SheetMembersType)) {
|
|
184
|
+
form = "array";
|
|
185
|
+
if (kind === "set") {
|
|
186
|
+
ownHalf = "to";
|
|
187
|
+
}
|
|
188
|
+
else if (cfg.to !== undefined && cfg.from === undefined) {
|
|
189
|
+
ownHalf = "from";
|
|
190
|
+
otherField = cfg.to;
|
|
191
|
+
}
|
|
192
|
+
else if (cfg.from !== undefined && cfg.to === undefined) {
|
|
193
|
+
ownHalf = "to";
|
|
194
|
+
otherField = cfg.from;
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
throw new Error(`Sheet: link column "${key}" sits on an Array<Member> field, so it must name the OTHER half's field — \`to: "…"\` when this field holds the from members, \`from: "…"\` when it holds the to members`);
|
|
198
|
+
}
|
|
199
|
+
const otherType = otherField !== undefined ? fields[otherField] : undefined;
|
|
200
|
+
if (otherField !== undefined && (otherType === undefined || !isTypeEqual(otherType, SheetMembersType))) {
|
|
201
|
+
throw new Error(`Sheet: link column "${key}" names "${otherField}" as its other half, but that is not an Array<Sheet.Types.Member> field of the row`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else if (unwrapped.type === "String") {
|
|
205
|
+
form = "string";
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
throw new Error(`Sheet: ${kind} column "${key}" must sit on a Sheet.Types.Link, Array<Sheet.Types.Member> or String field — got ${fieldType.type}`);
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
key, field: key, kind, config: cfg, fieldType, optional: form === "link" ? isTypeEqual(unwrapped, SheetLinkType) && optional : optional,
|
|
212
|
+
payloadType: SheetLinkType, cellTag: "Link", editable: cfg.editable !== false,
|
|
213
|
+
form,
|
|
214
|
+
...(ownHalf !== undefined ? { ownHalf } : {}),
|
|
215
|
+
...(otherField !== undefined ? { otherField } : {}),
|
|
216
|
+
...(col.register !== undefined ? { register: col.register } : {}),
|
|
217
|
+
...(col.driverType !== undefined ? { driverType: col.driverType } : {}),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (kind === "custom") {
|
|
221
|
+
const parseFn = East.value(cfg.parse);
|
|
222
|
+
const parseType = typeOf(parseFn);
|
|
223
|
+
const parsed = optionPayload(parseType.output);
|
|
224
|
+
if (parsed === undefined) {
|
|
225
|
+
throw new Error(`Sheet: custom column "${key}" has a \`parse\` whose output is not an Option — it must return Option<payload>, \`none\` meaning unrecognised`);
|
|
226
|
+
}
|
|
227
|
+
if (!isTypeEqual(parsed, unwrapped)) {
|
|
228
|
+
throw new Error(`Sheet: custom column "${key}" parses to ${parsed.type} but sits on a ${unwrapped.type} field — the payload must be the field's type (or its Option's)`);
|
|
229
|
+
}
|
|
230
|
+
const tag = cellTagOf(parsed);
|
|
231
|
+
if (tag === undefined) {
|
|
232
|
+
throw new Error(`Sheet: custom column "${key}" has payload ${parsed.type}, which no cell holds — use String, Integer, Float, DateTime, Boolean or Sheet.Types.Link`);
|
|
233
|
+
}
|
|
234
|
+
return { key, field: key, kind, config: cfg, fieldType, optional, payloadType: parsed, cellTag: tag, editable: cfg.editable !== false };
|
|
235
|
+
}
|
|
236
|
+
const payloadType = SCALAR_PAYLOAD[kind];
|
|
237
|
+
if (!isTypeEqual(unwrapped, payloadType)) {
|
|
238
|
+
throw new Error(`Sheet: ${kind} column "${key}" must sit on a ${payloadType.type} or Option<${payloadType.type}> field — got ${fieldType.type}`);
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
key, field: key, kind, config: cfg, fieldType, optional, payloadType,
|
|
242
|
+
cellTag: cellTagOf(payloadType),
|
|
243
|
+
editable: kind !== "stamped" && cfg.editable !== false,
|
|
244
|
+
...(col.register !== undefined ? { register: col.register } : {}),
|
|
245
|
+
...(col.driverType !== undefined ? { driverType: col.driverType } : {}),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Describe one band cell against the GROUP's row type (#740) — a column
|
|
250
|
+
* declared over the group's field, keyed by the line column it sits under.
|
|
251
|
+
*
|
|
252
|
+
* @param cellKey - The line column the cell sits under, or `$title`
|
|
253
|
+
* @param cell - The built band cell
|
|
254
|
+
* @param groupType - The group's row type
|
|
255
|
+
* @returns The cell's metadata
|
|
256
|
+
* @throws Error naming the band cell when the kind cannot sit on the field, or is not a band kind
|
|
257
|
+
*/
|
|
258
|
+
export function describeGroupCell(cellKey, cell, groupType) {
|
|
259
|
+
if (cell.kind === "lookup" || cell.kind === "set" || cell.kind === "link" || cell.kind === "custom") {
|
|
260
|
+
throw new Error(`Sheet: band cell "${cellKey}" is a ${cell.kind} cell — a band draws text, date, quantity, integer, reference, enum and stamped cells only`);
|
|
261
|
+
}
|
|
262
|
+
let meta;
|
|
263
|
+
try {
|
|
264
|
+
meta = describeColumn(cell.field, { kind: cell.kind, config: cell.config, ...(cell.register !== undefined ? { register: cell.register } : {}) }, groupType);
|
|
265
|
+
}
|
|
266
|
+
catch (e) {
|
|
267
|
+
throw new Error(`Sheet: band cell "${cellKey}" — ${e.message.replace(/^Sheet: /, "")}`);
|
|
268
|
+
}
|
|
269
|
+
return { ...meta, key: cellKey, field: cell.field };
|
|
270
|
+
}
|
|
271
|
+
// ============================================================================
|
|
272
|
+
// Cells — payload ↔ cell (the static-tag moves)
|
|
273
|
+
// ============================================================================
|
|
274
|
+
/** A payload value as a cell. */
|
|
275
|
+
export function cellOfPayload(tag, v) {
|
|
276
|
+
return East.value(variant(tag, v), SheetCellType);
|
|
277
|
+
}
|
|
278
|
+
/** The blank cell. */
|
|
279
|
+
export const NULL_CELL = East.value(variant("Null", null), SheetCellType);
|
|
280
|
+
/** A field VALUE (the payload or its Option) as a cell, by the column's static shape. */
|
|
281
|
+
function cellOfFieldValue(tag, v, optional) {
|
|
282
|
+
if (!optional)
|
|
283
|
+
return cellOfPayload(tag, v);
|
|
284
|
+
return v.match({
|
|
285
|
+
some: (_$, x) => cellOfPayload(tag, x),
|
|
286
|
+
none: (_$) => NULL_CELL,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* The cell of a column for one row — the projection `make` and `encode`
|
|
291
|
+
* share. `members` is the column's register (bound in the caller's block)
|
|
292
|
+
* for the `String` link form.
|
|
293
|
+
*/
|
|
294
|
+
function projectCell(meta, r, members) {
|
|
295
|
+
const row = r;
|
|
296
|
+
if (meta.derived !== undefined) {
|
|
297
|
+
return cellOfFieldValue(meta.cellTag, meta.derived(r), meta.derivedOptional === true);
|
|
298
|
+
}
|
|
299
|
+
const fv = row[meta.field];
|
|
300
|
+
if (meta.form === undefined)
|
|
301
|
+
return cellOfFieldValue(meta.cellTag, fv, meta.optional);
|
|
302
|
+
if (meta.form === "link")
|
|
303
|
+
return cellOfFieldValue(meta.cellTag, fv, meta.optional);
|
|
304
|
+
if (meta.form === "array") {
|
|
305
|
+
const half = fv;
|
|
306
|
+
const empty = East.value([], SheetMembersType);
|
|
307
|
+
const other = meta.otherField !== undefined ? row[meta.otherField] : empty;
|
|
308
|
+
const link = meta.ownHalf === "from"
|
|
309
|
+
? East.value({ from: half, to: other }, SheetLinkType)
|
|
310
|
+
: East.value({ from: other, to: half }, SheetLinkType);
|
|
311
|
+
return cellOfPayload("Link", link);
|
|
312
|
+
}
|
|
313
|
+
// The String form — the grammar parses it on read; blank text is the blank cell.
|
|
314
|
+
const parse = (text) => text.trim().length().equal(0n).ifElse((_$) => NULL_CELL, (_$) => cellOfPayload("Link", parseLink(text, members ?? East.value([], SheetRegisterMembersType))));
|
|
315
|
+
if (!meta.optional)
|
|
316
|
+
return parse(fv);
|
|
317
|
+
return fv.match({
|
|
318
|
+
some: (_$, s) => parse(s),
|
|
319
|
+
none: (_$) => NULL_CELL,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* A row's id → its key in a keyed source: a String key is its own text, any
|
|
324
|
+
* other key its `.east` printing (as `Record.onApply` reads it), and text that
|
|
325
|
+
* names no key of the type names none.
|
|
326
|
+
*
|
|
327
|
+
* @param keyType - The source's key type
|
|
328
|
+
* @returns `id` → `some(key)`, or `none`
|
|
329
|
+
*/
|
|
330
|
+
function keyOfId(keyType) {
|
|
331
|
+
if (keyType.type === "String") {
|
|
332
|
+
return East.function([StringType], OptionType(StringType), (_$, id) => some(id));
|
|
333
|
+
}
|
|
334
|
+
return East.function([StringType], OptionType(keyType), ($, id) => {
|
|
335
|
+
const key = $.let(none, OptionType(keyType));
|
|
336
|
+
$.try(($2) => { $2.assign(key, some(id.parse(keyType))); }).catch((_$2) => { });
|
|
337
|
+
return key;
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* The id of a row — the id field; on a source with loose rows (#846) the id
|
|
342
|
+
* field of whichever arm the entry holds.
|
|
343
|
+
*/
|
|
344
|
+
function idOf(r, idField) {
|
|
345
|
+
if (idField === undefined)
|
|
346
|
+
throw new Error("Sheet: a positional source needs `id` — the String field that identifies a row");
|
|
347
|
+
if (Expr.type(r).type === "Variant") {
|
|
348
|
+
return r.match({
|
|
349
|
+
group: (_$, g) => g[idField],
|
|
350
|
+
row: (_$, l) => l[idField],
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
return r[idField];
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Compile the bridge for one sheet.
|
|
357
|
+
*
|
|
358
|
+
* @param input - The root's resolved declaration
|
|
359
|
+
* @returns The bridge
|
|
360
|
+
*/
|
|
361
|
+
export function buildBridge(input) {
|
|
362
|
+
const { rowType, idField, metas, registers, driver, source, group: groupInput, subRows } = input;
|
|
363
|
+
const driverType = driver !== undefined ? driver.rowType : NullType;
|
|
364
|
+
// A grouped sheet's columns are declared over the LINE type; its source
|
|
365
|
+
// rows are groups — or, with loose rows between them (#846), entries of a
|
|
366
|
+
// group or a row of the line type. A flat sheet's line type is its row type.
|
|
367
|
+
const loose = rowType.type === "Variant";
|
|
368
|
+
const groupType = loose ? rowType.cases.group : rowType;
|
|
369
|
+
const shape = groupInput !== undefined ? sheetLinesOf(groupType, groupInput.linesField) : undefined;
|
|
370
|
+
const lineType = shape?.lineType ?? groupType;
|
|
371
|
+
const lineIdField = shape === undefined ? idField : undefined;
|
|
372
|
+
const ctxType = (groupInput !== undefined
|
|
373
|
+
? SheetGroupContextTypeFor(groupType, groupInput.linesField, driverType)
|
|
374
|
+
: SheetContextTypeFor(lineType, driverType));
|
|
375
|
+
const checkCtxType = (groupInput !== undefined
|
|
376
|
+
? SheetGroupCheckContextTypeFor(groupType, groupInput.linesField)
|
|
377
|
+
: SheetCheckContextTypeFor(lineType));
|
|
378
|
+
const patchType = SheetPatchTypeFor(lineType);
|
|
379
|
+
const proposalType = SheetProposalTypeFor(lineType);
|
|
380
|
+
// L → the wire cells. Registers are bound ONCE per body for the String
|
|
381
|
+
// link form (the capture rule: data, never a spliced expression).
|
|
382
|
+
const stringFormRegisters = [...new Set(metas.filter((m) => m.form === "string" && m.register !== undefined).map((m) => m.register))];
|
|
383
|
+
const projectRow = East.function([lineType], SheetCellsType, ($, r) => {
|
|
384
|
+
const cells = $.let(new Map(), SheetCellsType);
|
|
385
|
+
const bound = new Map();
|
|
386
|
+
for (const name of stringFormRegisters) {
|
|
387
|
+
bound.set(name, $.const(registers[name], SheetRegisterMembersType));
|
|
388
|
+
}
|
|
389
|
+
for (const m of metas) {
|
|
390
|
+
$(cells.insert(m.key, projectCell(m, r, m.register !== undefined ? bound.get(m.register) : undefined)));
|
|
391
|
+
for (const rc of m.ruleCells ?? []) {
|
|
392
|
+
const rule = $.const(rc.fn);
|
|
393
|
+
$(cells.insert(rc.key, cellOfFieldValue(rc.tag, rule(r), rc.optional)));
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return cells;
|
|
397
|
+
});
|
|
398
|
+
const projectSubRows = buildSubRowProjection(lineType, subRows);
|
|
399
|
+
// (id, offset) → the real source row. Inline, the row at its offset
|
|
400
|
+
// answers first — the renderer reads a resident row where it sits — and
|
|
401
|
+
// otherwise the nearest match outward from there, so a row that a local
|
|
402
|
+
// edit shifted is found in the steps it moved: a read is never a scan of
|
|
403
|
+
// the collection, which made every context and every readiness check
|
|
404
|
+
// quadratic in the rows (#859). The search starts inside the rows, so a
|
|
405
|
+
// stale offset still finds the row. A keyed source — a record read whole
|
|
406
|
+
// (#1182), or a keyed window — reads the row by its key.
|
|
407
|
+
const pagedKeyOf = source.kind === "paged" && source.keyed && source.keyType !== undefined && source.keyType.type !== "String"
|
|
408
|
+
? keyOfId(source.keyType) : undefined;
|
|
409
|
+
const rowById = source.kind === "keyed"
|
|
410
|
+
? East.function([StringType, IntegerType], OptionType(rowType), ($, id, _offset) => {
|
|
411
|
+
const rows = $.const(source.rows, DictType(source.keyType, rowType));
|
|
412
|
+
const keyOf = $.const(keyOfId(source.keyType));
|
|
413
|
+
return keyOf(id).match({
|
|
414
|
+
none: (_$2) => East.value(none, OptionType(rowType)),
|
|
415
|
+
some: (_$2, key) => rows.tryGet(key),
|
|
416
|
+
});
|
|
417
|
+
})
|
|
418
|
+
: source.kind === "inline"
|
|
419
|
+
? East.function([StringType, IntegerType], OptionType(rowType), ($, id, offset) => {
|
|
420
|
+
const rows = $.const(source.rows, ArrayType(rowType));
|
|
421
|
+
const size = $.let(rows.size());
|
|
422
|
+
const start = $.let(offset.less(0n).ifElse(() => 0n, () => offset.greaterEqual(size).ifElse(() => size.subtract(1n), () => offset)));
|
|
423
|
+
const step = $.let(0n);
|
|
424
|
+
$.while(start.subtract(step).greaterEqual(0n).or(() => start.add(step).less(size)), ($2) => {
|
|
425
|
+
const below = $2.let(start.subtract(step));
|
|
426
|
+
$2.if(below.greaterEqual(0n), ($3) => {
|
|
427
|
+
const r = $3.let(rows.get(below));
|
|
428
|
+
$3.if(idOf(r, idField).equal(id), ($4) => { $4.return(East.value(some(r), OptionType(rowType))); });
|
|
429
|
+
});
|
|
430
|
+
const above = $2.let(start.add(step));
|
|
431
|
+
$2.if(step.greater(0n).and(() => above.less(size)), ($3) => {
|
|
432
|
+
const r = $3.let(rows.get(above));
|
|
433
|
+
$3.if(idOf(r, idField).equal(id), ($4) => { $4.return(East.value(some(r), OptionType(rowType))); });
|
|
434
|
+
});
|
|
435
|
+
$2.assign(step, step.add(1n));
|
|
436
|
+
});
|
|
437
|
+
return East.value(none, OptionType(rowType));
|
|
438
|
+
})
|
|
439
|
+
: East.function([StringType, IntegerType], OptionType(rowType), ($, id, offset) => {
|
|
440
|
+
const src = $.const(source.source);
|
|
441
|
+
const noRow = $.const(none, OptionType(rowType));
|
|
442
|
+
const keyOf = pagedKeyOf !== undefined ? $.const(pagedKeyOf) : undefined;
|
|
443
|
+
return src.page(offset, 1n).match({
|
|
444
|
+
none: (_$) => noRow,
|
|
445
|
+
some: (_$, win) => !source.keyed
|
|
446
|
+
? win.firstMap((_$2, r) => idOf(r, idField).equal(id).ifElse((_$3) => East.value(some(r), OptionType(rowType)), (_$3) => East.value(none, OptionType(rowType))))
|
|
447
|
+
: keyOf === undefined
|
|
448
|
+
? win.tryGet(id)
|
|
449
|
+
: keyOf(id).match({
|
|
450
|
+
none: (_$2) => noRow,
|
|
451
|
+
some: (_$2, key) => win.tryGet(key),
|
|
452
|
+
}),
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
// The driver lookup — the driver data folded once into a dictionary; a
|
|
456
|
+
// driverless sheet's lookup is always `none` (its `D` is `Null`).
|
|
457
|
+
const lookupDriver = driver !== undefined
|
|
458
|
+
? East.function([StringType], OptionType(driverType), ($, k) => {
|
|
459
|
+
const dict = $.const(driver.data.toDict((_$, d) => driver.keyFn(d), (_$, d) => d), DictType(StringType, driverType));
|
|
460
|
+
return dict.tryGet(k);
|
|
461
|
+
})
|
|
462
|
+
: East.function([StringType], OptionType(driverType), ($, _k) => $.const(none, OptionType(driverType)));
|
|
463
|
+
// The drafts: a line's cells over its draft (no id — a line's identity is
|
|
464
|
+
// its place), and on a source with loose rows (#846) a loose row's, whose
|
|
465
|
+
// id field is its own.
|
|
466
|
+
const draftRow = buildDraftRowDecoder(lineType, metas, registers, lineIdField);
|
|
467
|
+
const looseRow = loose ? buildDraftRowDecoder(lineType, metas, registers, idField) : undefined;
|
|
468
|
+
const decodeGroup = groupInput !== undefined
|
|
469
|
+
? buildDraftGroupDecoder(groupType, groupInput.linesField, groupInput.cellMetas, draftRow, registers, idField)
|
|
470
|
+
: undefined;
|
|
471
|
+
const flatDraft = SheetDraftTypeFor(groupType);
|
|
472
|
+
const draftDecode = (decodeGroup === undefined
|
|
473
|
+
? East.function([SheetRowType, OptionType(flatDraft), OptionType(SheetRowType)], flatDraft, ($, row, base, _previous) => {
|
|
474
|
+
const decode = $.const(draftRow);
|
|
475
|
+
return decode(row.id, row.cells, base);
|
|
476
|
+
})
|
|
477
|
+
: looseRow !== undefined && groupInput !== undefined
|
|
478
|
+
? buildDraftEntryDecoder(rowType, groupInput.linesField, decodeGroup, looseRow)
|
|
479
|
+
: decodeGroup);
|
|
480
|
+
// The group half (#740).
|
|
481
|
+
const group = groupInput !== undefined && shape !== undefined && decodeGroup !== undefined
|
|
482
|
+
? { ...buildGroupBridge(groupType, groupInput, projectRow, projectSubRows), groupType, loose, decodeGroup, decodeLoose: looseRow }
|
|
483
|
+
: undefined;
|
|
484
|
+
const bridgeCtx = buildDraftContextBridge(rowType, lineType, groupInput?.linesField, ctxType, driverType, rowById, draftDecode, draftRow, lookupDriver, looseRow);
|
|
485
|
+
const bridgeReady = buildReadyContexts(rowType, groupInput?.linesField, ctxType, driverType, rowById, draftDecode, lookupDriver, loose);
|
|
486
|
+
const encodePatch = buildPatchCells(lineType, metas, registers);
|
|
487
|
+
return {
|
|
488
|
+
rowType, lineType, driverType, ctxType, checkCtxType, patchType, proposalType,
|
|
489
|
+
projectRow, projectSubRows, draftDecode, draftRow,
|
|
490
|
+
rowById: rowById,
|
|
491
|
+
bridgeCtx: bridgeCtx,
|
|
492
|
+
bridgeReady,
|
|
493
|
+
encodePatch, seedCells: buildPatchCells(lineType, metas, registers, false),
|
|
494
|
+
...(group !== undefined ? { group } : {}),
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
/** Project supplied constructor/proposal fields without inventing a complete row. @internal */
|
|
498
|
+
export function buildPatchCells(rowType, metas, registers, editableOnly = true) {
|
|
499
|
+
const patchType = SheetPatchTypeFor(rowType);
|
|
500
|
+
const fields = rowType.fields;
|
|
501
|
+
const stringFormRegisters = [...new Set(metas.filter(m => m.form === "string" && m.register !== undefined).map(m => m.register))];
|
|
502
|
+
return East.function([patchType], SheetCellsType, ($, patch) => {
|
|
503
|
+
const cells = $.let(new Map(), SheetCellsType);
|
|
504
|
+
const p = patch;
|
|
505
|
+
const bound = new Map();
|
|
506
|
+
for (const name of stringFormRegisters) {
|
|
507
|
+
bound.set(name, $.const(registers[name], SheetRegisterMembersType));
|
|
508
|
+
}
|
|
509
|
+
for (const m of metas) {
|
|
510
|
+
if ((editableOnly && !m.editable) || m.derived !== undefined)
|
|
511
|
+
continue;
|
|
512
|
+
if (m.form === "array") {
|
|
513
|
+
// The other half is `none` when the column names no other field.
|
|
514
|
+
const otherKey = m.otherField;
|
|
515
|
+
const own = $.let(p[m.field], OptionType(SheetMembersType));
|
|
516
|
+
const other = $.let((otherKey !== undefined ? p[otherKey] : none), OptionType(SheetMembersType));
|
|
517
|
+
$.if(own.hasTag("some").or(() => other.hasTag("some")), ($2) => {
|
|
518
|
+
const empty = $2.const([], SheetMembersType);
|
|
519
|
+
const ownVal = $2.let(own.match({ some: (_$, v) => v, none: (_$) => empty }), SheetMembersType);
|
|
520
|
+
const otherVal = $2.let(other.match({ some: (_$, v) => v, none: (_$) => empty }), SheetMembersType);
|
|
521
|
+
const link = m.ownHalf === "from"
|
|
522
|
+
? $2.let({ from: ownVal, to: otherVal }, SheetLinkType)
|
|
523
|
+
: $2.let({ from: otherVal, to: ownVal }, SheetLinkType);
|
|
524
|
+
$2(cells.insert(m.key, cellOfPayload("Link", link)));
|
|
525
|
+
});
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
const field = $.let(p[m.field], OptionType(fields[m.field]));
|
|
529
|
+
$.if(field.hasTag("some"), ($2) => {
|
|
530
|
+
const v = $2.let(field.unwrap("some"), fields[m.field]);
|
|
531
|
+
const asRow = { [m.field]: v };
|
|
532
|
+
$2(cells.insert(m.key, projectCell(m, asRow, m.register !== undefined ? bound.get(m.register) : undefined)));
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
return cells;
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
// ============================================================================
|
|
539
|
+
// Sub rows (#844)
|
|
540
|
+
// ============================================================================
|
|
541
|
+
/**
|
|
542
|
+
* Compile the sub-row sources into one projection over the line type: each
|
|
543
|
+
* mapper is compiled ONCE as `East.function([L, T], SubRow)` and called in
|
|
544
|
+
* an eager map over its field; the sources concatenate in declaration order.
|
|
545
|
+
*
|
|
546
|
+
* @param lineType - The type the columns are built over
|
|
547
|
+
* @param subRows - The `subRows` declaration, if any
|
|
548
|
+
* @returns `L` → its sub rows
|
|
549
|
+
* @throws Error when the declaration was built over another type, or a key is not an array field
|
|
550
|
+
*/
|
|
551
|
+
export function buildSubRowProjection(lineType, subRows) {
|
|
552
|
+
const fields = lineType.fields;
|
|
553
|
+
const mappers = [];
|
|
554
|
+
if (subRows !== undefined) {
|
|
555
|
+
if (!isTypeEqual(subRows.rowType, lineType)) {
|
|
556
|
+
throw new Error("Sheet: `subRows` was built over a different type than the columns — pass the columns' type (a grouped sheet: the line type) to `Sheet.subRows(…)`");
|
|
557
|
+
}
|
|
558
|
+
for (const [field, map] of Object.entries(subRows.sources)) {
|
|
559
|
+
if (map === undefined)
|
|
560
|
+
continue;
|
|
561
|
+
const t = fields[field];
|
|
562
|
+
if (t === undefined || t.type !== "Array" || t.value === undefined) {
|
|
563
|
+
throw new Error(`Sheet: \`subRows\` names "${field}", which is ${t === undefined ? "not a field of the row type" : `a ${t.type} field`} — a sub-row source must be an Array field`);
|
|
564
|
+
}
|
|
565
|
+
mappers.push({ field, fn: East.function([lineType, t.value], SheetSubRowType, (_$, row, item) => map(item, row)) });
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return East.function([lineType], ArrayType(SheetSubRowType), ($, r) => {
|
|
569
|
+
const row = r;
|
|
570
|
+
let all = $.const([], ArrayType(SheetSubRowType));
|
|
571
|
+
for (const m of mappers) {
|
|
572
|
+
const map = $.const(m.fn);
|
|
573
|
+
const part = $.let(row[m.field].map((_$2, x) => map(r, x)), ArrayType(SheetSubRowType));
|
|
574
|
+
all = mappers[0] === m ? part : $.let(all.concat(part), ArrayType(SheetSubRowType));
|
|
575
|
+
}
|
|
576
|
+
return all;
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
// ============================================================================
|
|
580
|
+
// The group half (#740)
|
|
581
|
+
// ============================================================================
|
|
582
|
+
/**
|
|
583
|
+
* Compile a group's summary cells and ordered children into wire rows.
|
|
584
|
+
*
|
|
585
|
+
* @param rowType - The group's row type
|
|
586
|
+
* @param input - The child field and summary cell metadata
|
|
587
|
+
* @param projectRow - Child row to wire cells
|
|
588
|
+
* @param projectSubRows - Child row to its sub rows
|
|
589
|
+
* @returns The group half
|
|
590
|
+
*/
|
|
591
|
+
function buildGroupBridge(rowType, input, projectRow, projectSubRows) {
|
|
592
|
+
const { linesField, cellMetas } = input;
|
|
593
|
+
const linesType = rowType.fields[linesField];
|
|
594
|
+
const projectGroupCells = East.function([rowType], SheetCellsType, ($, p) => {
|
|
595
|
+
const cells = $.let(new Map(), SheetCellsType);
|
|
596
|
+
for (const m of cellMetas)
|
|
597
|
+
$(cells.insert(m.key, projectCell(m, p, undefined)));
|
|
598
|
+
return cells;
|
|
599
|
+
});
|
|
600
|
+
const projectLines = East.function([rowType], ArrayType(SheetLineType), ($, p) => {
|
|
601
|
+
const project = $.const(projectRow);
|
|
602
|
+
const subRowsOf = $.const(projectSubRows);
|
|
603
|
+
const lines = $.const(p[linesField], linesType);
|
|
604
|
+
return lines.map((_$, line, index) => East.value({ key: East.print(index), cells: project(line), subRows: subRowsOf(line) }, SheetLineType));
|
|
605
|
+
});
|
|
606
|
+
return { linesField, cellMetas, projectGroupCells, projectLines };
|
|
607
|
+
}
|
|
608
|
+
// ============================================================================
|
|
609
|
+
// Wrappers — the author's typed functions inside their wire twins
|
|
610
|
+
// ============================================================================
|
|
611
|
+
/** Whether an East function value is asynchronous. */
|
|
612
|
+
export function isAsyncFunction(fn) {
|
|
613
|
+
return typeOf(fn).type === "AsyncFunction";
|
|
614
|
+
}
|
|
615
|
+
/** Pin an author function value and check its East input type against the bridge's context type. */
|
|
616
|
+
function pinProvider(bridge, fn, where, expectedOutput) {
|
|
617
|
+
const expr = East.value(fn);
|
|
618
|
+
const t = typeOf(expr);
|
|
619
|
+
if (t.type !== "Function" && t.type !== "AsyncFunction") {
|
|
620
|
+
throw new Error(`Sheet: ${where} must be an East.function or East.asyncFunction — got a ${t.type}`);
|
|
621
|
+
}
|
|
622
|
+
const inputs = t.inputs ?? [];
|
|
623
|
+
if (inputs.length !== 1 || !isTypeEqual(inputs[0], bridge.ctxType)) {
|
|
624
|
+
throw new Error(`Sheet: ${where} must take exactly this sheet's context — ${contextName(bridge)} — as its one argument; a function written over another row or driver type cannot run here`);
|
|
625
|
+
}
|
|
626
|
+
if (t.output === undefined || !isTypeEqual(t.output, expectedOutput)) {
|
|
627
|
+
throw new Error(`Sheet: ${where} returns the wrong type — it must return ${describeType(expectedOutput)}`);
|
|
628
|
+
}
|
|
629
|
+
return { fn: expr, async: t.type === "AsyncFunction" };
|
|
630
|
+
}
|
|
631
|
+
/** How this sheet's context is spelt, for a refusal message. */
|
|
632
|
+
function contextName(bridge) {
|
|
633
|
+
const driver = isTypeEqual(bridge.driverType, NullType) ? "" : ", DriverType";
|
|
634
|
+
return bridge.group !== undefined
|
|
635
|
+
? `Sheet.Types.DraftContext(GroupType, "${bridge.group.linesField}"${driver})`
|
|
636
|
+
: `Sheet.Types.DraftContext(RowType${driver})`;
|
|
637
|
+
}
|
|
638
|
+
/** A short description of an East type for a refusal message. */
|
|
639
|
+
function describeType(t) {
|
|
640
|
+
const payload = optionPayload(t);
|
|
641
|
+
if (payload !== undefined)
|
|
642
|
+
return `Option<${describeType(payload)}>`;
|
|
643
|
+
if (t.type === "Struct")
|
|
644
|
+
return `{ ${Object.entries(t.fields).map(([k, f]) => `${k}: ${describeType(f)}`).join(", ")} }`;
|
|
645
|
+
if (t.type === "Array")
|
|
646
|
+
return `Array<${describeType(t.value)}>`;
|
|
647
|
+
return t.type;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Wrap a fill provider — `Sheet.Types.DraftContext(R, D)` → `Option<Fill(T)>` —
|
|
651
|
+
* into the wire provider variant (sync or async by the function's type).
|
|
652
|
+
*/
|
|
653
|
+
export function wrapProvider(bridge, meta, fn, index) {
|
|
654
|
+
const fillType = StructType({ value: meta.payloadType, meta: StringType });
|
|
655
|
+
const { fn: author, async } = pinProvider(bridge, fn, `column "${meta.key}" fill provider #${index + 1}`, OptionType(fillType));
|
|
656
|
+
const body = ($, ctx) => {
|
|
657
|
+
const a = $.const(author);
|
|
658
|
+
const bc = $.const(bridge.bridgeCtx);
|
|
659
|
+
const typed = $.let(bc(ctx), bridge.ctxType);
|
|
660
|
+
return a(typed).match({
|
|
661
|
+
none: (_$) => East.value(none, OptionType(SheetFillType)),
|
|
662
|
+
some: (_$, f) => East.value(some({ value: cellOfPayload(meta.cellTag, f.value), meta: f.meta }), OptionType(SheetFillType)),
|
|
663
|
+
});
|
|
664
|
+
};
|
|
665
|
+
const wire = async
|
|
666
|
+
? East.asyncFunction([SheetContextType], OptionType(SheetFillType), body)
|
|
667
|
+
: East.function([SheetContextType], OptionType(SheetFillType), body);
|
|
668
|
+
return East.value(variant(async ? "async" : "sync", wire), SheetProviderType);
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Wrap a row proposer — `Sheet.Types.DraftContext(R, D)` → `Array<Proposal(R)>` —
|
|
672
|
+
* into the wire proposer variant.
|
|
673
|
+
*/
|
|
674
|
+
export function wrapProposer(bridge, fn, index) {
|
|
675
|
+
const { fn: author, async } = pinProvider(bridge, fn, `suggest.propose #${index + 1}`, ArrayType(bridge.proposalType));
|
|
676
|
+
const body = ($, ctx) => {
|
|
677
|
+
const a = $.const(author);
|
|
678
|
+
const bc = $.const(bridge.bridgeCtx);
|
|
679
|
+
const enc = $.const(bridge.encodePatch);
|
|
680
|
+
const typed = $.let(bc(ctx), bridge.ctxType);
|
|
681
|
+
return a(typed).map((_$, p) => East.value({ cells: enc(p.patch), meta: p.meta }, SheetProposalType));
|
|
682
|
+
};
|
|
683
|
+
const wire = async
|
|
684
|
+
? East.asyncFunction([SheetContextType], ArrayType(SheetProposalType), body)
|
|
685
|
+
: East.function([SheetContextType], ArrayType(SheetProposalType), body);
|
|
686
|
+
return East.value(variant(async ? "async" : "sync", wire), SheetProposerType);
|
|
687
|
+
}
|
|
688
|
+
/** Wrap an arity rule — `Sheet.Types.DraftContext(R, D)` → `Option<Counted>`. */
|
|
689
|
+
export function wrapArity(bridge, meta, fn) {
|
|
690
|
+
const { fn: author, async } = pinProvider(bridge, fn, `column "${meta.key}" arity rule`, OptionType(SheetCountedType));
|
|
691
|
+
if (async)
|
|
692
|
+
throw new Error(`Sheet: column "${meta.key}" arity rule must be synchronous — the strip reads it while the half is edited`);
|
|
693
|
+
return East.function([SheetContextType], OptionType(SheetCountedType), ($, ctx) => {
|
|
694
|
+
const a = $.const(author);
|
|
695
|
+
const bc = $.const(bridge.bridgeCtx);
|
|
696
|
+
return a(bc(ctx));
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Wrap an options rule (#844) — `Sheet.Types.DraftContext(R, D)` →
|
|
701
|
+
* `Option<Array<String>>` — into the wire rule. Synchronous: the menu reads
|
|
702
|
+
* it as the cell opens.
|
|
703
|
+
*/
|
|
704
|
+
export function wrapOptions(bridge, meta, fn) {
|
|
705
|
+
const { fn: author, async } = pinProvider(bridge, fn, `column "${meta.key}" options rule`, OptionType(ArrayType(StringType)));
|
|
706
|
+
if (async)
|
|
707
|
+
throw new Error(`Sheet: column "${meta.key}" options rule must be synchronous — the menu reads it as the cell opens`);
|
|
708
|
+
return East.function([SheetContextType], OptionType(ArrayType(StringType)), ($, ctx) => {
|
|
709
|
+
const a = $.const(author);
|
|
710
|
+
const bc = $.const(bridge.bridgeCtx);
|
|
711
|
+
return a(bc(ctx));
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
/** Wrap an author member check — `Sheet.Types.CheckContext(R)` → `Option<String>` — into the `custom` check arm. */
|
|
715
|
+
export function wrapCheck(bridge, meta, fn, index) {
|
|
716
|
+
const expr = East.value(fn);
|
|
717
|
+
const t = typeOf(expr);
|
|
718
|
+
if (t.type !== "Function" || (t.inputs ?? []).length !== 1 || !isTypeEqual(t.inputs[0], bridge.checkCtxType) ||
|
|
719
|
+
t.output === undefined || !isTypeEqual(t.output, OptionType(StringType))) {
|
|
720
|
+
const spelt = bridge.group !== undefined ? `Sheet.Types.CheckContext(GroupType, "${bridge.group.linesField}")` : "Sheet.Types.CheckContext(RowType)";
|
|
721
|
+
throw new Error(`Sheet: column "${meta.key}" check #${index + 1} must be an East.function over ${spelt} returning Option<String>`);
|
|
722
|
+
}
|
|
723
|
+
const groupHalf = bridge.group;
|
|
724
|
+
const field = groupHalf?.linesField;
|
|
725
|
+
const entryDraft = SheetDraftEntryTypeFor(bridge.rowType, field);
|
|
726
|
+
const draftType = (groupHalf === undefined ? entryDraft : SheetDraftGroupTypeFor(groupHalf.groupType, groupHalf.linesField));
|
|
727
|
+
const rowDraft = SheetDraftTypeFor(bridge.lineType);
|
|
728
|
+
const base = buildDraftBase(bridge.rowType, field, bridge.rowById);
|
|
729
|
+
const wire = East.function([SheetCheckContextType], OptionType(StringType), ($, c) => {
|
|
730
|
+
const a = $.const(expr);
|
|
731
|
+
const read = $.const(base);
|
|
732
|
+
const dec = $.const(bridge.draftRow);
|
|
733
|
+
const original = $.const(read(c.drafts, c.rowId, c.offset), OptionType(entryDraft));
|
|
734
|
+
if (field === undefined || groupHalf === undefined) {
|
|
735
|
+
const row = $.const(dec(c.rowId, c.row, original));
|
|
736
|
+
return a($.const({ rowIndex: c.rowIndex, row, group: none, half: c.half, member: c.member }, bridge.checkCtxType));
|
|
737
|
+
}
|
|
738
|
+
const decGroup = $.const(groupHalf.decodeGroup);
|
|
739
|
+
/** A line's check: the line over its prior draft in its group, the group decoded over the wire group row. */
|
|
740
|
+
const lineCheck = ($2, own) => {
|
|
741
|
+
const group = $2.const(c.group.match({
|
|
742
|
+
none: () => own,
|
|
743
|
+
some: (_$, wire) => some(decGroup(wire, own, some(wire))),
|
|
744
|
+
}), OptionType(draftType));
|
|
745
|
+
const prior = $2.const(group.match({
|
|
746
|
+
none: () => none,
|
|
747
|
+
some: (_$, value) => {
|
|
748
|
+
const children = value[field];
|
|
749
|
+
return c.rowIndex.greaterEqual(0n).and(() => c.rowIndex.less(children.size())).ifElse(() => some(children.get(c.rowIndex)), () => none);
|
|
750
|
+
},
|
|
751
|
+
}), OptionType(rowDraft));
|
|
752
|
+
const row = $2.const(dec("", c.row, prior));
|
|
753
|
+
return a($2.const({ rowIndex: c.rowIndex, row, group, half: c.half, member: c.member }, bridge.checkCtxType));
|
|
754
|
+
};
|
|
755
|
+
const decLoose = groupHalf.decodeLoose;
|
|
756
|
+
if (decLoose === undefined)
|
|
757
|
+
return lineCheck($, original);
|
|
758
|
+
// A source with loose rows (#846): the entry's arms. A loose row has
|
|
759
|
+
// no line address — its own cells over its own draft, and no group.
|
|
760
|
+
const entry = original;
|
|
761
|
+
const decodeLoose = $.const(decLoose);
|
|
762
|
+
return c.line.match({
|
|
763
|
+
some: ($2) => {
|
|
764
|
+
const own = $2.const(entry.match({ none: () => none, some: (_$, e) => e.match({ group: (_$3, g) => some(g), row: () => none }) }), OptionType(draftType));
|
|
765
|
+
return lineCheck($2, own);
|
|
766
|
+
},
|
|
767
|
+
none: ($2) => {
|
|
768
|
+
const own = $2.const(entry.match({ none: () => none, some: (_$, e) => e.match({ row: (_$3, r) => some(r), group: () => none }) }), OptionType(rowDraft));
|
|
769
|
+
const row = $2.const(decodeLoose(c.rowId, c.row, own));
|
|
770
|
+
return a($2.const({ rowIndex: c.rowIndex, row, group: none, half: c.half, member: c.member }, bridge.checkCtxType));
|
|
771
|
+
},
|
|
772
|
+
});
|
|
773
|
+
});
|
|
774
|
+
return East.value(variant("custom", wire), SheetCheckType);
|
|
775
|
+
}
|
|
776
|
+
/** Wrap a custom kind's `parse` — `(text, Context(R, D))` → `Option<P>` — into the wire parse. */
|
|
777
|
+
export function wrapCustomParse(bridge, meta, fn) {
|
|
778
|
+
const expr = East.value(fn);
|
|
779
|
+
const t = typeOf(expr);
|
|
780
|
+
const inputs = t.inputs ?? [];
|
|
781
|
+
if (t.type !== "Function" || inputs.length !== 2 || !isTypeEqual(inputs[0], StringType) || !isTypeEqual(inputs[1], bridge.ctxType)) {
|
|
782
|
+
throw new Error(`Sheet: custom column "${meta.key}" \`parse\` must be an East.function over (String, ${contextName(bridge)}) returning Option<payload>`);
|
|
783
|
+
}
|
|
784
|
+
return East.function([StringType, SheetContextType], OptionType(SheetCellType), ($, text, ctx) => {
|
|
785
|
+
const a = $.const(expr);
|
|
786
|
+
const bc = $.const(bridge.bridgeCtx);
|
|
787
|
+
return a(text, bc(ctx)).match({
|
|
788
|
+
none: (_$) => East.value(none, OptionType(SheetCellType)),
|
|
789
|
+
some: (_$, v) => East.value(some(cellOfPayload(meta.cellTag, v)), OptionType(SheetCellType)),
|
|
790
|
+
});
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
/** Wrap a custom kind's `print` — `P` → `String` — into the wire print. */
|
|
794
|
+
export function wrapCustomPrint(meta, fn) {
|
|
795
|
+
const expr = East.value(fn);
|
|
796
|
+
const t = typeOf(expr);
|
|
797
|
+
const inputs = t.inputs ?? [];
|
|
798
|
+
if (t.type !== "Function" || inputs.length !== 1 || !isTypeEqual(inputs[0], meta.payloadType) ||
|
|
799
|
+
t.output === undefined || !isTypeEqual(t.output, StringType)) {
|
|
800
|
+
throw new Error(`Sheet: custom column "${meta.key}" \`print\` must be an East.function over the payload (${describeType(meta.payloadType)}) returning String`);
|
|
801
|
+
}
|
|
802
|
+
return East.function([SheetCellType], StringType, ($, cell) => {
|
|
803
|
+
const a = $.const(expr);
|
|
804
|
+
return cell.match({ [meta.cellTag]: (_$, v) => a(v) }, (_$) => East.value("", StringType));
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
/** Re-exported for the root's driver-typed refusals. */
|
|
808
|
+
export { SheetHalfType, BooleanType, AsyncFunctionType, EMPTY_LINK };
|
|
809
|
+
//# sourceMappingURL=bridge.js.map
|