@pilotiq/pilotiq 0.23.1 → 0.24.2
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 +91 -0
- package/boost/guidelines.md +566 -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/actions/exportFactory.d.ts +10 -0
- package/dist/actions/exportFactory.d.ts.map +1 -1
- package/dist/actions/exportFactory.js +10 -0
- package/dist/actions/exportFactory.js.map +1 -1
- package/dist/react/CollabRoomContext.d.ts +5 -5
- package/dist/react/index.d.ts +0 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +0 -1
- package/dist/react/index.js.map +1 -1
- package/dist/routes/helpers.d.ts.map +1 -1
- package/dist/routes/helpers.js +6 -2
- package/dist/routes/helpers.js.map +1 -1
- package/dist/routes/relations.d.ts.map +1 -1
- package/dist/routes/relations.js +12 -0
- package/dist/routes/relations.js.map +1 -1
- package/package.json +6 -1
- package/.turbo/turbo-build.log +0 -8
- package/CLAUDE.md +0 -265
- package/dist/react/useCollabSeed.d.ts +0 -23
- package/dist/react/useCollabSeed.d.ts.map +0 -1
- package/dist/react/useCollabSeed.js +0 -82
- package/dist/react/useCollabSeed.js.map +0 -1
- 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 -215
- 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 -195
- 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/useCollabSeed.ts +0 -86
- 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 -700
- package/src/routes/pages.ts +0 -175
- package/src/routes/panel.ts +0 -204
- package/src/routes/relations.ts +0 -1227
- 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
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { describe, it, beforeEach } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import { prismaThemeStorage } from './storage.js'
|
|
4
|
-
import type { PanelGlobalDelegate } from './storage.js'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Per-test stub for the prisma panelGlobal delegate. Captures the args
|
|
8
|
-
* each method was called with so tests can assert the exact wire shape
|
|
9
|
-
* we send Prisma (slug, JSON-encoded data, etc).
|
|
10
|
-
*/
|
|
11
|
-
interface PrismaStub extends PanelGlobalDelegate {
|
|
12
|
-
rows: Map<string, { data: string | object | null }>
|
|
13
|
-
calls: { method: string; args: unknown }[]
|
|
14
|
-
/** When set, the next call to this method throws this error. */
|
|
15
|
-
throwOnce?: { method: 'findUnique' | 'upsert' | 'delete'; error: unknown }
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function makeStub(initial: Record<string, unknown> = {}): PrismaStub {
|
|
19
|
-
const rows = new Map<string, { data: string | object | null }>()
|
|
20
|
-
for (const [slug, data] of Object.entries(initial)) {
|
|
21
|
-
rows.set(slug, { data: typeof data === 'string' ? data : JSON.stringify(data) })
|
|
22
|
-
}
|
|
23
|
-
const calls: { method: string; args: unknown }[] = []
|
|
24
|
-
const stub: PrismaStub = {
|
|
25
|
-
rows,
|
|
26
|
-
calls,
|
|
27
|
-
panelGlobal: {
|
|
28
|
-
async findUnique(args) {
|
|
29
|
-
calls.push({ method: 'findUnique', args })
|
|
30
|
-
if (stub.throwOnce?.method === 'findUnique') {
|
|
31
|
-
const e = stub.throwOnce.error; delete stub.throwOnce; throw e
|
|
32
|
-
}
|
|
33
|
-
return rows.get(args.where.slug) ?? null
|
|
34
|
-
},
|
|
35
|
-
async upsert(args) {
|
|
36
|
-
calls.push({ method: 'upsert', args })
|
|
37
|
-
if (stub.throwOnce?.method === 'upsert') {
|
|
38
|
-
const e = stub.throwOnce.error; delete stub.throwOnce; throw e
|
|
39
|
-
}
|
|
40
|
-
rows.set(args.where.slug, { data: args.update.data })
|
|
41
|
-
return undefined
|
|
42
|
-
},
|
|
43
|
-
async delete(args) {
|
|
44
|
-
calls.push({ method: 'delete', args })
|
|
45
|
-
if (stub.throwOnce?.method === 'delete') {
|
|
46
|
-
const e = stub.throwOnce.error; delete stub.throwOnce; throw e
|
|
47
|
-
}
|
|
48
|
-
if (!rows.has(args.where.slug)) {
|
|
49
|
-
const e: Error & { code?: string } = new Error('Record not found')
|
|
50
|
-
e.code = 'P2025'
|
|
51
|
-
throw e
|
|
52
|
-
}
|
|
53
|
-
rows.delete(args.where.slug)
|
|
54
|
-
return undefined
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
}
|
|
58
|
-
return stub
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
describe('prismaThemeStorage', () => {
|
|
62
|
-
let prisma: PrismaStub
|
|
63
|
-
|
|
64
|
-
beforeEach(() => { prisma = makeStub() })
|
|
65
|
-
|
|
66
|
-
it('load() returns null when no row exists', async () => {
|
|
67
|
-
const storage = prismaThemeStorage(prisma, { slug: 'admin__theme' })
|
|
68
|
-
assert.equal(await storage.load(), null)
|
|
69
|
-
assert.deepEqual(prisma.calls, [{ method: 'findUnique', args: { where: { slug: 'admin__theme' } } }])
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it('load() parses JSON-string data', async () => {
|
|
73
|
-
prisma.rows.set('admin__theme', { data: JSON.stringify({ preset: 'nova' }) })
|
|
74
|
-
const storage = prismaThemeStorage(prisma, { slug: 'admin__theme' })
|
|
75
|
-
assert.deepEqual(await storage.load(), { preset: 'nova' })
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('load() passes through pre-parsed object data', async () => {
|
|
79
|
-
prisma.rows.set('admin__theme', { data: { preset: 'maia' } })
|
|
80
|
-
const storage = prismaThemeStorage(prisma, { slug: 'admin__theme' })
|
|
81
|
-
assert.deepEqual(await storage.load(), { preset: 'maia' })
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
it('save() JSON-encodes the overrides via upsert', async () => {
|
|
85
|
-
const storage = prismaThemeStorage(prisma, { slug: 'admin__theme' })
|
|
86
|
-
await storage.save({ preset: 'lyra', radius: 'medium' })
|
|
87
|
-
const stored = prisma.rows.get('admin__theme')
|
|
88
|
-
assert.ok(stored)
|
|
89
|
-
assert.equal(typeof stored.data, 'string')
|
|
90
|
-
assert.deepEqual(JSON.parse(stored.data as string), { preset: 'lyra', radius: 'medium' })
|
|
91
|
-
const upsertCall = prisma.calls.find(c => c.method === 'upsert')
|
|
92
|
-
assert.ok(upsertCall, 'expected upsert call')
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
it('clear() deletes the row', async () => {
|
|
96
|
-
prisma.rows.set('admin__theme', { data: '{}' })
|
|
97
|
-
const storage = prismaThemeStorage(prisma, { slug: 'admin__theme' })
|
|
98
|
-
await storage.clear()
|
|
99
|
-
assert.equal(prisma.rows.has('admin__theme'), false)
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
it('clear() tolerates "row not found" (P2025)', async () => {
|
|
103
|
-
const storage = prismaThemeStorage(prisma, { slug: 'admin__theme' })
|
|
104
|
-
// Row does not exist — stub throws P2025 — clear() must not propagate.
|
|
105
|
-
await storage.clear()
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
it('clear() rethrows non-P2025 errors', async () => {
|
|
109
|
-
prisma.rows.set('admin__theme', { data: '{}' })
|
|
110
|
-
prisma.throwOnce = { method: 'delete', error: new Error('connection lost') }
|
|
111
|
-
const storage = prismaThemeStorage(prisma, { slug: 'admin__theme' })
|
|
112
|
-
await assert.rejects(() => storage.clear(), /connection lost/)
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it('save() bubbles non-P2025 errors', async () => {
|
|
116
|
-
prisma.throwOnce = { method: 'upsert', error: new Error('connection lost') }
|
|
117
|
-
const storage = prismaThemeStorage(prisma, { slug: 'admin__theme' })
|
|
118
|
-
await assert.rejects(() => storage.save({ preset: 'nova' }), /connection lost/)
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
it('load() bubbles errors (callers swallow if they want back-compat)', async () => {
|
|
122
|
-
prisma.throwOnce = { method: 'findUnique', error: new Error('connection lost') }
|
|
123
|
-
const storage = prismaThemeStorage(prisma, { slug: 'admin__theme' })
|
|
124
|
-
await assert.rejects(() => storage.load(), /connection lost/)
|
|
125
|
-
})
|
|
126
|
-
})
|
package/src/theme/storage.ts
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import type { ThemeConfig } from './types.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Adapter that persists a panel's theme overrides — the JSON blob
|
|
5
|
-
* written when a user edits theme settings via the `themeEditor()`
|
|
6
|
-
* plugin and reloaded on next boot.
|
|
7
|
-
*
|
|
8
|
-
* The shipped implementation is `prismaThemeStorage`, which writes to
|
|
9
|
-
* the `panelGlobal` row created by `@rudderjs/orm-prisma`. Apps on a
|
|
10
|
-
* different ORM, key-value store, or filesystem can implement the
|
|
11
|
-
* three methods themselves.
|
|
12
|
-
*
|
|
13
|
-
* Contract:
|
|
14
|
-
*
|
|
15
|
-
* - `load()` returns `null` when no overrides have been persisted yet
|
|
16
|
-
* (fresh install). Throwing surfaces a configuration error to the
|
|
17
|
-
* caller — pilotiq does not swallow.
|
|
18
|
-
* - `save(overrides)` writes the blob verbatim. The next `load()` must
|
|
19
|
-
* return a deep-equal copy. Throwing surfaces to the route handler
|
|
20
|
-
* as a 500.
|
|
21
|
-
* - `clear()` deletes the row. Tolerating "not found" is the adapter's
|
|
22
|
-
* responsibility — `clear()` on an empty store is a no-op.
|
|
23
|
-
*/
|
|
24
|
-
export interface ThemeStorageAdapter {
|
|
25
|
-
load(): Promise<Partial<ThemeConfig> | null>
|
|
26
|
-
save(overrides: Partial<ThemeConfig>): Promise<void>
|
|
27
|
-
clear(): Promise<void>
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Minimal Prisma surface used by `prismaThemeStorage`. Narrow enough
|
|
32
|
-
* to keep the import surface decoupled from `PrismaClient`'s generated
|
|
33
|
-
* types — apps swap in any client whose `panelGlobal` delegate matches
|
|
34
|
-
* this shape.
|
|
35
|
-
*/
|
|
36
|
-
export interface PanelGlobalDelegate {
|
|
37
|
-
panelGlobal: {
|
|
38
|
-
findUnique(args: { where: { slug: string } }): Promise<{ data: string | object | null } | null>
|
|
39
|
-
upsert(args: {
|
|
40
|
-
where: { slug: string }
|
|
41
|
-
update: { data: string }
|
|
42
|
-
create: { slug: string; data: string }
|
|
43
|
-
}): Promise<unknown>
|
|
44
|
-
delete(args: { where: { slug: string } }): Promise<unknown>
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface PrismaThemeStorageOptions {
|
|
49
|
-
/** Row key written to `panelGlobal.slug`. Pass per-panel so multiple
|
|
50
|
-
* panels in the same app don't clobber each other. Typically
|
|
51
|
-
* `${panel.name}__theme`. */
|
|
52
|
-
slug: string
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Default storage adapter — writes JSON to the `panelGlobal` row keyed
|
|
57
|
-
* by `opts.slug`. The Prisma delegate is dependency-injected so consumers
|
|
58
|
-
* pick how to resolve it (e.g. `app.make('prisma')`, a direct import, a
|
|
59
|
-
* test stub).
|
|
60
|
-
*
|
|
61
|
-
* @example
|
|
62
|
-
* ```ts
|
|
63
|
-
* import { Pilotiq } from '@pilotiq/pilotiq'
|
|
64
|
-
* import { themeEditor, prismaThemeStorage } from '@pilotiq/pilotiq/plugins'
|
|
65
|
-
*
|
|
66
|
-
* const adminPanel = Pilotiq.make('Admin')
|
|
67
|
-
* .use(themeEditor({
|
|
68
|
-
* storage: prismaThemeStorage(prisma, { slug: 'admin__theme' }),
|
|
69
|
-
* }))
|
|
70
|
-
* ```
|
|
71
|
-
*/
|
|
72
|
-
export function prismaThemeStorage(
|
|
73
|
-
prisma: PanelGlobalDelegate,
|
|
74
|
-
opts: PrismaThemeStorageOptions,
|
|
75
|
-
): ThemeStorageAdapter {
|
|
76
|
-
const { slug } = opts
|
|
77
|
-
return {
|
|
78
|
-
async load() {
|
|
79
|
-
const row = await prisma.panelGlobal.findUnique({ where: { slug } })
|
|
80
|
-
if (!row?.data) return null
|
|
81
|
-
const raw = typeof row.data === 'string' ? JSON.parse(row.data) : row.data
|
|
82
|
-
return raw as Partial<ThemeConfig>
|
|
83
|
-
},
|
|
84
|
-
async save(overrides) {
|
|
85
|
-
const data = JSON.stringify(overrides)
|
|
86
|
-
await prisma.panelGlobal.upsert({
|
|
87
|
-
where: { slug },
|
|
88
|
-
update: { data },
|
|
89
|
-
create: { slug, data },
|
|
90
|
-
})
|
|
91
|
-
},
|
|
92
|
-
async clear() {
|
|
93
|
-
try {
|
|
94
|
-
await prisma.panelGlobal.delete({ where: { slug } })
|
|
95
|
-
} catch (e) {
|
|
96
|
-
if (!isRecordNotFound(e)) throw e
|
|
97
|
-
}
|
|
98
|
-
},
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function isRecordNotFound(e: unknown): boolean {
|
|
103
|
-
return typeof e === 'object'
|
|
104
|
-
&& e !== null
|
|
105
|
-
&& (e as { code?: string }).code === 'P2025'
|
|
106
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { colors, HUE_NAMES, type ColorScale } from './colors.js'
|
|
2
|
-
import type { BaseColor, HueColor, ThemeColor, PresetDefinition } from './types.js'
|
|
3
|
-
import { parseSeedToScale } from './generate-scale.js'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Theme (primary) color resolution.
|
|
7
|
-
*
|
|
8
|
-
* The theme color drives `--primary`, `--primary-foreground`, `--ring`, and
|
|
9
|
-
* the matching sidebar variants. Built from a `ColorScale` so adding a new
|
|
10
|
-
* hue is just adding an entry to `colors.ts`.
|
|
11
|
-
*
|
|
12
|
-
* The `'base'` sentinel means "use the current base color" — resolution
|
|
13
|
-
* pulls the matching scale from `colors[baseColor]` at call time.
|
|
14
|
-
*
|
|
15
|
-
* Custom seeds (raw hex / oklch strings outside the `HueColor` union) are
|
|
16
|
-
* parsed into a synthetic 50…950 scale via `parseSeedToScale`.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Build the primary/ring overrides for a given color scale.
|
|
21
|
-
*
|
|
22
|
-
* `isBase` toggles between two strategies:
|
|
23
|
-
* - **base sentinel** (e.g. Theme = "Neutral"): use the strongest contrast
|
|
24
|
-
* *within the same scale* — primary is scale[900] in light, scale[50] in
|
|
25
|
-
* dark. Matches shadcn's neutral theme behavior (very dark/light primary).
|
|
26
|
-
* - **hue** (e.g. Theme = "Blue"): use scale[600] in BOTH light and dark so
|
|
27
|
-
* the brand color stays consistent across modes (instead of lifting to
|
|
28
|
-
* scale[400] in dark, which dilutes brand identity). Foreground stays the
|
|
29
|
-
* near-white neutral[50] for AA contrast against the saturated mid-tone.
|
|
30
|
-
*/
|
|
31
|
-
function buildTheme(scale: ColorScale, isBase = false): PresetDefinition {
|
|
32
|
-
if (isBase) {
|
|
33
|
-
return {
|
|
34
|
-
light: {
|
|
35
|
-
'--primary': scale[900],
|
|
36
|
-
'--primary-foreground': scale[50],
|
|
37
|
-
'--ring': scale[400],
|
|
38
|
-
'--sidebar-primary': scale[900],
|
|
39
|
-
'--sidebar-primary-foreground': scale[50],
|
|
40
|
-
'--sidebar-ring': scale[400],
|
|
41
|
-
},
|
|
42
|
-
dark: {
|
|
43
|
-
'--primary': scale[50],
|
|
44
|
-
'--primary-foreground': scale[900],
|
|
45
|
-
'--ring': scale[600],
|
|
46
|
-
'--sidebar-primary': scale[50],
|
|
47
|
-
'--sidebar-primary-foreground': scale[900],
|
|
48
|
-
'--sidebar-ring': scale[600],
|
|
49
|
-
},
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return {
|
|
53
|
-
light: {
|
|
54
|
-
'--primary': scale[600],
|
|
55
|
-
'--primary-foreground': colors.neutral[50],
|
|
56
|
-
'--ring': scale[600],
|
|
57
|
-
'--sidebar-primary': scale[600],
|
|
58
|
-
'--sidebar-primary-foreground': colors.neutral[50],
|
|
59
|
-
'--sidebar-ring': scale[600],
|
|
60
|
-
},
|
|
61
|
-
dark: {
|
|
62
|
-
'--primary': scale[600],
|
|
63
|
-
'--primary-foreground': colors.neutral[50],
|
|
64
|
-
'--ring': scale[600],
|
|
65
|
-
'--sidebar-primary': scale[600],
|
|
66
|
-
'--sidebar-primary-foreground': colors.neutral[50],
|
|
67
|
-
'--sidebar-ring': scale[600],
|
|
68
|
-
},
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const themeColorMap: Record<HueColor, PresetDefinition> = Object.fromEntries(
|
|
73
|
-
HUE_NAMES.map(name => [name, buildTheme(colors[name])]),
|
|
74
|
-
) as Record<HueColor, PresetDefinition>
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Resolve a theme color value to its CSS variable overrides.
|
|
78
|
-
*
|
|
79
|
-
* @param themeColor selected value — `'base'`, a known hue, or a raw seed string
|
|
80
|
-
* @param baseColor current base color (used to resolve `'base'` sentinel)
|
|
81
|
-
*/
|
|
82
|
-
export function resolveThemeColor(themeColor: ThemeColor, baseColor: BaseColor): PresetDefinition {
|
|
83
|
-
if (themeColor === 'base') return buildTheme(colors[baseColor], true)
|
|
84
|
-
if (themeColor in themeColorMap) return themeColorMap[themeColor as HueColor]
|
|
85
|
-
// Custom seed — parse and synthesize a scale.
|
|
86
|
-
const synthetic = parseSeedToScale(themeColor)
|
|
87
|
-
return synthetic ? buildTheme(synthetic) : buildTheme(colors[baseColor], true)
|
|
88
|
-
}
|
package/src/theme/types.ts
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { BASE_COLOR_NAMES, HUE_NAMES } from './colors.js'
|
|
2
|
-
|
|
3
|
-
// ─── Style Presets ─────────────────────────────────────────
|
|
4
|
-
|
|
5
|
-
/** Built-in style presets — each defines a complete set of OKLCH CSS variables.
|
|
6
|
-
* `vega` is the Pilotiq brand (terracotta on cream, Satoshi). The other six are
|
|
7
|
-
* placeholder neutrals awaiting visual differentiation. */
|
|
8
|
-
export type StylePreset = 'vega' | 'nova' | 'maia' | 'lyra' | 'mira' | 'luma' | 'sera'
|
|
9
|
-
|
|
10
|
-
// ─── Base Colors (gray/neutral scales) ─────────────────────
|
|
11
|
-
|
|
12
|
-
/** Base color scale — controls the neutral/gray tones across the UI. */
|
|
13
|
-
export type BaseColor = typeof BASE_COLOR_NAMES[number]
|
|
14
|
-
|
|
15
|
-
// ─── Theme + Chart Colors ──────────────────────────────────
|
|
16
|
-
|
|
17
|
-
/** Hue tokens shared by both the Theme color and Chart color pickers. */
|
|
18
|
-
export type HueColor = typeof HUE_NAMES[number]
|
|
19
|
-
|
|
20
|
-
/** Theme (primary) color — drives buttons, links, active states, ring.
|
|
21
|
-
* `'base'` means "use the current base color's hue". Strings outside the
|
|
22
|
-
* union are treated as raw hex/oklch seeds (custom color escape hatch). */
|
|
23
|
-
export type ThemeColor = 'base' | HueColor | (string & {})
|
|
24
|
-
|
|
25
|
-
/** Chart palette color — drives `--chart-1..5` as a single-hue ramp.
|
|
26
|
-
* Same shape as `ThemeColor`. */
|
|
27
|
-
export type ChartColor = ThemeColor
|
|
28
|
-
|
|
29
|
-
// ─── Radius ────────────────────────────────────────────────
|
|
30
|
-
|
|
31
|
-
/** Border radius preset. */
|
|
32
|
-
export type RadiusPreset = 'none' | 'small' | 'default' | 'medium' | 'large' | 'xlarge'
|
|
33
|
-
|
|
34
|
-
// ─── Spacing (UI density) ──────────────────────────────────
|
|
35
|
-
|
|
36
|
-
/** Spacing density preset — drives Tailwind's `--spacing` multiplier so every
|
|
37
|
-
* `p-*`, `gap-*`, `m-*` utility scales uniformly across the panel. */
|
|
38
|
-
export type SpacingPreset = 'default' | 'compact' | 'comfortable'
|
|
39
|
-
|
|
40
|
-
// ─── Icon Library ──────────────────────────────────────────
|
|
41
|
-
|
|
42
|
-
/** Icon library identifier — controls which icon set is resolved. */
|
|
43
|
-
export type IconLibrary = 'lucide' | 'tabler' | 'remix' | 'phosphor'
|
|
44
|
-
|
|
45
|
-
// ─── Fonts ─────────────────────────────────────────────────
|
|
46
|
-
|
|
47
|
-
export interface ThemeFonts {
|
|
48
|
-
/** Google Fonts family name for headings, e.g. 'Space Grotesk'. */
|
|
49
|
-
heading?: string
|
|
50
|
-
/** Google Fonts family name for body text, e.g. 'Inter'. */
|
|
51
|
-
body?: string
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// ─── Theme Config (user-facing) ────────────────────────────
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Theme configuration — passed to `Pilotiq.theme()`.
|
|
58
|
-
*
|
|
59
|
-
* @example
|
|
60
|
-
* ```ts
|
|
61
|
-
* Pilotiq.make('admin').theme({
|
|
62
|
-
* preset: 'vega',
|
|
63
|
-
* baseColor: 'taupe',
|
|
64
|
-
* themeColor: 'orange',
|
|
65
|
-
* chartColor: 'base',
|
|
66
|
-
* radius: 'medium',
|
|
67
|
-
* fonts: { heading: 'Space Grotesk', body: 'Inter' },
|
|
68
|
-
* })
|
|
69
|
-
* ```
|
|
70
|
-
*/
|
|
71
|
-
export interface ThemeConfig {
|
|
72
|
-
/** Style preset — sets all CSS variables at once. Defaults to `'vega'`. */
|
|
73
|
-
preset?: StylePreset
|
|
74
|
-
/** Base color scale — overrides neutral/gray tones from the preset. */
|
|
75
|
-
baseColor?: BaseColor
|
|
76
|
-
/** Theme (primary) color. `'base'` = derive from current base color. */
|
|
77
|
-
themeColor?: ThemeColor
|
|
78
|
-
/** Chart color. `'base'` = derive ramp from current base color. */
|
|
79
|
-
chartColor?: ChartColor
|
|
80
|
-
/** Border radius preset. Defaults to `'medium'` (Pilotiq brand). */
|
|
81
|
-
radius?: RadiusPreset
|
|
82
|
-
/** Spacing density preset. `'default'` falls through to the per-style value
|
|
83
|
-
* in `PRESET_SPACING` (e.g. Mira → compact, Vega → comfortable). */
|
|
84
|
-
spacing?: SpacingPreset
|
|
85
|
-
/** Font families (loaded from Google Fonts / Fontshare). */
|
|
86
|
-
fonts?: ThemeFonts
|
|
87
|
-
/** Icon library. */
|
|
88
|
-
iconLibrary?: IconLibrary
|
|
89
|
-
/** Escape hatch: raw CSS variable overrides in OKLCH. Applied last, highest priority. */
|
|
90
|
-
cssVariables?: {
|
|
91
|
-
light?: Record<string, string>
|
|
92
|
-
dark?: Record<string, string>
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// ─── Theme Meta (serialized, server → client) ──────────────
|
|
97
|
-
|
|
98
|
-
/** Resolved theme data sent from server to client via viewProps. */
|
|
99
|
-
export interface ThemeMeta {
|
|
100
|
-
/** CSS variable map for :root (light mode). Keys are CSS custom property names. */
|
|
101
|
-
light: Record<string, string>
|
|
102
|
-
/** CSS variable map for .dark (dark mode). */
|
|
103
|
-
dark: Record<string, string>
|
|
104
|
-
/** Border radius value (e.g. '0.625rem'). */
|
|
105
|
-
radius: string
|
|
106
|
-
/** Spacing multiplier for Tailwind's `--spacing` token (e.g. '0.25rem'). */
|
|
107
|
-
spacing: string
|
|
108
|
-
/** Google Fonts families to load via <link> tag. */
|
|
109
|
-
fonts?: ThemeFonts
|
|
110
|
-
/** Font family CSS values with fallbacks (e.g. "'Inter', sans-serif"). */
|
|
111
|
-
fontFamily?: {
|
|
112
|
-
heading?: string
|
|
113
|
-
body?: string
|
|
114
|
-
}
|
|
115
|
-
/** Icon library identifier for frontend icon resolution. */
|
|
116
|
-
iconLibrary: IconLibrary
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// ─── Preset Definition ─────────────────────────────────────
|
|
120
|
-
|
|
121
|
-
/** A complete set of CSS variable values for light and dark modes (OKLCH strings). */
|
|
122
|
-
export interface PresetDefinition {
|
|
123
|
-
light: Record<string, string>
|
|
124
|
-
dark: Record<string, string>
|
|
125
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pilotiq's upload contract. Apps register an adapter via
|
|
3
|
-
* `Pilotiq.uploads({ adapter })`; the `_uploads` route hands every
|
|
4
|
-
* incoming file to it. Pilotiq stays storage-agnostic — disk, S3,
|
|
5
|
-
* R2, GCS, or a custom storage backend all implement the same shape.
|
|
6
|
-
*/
|
|
7
|
-
export interface UploadAdapter {
|
|
8
|
-
/**
|
|
9
|
-
* Persist the file. Receive `{ file, directory? }`; return the
|
|
10
|
-
* URL the field should store. Throw to fail the upload — the
|
|
11
|
-
* route handler converts thrown errors into a 500 + toast.
|
|
12
|
-
*/
|
|
13
|
-
put(req: UploadRequest): Promise<UploadResult>
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface UploadRequest {
|
|
17
|
-
/** The browser-supplied File. Has `.name`, `.size`, `.type`, `.arrayBuffer()`. */
|
|
18
|
-
file: File
|
|
19
|
-
/** Optional sub-directory hint set by `FileUpload.directory(...)`. */
|
|
20
|
-
directory?: string
|
|
21
|
-
/** The source field name — useful for adapter routing or audit logs. */
|
|
22
|
-
fieldName: string
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface UploadResult {
|
|
26
|
-
/** The public URL the field stores in the form value. */
|
|
27
|
-
url: string
|
|
28
|
-
/**
|
|
29
|
-
* Optional metadata to round-trip alongside the URL — file size,
|
|
30
|
-
* content type, etc. Renderers that show previews may use this.
|
|
31
|
-
* Not part of the form value; can be used by `afterStateUpdated`
|
|
32
|
-
* hooks server-side.
|
|
33
|
-
*/
|
|
34
|
-
meta?: Record<string, unknown>
|
|
35
|
-
}
|
package/src/uploads/index.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { describe, it, before, after } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import { mkdtempSync, existsSync, readFileSync, rmSync } from 'node:fs'
|
|
4
|
-
import { tmpdir } from 'node:os'
|
|
5
|
-
import { join } from 'node:path'
|
|
6
|
-
|
|
7
|
-
import { localUpload } from './localUpload.js'
|
|
8
|
-
|
|
9
|
-
describe('localUpload adapter', () => {
|
|
10
|
-
let dir: string
|
|
11
|
-
|
|
12
|
-
before(() => {
|
|
13
|
-
dir = mkdtempSync(join(tmpdir(), 'pilotiq-uploads-'))
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
after(() => {
|
|
17
|
-
rmSync(dir, { recursive: true, force: true })
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it('writes the file under root + returns a URL', async () => {
|
|
21
|
-
const adapter = localUpload({ root: dir, urlPrefix: '/uploads' })
|
|
22
|
-
const file = new File([new Uint8Array([1, 2, 3, 4])], 'photo.png', { type: 'image/png' })
|
|
23
|
-
const result = await adapter.put({ file, fieldName: 'cover' })
|
|
24
|
-
|
|
25
|
-
assert.match(result.url, /^\/uploads\/[a-f0-9]{32}\.png$/)
|
|
26
|
-
assert.deepEqual(result.meta, { name: 'photo.png', size: 4, type: 'image/png' })
|
|
27
|
-
|
|
28
|
-
const filename = result.url.replace('/uploads/', '')
|
|
29
|
-
const fullPath = join(dir, filename)
|
|
30
|
-
assert.ok(existsSync(fullPath), 'file was written to disk')
|
|
31
|
-
const written = readFileSync(fullPath)
|
|
32
|
-
assert.deepEqual(Array.from(written), [1, 2, 3, 4])
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('honors the directory option', async () => {
|
|
36
|
-
const adapter = localUpload({ root: dir, urlPrefix: '/uploads' })
|
|
37
|
-
const file = new File([new Uint8Array([0])], 'img.jpg', { type: 'image/jpeg' })
|
|
38
|
-
const result = await adapter.put({ file, fieldName: 'avatar', directory: 'avatars/2026' })
|
|
39
|
-
assert.match(result.url, /^\/uploads\/avatars\/2026\/[a-f0-9]{32}\.jpg$/)
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('strips path-traversal segments from directory', async () => {
|
|
43
|
-
const adapter = localUpload({ root: dir, urlPrefix: '/uploads' })
|
|
44
|
-
const file = new File([new Uint8Array([0])], 'x.png', { type: 'image/png' })
|
|
45
|
-
const result = await adapter.put({ file, fieldName: 'x', directory: '../../../etc/passwd' })
|
|
46
|
-
assert.match(result.url, /^\/uploads\/etc\/passwd\/[a-f0-9]{32}\.png$/)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('drops over-long extensions (anti-traversal)', async () => {
|
|
50
|
-
const adapter = localUpload({ root: dir, urlPrefix: '/uploads' })
|
|
51
|
-
const file = new File([new Uint8Array([0])], 'note.thisisaverylongext', { type: 'text/plain' })
|
|
52
|
-
const result = await adapter.put({ file, fieldName: 'x' })
|
|
53
|
-
// Disallowed ext (>10 chars) → file written without extension
|
|
54
|
-
assert.match(result.url, /^\/uploads\/[a-f0-9]{32}$/)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it('drops extensions with non-alphanumeric chars', async () => {
|
|
58
|
-
const adapter = localUpload({ root: dir, urlPrefix: '/uploads' })
|
|
59
|
-
const file = new File([new Uint8Array([0])], 'note.png; rm -rf', { type: 'text/plain' })
|
|
60
|
-
const result = await adapter.put({ file, fieldName: 'x' })
|
|
61
|
-
assert.match(result.url, /^\/uploads\/[a-f0-9]{32}$/)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('strips trailing slash from urlPrefix', async () => {
|
|
65
|
-
const adapter = localUpload({ root: dir, urlPrefix: '/uploads/' })
|
|
66
|
-
const file = new File([new Uint8Array([0])], 'x.png', { type: 'image/png' })
|
|
67
|
-
const result = await adapter.put({ file, fieldName: 'x' })
|
|
68
|
-
assert.match(result.url, /^\/uploads\/[a-f0-9]{32}\.png$/)
|
|
69
|
-
})
|
|
70
|
-
})
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { mkdir, writeFile } from 'node:fs/promises'
|
|
2
|
-
import { join, extname } from 'node:path'
|
|
3
|
-
import { randomBytes } from 'node:crypto'
|
|
4
|
-
|
|
5
|
-
import type { UploadAdapter, UploadRequest, UploadResult } from './UploadAdapter.js'
|
|
6
|
-
|
|
7
|
-
export interface LocalUploadConfig {
|
|
8
|
-
/**
|
|
9
|
-
* Filesystem directory where files are written. Resolved relative to
|
|
10
|
-
* `process.cwd()` if not absolute. The app's static-file middleware
|
|
11
|
-
* must serve this directory at `urlPrefix`.
|
|
12
|
-
*
|
|
13
|
-
* Example: `{ root: 'public/uploads', urlPrefix: '/uploads' }` writes
|
|
14
|
-
* to `<cwd>/public/uploads/<dir>/<id>.<ext>` and returns
|
|
15
|
-
* `/uploads/<dir>/<id>.<ext>`.
|
|
16
|
-
*/
|
|
17
|
-
root: string
|
|
18
|
-
/** URL prefix the file is served from. Without trailing slash. */
|
|
19
|
-
urlPrefix: string
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Disk-backed upload adapter. Writes incoming files under
|
|
24
|
-
* `config.root/<directory>/<random-id>.<ext>` and returns the public
|
|
25
|
-
* URL `<urlPrefix>/<directory>/<random-id>.<ext>`.
|
|
26
|
-
*
|
|
27
|
-
* v1: synchronous-ish (single `writeFile` per upload). No chunking,
|
|
28
|
-
* no resumable uploads, no image processing.
|
|
29
|
-
*/
|
|
30
|
-
export function localUpload(config: LocalUploadConfig): UploadAdapter {
|
|
31
|
-
return {
|
|
32
|
-
async put(req: UploadRequest): Promise<UploadResult> {
|
|
33
|
-
const { file, directory } = req
|
|
34
|
-
const ext = sanitizeExt(extname(file.name))
|
|
35
|
-
const id = randomId()
|
|
36
|
-
const subDir = sanitizeDir(directory)
|
|
37
|
-
const fullDir = subDir
|
|
38
|
-
? join(config.root, subDir)
|
|
39
|
-
: config.root
|
|
40
|
-
|
|
41
|
-
await mkdir(fullDir, { recursive: true })
|
|
42
|
-
|
|
43
|
-
const filename = `${id}${ext}`
|
|
44
|
-
const fullPath = join(fullDir, filename)
|
|
45
|
-
const buffer = Buffer.from(await file.arrayBuffer())
|
|
46
|
-
await writeFile(fullPath, buffer)
|
|
47
|
-
|
|
48
|
-
const urlParts = [config.urlPrefix.replace(/\/$/, '')]
|
|
49
|
-
if (subDir) urlParts.push(subDir)
|
|
50
|
-
urlParts.push(filename)
|
|
51
|
-
return {
|
|
52
|
-
url: urlParts.join('/'),
|
|
53
|
-
meta: {
|
|
54
|
-
name: file.name,
|
|
55
|
-
size: file.size,
|
|
56
|
-
type: file.type,
|
|
57
|
-
},
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function sanitizeDir(d: string | undefined): string {
|
|
64
|
-
if (!d) return ''
|
|
65
|
-
// Strip leading/trailing slashes and any ../ segments to avoid path
|
|
66
|
-
// traversal. Adapters are responsible for their own input validation.
|
|
67
|
-
return d
|
|
68
|
-
.replace(/^[/\\]+/, '')
|
|
69
|
-
.replace(/[/\\]+$/, '')
|
|
70
|
-
.split(/[/\\]/)
|
|
71
|
-
.filter(s => s !== '' && s !== '..' && s !== '.')
|
|
72
|
-
.join('/')
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function sanitizeExt(ext: string): string {
|
|
76
|
-
// Extension comes from a user-uploaded filename — keep it conservative.
|
|
77
|
-
if (!ext || ext.length > 10) return ''
|
|
78
|
-
if (!/^\.[A-Za-z0-9]+$/.test(ext)) return ''
|
|
79
|
-
return ext.toLowerCase()
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function randomId(): string {
|
|
83
|
-
return randomBytes(16).toString('hex')
|
|
84
|
-
}
|