@pilotiq/pilotiq 0.24.1 → 0.24.3
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/CHANGELOG.md +57 -0
- package/boost/guidelines.md +571 -0
- package/boost/skills/pilotiq-actions/SKILL.md +49 -0
- package/boost/skills/pilotiq-actions/rules/dispatch-modes.md +177 -0
- package/boost/skills/pilotiq-actions/rules/factories.md +130 -0
- package/boost/skills/pilotiq-actions/rules/visibility-and-authorization.md +125 -0
- package/boost/skills/pilotiq-fields/SKILL.md +47 -0
- package/boost/skills/pilotiq-fields/rules/field-catalog.md +288 -0
- package/boost/skills/pilotiq-fields/rules/reactive-fields.md +199 -0
- package/boost/skills/pilotiq-fields/rules/validation.md +198 -0
- package/boost/skills/pilotiq-relations/SKILL.md +47 -0
- package/boost/skills/pilotiq-relations/rules/relation-managers.md +256 -0
- package/boost/skills/pilotiq-relations/rules/repeater-relationship.md +177 -0
- package/boost/skills/pilotiq-resource/SKILL.md +61 -0
- package/boost/skills/pilotiq-resource/rules/authorization.md +242 -0
- package/boost/skills/pilotiq-resource/rules/defining-resources.md +228 -0
- package/boost/skills/pilotiq-resource/rules/page-overrides.md +296 -0
- package/dist/Pilotiq.d.ts +31 -0
- package/dist/Pilotiq.d.ts.map +1 -1
- package/dist/Pilotiq.js +3 -1
- package/dist/Pilotiq.js.map +1 -1
- package/dist/PilotiqRegistry.d.ts +13 -0
- package/dist/PilotiqRegistry.d.ts.map +1 -1
- package/dist/PilotiqRegistry.js +15 -0
- package/dist/PilotiqRegistry.js.map +1 -1
- package/dist/pageData/misc.d.ts.map +1 -1
- package/dist/pageData/misc.js +6 -0
- package/dist/pageData/misc.js.map +1 -1
- package/dist/pageData/navigation.d.ts +1 -0
- package/dist/pageData/navigation.d.ts.map +1 -1
- package/dist/pageData/navigation.js +3 -0
- package/dist/pageData/navigation.js.map +1 -1
- package/dist/pageData/relationPages.d.ts.map +1 -1
- package/dist/pageData/relationPages.js +3 -0
- package/dist/pageData/relationPages.js.map +1 -1
- package/dist/pageData/resourcePages.d.ts.map +1 -1
- package/dist/pageData/resourcePages.js +8 -0
- package/dist/pageData/resourcePages.js.map +1 -1
- package/dist/react/AppShell.d.ts +8 -0
- package/dist/react/AppShell.d.ts.map +1 -1
- package/dist/react/AppShell.js.map +1 -1
- package/dist/react/layouts/SidebarLayout.d.ts.map +1 -1
- package/dist/react/layouts/SidebarLayout.js +10 -2
- package/dist/react/layouts/SidebarLayout.js.map +1 -1
- package/dist/react/widgets/StatsOverviewRenderer.d.ts.map +1 -1
- package/dist/react/widgets/StatsOverviewRenderer.js +32 -18
- package/dist/react/widgets/StatsOverviewRenderer.js.map +1 -1
- package/dist/routes/relations.d.ts.map +1 -1
- package/dist/routes/relations.js +25 -18
- package/dist/routes/relations.js.map +1 -1
- package/dist/routes/resources.js.map +1 -1
- package/package.json +10 -5
- package/.turbo/turbo-build.log +0 -8
- package/CLAUDE.md +0 -265
- package/src/Cluster.test.ts +0 -283
- package/src/Cluster.ts +0 -83
- package/src/Column.test.ts +0 -199
- package/src/Column.ts +0 -710
- package/src/Global.test.ts +0 -367
- package/src/Global.ts +0 -169
- package/src/Page.test.ts +0 -114
- package/src/Page.ts +0 -208
- package/src/Pilotiq.perf.test.ts +0 -252
- package/src/Pilotiq.test.ts +0 -129
- package/src/Pilotiq.ts +0 -1158
- package/src/PilotiqRegistry.ts +0 -36
- package/src/PilotiqServiceProvider.ts +0 -121
- package/src/RelationManager.test.ts +0 -400
- package/src/RelationManager.ts +0 -527
- package/src/RenderHook.test.ts +0 -252
- package/src/RenderHook.ts +0 -242
- package/src/Resource.test.ts +0 -284
- package/src/Resource.ts +0 -526
- package/src/RightPanel.test.ts +0 -202
- package/src/RightPanel.ts +0 -132
- package/src/Tab.test.ts +0 -91
- package/src/Tab.ts +0 -156
- package/src/UserMenuItem.ts +0 -145
- package/src/actions/Action.test.ts +0 -2526
- package/src/actions/Action.ts +0 -1515
- package/src/actions/ActionGroup.test.ts +0 -112
- package/src/actions/ActionGroup.ts +0 -173
- package/src/actions/attachFactory.ts +0 -172
- package/src/actions/bulkFactories.ts +0 -168
- package/src/actions/crudFactories.ts +0 -220
- package/src/actions/exportFactory.ts +0 -225
- package/src/actions/factoryHelpers.ts +0 -177
- package/src/actions/importFactory.ts +0 -243
- package/src/actions/index.ts +0 -17
- package/src/actions/m2mFactories.ts +0 -193
- package/src/actions/relationFactories.ts +0 -372
- package/src/applyPageHooks.test.ts +0 -463
- package/src/applyPageHooks.ts +0 -330
- package/src/authorization.test.ts +0 -483
- package/src/breadcrumbs.test.ts +0 -238
- package/src/cells/coerce.test.ts +0 -85
- package/src/cells/coerce.ts +0 -84
- package/src/clusterPaths.ts +0 -35
- package/src/columns/BadgeColumn.test.ts +0 -54
- package/src/columns/BadgeColumn.ts +0 -32
- package/src/columns/BooleanColumn.test.ts +0 -41
- package/src/columns/BooleanColumn.ts +0 -18
- package/src/columns/ColorColumn.test.ts +0 -37
- package/src/columns/ColorColumn.ts +0 -38
- package/src/columns/IconColumn.test.ts +0 -54
- package/src/columns/IconColumn.ts +0 -37
- package/src/columns/ImageColumn.test.ts +0 -41
- package/src/columns/ImageColumn.ts +0 -28
- package/src/columns/SelectColumn.ts +0 -98
- package/src/columns/TextColumn.test.ts +0 -190
- package/src/columns/TextColumn.ts +0 -20
- package/src/columns/TextInputColumn.ts +0 -68
- package/src/columns/ToggleColumn.ts +0 -46
- package/src/columns/editableColumns.test.ts +0 -238
- package/src/columns/index.ts +0 -9
- package/src/defaultGlobalPages.ts +0 -95
- package/src/defaultPages.test.ts +0 -634
- package/src/defaultPages.ts +0 -617
- package/src/defaultViewPage.test.ts +0 -147
- package/src/elements/Form.test.ts +0 -223
- package/src/elements/Form.ts +0 -416
- package/src/elements/ListTabs.ts +0 -28
- package/src/elements/Table.test.ts +0 -422
- package/src/elements/Table.ts +0 -850
- package/src/elements/TableGroup.test.ts +0 -260
- package/src/elements/TableGroup.ts +0 -334
- package/src/elements/dispatchAction.test.ts +0 -463
- package/src/elements/dispatchAction.ts +0 -355
- package/src/elements/dispatchForm.test.ts +0 -477
- package/src/elements/dispatchForm.ts +0 -1993
- package/src/elements/dispatchTable.test.ts +0 -1514
- package/src/elements/dispatchTable.ts +0 -745
- package/src/elements/index.ts +0 -21
- package/src/entries/BadgeEntry.ts +0 -39
- package/src/entries/CodeEntry.test.ts +0 -40
- package/src/entries/CodeEntry.ts +0 -52
- package/src/entries/ColorEntry.ts +0 -63
- package/src/entries/ComponentEntry.test.ts +0 -173
- package/src/entries/ComponentEntry.ts +0 -95
- package/src/entries/Entry.ts +0 -304
- package/src/entries/IconEntry.ts +0 -49
- package/src/entries/ImageEntry.ts +0 -61
- package/src/entries/KeyValueEntry.ts +0 -47
- package/src/entries/RepeatableEntry.test.ts +0 -239
- package/src/entries/RepeatableEntry.ts +0 -173
- package/src/entries/TextEntry.test.ts +0 -394
- package/src/entries/TextEntry.ts +0 -60
- package/src/entries/index.ts +0 -12
- package/src/entries/leaves.test.ts +0 -306
- package/src/entries/registry.ts +0 -54
- package/src/fields/BuilderField.test.ts +0 -1188
- package/src/fields/BuilderField.ts +0 -605
- package/src/fields/BuilderRelationship.test.ts +0 -811
- package/src/fields/CheckboxField.test.ts +0 -44
- package/src/fields/CheckboxField.ts +0 -27
- package/src/fields/CheckboxListField.test.ts +0 -99
- package/src/fields/CheckboxListField.ts +0 -66
- package/src/fields/ColorPickerField.test.ts +0 -33
- package/src/fields/ColorPickerField.ts +0 -25
- package/src/fields/DateField.ts +0 -54
- package/src/fields/DateTimeField.test.ts +0 -55
- package/src/fields/EmailField.ts +0 -16
- package/src/fields/Field.test.ts +0 -654
- package/src/fields/Field.ts +0 -817
- package/src/fields/FileUploadField.test.ts +0 -143
- package/src/fields/FileUploadField.ts +0 -159
- package/src/fields/HiddenField.test.ts +0 -27
- package/src/fields/HiddenField.ts +0 -28
- package/src/fields/KeyValueField.test.ts +0 -105
- package/src/fields/KeyValueField.ts +0 -55
- package/src/fields/MarkdownField.test.ts +0 -167
- package/src/fields/MarkdownField.ts +0 -162
- package/src/fields/NumberField.ts +0 -33
- package/src/fields/RadioField.test.ts +0 -94
- package/src/fields/RadioField.ts +0 -67
- package/src/fields/RepeaterField.test.ts +0 -1806
- package/src/fields/RepeaterField.ts +0 -939
- package/src/fields/RepeaterRelationship.test.ts +0 -1923
- package/src/fields/RepeaterSimple.test.ts +0 -248
- package/src/fields/RowButton.test.ts +0 -219
- package/src/fields/RowButton.ts +0 -135
- package/src/fields/SelectField.test.ts +0 -192
- package/src/fields/SelectField.ts +0 -235
- package/src/fields/SliderField.test.ts +0 -50
- package/src/fields/SliderField.ts +0 -53
- package/src/fields/SlugField.ts +0 -24
- package/src/fields/TagsInputField.test.ts +0 -154
- package/src/fields/TagsInputField.ts +0 -133
- package/src/fields/TextField.test.ts +0 -213
- package/src/fields/TextField.ts +0 -177
- package/src/fields/TextareaField.test.ts +0 -58
- package/src/fields/TextareaField.ts +0 -59
- package/src/fields/ToggleButtonsField.test.ts +0 -106
- package/src/fields/ToggleButtonsField.ts +0 -59
- package/src/fields/ToggleField.ts +0 -16
- package/src/fields/disableOptionsWhenSelectedInSiblingRepeaterItems.test.ts +0 -319
- package/src/fields/optionsResolver.ts +0 -95
- package/src/fields/resolveField.ts +0 -28
- package/src/filters/BooleanFilter.ts +0 -35
- package/src/filters/DateRangeFilter.test.ts +0 -194
- package/src/filters/DateRangeFilter.ts +0 -148
- package/src/filters/Filter.test.ts +0 -268
- package/src/filters/Filter.ts +0 -184
- package/src/filters/FormFilter.test.ts +0 -238
- package/src/filters/FormFilter.ts +0 -215
- package/src/filters/MultiSelectFilter.test.ts +0 -119
- package/src/filters/MultiSelectFilter.ts +0 -78
- package/src/filters/QueryBuilderFilter.test.ts +0 -662
- package/src/filters/QueryBuilderFilter.ts +0 -398
- package/src/filters/SelectFilter.ts +0 -46
- package/src/filters/TernaryFilter.test.ts +0 -160
- package/src/filters/TernaryFilter.ts +0 -72
- package/src/filters/TrashedFilter.test.ts +0 -149
- package/src/filters/TrashedFilter.ts +0 -55
- package/src/filters/queryBuilder/BooleanConstraint.ts +0 -31
- package/src/filters/queryBuilder/Constraint.ts +0 -115
- package/src/filters/queryBuilder/DateConstraint.ts +0 -69
- package/src/filters/queryBuilder/NumberConstraint.ts +0 -66
- package/src/filters/queryBuilder/SelectConstraint.ts +0 -72
- package/src/filters/queryBuilder/TextConstraint.ts +0 -64
- package/src/filters/queryBuilder/index.ts +0 -12
- package/src/icons/index.ts +0 -2
- package/src/icons/lucide.ts +0 -204
- package/src/icons/registry.test.ts +0 -56
- package/src/icons/registry.ts +0 -41
- package/src/icons/types.ts +0 -47
- package/src/index.ts +0 -525
- package/src/io/csv.test.ts +0 -142
- package/src/io/csv.ts +0 -170
- package/src/nestedRelationManagerData.test.ts +0 -547
- package/src/notifications/Notification.test.ts +0 -210
- package/src/notifications/Notification.ts +0 -354
- package/src/notifications/broadcast.test.ts +0 -110
- package/src/notifications/broadcast.ts +0 -95
- package/src/notifications/database.test.ts +0 -383
- package/src/notifications/database.ts +0 -398
- package/src/notifications/databaseNotifications.test.ts +0 -187
- package/src/notifications/dispatchNotificationAction.test.ts +0 -341
- package/src/notifications/dispatchNotificationAction.ts +0 -142
- package/src/notifications/flash.test.ts +0 -89
- package/src/notifications/flash.ts +0 -71
- package/src/notifications/index.ts +0 -45
- package/src/notifications/registerBroadcastAuth.test.ts +0 -134
- package/src/notifications/registerBroadcastAuth.ts +0 -100
- package/src/notifications/resolveSavedNotification.test.ts +0 -82
- package/src/notifications/resolveSavedNotification.ts +0 -59
- package/src/notifications/types.ts +0 -93
- package/src/orm/m2mAccessor.ts +0 -66
- package/src/orm/modelDefaults.test.ts +0 -633
- package/src/orm/modelDefaults.ts +0 -666
- package/src/pageData/breadcrumbs.ts +0 -288
- package/src/pageData/forms.ts +0 -578
- package/src/pageData/helpers.ts +0 -857
- package/src/pageData/misc.ts +0 -347
- package/src/pageData/navigation.ts +0 -842
- package/src/pageData/relationPages.ts +0 -1248
- package/src/pageData/relationTabs.ts +0 -286
- package/src/pageData/resourcePages.ts +0 -609
- package/src/pageData.test.ts +0 -1545
- package/src/pageData.ts +0 -341
- package/src/plugins/index.ts +0 -8
- package/src/plugins/themeEditor.test.ts +0 -36
- package/src/plugins/themeEditor.ts +0 -45
- package/src/react/AppShell.tsx +0 -251
- package/src/react/CollabExtensionFactoryRegistry.ts +0 -55
- package/src/react/CollabRoomContext.ts +0 -98
- package/src/react/CollabTextRendererRegistry.ts +0 -102
- package/src/react/CommandPalette.tsx +0 -375
- package/src/react/CurrentUserContext.tsx +0 -50
- package/src/react/CustomPageWrapperGate.tsx +0 -69
- package/src/react/CustomPageWrapperRegistry.ts +0 -45
- package/src/react/FieldFocusReporterRegistry.ts +0 -37
- package/src/react/FieldLabelSlotRegistry.ts +0 -30
- package/src/react/FieldPresenceRegistry.ts +0 -46
- package/src/react/FormCollabBindingRegistry.ts +0 -242
- package/src/react/FormStateContext.tsx +0 -591
- package/src/react/HeadHooks.tsx +0 -126
- package/src/react/MarkdownEditorRegistry.test.ts +0 -38
- package/src/react/MarkdownEditorRegistry.ts +0 -107
- package/src/react/NotificationActionStrip.tsx +0 -263
- package/src/react/NotificationBell.tsx +0 -426
- package/src/react/PendingSuggestionApplierRegistry.test.ts +0 -97
- package/src/react/PendingSuggestionApplierRegistry.ts +0 -98
- package/src/react/PendingSuggestionOverlayRegistry.ts +0 -54
- package/src/react/PendingSuggestionsContext.tsx +0 -172
- package/src/react/RecordWrapperGate.tsx +0 -58
- package/src/react/RecordWrapperRegistry.ts +0 -39
- package/src/react/RenderHookSlot.tsx +0 -32
- package/src/react/RightSidebar.tsx +0 -257
- package/src/react/RightSidebarContext.tsx +0 -234
- package/src/react/RightSidebarTrigger.tsx +0 -53
- package/src/react/RowCoordsContext.tsx +0 -23
- package/src/react/SchemaRenderer.tsx +0 -549
- package/src/react/SearchTrigger.tsx +0 -46
- package/src/react/ThemeProvider.tsx +0 -93
- package/src/react/ThemeSettingsPage.tsx +0 -579
- package/src/react/ThemeToggle.tsx +0 -20
- package/src/react/Toaster.tsx +0 -158
- package/src/react/UserMenu.tsx +0 -196
- package/src/react/WidgetDataContext.tsx +0 -157
- package/src/react/cells/EditableCell.tsx +0 -389
- package/src/react/component-slots.test.ts +0 -103
- package/src/react/component-slots.ts +0 -116
- package/src/react/fieldJsHandler.test.ts +0 -166
- package/src/react/fieldJsHandler.ts +0 -79
- package/src/react/fields/BuilderInput.tsx +0 -1078
- package/src/react/fields/CheckboxInput.tsx +0 -39
- package/src/react/fields/CheckboxListInput.tsx +0 -102
- package/src/react/fields/ColorInput.tsx +0 -71
- package/src/react/fields/DateFieldInput.tsx +0 -70
- package/src/react/fields/DateTimeInput.tsx +0 -62
- package/src/react/fields/FieldShell.tsx +0 -348
- package/src/react/fields/FileUploadInput.tsx +0 -639
- package/src/react/fields/HiddenInput.tsx +0 -17
- package/src/react/fields/KeyValueInput.tsx +0 -230
- package/src/react/fields/MarkdownInput.tsx +0 -560
- package/src/react/fields/RadioInput.tsx +0 -81
- package/src/react/fields/RepeaterInput.test.ts +0 -116
- package/src/react/fields/RepeaterInput.tsx +0 -1420
- package/src/react/fields/SelectFieldInput.tsx +0 -280
- package/src/react/fields/SliderInput.tsx +0 -81
- package/src/react/fields/TagsInput.tsx +0 -283
- package/src/react/fields/TextLikeInput.tsx +0 -256
- package/src/react/fields/ToggleButtonsInput.tsx +0 -60
- package/src/react/fields/ToggleFieldInput.tsx +0 -56
- package/src/react/fields/relationshipRenameDispatch.test.ts +0 -106
- package/src/react/fields/relationshipRenameDispatch.ts +0 -97
- package/src/react/fields/repeaterReconcile.test.ts +0 -114
- package/src/react/fields/repeaterReconcile.ts +0 -104
- package/src/react/fields/rowChromeButton.tsx +0 -336
- package/src/react/fields/rowState.ts +0 -106
- package/src/react/fields/syncRowGates.test.ts +0 -202
- package/src/react/fields/syncRowGates.ts +0 -66
- package/src/react/fields/textInputControls.tsx +0 -238
- package/src/react/fields/useRowReorderDnd.ts +0 -78
- package/src/react/formStateHelpers.test.ts +0 -508
- package/src/react/formStateHelpers.ts +0 -381
- package/src/react/hooks/use-mobile.ts +0 -19
- package/src/react/icon-context.tsx +0 -60
- package/src/react/index.ts +0 -194
- package/src/react/layouts/SidebarLayout.tsx +0 -250
- package/src/react/layouts/TopbarLayout.tsx +0 -258
- package/src/react/navigate.tsx +0 -37
- package/src/react/onProviderSynced.test.ts +0 -90
- package/src/react/parseRecordEditUrl.test.ts +0 -122
- package/src/react/parseRecordEditUrl.ts +0 -94
- package/src/react/persistedState.ts +0 -40
- package/src/react/registry.ts +0 -48
- package/src/react/right-panel-registry.tsx +0 -47
- package/src/react/schemaRenderer/AlertRenderer.tsx +0 -112
- package/src/react/schemaRenderer/EntryRenderer.tsx +0 -501
- package/src/react/schemaRenderer/SectionRenderer.tsx +0 -120
- package/src/react/schemaRenderer/SimpleElements.tsx +0 -306
- package/src/react/schemaRenderer/TabsRenderer.tsx +0 -62
- package/src/react/schemaRenderer/WizardRenderer.tsx +0 -338
- package/src/react/schemaRenderer/action/ActionGroupTrigger.tsx +0 -177
- package/src/react/schemaRenderer/action/ActionModalDialog.tsx +0 -273
- package/src/react/schemaRenderer/action/ConfirmActionDialog.tsx +0 -61
- package/src/react/schemaRenderer/action/HandlerActionButton.tsx +0 -43
- package/src/react/schemaRenderer/action/MethodActionButton.tsx +0 -64
- package/src/react/schemaRenderer/action/buttons.tsx +0 -99
- package/src/react/schemaRenderer/action/helpers.ts +0 -140
- package/src/react/schemaRenderer/action/renderAction.tsx +0 -245
- package/src/react/schemaRenderer/columnFormat.ts +0 -65
- package/src/react/schemaRenderer/constants.ts +0 -50
- package/src/react/schemaRenderer/form/FormRenderer.tsx +0 -274
- package/src/react/schemaRenderer/form/renderField.tsx +0 -511
- package/src/react/schemaRenderer/helpers.tsx +0 -81
- package/src/react/schemaRenderer/table/CardsLayoutBody.tsx +0 -308
- package/src/react/schemaRenderer/table/TableRenderer.tsx +0 -123
- package/src/react/schemaRenderer/table/TableRendererBody.tsx +0 -974
- package/src/react/schemaRenderer/table/filters.tsx +0 -1233
- package/src/react/schemaRenderer/table/formatCell.tsx +0 -264
- package/src/react/schemaRenderer/table/links.tsx +0 -112
- package/src/react/schemaRenderer/table/renderRowActions.tsx +0 -52
- package/src/react/schemaRenderer/table/url.tsx +0 -143
- package/src/react/theme-preview/apply.ts +0 -99
- package/src/react/theme-preview/build-html.ts +0 -436
- package/src/react/ui/button.tsx +0 -51
- package/src/react/ui/calendar.tsx +0 -67
- package/src/react/ui/checkbox.tsx +0 -29
- package/src/react/ui/dialog.tsx +0 -108
- package/src/react/ui/dropdown-menu.tsx +0 -97
- package/src/react/ui/input.tsx +0 -20
- package/src/react/ui/label.tsx +0 -21
- package/src/react/ui/popover.tsx +0 -50
- package/src/react/ui/select.tsx +0 -169
- package/src/react/ui/separator.tsx +0 -25
- package/src/react/ui/sheet.tsx +0 -136
- package/src/react/ui/sidebar.tsx +0 -723
- package/src/react/ui/skeleton.tsx +0 -13
- package/src/react/ui/slider.tsx +0 -34
- package/src/react/ui/switch.tsx +0 -28
- package/src/react/ui/table.tsx +0 -105
- package/src/react/ui/tabs.tsx +0 -63
- package/src/react/ui/textarea.tsx +0 -18
- package/src/react/ui/tooltip.tsx +0 -64
- package/src/react/useResizableWidth.ts +0 -139
- package/src/react/utils.ts +0 -6
- package/src/react/widgetRegistry.test.ts +0 -43
- package/src/react/widgetRegistry.ts +0 -50
- package/src/react/widgets/StatsOverviewRenderer.tsx +0 -232
- package/src/react/widgets/TableWidgetRenderer.tsx +0 -231
- package/src/react/widgets/ViewRenderer.tsx +0 -71
- package/src/relationManagerData.test.ts +0 -1595
- package/src/richtext/index.ts +0 -8
- package/src/richtext/registry.ts +0 -89
- package/src/routes/globals.ts +0 -148
- package/src/routes/guard.test.ts +0 -325
- package/src/routes/helpers.ts +0 -704
- package/src/routes/pages.ts +0 -175
- package/src/routes/panel.ts +0 -204
- package/src/routes/relations.ts +0 -1243
- package/src/routes/resources.ts +0 -781
- package/src/routes/theme.ts +0 -91
- package/src/routes-nested-relations.test.ts +0 -676
- package/src/routes-relations.test.ts +0 -972
- package/src/routes.test.ts +0 -2027
- package/src/routes.ts +0 -303
- package/src/schema/Alert.test.ts +0 -109
- package/src/schema/Alert.ts +0 -131
- package/src/schema/Block.ts +0 -169
- package/src/schema/Breadcrumbs.ts +0 -40
- package/src/schema/Card.ts +0 -35
- package/src/schema/Divider.ts +0 -20
- package/src/schema/Element.ts +0 -219
- package/src/schema/EmptyState.test.ts +0 -37
- package/src/schema/EmptyState.ts +0 -63
- package/src/schema/Fieldset.ts +0 -43
- package/src/schema/Grid.ts +0 -43
- package/src/schema/Group.ts +0 -30
- package/src/schema/Heading.ts +0 -39
- package/src/schema/Html.ts +0 -67
- package/src/schema/Icon.ts +0 -54
- package/src/schema/Image.ts +0 -57
- package/src/schema/LinkTag.ts +0 -41
- package/src/schema/Markdown.ts +0 -85
- package/src/schema/MetaTag.ts +0 -41
- package/src/schema/RelationTabs.ts +0 -71
- package/src/schema/ScriptTag.ts +0 -55
- package/src/schema/Section.ts +0 -160
- package/src/schema/ServerDataElement.test.ts +0 -140
- package/src/schema/ServerDataElement.ts +0 -156
- package/src/schema/SlotComponent.test.ts +0 -77
- package/src/schema/SlotComponent.ts +0 -71
- package/src/schema/Split.ts +0 -50
- package/src/schema/Stat.test.ts +0 -118
- package/src/schema/Stat.ts +0 -154
- package/src/schema/StatsOverview.test.ts +0 -141
- package/src/schema/StatsOverview.ts +0 -119
- package/src/schema/StyleTag.ts +0 -35
- package/src/schema/TableWidget.test.ts +0 -297
- package/src/schema/TableWidget.ts +0 -289
- package/src/schema/Tabs.ts +0 -79
- package/src/schema/Text.ts +0 -58
- package/src/schema/UnorderedList.ts +0 -49
- package/src/schema/View.test.ts +0 -111
- package/src/schema/View.ts +0 -127
- package/src/schema/Wizard.ts +0 -220
- package/src/schema/containers.test.ts +0 -564
- package/src/schema/headTags.test.ts +0 -134
- package/src/schema/index.ts +0 -40
- package/src/schema/primes.test.ts +0 -269
- package/src/schema/resolveSchema.test.ts +0 -379
- package/src/schema/resolveSchema.ts +0 -917
- package/src/schema/sanitize.ts +0 -58
- package/src/search.test.ts +0 -446
- package/src/search.ts +0 -178
- package/src/sessionFilters.test.ts +0 -375
- package/src/sessionFilters.ts +0 -143
- package/src/slot-components/index.ts +0 -10
- package/src/slot-components/registry.ts +0 -56
- package/src/styles/file-upload.css +0 -13
- package/src/summarizers/Summarizer.test.ts +0 -84
- package/src/summarizers/Summarizer.ts +0 -123
- package/src/summarizers/index.ts +0 -11
- package/src/theme/base-colors.ts +0 -68
- package/src/theme/chart-colors.ts +0 -50
- package/src/theme/colors.ts +0 -447
- package/src/theme/generate-css.test.ts +0 -139
- package/src/theme/generate-css.ts +0 -44
- package/src/theme/generate-scale.test.ts +0 -106
- package/src/theme/generate-scale.ts +0 -97
- package/src/theme/icon-map.ts +0 -42
- package/src/theme/index.ts +0 -34
- package/src/theme/migrate.test.ts +0 -178
- package/src/theme/migrate.ts +0 -81
- package/src/theme/presets.ts +0 -135
- package/src/theme/radius.ts +0 -18
- package/src/theme/resolve.test.ts +0 -238
- package/src/theme/resolve.ts +0 -96
- package/src/theme/spacing.ts +0 -18
- package/src/theme/storage.test.ts +0 -126
- package/src/theme/storage.ts +0 -106
- package/src/theme/theme-colors.ts +0 -88
- package/src/theme/types.ts +0 -125
- package/src/uploads/UploadAdapter.ts +0 -35
- package/src/uploads/index.ts +0 -2
- package/src/uploads/localUpload.test.ts +0 -70
- package/src/uploads/localUpload.ts +0 -84
- package/src/validation/Validator.ts +0 -49
- package/src/validation/index.ts +0 -28
- package/src/validation/rules.ts +0 -78
- package/src/validation/runValidators.ts +0 -435
- package/src/validation/uniqueValidator.test.ts +0 -196
- package/src/validation/uniqueValidator.ts +0 -133
- package/src/validation/validators.test.ts +0 -268
- package/src/vite.test.ts +0 -184
- package/src/vite.ts +0 -787
- package/src/widgets/index.ts +0 -10
- package/src/widgets/registry.ts +0 -45
- package/src/widgets.test.ts +0 -592
- package/tsconfig.build.json +0 -11
- package/tsconfig.json +0 -4
- package/tsconfig.test.json +0 -10
- package/views/react/Dashboard.tsx +0 -27
- package/views/react/Resources/Form.tsx +0 -102
- package/views/react/Resources/Index.tsx +0 -49
package/src/pageData/forms.ts
DELETED
|
@@ -1,578 +0,0 @@
|
|
|
1
|
-
import type { Pilotiq } from '../Pilotiq.js'
|
|
2
|
-
import type { Page } from '../Page.js'
|
|
3
|
-
import { Element } from '../schema/Element.js'
|
|
4
|
-
import { resolveSchema, type SchemaContext, type RenderContext } from '../schema/resolveSchema.js'
|
|
5
|
-
import { Form } from '../elements/Form.js'
|
|
6
|
-
import { applyStateUpdate, coerceFormValues, findForms, findWizardStep, selectFormById } from '../elements/dispatchForm.js'
|
|
7
|
-
import { findRecord } from '../orm/modelDefaults.js'
|
|
8
|
-
import { isRepeaterField, RepeaterField } from '../fields/RepeaterField.js'
|
|
9
|
-
import { isBuilderField, BuilderField } from '../fields/BuilderField.js'
|
|
10
|
-
import { SelectField } from '../fields/SelectField.js'
|
|
11
|
-
import { validateSchema } from '../validation/index.js'
|
|
12
|
-
import { callPageSchema, uploadCtx, userCtx } from './helpers.js'
|
|
13
|
-
|
|
14
|
-
// ─── Shared scope → page+form+record resolver ────────────────
|
|
15
|
-
//
|
|
16
|
-
// Every form-subresource builder (`formStateData / formWizardData /
|
|
17
|
-
// formCreateOptionData / mentionResolveData`) needs to: resolve the
|
|
18
|
-
// `FormStateScope` to a concrete `PageClass`, build the upload/user-aware
|
|
19
|
-
// `SchemaContext`, call the page's `schema()`, and pick the requested form
|
|
20
|
-
// by id. `resolveScopeForm` does that whole prelude in one async step.
|
|
21
|
-
|
|
22
|
-
interface ResolveScopeFormSuccess {
|
|
23
|
-
ok: true
|
|
24
|
-
PageClass: typeof Page
|
|
25
|
-
baseCtx: SchemaContext
|
|
26
|
-
elements: Element[]
|
|
27
|
-
form: Form
|
|
28
|
-
record: unknown
|
|
29
|
-
mode: 'create' | 'edit'
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
interface ResolveScopeFormFormMissing {
|
|
33
|
-
ok: false
|
|
34
|
-
status: 404
|
|
35
|
-
error: string
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Resolve `FormStateScope` → `{ PageClass, baseCtx, elements, form, record, mode }`.
|
|
40
|
-
*
|
|
41
|
-
* Returns `null` when the scope's slug doesn't resolve to a real
|
|
42
|
-
* resource/global/page or the matching page-role (`create`/`edit`) isn't
|
|
43
|
-
* registered — caller returns 404 to the client. Returns
|
|
44
|
-
* `{ ok: false, status: 404, error }` when the page resolves but the
|
|
45
|
-
* requested `formId` isn't on it.
|
|
46
|
-
*/
|
|
47
|
-
async function resolveScopeForm(
|
|
48
|
-
pilotiq: Pilotiq,
|
|
49
|
-
scope: FormStateScope,
|
|
50
|
-
formId: string,
|
|
51
|
-
user: unknown,
|
|
52
|
-
): Promise<ResolveScopeFormSuccess | ResolveScopeFormFormMissing | null> {
|
|
53
|
-
const cfg = pilotiq.getConfig()
|
|
54
|
-
|
|
55
|
-
let PageClass: typeof Page | undefined
|
|
56
|
-
let mode: 'create' | 'edit'
|
|
57
|
-
let record: unknown = undefined
|
|
58
|
-
let baseCtxExtras: Record<string, unknown> = {}
|
|
59
|
-
|
|
60
|
-
if (scope.kind === 'resource-create' || scope.kind === 'resource-edit') {
|
|
61
|
-
const R = pilotiq.findResource(scope.slug)
|
|
62
|
-
if (!R) return null
|
|
63
|
-
const pages = R.resolvePages()
|
|
64
|
-
if (scope.kind === 'resource-create') {
|
|
65
|
-
if (!pages.create) return null
|
|
66
|
-
PageClass = pages.create
|
|
67
|
-
mode = 'create'
|
|
68
|
-
} else {
|
|
69
|
-
if (!pages.edit) return null
|
|
70
|
-
PageClass = pages.edit
|
|
71
|
-
mode = 'edit'
|
|
72
|
-
baseCtxExtras = { recordId: scope.recordId }
|
|
73
|
-
if (R.model) {
|
|
74
|
-
try { record = await findRecord(R, scope.recordId, { user }) } catch { /* ignore */ }
|
|
75
|
-
} else {
|
|
76
|
-
record = { id: scope.recordId }
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
} else if (scope.kind === 'global-edit') {
|
|
80
|
-
const G = pilotiq.findGlobal(scope.slug)
|
|
81
|
-
if (!G) return null
|
|
82
|
-
const pages = G.resolvePages()
|
|
83
|
-
if (!pages.edit) return null
|
|
84
|
-
PageClass = pages.edit
|
|
85
|
-
mode = 'edit'
|
|
86
|
-
} else {
|
|
87
|
-
const P = pilotiq.findPage(scope.pageSlug)
|
|
88
|
-
if (!P) return null
|
|
89
|
-
PageClass = P
|
|
90
|
-
// Custom pages don't have a record/edit-mode concept — pass mode
|
|
91
|
-
// 'edit' so resolveSchema treats fields as form inputs (not table
|
|
92
|
-
// cells / view-mode read-only).
|
|
93
|
-
mode = 'edit'
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const baseCtx: SchemaContext = uploadCtx(userCtx({ mode, basePath: cfg.path, ...baseCtxExtras }, user), cfg)
|
|
97
|
-
const elements = await callPageSchema(PageClass, baseCtx)
|
|
98
|
-
const form = selectFormById(findForms(elements), formId)
|
|
99
|
-
if (!form) return { ok: false, status: 404, error: `Form "${formId}" not found on page` }
|
|
100
|
-
|
|
101
|
-
return { ok: true, PageClass, baseCtx, elements, form, record, mode }
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// ─── Form-related data builders ─────────────────────────────
|
|
105
|
-
//
|
|
106
|
-
// Plan #5 partial-resolve (formStateData), Plan #8 wizard step-validate
|
|
107
|
-
// (formWizardData), inline-create-option (formCreateOptionData), and
|
|
108
|
-
// async-mention resolve (mentionResolveData). All four sit downstream
|
|
109
|
-
// of `resolveSchema` and re-run the form lifecycle in narrower modes
|
|
110
|
-
// (just the changed field; just the requested step; etc.).
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
// ─── Plan #5 partial-resolve data builder ────────────────────
|
|
114
|
-
|
|
115
|
-
export type FormStateScope =
|
|
116
|
-
| { kind: 'resource-create'; slug: string }
|
|
117
|
-
| { kind: 'resource-edit'; slug: string; recordId: string }
|
|
118
|
-
| { kind: 'global-edit'; slug: string }
|
|
119
|
-
| { kind: 'page'; pageSlug: string }
|
|
120
|
-
|
|
121
|
-
export interface FormStateRequest {
|
|
122
|
-
formId: string
|
|
123
|
-
changed: string
|
|
124
|
-
values: Record<string, unknown>
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export interface FormStateResult {
|
|
128
|
-
ok: true
|
|
129
|
-
form: Record<string, unknown> // resolved FormMeta
|
|
130
|
-
dirty: string[]
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export interface FormStateError {
|
|
134
|
-
ok: false
|
|
135
|
-
status: 404 | 422
|
|
136
|
-
error: string
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Plan #5 — handle a partial-resolve roundtrip from a `live()` field.
|
|
141
|
-
*
|
|
142
|
-
* Locates the page's schema, finds the targeted form by `formId`, runs
|
|
143
|
-
* `applyStateUpdate` to apply the changed value + run
|
|
144
|
-
* `afterStateUpdated`, then re-resolves the form's children with the
|
|
145
|
-
* mutated values + bound `$get / $set` so dependent options /
|
|
146
|
-
* conditional visibility re-evaluate. Returns the resolved FormMeta the
|
|
147
|
-
* client uses to replace its rendered form.
|
|
148
|
-
*
|
|
149
|
-
* Returns `null` when the route prefix doesn't resolve to a real
|
|
150
|
-
* resource/global/page — the route handler turns this into a 404. The
|
|
151
|
-
* inner `{ status: 422 }` failure is for "form found but `changed`
|
|
152
|
-
* field doesn't exist on it" — also a client-side bug.
|
|
153
|
-
*/
|
|
154
|
-
export async function formStateData(
|
|
155
|
-
pilotiq: Pilotiq,
|
|
156
|
-
scope: FormStateScope,
|
|
157
|
-
body: FormStateRequest,
|
|
158
|
-
req?: unknown,
|
|
159
|
-
): Promise<FormStateResult | FormStateError | null> {
|
|
160
|
-
const user = await pilotiq.resolveUser(req)
|
|
161
|
-
const loaded = await resolveScopeForm(pilotiq, scope, body.formId, user)
|
|
162
|
-
if (!loaded) return null
|
|
163
|
-
if (!loaded.ok) return loaded
|
|
164
|
-
const { baseCtx, form, record } = loaded
|
|
165
|
-
|
|
166
|
-
const update = await applyStateUpdate(form, body.values, body.changed, {
|
|
167
|
-
...(record !== undefined ? { record } : {}),
|
|
168
|
-
...(user !== null ? { user } : {}),
|
|
169
|
-
request: req,
|
|
170
|
-
})
|
|
171
|
-
if (!update) {
|
|
172
|
-
return { ok: false, status: 422, error: `Field "${body.changed}" not found on form "${body.formId}"` }
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Re-resolve the form with the mutated values bound. We bind
|
|
176
|
-
// `$get / $set` against the post-update values map so further
|
|
177
|
-
// resolve-time logic (SelectField.options(fn), reactive
|
|
178
|
-
// visibility) reads current state.
|
|
179
|
-
const $get = (name: string): unknown => update.values[name]
|
|
180
|
-
// $set on the resolve pass is a no-op — only afterStateUpdated
|
|
181
|
-
// mutations survive into the response. Resolve-time `$set` would
|
|
182
|
-
// race against the client's view of the world.
|
|
183
|
-
const $set = (_name: string, _v: unknown): void => { /* intentional no-op */ }
|
|
184
|
-
|
|
185
|
-
const resolveCtx = {
|
|
186
|
-
...baseCtx,
|
|
187
|
-
values: update.values,
|
|
188
|
-
$get,
|
|
189
|
-
$set,
|
|
190
|
-
changed: body.changed,
|
|
191
|
-
...(record !== undefined ? { record } : {}),
|
|
192
|
-
}
|
|
193
|
-
// Snapshot values onto the form so its FormMeta carries them.
|
|
194
|
-
form.withValues(update.values)
|
|
195
|
-
const resolved = await resolveSchema([form], resolveCtx)
|
|
196
|
-
const formMeta = resolved[0]
|
|
197
|
-
if (!formMeta || formMeta.type !== 'form') {
|
|
198
|
-
return { ok: false, status: 422, error: 'Form re-resolved to non-form meta' }
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return { ok: true, form: formMeta, dirty: update.dirty }
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// ─── Plan #8 wizard step-validate data builder ────────────────
|
|
205
|
-
|
|
206
|
-
export interface FormWizardRequest {
|
|
207
|
-
formId: string
|
|
208
|
-
step: number
|
|
209
|
-
values: Record<string, unknown>
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export interface FormWizardSuccess {
|
|
213
|
-
ok: true
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export interface FormWizardFailure {
|
|
217
|
-
ok: false
|
|
218
|
-
status: 404 | 422
|
|
219
|
-
error?: string
|
|
220
|
-
errors?: Record<string, string[]>
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Plan #8 — handle a Wizard step-validate POST. Locates the form by id,
|
|
225
|
-
* walks to the Wizard descendant, validates only the fields inside step
|
|
226
|
-
* `step` against `values`. Returns `{ ok: true }` on success or
|
|
227
|
-
* `{ ok: false, status: 422, errors }` when fields fail validation.
|
|
228
|
-
*
|
|
229
|
-
* Errors are keyed by field name, same shape as the form-submit 422 path,
|
|
230
|
-
* so the client (`FormStateApi.applyErrors`) can surface them in-place.
|
|
231
|
-
*/
|
|
232
|
-
export async function formWizardData(
|
|
233
|
-
pilotiq: Pilotiq,
|
|
234
|
-
scope: FormStateScope,
|
|
235
|
-
body: FormWizardRequest,
|
|
236
|
-
req?: unknown,
|
|
237
|
-
): Promise<FormWizardSuccess | FormWizardFailure | null> {
|
|
238
|
-
const user = await pilotiq.resolveUser(req)
|
|
239
|
-
const loaded = await resolveScopeForm(pilotiq, scope, body.formId, user)
|
|
240
|
-
if (!loaded) return null
|
|
241
|
-
if (!loaded.ok) return loaded
|
|
242
|
-
const { form, record } = loaded
|
|
243
|
-
|
|
244
|
-
const formChildren = form.getChildren() ?? []
|
|
245
|
-
const step = findWizardStep(formChildren, body.step)
|
|
246
|
-
if (!step) return { ok: false, status: 404, error: `Step ${body.step} not found on form "${body.formId}"` }
|
|
247
|
-
|
|
248
|
-
// Step.beforeValidation — runs before validators. May mutate `body.values`
|
|
249
|
-
// in place (the validator reads from the same object), or throw to halt
|
|
250
|
-
// with a 422 stamped under the reserved `_step` key.
|
|
251
|
-
type StepHook = (values: Record<string, unknown>, ctx: { record?: unknown; user?: unknown }) => void | Promise<void>
|
|
252
|
-
const stepHooks = step as {
|
|
253
|
-
getBeforeValidation?: () => StepHook | undefined
|
|
254
|
-
getAfterValidation?: () => StepHook | undefined
|
|
255
|
-
}
|
|
256
|
-
const beforeHook = stepHooks.getBeforeValidation?.call(step)
|
|
257
|
-
if (beforeHook) {
|
|
258
|
-
try { await beforeHook(body.values, { record, user }) }
|
|
259
|
-
catch (err) {
|
|
260
|
-
return { ok: false, status: 422, errors: { _step: [stepHookErrorMessage(err)] } }
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const errors = await validateSchema(step.getChildren() ?? [], body.values, record)
|
|
265
|
-
if (Object.keys(errors).length > 0) {
|
|
266
|
-
return { ok: false, status: 422, errors }
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Step.afterValidation — fires only when validators pass. Same throw →
|
|
270
|
-
// 422 contract as beforeValidation.
|
|
271
|
-
const afterHook = stepHooks.getAfterValidation?.call(step)
|
|
272
|
-
if (afterHook) {
|
|
273
|
-
try { await afterHook(body.values, { record, user }) }
|
|
274
|
-
catch (err) {
|
|
275
|
-
return { ok: false, status: 422, errors: { _step: [stepHookErrorMessage(err)] } }
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return { ok: true }
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function stepHookErrorMessage(err: unknown): string {
|
|
283
|
-
if (err instanceof Error && err.message) return err.message
|
|
284
|
-
if (typeof err === 'string' && err.length > 0) return err
|
|
285
|
-
return 'Step validation failed'
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// ─── SelectField inline-create-option data builder ───────────
|
|
289
|
-
|
|
290
|
-
export interface FormCreateOptionRequest {
|
|
291
|
-
formId: string
|
|
292
|
-
fieldName: string
|
|
293
|
-
values: Record<string, unknown>
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
export interface FormCreateOptionSuccess {
|
|
297
|
-
ok: true
|
|
298
|
-
option: { value: string; label: string }
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
export interface FormCreateOptionFailure {
|
|
302
|
-
ok: false
|
|
303
|
-
status: 403 | 404 | 422 | 500
|
|
304
|
-
error?: string
|
|
305
|
-
errors?: Record<string, string[]>
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/** Find a `SelectField` by name inside a form's children, walking through
|
|
309
|
-
* layout containers but stopping at Repeater / Builder boundaries
|
|
310
|
-
* (parallel to `tagSelectCreateOptionUrls`'s walker). Returns the first
|
|
311
|
-
* match or `undefined`. */
|
|
312
|
-
function findSelectFieldByName(elements: Element[], name: string): SelectField | undefined {
|
|
313
|
-
for (const el of elements) {
|
|
314
|
-
if (el instanceof SelectField) {
|
|
315
|
-
if (el.name === name) return el
|
|
316
|
-
continue
|
|
317
|
-
}
|
|
318
|
-
if (el instanceof RepeaterField) continue
|
|
319
|
-
if (el instanceof BuilderField) continue
|
|
320
|
-
const children = el.getChildren()
|
|
321
|
-
if (children && children.length > 0) {
|
|
322
|
-
const found = findSelectFieldByName(children as Element[], name)
|
|
323
|
-
if (found) return found
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
return undefined
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Audit row 2026-05-07 cont'd⁸ — handle a `SelectField.createOptionForm()`
|
|
331
|
-
* modal submit. Locates the parent form by `formId`, finds the SelectField
|
|
332
|
-
* by `fieldName`, re-evaluates the `createOptionAuthorize` rule (so a
|
|
333
|
-
* tampered URL can't bypass), coerces + validates the body against the
|
|
334
|
-
* sub-form's fields, then calls `createOptionUsing(handler)` and returns
|
|
335
|
-
* `{ option }` for the client to append + select.
|
|
336
|
-
*
|
|
337
|
-
* Returns `null` when the route prefix doesn't resolve to a real
|
|
338
|
-
* resource/global/page (route handler turns into 404).
|
|
339
|
-
*/
|
|
340
|
-
export async function formCreateOptionData(
|
|
341
|
-
pilotiq: Pilotiq,
|
|
342
|
-
scope: FormStateScope,
|
|
343
|
-
body: FormCreateOptionRequest,
|
|
344
|
-
req?: unknown,
|
|
345
|
-
): Promise<FormCreateOptionSuccess | FormCreateOptionFailure | null> {
|
|
346
|
-
const user = await pilotiq.resolveUser(req)
|
|
347
|
-
const loaded = await resolveScopeForm(pilotiq, scope, body.formId, user)
|
|
348
|
-
if (!loaded) return null
|
|
349
|
-
if (!loaded.ok) return loaded
|
|
350
|
-
const { baseCtx, form, record } = loaded
|
|
351
|
-
|
|
352
|
-
const field = findSelectFieldByName(form.getChildren() as Element[] ?? [], body.fieldName)
|
|
353
|
-
if (!field) return { ok: false, status: 404, error: `SelectField "${body.fieldName}" not found on form "${body.formId}"` }
|
|
354
|
-
if (!field.hasCreateOption()) return { ok: false, status: 404, error: `SelectField "${body.fieldName}" does not configure createOptionForm()` }
|
|
355
|
-
|
|
356
|
-
const createForm = field.getCreateOptionForm()!
|
|
357
|
-
const handler = field.getCreateOptionHandler()
|
|
358
|
-
if (!handler) {
|
|
359
|
-
return { ok: false, status: 500, error: `SelectField "${body.fieldName}" has createOptionForm() but no createOptionUsing() handler` }
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// Re-evaluate authorize. Build the same ActionVisibilityContext shape
|
|
363
|
-
// the field's `toMeta` did — keeps server / meta-build paths consistent.
|
|
364
|
-
const authorize = field.getCreateOptionAuthorize()
|
|
365
|
-
if (authorize !== undefined) {
|
|
366
|
-
const authVisible = await (async () => {
|
|
367
|
-
if (typeof authorize !== 'function') return authorize
|
|
368
|
-
const visCtx: import('../actions/Action.js').ActionVisibilityContext = {}
|
|
369
|
-
if (record !== undefined) visCtx.record = record
|
|
370
|
-
if (user !== null ) visCtx.user = user
|
|
371
|
-
try { return await authorize(visCtx) } catch { return false }
|
|
372
|
-
})()
|
|
373
|
-
if (!authVisible) return { ok: false, status: 403, error: 'createOptionAuthorize denied' }
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// Coerce + validate body against the sub-form's fields. The createOption
|
|
377
|
-
// sub-schema is detached from the parent form so we run it against its
|
|
378
|
-
// own children only — coerceFormValues mutates `out` to normalize toggle
|
|
379
|
-
// / number / date / etc. shapes (same shape parent forms use).
|
|
380
|
-
const coerced = coerceFormValues(createForm, { ...body.values })
|
|
381
|
-
const errors = await validateSchema(createForm, coerced, undefined)
|
|
382
|
-
if (Object.keys(errors).length > 0) {
|
|
383
|
-
return { ok: false, status: 422, errors }
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
const ctx: RenderContext = {
|
|
387
|
-
...baseCtx,
|
|
388
|
-
values: coerced,
|
|
389
|
-
...(record !== undefined ? { record } : {}),
|
|
390
|
-
}
|
|
391
|
-
let option: { value: string; label: string }
|
|
392
|
-
try {
|
|
393
|
-
option = await handler(coerced, ctx)
|
|
394
|
-
} catch (e) {
|
|
395
|
-
return { ok: false, status: 500, error: e instanceof Error ? e.message : String(e) }
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
if (!option || typeof option.value !== 'string' || typeof option.label !== 'string') {
|
|
399
|
-
return { ok: false, status: 500, error: `createOptionUsing must return { value: string, label: string }` }
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
return { ok: true, option }
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// ─── Async-mention resolve data builder ──────────────────────
|
|
406
|
-
|
|
407
|
-
export interface MentionResolveRequest {
|
|
408
|
-
formId: string
|
|
409
|
-
field: string
|
|
410
|
-
trigger: string
|
|
411
|
-
query: string
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/** Wire-side shape for a single resolved item — mirrors `MentionItem` from
|
|
415
|
-
* `@pilotiq/tiptap`. Pilotiq core doesn't import that package, so the
|
|
416
|
-
* duck-typed shape lives here. */
|
|
417
|
-
export interface MentionResolveItem {
|
|
418
|
-
id: string
|
|
419
|
-
label: string
|
|
420
|
-
group?: string
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
export interface MentionResolveSuccess {
|
|
424
|
-
ok: true
|
|
425
|
-
items: MentionResolveItem[]
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
export interface MentionResolveError {
|
|
429
|
-
ok: false
|
|
430
|
-
status: 404 | 422
|
|
431
|
-
error: string
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
interface AsyncMentionResolverField {
|
|
435
|
-
resolveMention(
|
|
436
|
-
trigger: string,
|
|
437
|
-
query: string,
|
|
438
|
-
ctx: { user?: unknown; record?: unknown; request?: unknown },
|
|
439
|
-
): Promise<MentionResolveItem[] | null>
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
function isMentionResolverField(el: Element): el is Element & AsyncMentionResolverField {
|
|
443
|
-
if (el.getType() !== 'richtext') return false
|
|
444
|
-
const candidate = el as unknown as Partial<AsyncMentionResolverField>
|
|
445
|
-
return typeof candidate.resolveMention === 'function'
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
/**
|
|
449
|
-
* Walk a form's tree looking for the named field. Descends into Repeater /
|
|
450
|
-
* Builder rows when the requested name carries the row-prefix shape:
|
|
451
|
-
*
|
|
452
|
-
* - Repeater rows: `<repeaterName>.<index>.<innerPath>` — looks up
|
|
453
|
-
* `<innerPath>` against the Repeater's template schema. Field config
|
|
454
|
-
* (providers, async resolver) is shared across rows, so any row index
|
|
455
|
-
* resolves to the same template field.
|
|
456
|
-
* - Builder rows: `<builderName>.<index>.data.<innerPath>` — looks up
|
|
457
|
-
* `<innerPath>` against every block's schema; first match wins. Block
|
|
458
|
-
* schemas often share leaf names — if two blocks define a RichTextField
|
|
459
|
-
* with the same name and different async-mention providers, only the
|
|
460
|
-
* first block in declaration order is reachable here. Authors needing
|
|
461
|
-
* per-block resolution should give the leaves distinct names.
|
|
462
|
-
*
|
|
463
|
-
* Mirrors the boundary-stopping posture of `findFieldByName` inside
|
|
464
|
-
* `dispatchForm.ts` for top-level matches — only the dotted-prefix branch
|
|
465
|
-
* crosses into row schemas.
|
|
466
|
-
*/
|
|
467
|
-
function findRichTextFieldByName(
|
|
468
|
-
elements: ReadonlyArray<Element>,
|
|
469
|
-
name: string,
|
|
470
|
-
): (Element & AsyncMentionResolverField) | undefined {
|
|
471
|
-
for (const el of elements) {
|
|
472
|
-
if (isMentionResolverField(el) && (el as unknown as { name: string }).name === name) {
|
|
473
|
-
return el
|
|
474
|
-
}
|
|
475
|
-
if (isRepeaterField(el)) {
|
|
476
|
-
const inner = stripRepeaterRowPrefix(name, (el as RepeaterField).name)
|
|
477
|
-
if (inner !== undefined) {
|
|
478
|
-
const hit = findRichTextFieldByName((el as RepeaterField).getInnerSchema(), inner)
|
|
479
|
-
if (hit) return hit
|
|
480
|
-
}
|
|
481
|
-
continue
|
|
482
|
-
}
|
|
483
|
-
if (isBuilderField(el)) {
|
|
484
|
-
const inner = stripBuilderRowPrefix(name, (el as BuilderField).name)
|
|
485
|
-
if (inner !== undefined) {
|
|
486
|
-
for (const block of (el as BuilderField).getBlocks()) {
|
|
487
|
-
const hit = findRichTextFieldByName(block.getSchema(), inner)
|
|
488
|
-
if (hit) return hit
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
continue
|
|
492
|
-
}
|
|
493
|
-
const children = el.getChildren()
|
|
494
|
-
if (children && children.length > 0) {
|
|
495
|
-
const hit = findRichTextFieldByName(children, name)
|
|
496
|
-
if (hit) return hit
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
return undefined
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
/**
|
|
503
|
-
* `items.0.body` → `body`. Returns `undefined` when the path doesn't match
|
|
504
|
-
* the `<repeaterName>.<digits>.<rest>` shape so the walker keeps searching
|
|
505
|
-
* other branches instead of misinterpreting an unrelated dotted name.
|
|
506
|
-
*/
|
|
507
|
-
function stripRepeaterRowPrefix(path: string, repeaterName: string): string | undefined {
|
|
508
|
-
const parts = path.split('.')
|
|
509
|
-
if (parts.length < 3) return undefined
|
|
510
|
-
if (parts[0] !== repeaterName) return undefined
|
|
511
|
-
if (!/^\d+$/.test(parts[1] ?? '')) return undefined
|
|
512
|
-
return parts.slice(2).join('.')
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* `blocks.0.data.heading` → `heading`. The literal `data` segment matches
|
|
517
|
-
* Builder's wire shape (`{ __id, type, data: {…} }`) and distinguishes a
|
|
518
|
-
* Builder leaf from a Repeater leaf at the same depth.
|
|
519
|
-
*/
|
|
520
|
-
function stripBuilderRowPrefix(path: string, builderName: string): string | undefined {
|
|
521
|
-
const parts = path.split('.')
|
|
522
|
-
if (parts.length < 4) return undefined
|
|
523
|
-
if (parts[0] !== builderName) return undefined
|
|
524
|
-
if (!/^\d+$/.test(parts[1] ?? '')) return undefined
|
|
525
|
-
if (parts[2] !== 'data') return undefined
|
|
526
|
-
return parts.slice(3).join('.')
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* Resolve one async-mention round-trip. Locates the page's schema, finds
|
|
531
|
-
* the form by `formId` and the RichTextField by `field`, calls its
|
|
532
|
-
* `resolveMention(trigger, query, ctx)`. Returns `{ ok, items }`, a 404
|
|
533
|
-
* when the form / field / trigger isn't present, or `null` for a missing
|
|
534
|
-
* page (the route handler turns `null` into a 404 too).
|
|
535
|
-
*
|
|
536
|
-
* The dispatcher is duck-typed against the contract in `@pilotiq/tiptap`'s
|
|
537
|
-
* `RichTextField` — pilotiq core never imports the adapter. Any future
|
|
538
|
-
* field-type that ships an async-resolve trigger can implement the same
|
|
539
|
-
* shape and pick up routing for free.
|
|
540
|
-
*/
|
|
541
|
-
export async function mentionResolveData(
|
|
542
|
-
pilotiq: Pilotiq,
|
|
543
|
-
scope: FormStateScope,
|
|
544
|
-
body: MentionResolveRequest,
|
|
545
|
-
req?: unknown,
|
|
546
|
-
): Promise<MentionResolveSuccess | MentionResolveError | null> {
|
|
547
|
-
const user = await pilotiq.resolveUser(req)
|
|
548
|
-
const loaded = await resolveScopeForm(pilotiq, scope, body.formId, user)
|
|
549
|
-
if (!loaded) return null
|
|
550
|
-
if (!loaded.ok) return loaded
|
|
551
|
-
const { form, record } = loaded
|
|
552
|
-
|
|
553
|
-
const field = findRichTextFieldByName(form.getChildren() ?? [], body.field)
|
|
554
|
-
if (!field) {
|
|
555
|
-
return { ok: false, status: 404, error: `Rich-text field "${body.field}" not found on form "${body.formId}"` }
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
let items: MentionResolveItem[] | null
|
|
559
|
-
try {
|
|
560
|
-
items = await field.resolveMention(body.trigger, body.query, {
|
|
561
|
-
...(record !== undefined ? { record } : {}),
|
|
562
|
-
...(user !== null ? { user } : {}),
|
|
563
|
-
request: req,
|
|
564
|
-
})
|
|
565
|
-
} catch (err) {
|
|
566
|
-
return {
|
|
567
|
-
ok: false,
|
|
568
|
-
status: 422,
|
|
569
|
-
error: err instanceof Error ? err.message : 'Mention resolver threw',
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
if (items === null) {
|
|
574
|
-
return { ok: false, status: 404, error: `No mention provider for trigger "${body.trigger}" on field "${body.field}"` }
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
return { ok: true, items }
|
|
578
|
-
}
|