@rebasepro/studio 0.0.1-canary.09e5ec5
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/LICENSE +114 -0
- package/README.md +159 -0
- package/dist/ApiExplorer-gMJt5JrS.js +1053 -0
- package/dist/ApiExplorer-gMJt5JrS.js.map +1 -0
- package/dist/AuthSimulationSelector-BF4rkRGp.js +118 -0
- package/dist/AuthSimulationSelector-BF4rkRGp.js.map +1 -0
- package/dist/BranchesView-DcHZtvXo.js +292 -0
- package/dist/BranchesView-DcHZtvXo.js.map +1 -0
- package/dist/CronJobsView-CijCToeK.js +456 -0
- package/dist/CronJobsView-CijCToeK.js.map +1 -0
- package/dist/JSEditor-D8nVp3Lp.js +1308 -0
- package/dist/JSEditor-D8nVp3Lp.js.map +1 -0
- package/dist/MonacoEditor-CMYEjiRf.js +161 -0
- package/dist/MonacoEditor-CMYEjiRf.js.map +1 -0
- package/dist/RLSEditor-DBH09u9v.js +1831 -0
- package/dist/RLSEditor-DBH09u9v.js.map +1 -0
- package/dist/SQLEditor-CkVx9vgr.js +1792 -0
- package/dist/SQLEditor-CkVx9vgr.js.map +1 -0
- package/dist/SchemaVisualizer-BgD5Zb77.js +1069 -0
- package/dist/SchemaVisualizer-BgD5Zb77.js.map +1 -0
- package/dist/StorageView-CTqGFhY9.js +907 -0
- package/dist/StorageView-CTqGFhY9.js.map +1 -0
- package/dist/common/src/collections/CollectionRegistry.d.ts +56 -0
- package/dist/common/src/collections/index.d.ts +1 -0
- package/dist/common/src/data/buildRebaseData.d.ts +14 -0
- package/dist/common/src/index.d.ts +3 -0
- package/dist/common/src/util/builders.d.ts +57 -0
- package/dist/common/src/util/callbacks.d.ts +6 -0
- package/dist/common/src/util/collections.d.ts +11 -0
- package/dist/common/src/util/common.d.ts +2 -0
- package/dist/common/src/util/conditions.d.ts +26 -0
- package/dist/common/src/util/entities.d.ts +58 -0
- package/dist/common/src/util/enums.d.ts +3 -0
- package/dist/common/src/util/index.d.ts +16 -0
- package/dist/common/src/util/navigation_from_path.d.ts +34 -0
- package/dist/common/src/util/navigation_utils.d.ts +20 -0
- package/dist/common/src/util/parent_references_from_path.d.ts +6 -0
- package/dist/common/src/util/paths.d.ts +14 -0
- package/dist/common/src/util/permissions.d.ts +5 -0
- package/dist/common/src/util/references.d.ts +2 -0
- package/dist/common/src/util/relations.d.ts +22 -0
- package/dist/common/src/util/resolutions.d.ts +72 -0
- package/dist/common/src/util/storage.d.ts +24 -0
- package/dist/core/src/components/AIIcon.d.ts +16 -0
- package/dist/core/src/components/ConfirmationDialog.d.ts +9 -0
- package/dist/core/src/components/Debug/UIReferenceView.d.ts +1 -0
- package/dist/core/src/components/Debug/UIStyleGuide.d.ts +1 -0
- package/dist/core/src/components/ErrorTooltip.d.ts +2 -0
- package/dist/core/src/components/ErrorView.d.ts +21 -0
- package/dist/core/src/components/LanguageToggle.d.ts +1 -0
- package/dist/core/src/components/LoginView/LoginView.d.ts +68 -0
- package/dist/core/src/components/LoginView/index.d.ts +2 -0
- package/dist/core/src/components/NotFoundPage.d.ts +1 -0
- package/dist/core/src/components/RebaseAuth.d.ts +10 -0
- package/dist/core/src/components/RebaseLogo.d.ts +7 -0
- package/dist/core/src/components/UnsavedChangesDialog.d.ts +9 -0
- package/dist/core/src/components/UserDisplay.d.ts +7 -0
- package/dist/core/src/components/UserSelectPopover.d.ts +62 -0
- package/dist/core/src/components/UserSettingsView.d.ts +1 -0
- package/dist/core/src/components/common/index.d.ts +6 -0
- package/dist/core/src/components/common/table_height.d.ts +5 -0
- package/dist/core/src/components/common/types.d.ts +63 -0
- package/dist/core/src/components/common/useColumnsIds.d.ts +9 -0
- package/dist/core/src/components/common/useDataTableController.d.ts +45 -0
- package/dist/core/src/components/common/useDebouncedData.d.ts +9 -0
- package/dist/core/src/components/common/useScrollRestoration.d.ts +14 -0
- package/dist/core/src/components/index.d.ts +16 -0
- package/dist/core/src/contexts/AdminModeController.d.ts +4 -0
- package/dist/core/src/contexts/AnalyticsContext.d.ts +3 -0
- package/dist/core/src/contexts/AuthControllerContext.d.ts +3 -0
- package/dist/core/src/contexts/CustomizationControllerContext.d.ts +3 -0
- package/dist/core/src/contexts/DataDriverContext.d.ts +3 -0
- package/dist/core/src/contexts/DatabaseAdminContext.d.ts +3 -0
- package/dist/core/src/contexts/DialogsProvider.d.ts +4 -0
- package/dist/core/src/contexts/EffectiveRoleController.d.ts +4 -0
- package/dist/core/src/contexts/InternalUserManagementContext.d.ts +3 -0
- package/dist/core/src/contexts/ModeController.d.ts +4 -0
- package/dist/core/src/contexts/RebaseClientInstanceContext.d.ts +6 -0
- package/dist/core/src/contexts/RebaseDataContext.d.ts +3 -0
- package/dist/core/src/contexts/SnackbarProvider.d.ts +2 -0
- package/dist/core/src/contexts/StorageSourceContext.d.ts +3 -0
- package/dist/core/src/contexts/UserConfigurationPersistenceContext.d.ts +3 -0
- package/dist/core/src/contexts/index.d.ts +13 -0
- package/dist/core/src/core/PluginLifecycleManager.d.ts +17 -0
- package/dist/core/src/core/PluginProviderStack.d.ts +21 -0
- package/dist/core/src/core/Rebase.d.ts +14 -0
- package/dist/core/src/core/RebaseProps.d.ts +136 -0
- package/dist/core/src/core/RebaseRouter.d.ts +4 -0
- package/dist/core/src/core/RebaseRoutes.d.ts +17 -0
- package/dist/core/src/core/index.d.ts +4 -0
- package/dist/core/src/hooks/ApiConfigContext.d.ts +24 -0
- package/dist/core/src/hooks/data/delete.d.ts +31 -0
- package/dist/core/src/hooks/data/save.d.ts +34 -0
- package/dist/core/src/hooks/data/useCollectionFetch.d.ts +51 -0
- package/dist/core/src/hooks/data/useData.d.ts +13 -0
- package/dist/core/src/hooks/data/useDataOrder.d.ts +12 -0
- package/dist/core/src/hooks/data/useEntityFetch.d.ts +38 -0
- package/dist/core/src/hooks/data/useRelationSelector.d.ts +52 -0
- package/dist/core/src/hooks/data/useUserSelector.d.ts +31 -0
- package/dist/core/src/hooks/index.d.ts +37 -0
- package/dist/core/src/hooks/useAdminModeController.d.ts +19 -0
- package/dist/core/src/hooks/useAnalyticsController.d.ts +5 -0
- package/dist/core/src/hooks/useAuthController.d.ts +11 -0
- package/dist/core/src/hooks/useAuthSubscription.d.ts +2 -0
- package/dist/core/src/hooks/useBackendStorageSource.d.ts +30 -0
- package/dist/core/src/hooks/useBridgeRegistration.d.ts +18 -0
- package/dist/core/src/hooks/useBrowserTitleAndIcon.d.ts +6 -0
- package/dist/core/src/hooks/useBuildAdminModeController.d.ts +6 -0
- package/dist/core/src/hooks/useBuildEffectiveRoleController.d.ts +8 -0
- package/dist/core/src/hooks/useBuildLocalConfigurationPersistence.d.ts +2 -0
- package/dist/core/src/hooks/useBuildModeController.d.ts +6 -0
- package/dist/core/src/hooks/useClipboard.d.ts +57 -0
- package/dist/core/src/hooks/useCollapsedGroups.d.ts +12 -0
- package/dist/core/src/hooks/useCustomizationController.d.ts +11 -0
- package/dist/core/src/hooks/useDialogsController.d.ts +11 -0
- package/dist/core/src/hooks/useEffectiveRoleController.d.ts +7 -0
- package/dist/core/src/hooks/useInternalUserManagementController.d.ts +12 -0
- package/dist/core/src/hooks/useLargeLayout.d.ts +1 -0
- package/dist/core/src/hooks/useModeController.d.ts +19 -0
- package/dist/core/src/hooks/usePermissions.d.ts +12 -0
- package/dist/core/src/hooks/useRebaseClient.d.ts +5 -0
- package/dist/core/src/hooks/useRebaseContext.d.ts +11 -0
- package/dist/core/src/hooks/useRebaseRegistry.d.ts +34 -0
- package/dist/core/src/hooks/useSlot.d.ts +18 -0
- package/dist/core/src/hooks/useSnackbarController.d.ts +20 -0
- package/dist/core/src/hooks/useStorageSource.d.ts +7 -0
- package/dist/core/src/hooks/useStudioBridge.d.ts +91 -0
- package/dist/core/src/hooks/useTranslation.d.ts +17 -0
- package/dist/core/src/hooks/useUnsavedChangesDialog.d.ts +12 -0
- package/dist/core/src/hooks/useUserConfigurationPersistence.d.ts +8 -0
- package/dist/core/src/hooks/useValidateAuthenticator.d.ts +21 -0
- package/dist/core/src/i18n/RebaseI18nProvider.d.ts +33 -0
- package/dist/core/src/index.d.ts +15 -0
- package/dist/core/src/internal/common.d.ts +3 -0
- package/dist/core/src/internal/useRestoreScroll.d.ts +6 -0
- package/dist/core/src/locales/de.d.ts +2 -0
- package/dist/core/src/locales/en.d.ts +10 -0
- package/dist/core/src/locales/es.d.ts +10 -0
- package/dist/core/src/locales/fr.d.ts +2 -0
- package/dist/core/src/locales/hi.d.ts +2 -0
- package/dist/core/src/locales/it.d.ts +2 -0
- package/dist/core/src/locales/pt.d.ts +7 -0
- package/dist/core/src/util/constants.d.ts +1 -0
- package/dist/core/src/util/createFormexStub.d.ts +2 -0
- package/dist/core/src/util/entity_cache.d.ts +27 -0
- package/dist/core/src/util/enums.d.ts +5 -0
- package/dist/core/src/util/icon_list.d.ts +5 -0
- package/dist/core/src/util/icon_synonyms.d.ts +1 -0
- package/dist/core/src/util/icons.d.ts +20 -0
- package/dist/core/src/util/index.d.ts +10 -0
- package/dist/core/src/util/previews.d.ts +4 -0
- package/dist/core/src/util/useStorageUploadController.d.ts +38 -0
- package/dist/core/src/util/useTraceUpdate.d.ts +2 -0
- package/dist/formex/src/Field.d.ts +52 -0
- package/dist/formex/src/Formex.d.ts +7 -0
- package/dist/formex/src/index.d.ts +5 -0
- package/dist/formex/src/types.d.ts +40 -0
- package/dist/formex/src/useCreateFormex.d.ts +14 -0
- package/dist/formex/src/utils.d.ts +16 -0
- package/dist/index.es.js +726 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +9647 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/studio/src/components/ApiExplorer/ApiExplorer.d.ts +9 -0
- package/dist/studio/src/components/ApiExplorer/EndpointDetail.d.ts +9 -0
- package/dist/studio/src/components/ApiExplorer/TryItPanel.d.ts +15 -0
- package/dist/studio/src/components/ApiExplorer/parseSpec.d.ts +16 -0
- package/dist/studio/src/components/ApiExplorer/types.d.ts +90 -0
- package/dist/studio/src/components/AuthSimulationSelector.d.ts +11 -0
- package/dist/studio/src/components/Branches/BranchesView.d.ts +1 -0
- package/dist/studio/src/components/CronJobs/CronJobsView.d.ts +1 -0
- package/dist/studio/src/components/JSEditor/JSEditor.d.ts +1 -0
- package/dist/studio/src/components/JSEditor/JSEditorSidebar.d.ts +21 -0
- package/dist/studio/src/components/JSEditor/JSMonacoEditor.d.ts +18 -0
- package/dist/studio/src/components/RLSEditor/PolicyEditor.d.ts +9 -0
- package/dist/studio/src/components/RLSEditor/RLSEditor.d.ts +19 -0
- package/dist/studio/src/components/RLSEditor/index.d.ts +1 -0
- package/dist/studio/src/components/RebaseStudio.d.ts +2 -0
- package/dist/studio/src/components/SQLEditor/ExplainVisualizer.d.ts +24 -0
- package/dist/studio/src/components/SQLEditor/MonacoEditor.d.ts +17 -0
- package/dist/studio/src/components/SQLEditor/SQLEditor.d.ts +11 -0
- package/dist/studio/src/components/SQLEditor/SQLEditorSidebar.d.ts +21 -0
- package/dist/studio/src/components/SQLEditor/SchemaBrowser.d.ts +8 -0
- package/dist/studio/src/components/SchemaVisualizer/RelationEdge.d.ts +3 -0
- package/dist/studio/src/components/SchemaVisualizer/SchemaVisualizer.d.ts +2 -0
- package/dist/studio/src/components/SchemaVisualizer/TableNode.d.ts +3 -0
- package/dist/studio/src/components/SchemaVisualizer/index.d.ts +5 -0
- package/dist/studio/src/components/SchemaVisualizer/schema-visualizer.utils.d.ts +42 -0
- package/dist/studio/src/components/SchemaVisualizer/useSchemaGraph.d.ts +37 -0
- package/dist/studio/src/components/StorageView/StorageView.d.ts +1 -0
- package/dist/studio/src/components/StudioHomePage.d.ts +9 -0
- package/dist/studio/src/index.d.ts +4 -0
- package/dist/studio/src/utils/entities.d.ts +0 -0
- package/dist/studio/src/utils/pgColumnToProperty.d.ts +6 -0
- package/dist/studio/src/utils/sql_utils.d.ts +52 -0
- package/dist/types/src/controllers/analytics_controller.d.ts +7 -0
- package/dist/types/src/controllers/auth.d.ts +119 -0
- package/dist/types/src/controllers/client.d.ts +170 -0
- package/dist/types/src/controllers/collection_registry.d.ts +45 -0
- package/dist/types/src/controllers/customization_controller.d.ts +60 -0
- package/dist/types/src/controllers/data.d.ts +168 -0
- package/dist/types/src/controllers/data_driver.d.ts +160 -0
- package/dist/types/src/controllers/database_admin.d.ts +11 -0
- package/dist/types/src/controllers/dialogs_controller.d.ts +36 -0
- package/dist/types/src/controllers/effective_role.d.ts +4 -0
- package/dist/types/src/controllers/email.d.ts +34 -0
- package/dist/types/src/controllers/index.d.ts +18 -0
- package/dist/types/src/controllers/local_config_persistence.d.ts +20 -0
- package/dist/types/src/controllers/navigation.d.ts +213 -0
- package/dist/types/src/controllers/registry.d.ts +54 -0
- package/dist/types/src/controllers/side_dialogs_controller.d.ts +67 -0
- package/dist/types/src/controllers/side_entity_controller.d.ts +90 -0
- package/dist/types/src/controllers/snackbar.d.ts +24 -0
- package/dist/types/src/controllers/storage.d.ts +171 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/rebase_context.d.ts +105 -0
- package/dist/types/src/types/backend.d.ts +536 -0
- package/dist/types/src/types/builders.d.ts +15 -0
- package/dist/types/src/types/chips.d.ts +5 -0
- package/dist/types/src/types/collections.d.ts +856 -0
- package/dist/types/src/types/cron.d.ts +102 -0
- package/dist/types/src/types/data_source.d.ts +64 -0
- package/dist/types/src/types/entities.d.ts +145 -0
- package/dist/types/src/types/entity_actions.d.ts +98 -0
- package/dist/types/src/types/entity_callbacks.d.ts +173 -0
- package/dist/types/src/types/entity_link_builder.d.ts +7 -0
- package/dist/types/src/types/entity_overrides.d.ts +10 -0
- package/dist/types/src/types/entity_views.d.ts +61 -0
- package/dist/types/src/types/export_import.d.ts +21 -0
- package/dist/types/src/types/index.d.ts +23 -0
- package/dist/types/src/types/locales.d.ts +4 -0
- package/dist/types/src/types/modify_collections.d.ts +5 -0
- package/dist/types/src/types/plugins.d.ts +279 -0
- package/dist/types/src/types/properties.d.ts +1176 -0
- package/dist/types/src/types/property_config.d.ts +70 -0
- package/dist/types/src/types/relations.d.ts +336 -0
- package/dist/types/src/types/slots.d.ts +252 -0
- package/dist/types/src/types/translations.d.ts +870 -0
- package/dist/types/src/types/user_management_delegate.d.ts +121 -0
- package/dist/types/src/types/websockets.d.ts +78 -0
- package/dist/types/src/users/index.d.ts +2 -0
- package/dist/types/src/users/roles.d.ts +22 -0
- package/dist/types/src/users/user.d.ts +46 -0
- package/dist/ui/src/components/Alert.d.ts +12 -0
- package/dist/ui/src/components/Autocomplete.d.ts +21 -0
- package/dist/ui/src/components/Avatar.d.ts +11 -0
- package/dist/ui/src/components/Badge.d.ts +8 -0
- package/dist/ui/src/components/BooleanSwitch.d.ts +14 -0
- package/dist/ui/src/components/BooleanSwitchWithLabel.d.ts +17 -0
- package/dist/ui/src/components/Button.d.ts +14 -0
- package/dist/ui/src/components/Card.d.ts +9 -0
- package/dist/ui/src/components/CenteredView.d.ts +9 -0
- package/dist/ui/src/components/Checkbox.d.ts +13 -0
- package/dist/ui/src/components/Chip.d.ts +26 -0
- package/dist/ui/src/components/CircularProgress.d.ts +5 -0
- package/dist/ui/src/components/CircularProgressCenter.d.ts +11 -0
- package/dist/ui/src/components/Collapse.d.ts +9 -0
- package/dist/ui/src/components/ColorPicker.d.ts +30 -0
- package/dist/ui/src/components/Container.d.ts +8 -0
- package/dist/ui/src/components/DateTimeField.d.ts +24 -0
- package/dist/ui/src/components/DebouncedTextField.d.ts +2 -0
- package/dist/ui/src/components/Dialog.d.ts +39 -0
- package/dist/ui/src/components/DialogActions.d.ts +7 -0
- package/dist/ui/src/components/DialogContent.d.ts +7 -0
- package/dist/ui/src/components/DialogTitle.d.ts +10 -0
- package/dist/ui/src/components/ErrorBoundary.d.ts +11 -0
- package/dist/ui/src/components/ExpandablePanel.d.ts +12 -0
- package/dist/ui/src/components/FileUpload.d.ts +23 -0
- package/dist/ui/src/components/IconButton.d.ts +12 -0
- package/dist/ui/src/components/InfoLabel.d.ts +5 -0
- package/dist/ui/src/components/InputLabel.d.ts +11 -0
- package/dist/ui/src/components/Label.d.ts +7 -0
- package/dist/ui/src/components/LoadingButton.d.ts +7 -0
- package/dist/ui/src/components/Markdown.d.ts +10 -0
- package/dist/ui/src/components/Menu.d.ts +23 -0
- package/dist/ui/src/components/Menubar.d.ts +80 -0
- package/dist/ui/src/components/MultiSelect.d.ts +48 -0
- package/dist/ui/src/components/Paper.d.ts +6 -0
- package/dist/ui/src/components/Popover.d.ts +24 -0
- package/dist/ui/src/components/RadioGroup.d.ts +28 -0
- package/dist/ui/src/components/ResizablePanels.d.ts +18 -0
- package/dist/ui/src/components/SearchBar.d.ts +22 -0
- package/dist/ui/src/components/Select.d.ts +43 -0
- package/dist/ui/src/components/Separator.d.ts +5 -0
- package/dist/ui/src/components/Sheet.d.ts +22 -0
- package/dist/ui/src/components/Skeleton.d.ts +6 -0
- package/dist/ui/src/components/Slider.d.ts +21 -0
- package/dist/ui/src/components/Table.d.ts +34 -0
- package/dist/ui/src/components/Tabs.d.ts +19 -0
- package/dist/ui/src/components/TextField.d.ts +58 -0
- package/dist/ui/src/components/TextareaAutosize.d.ts +43 -0
- package/dist/ui/src/components/ToggleButtonGroup.d.ts +30 -0
- package/dist/ui/src/components/Tooltip.d.ts +19 -0
- package/dist/ui/src/components/Typography.d.ts +36 -0
- package/dist/ui/src/components/VirtualTable/VirtualTable.d.ts +11 -0
- package/dist/ui/src/components/VirtualTable/VirtualTableCell.d.ts +21 -0
- package/dist/ui/src/components/VirtualTable/VirtualTableHeader.d.ts +29 -0
- package/dist/ui/src/components/VirtualTable/VirtualTableHeaderRow.d.ts +2 -0
- package/dist/ui/src/components/VirtualTable/VirtualTableProps.d.ts +243 -0
- package/dist/ui/src/components/VirtualTable/VirtualTableRow.d.ts +3 -0
- package/dist/ui/src/components/VirtualTable/index.d.ts +3 -0
- package/dist/ui/src/components/VirtualTable/types.d.ts +38 -0
- package/dist/ui/src/components/common/SelectInputLabel.d.ts +5 -0
- package/dist/ui/src/components/index.d.ts +53 -0
- package/dist/ui/src/hooks/PortalContainerContext.d.ts +31 -0
- package/dist/ui/src/hooks/index.d.ts +6 -0
- package/dist/ui/src/hooks/useDebounceCallback.d.ts +1 -0
- package/dist/ui/src/hooks/useDebounceValue.d.ts +1 -0
- package/dist/ui/src/hooks/useDebouncedCallback.d.ts +1 -0
- package/dist/ui/src/hooks/useInjectStyles.d.ts +7 -0
- package/dist/ui/src/hooks/useOutsideAlerter.d.ts +5 -0
- package/dist/ui/src/icons/GitHubIcon.d.ts +2 -0
- package/dist/ui/src/icons/HandleIcon.d.ts +1 -0
- package/dist/ui/src/icons/Icon.d.ts +20 -0
- package/dist/ui/src/icons/cool_icon_keys.d.ts +1 -0
- package/dist/ui/src/icons/icon_keys.d.ts +1 -0
- package/dist/ui/src/icons/index.d.ts +6 -0
- package/dist/ui/src/index.d.ts +5 -0
- package/dist/ui/src/styles.d.ts +12 -0
- package/dist/ui/src/util/chip_colors.d.ts +4 -0
- package/dist/ui/src/util/cls.d.ts +2 -0
- package/dist/ui/src/util/debounce.d.ts +10 -0
- package/dist/ui/src/util/hash.d.ts +1 -0
- package/dist/ui/src/util/index.d.ts +4 -0
- package/dist/ui/src/util/key_to_icon_component.d.ts +1 -0
- package/package.json +84 -0
- package/src/components/ApiExplorer/ApiExplorer.tsx +290 -0
- package/src/components/ApiExplorer/EndpointDetail.tsx +271 -0
- package/src/components/ApiExplorer/TryItPanel.tsx +510 -0
- package/src/components/ApiExplorer/parseSpec.ts +104 -0
- package/src/components/ApiExplorer/types.ts +84 -0
- package/src/components/AuthSimulationSelector.tsx +77 -0
- package/src/components/Branches/BranchesView.tsx +370 -0
- package/src/components/CronJobs/CronJobsView.tsx +346 -0
- package/src/components/JSEditor/JSEditor.tsx +1033 -0
- package/src/components/JSEditor/JSEditorSidebar.tsx +340 -0
- package/src/components/JSEditor/JSMonacoEditor.tsx +390 -0
- package/src/components/RLSEditor/PolicyEditor.tsx +444 -0
- package/src/components/RLSEditor/RLSEditor.tsx +692 -0
- package/src/components/RLSEditor/index.ts +1 -0
- package/src/components/RebaseStudio.tsx +121 -0
- package/src/components/SQLEditor/ExplainVisualizer.tsx +128 -0
- package/src/components/SQLEditor/MonacoEditor.tsx +203 -0
- package/src/components/SQLEditor/SQLEditor.tsx +1419 -0
- package/src/components/SQLEditor/SQLEditorSidebar.tsx +174 -0
- package/src/components/SQLEditor/SchemaBrowser.tsx +158 -0
- package/src/components/SchemaVisualizer/RelationEdge.tsx +102 -0
- package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +665 -0
- package/src/components/SchemaVisualizer/TableNode.tsx +257 -0
- package/src/components/SchemaVisualizer/index.ts +5 -0
- package/src/components/SchemaVisualizer/schema-visualizer.utils.ts +140 -0
- package/src/components/SchemaVisualizer/useSchemaGraph.ts +397 -0
- package/src/components/StorageView/StorageView.tsx +1035 -0
- package/src/components/StudioHomePage.tsx +357 -0
- package/src/index.ts +31 -0
- package/src/utils/entities.ts +2 -0
- package/src/utils/pgColumnToProperty.test.ts +401 -0
- package/src/utils/pgColumnToProperty.ts +275 -0
- package/src/utils/sql_utils.test.ts +265 -0
- package/src/utils/sql_utils.ts +291 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
|
|
2
|
+
import { describe, it, expect } from "@jest/globals";
|
|
3
|
+
import { determineTableAndPK, extractTablesFromQuery, resolveQueryCollections } from "./sql_utils";
|
|
4
|
+
import { TableInfo } from "../components/SQLEditor/SQLEditor";
|
|
5
|
+
import { EntityCollection } from "@rebasepro/types";
|
|
6
|
+
|
|
7
|
+
const mockSchemas: Record<string, TableInfo[]> = {
|
|
8
|
+
"public": [
|
|
9
|
+
{
|
|
10
|
+
schemaName: "public",
|
|
11
|
+
tableName: "users",
|
|
12
|
+
columns: [
|
|
13
|
+
{ name: "id",
|
|
14
|
+
dataType: "integer",
|
|
15
|
+
isPrimaryKey: true },
|
|
16
|
+
{ name: "email",
|
|
17
|
+
dataType: "text",
|
|
18
|
+
isPrimaryKey: false },
|
|
19
|
+
{ name: "created_at",
|
|
20
|
+
dataType: "timestamp",
|
|
21
|
+
isPrimaryKey: false }
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
schemaName: "public",
|
|
26
|
+
tableName: "roles",
|
|
27
|
+
columns: [
|
|
28
|
+
{ name: "id",
|
|
29
|
+
dataType: "integer",
|
|
30
|
+
isPrimaryKey: true },
|
|
31
|
+
{ name: "role_name",
|
|
32
|
+
dataType: "text",
|
|
33
|
+
isPrimaryKey: false },
|
|
34
|
+
{ name: "created_at",
|
|
35
|
+
dataType: "timestamp",
|
|
36
|
+
isPrimaryKey: false }
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
schemaName: "public",
|
|
41
|
+
tableName: "settings", // No primary key
|
|
42
|
+
columns: [
|
|
43
|
+
{ name: "key",
|
|
44
|
+
dataType: "text",
|
|
45
|
+
isPrimaryKey: false },
|
|
46
|
+
{ name: "value",
|
|
47
|
+
dataType: "text",
|
|
48
|
+
isPrimaryKey: false }
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
schemaName: "public",
|
|
53
|
+
tableName: "blog_posts",
|
|
54
|
+
columns: [
|
|
55
|
+
{ name: "id",
|
|
56
|
+
dataType: "integer",
|
|
57
|
+
isPrimaryKey: true },
|
|
58
|
+
{ name: "title",
|
|
59
|
+
dataType: "text",
|
|
60
|
+
isPrimaryKey: false },
|
|
61
|
+
{ name: "author_id",
|
|
62
|
+
dataType: "integer",
|
|
63
|
+
isPrimaryKey: false }
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
schemaName: "public",
|
|
68
|
+
tableName: "order_items", // Composite PK
|
|
69
|
+
columns: [
|
|
70
|
+
{ name: "order_id",
|
|
71
|
+
dataType: "integer",
|
|
72
|
+
isPrimaryKey: true },
|
|
73
|
+
{ name: "item_id",
|
|
74
|
+
dataType: "integer",
|
|
75
|
+
isPrimaryKey: true },
|
|
76
|
+
{ name: "quantity",
|
|
77
|
+
dataType: "integer",
|
|
78
|
+
isPrimaryKey: false }
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const mockCollections: EntityCollection[] = [
|
|
85
|
+
{
|
|
86
|
+
slug: "users",
|
|
87
|
+
name: "Users",
|
|
88
|
+
table: "users",
|
|
89
|
+
properties: {}
|
|
90
|
+
} as EntityCollection,
|
|
91
|
+
{
|
|
92
|
+
slug: "roles",
|
|
93
|
+
name: "Roles",
|
|
94
|
+
table: "roles",
|
|
95
|
+
properties: {}
|
|
96
|
+
} as EntityCollection,
|
|
97
|
+
{
|
|
98
|
+
slug: "blogPosts",
|
|
99
|
+
name: "Blog Posts",
|
|
100
|
+
table: "blog_posts",
|
|
101
|
+
properties: {}
|
|
102
|
+
} as EntityCollection,
|
|
103
|
+
{
|
|
104
|
+
slug: "blog-entries", // slug with hyphen, no table → falls back to snake_case "blog_entries"
|
|
105
|
+
name: "Blog Entries",
|
|
106
|
+
table: "",
|
|
107
|
+
properties: {}
|
|
108
|
+
} as EntityCollection
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
describe("determineTableAndPK", () => {
|
|
112
|
+
it("resolves basic SELECT with actual PK from schema", () => {
|
|
113
|
+
const sql = "SELECT * FROM users";
|
|
114
|
+
const result = determineTableAndPK(sql, "email", mockSchemas);
|
|
115
|
+
expect(result.tableName).toBe("users");
|
|
116
|
+
expect(result.primaryKeys).toEqual([{ dbColumn: "id",
|
|
117
|
+
resultColumn: "id" }]);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("resolves aliased FROM", () => {
|
|
121
|
+
const sql = "SELECT u.email FROM users u";
|
|
122
|
+
const result = determineTableAndPK(sql, "email", mockSchemas);
|
|
123
|
+
expect(result.tableName).toBe("users");
|
|
124
|
+
expect(result.primaryKeys).toEqual([{ dbColumn: "id",
|
|
125
|
+
resultColumn: "id" }]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("resolves simple unambiguous JOIN", () => {
|
|
129
|
+
const sql = "SELECT * FROM users u JOIN roles r ON u.role_id = r.id";
|
|
130
|
+
const result = determineTableAndPK(sql, "email", mockSchemas);
|
|
131
|
+
expect(result.tableName).toBe("users");
|
|
132
|
+
expect(result.primaryKeys).toEqual([{ dbColumn: "id",
|
|
133
|
+
resultColumn: "id" }]);
|
|
134
|
+
|
|
135
|
+
const result2 = determineTableAndPK(sql, "role_name", mockSchemas);
|
|
136
|
+
expect(result2.tableName).toBe("roles");
|
|
137
|
+
expect(result2.primaryKeys).toEqual([{ dbColumn: "id",
|
|
138
|
+
resultColumn: "id" }]);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("resolves aliased PK columns in JOINs", () => {
|
|
142
|
+
const sql = "SELECT u.id AS user_id, u.email, r.id AS role_id, r.role_name FROM users u JOIN roles r ON u.role_id = r.id";
|
|
143
|
+
const result = determineTableAndPK(sql, "email", mockSchemas);
|
|
144
|
+
expect(result.tableName).toBe("users");
|
|
145
|
+
expect(result.primaryKeys).toEqual([{ dbColumn: "id",
|
|
146
|
+
resultColumn: "user_id" }]);
|
|
147
|
+
|
|
148
|
+
const result2 = determineTableAndPK(sql, "role_name", mockSchemas);
|
|
149
|
+
expect(result2.tableName).toBe("roles");
|
|
150
|
+
expect(result2.primaryKeys).toEqual([{ dbColumn: "id",
|
|
151
|
+
resultColumn: "role_id" }]);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("resolves aliased edit columns back to DB names", () => {
|
|
155
|
+
const sql = "SELECT u.email AS user_email FROM users u";
|
|
156
|
+
const result = determineTableAndPK(sql, "user_email", mockSchemas);
|
|
157
|
+
expect(result.tableName).toBe("users");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("returns error for ambiguous JOIN columns", () => {
|
|
161
|
+
const sql = "SELECT * FROM users u JOIN roles r ON u.role_id = r.id";
|
|
162
|
+
// Both tables have created_at
|
|
163
|
+
const result = determineTableAndPK(sql, "created_at", mockSchemas);
|
|
164
|
+
expect(result.error).toContain("Ambiguous column");
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("returns error for unsupported non-SELECT queries", () => {
|
|
168
|
+
const sql = "DELETE FROM users WHERE id = 1";
|
|
169
|
+
const result = determineTableAndPK(sql, "email", mockSchemas);
|
|
170
|
+
expect(result.error).toContain("only supported for SELECT");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("returns error for tables without primary keys", () => {
|
|
174
|
+
const sql = "SELECT * FROM settings";
|
|
175
|
+
const result = determineTableAndPK(sql, "key", mockSchemas);
|
|
176
|
+
expect(result.error).toContain("has no primary key defined");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("supports composite primary keys", () => {
|
|
180
|
+
const sql = "SELECT * FROM order_items";
|
|
181
|
+
const result = determineTableAndPK(sql, "quantity", mockSchemas);
|
|
182
|
+
expect(result.tableName).toBe("order_items");
|
|
183
|
+
expect(result.primaryKeys).toHaveLength(2);
|
|
184
|
+
expect(result.primaryKeys).toEqual([
|
|
185
|
+
{ dbColumn: "order_id",
|
|
186
|
+
resultColumn: "order_id" },
|
|
187
|
+
{ dbColumn: "item_id",
|
|
188
|
+
resultColumn: "item_id" }
|
|
189
|
+
]);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
describe("extractTablesFromQuery", () => {
|
|
194
|
+
it("extracts a single table", () => {
|
|
195
|
+
const tables = extractTablesFromQuery("SELECT * FROM users");
|
|
196
|
+
expect(tables).toEqual([{ name: "users",
|
|
197
|
+
alias: undefined }]);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("extracts aliased tables", () => {
|
|
201
|
+
const tables = extractTablesFromQuery("SELECT u.email FROM users u");
|
|
202
|
+
expect(tables).toEqual([{ name: "users",
|
|
203
|
+
alias: "u" }]);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("extracts multiple tables from JOINs", () => {
|
|
207
|
+
const tables = extractTablesFromQuery("SELECT * FROM users u JOIN roles r ON u.role_id = r.id");
|
|
208
|
+
expect(tables).toHaveLength(2);
|
|
209
|
+
expect(tables[0].name).toBe("users");
|
|
210
|
+
expect(tables[1].name).toBe("roles");
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("returns empty for non-SELECT queries", () => {
|
|
214
|
+
expect(extractTablesFromQuery("DELETE FROM users")).toEqual([]);
|
|
215
|
+
expect(extractTablesFromQuery("INSERT INTO users (id) VALUES (1)")).toEqual([]);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("returns empty for invalid SQL", () => {
|
|
219
|
+
expect(extractTablesFromQuery("NOT VALID SQL AT ALL ???")).toEqual([]);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe("resolveQueryCollections", () => {
|
|
224
|
+
it("matches a single CMS collection", () => {
|
|
225
|
+
const result = resolveQueryCollections("SELECT * FROM users", mockSchemas, mockCollections);
|
|
226
|
+
expect(result).toHaveLength(1);
|
|
227
|
+
expect(result[0].tableName).toBe("users");
|
|
228
|
+
expect(result[0].collection.name).toBe("Users");
|
|
229
|
+
expect(result[0].columns).toContain("id");
|
|
230
|
+
expect(result[0].columns).toContain("email");
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("matches multiple CMS collections in a JOIN", () => {
|
|
234
|
+
const result = resolveQueryCollections(
|
|
235
|
+
"SELECT * FROM users u JOIN roles r ON u.role_id = r.id",
|
|
236
|
+
mockSchemas,
|
|
237
|
+
mockCollections
|
|
238
|
+
);
|
|
239
|
+
expect(result).toHaveLength(2);
|
|
240
|
+
expect(result.map(r => r.collection.name).sort()).toEqual(["Roles", "Users"]);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("returns empty when no tables match any collection", () => {
|
|
244
|
+
const result = resolveQueryCollections("SELECT * FROM settings", mockSchemas, mockCollections);
|
|
245
|
+
expect(result).toHaveLength(0);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("matches collection by table (not slug)", () => {
|
|
249
|
+
const result = resolveQueryCollections("SELECT * FROM blog_posts", mockSchemas, mockCollections);
|
|
250
|
+
expect(result).toHaveLength(1);
|
|
251
|
+
expect(result[0].collection.slug).toBe("blogPosts");
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("returns empty for non-SELECT queries", () => {
|
|
255
|
+
const result = resolveQueryCollections("DELETE FROM users WHERE id = 1", mockSchemas, mockCollections);
|
|
256
|
+
expect(result).toHaveLength(0);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it("preserves table alias information", () => {
|
|
260
|
+
const result = resolveQueryCollections("SELECT u.* FROM users u", mockSchemas, mockCollections);
|
|
261
|
+
expect(result).toHaveLength(1);
|
|
262
|
+
expect(result[0].tableAlias).toBe("u");
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { parseFirst } from "pgsql-ast-parser";
|
|
2
|
+
import { TableInfo } from "../components/SQLEditor/SQLEditor";
|
|
3
|
+
import { EntityCollection } from "@rebasepro/types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A table extracted from a SQL query's FROM/JOIN clauses.
|
|
7
|
+
*/
|
|
8
|
+
export interface ExtractedTable {
|
|
9
|
+
name: string;
|
|
10
|
+
alias?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A collection matched to a table in a SQL query.
|
|
15
|
+
*/
|
|
16
|
+
export interface ResolvedQueryCollection {
|
|
17
|
+
/** DB table name from the SQL AST (e.g. "blog_posts") */
|
|
18
|
+
tableName: string;
|
|
19
|
+
/** SQL alias if present (e.g. "bp") */
|
|
20
|
+
tableAlias?: string;
|
|
21
|
+
/** The matched collection */
|
|
22
|
+
collection: EntityCollection;
|
|
23
|
+
/** Columns from this table that are present in the result set */
|
|
24
|
+
columns: string[];
|
|
25
|
+
/** The result column name that holds the primary key for this table (e.g. "id", "author_id") */
|
|
26
|
+
pkColumn?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Extract all tables referenced in a SQL query's FROM and JOIN clauses.
|
|
31
|
+
* Returns an empty array for non-SELECT queries or parse failures.
|
|
32
|
+
*/
|
|
33
|
+
export function extractTablesFromQuery(sqlString: string): ExtractedTable[] {
|
|
34
|
+
try {
|
|
35
|
+
const ast = parseFirst(sqlString);
|
|
36
|
+
if (ast.type !== "select") return [];
|
|
37
|
+
|
|
38
|
+
const tables: ExtractedTable[] = [];
|
|
39
|
+
|
|
40
|
+
// pgsql-ast-parser From items — tables and joins with left/right branches
|
|
41
|
+
type FromNode = { type: string; name?: { name: string; alias?: string }; left?: FromNode; right?: FromNode };
|
|
42
|
+
const processFrom = (fromItems: FromNode[]) => {
|
|
43
|
+
for (const item of fromItems) {
|
|
44
|
+
if (item.type === "table" && item.name) {
|
|
45
|
+
tables.push({ name: item.name.name,
|
|
46
|
+
alias: item.name.alias });
|
|
47
|
+
}
|
|
48
|
+
if (item.type === "join") {
|
|
49
|
+
if (item.left) processFrom([item.left]);
|
|
50
|
+
if (item.right) processFrom([item.right]);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
if (ast.from) {
|
|
56
|
+
processFrom(ast.from);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return tables;
|
|
60
|
+
} catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
import { toSnakeCase } from "@rebasepro/utils";
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Resolve which collections are referenced by a SQL query.
|
|
69
|
+
*
|
|
70
|
+
* Parses the SQL, extracts table names, and matches each against
|
|
71
|
+
* registered collections via `collection.table` (falling back to
|
|
72
|
+
* snake_case of `collection.slug`).
|
|
73
|
+
*
|
|
74
|
+
* For each matched collection, determines which result columns
|
|
75
|
+
* belong to that table using the database schema information.
|
|
76
|
+
*/
|
|
77
|
+
export function resolveQueryCollections(
|
|
78
|
+
sqlString: string,
|
|
79
|
+
schemas: Record<string, TableInfo[]>,
|
|
80
|
+
collections: EntityCollection[],
|
|
81
|
+
resultColumns?: string[]
|
|
82
|
+
): ResolvedQueryCollection[] {
|
|
83
|
+
const tables = extractTablesFromQuery(sqlString);
|
|
84
|
+
if (tables.length === 0) return [];
|
|
85
|
+
|
|
86
|
+
// Parse the AST to resolve SELECT column aliases
|
|
87
|
+
const selectColumns: { table?: string; column: string; alias?: string }[] = [];
|
|
88
|
+
try {
|
|
89
|
+
const ast = parseFirst(sqlString);
|
|
90
|
+
if (ast.type === "select" && ast.columns) {
|
|
91
|
+
for (const col of ast.columns) {
|
|
92
|
+
if (col.expr?.type === "ref") {
|
|
93
|
+
selectColumns.push({
|
|
94
|
+
table: col.expr.table?.name,
|
|
95
|
+
column: col.expr.name,
|
|
96
|
+
alias: col.alias?.name
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch { /* parse failure is ok, we'll fall back */ }
|
|
102
|
+
|
|
103
|
+
const results: ResolvedQueryCollection[] = [];
|
|
104
|
+
|
|
105
|
+
for (const table of tables) {
|
|
106
|
+
// Match table name against collection table or slug->snake_case
|
|
107
|
+
const matched = collections.find(c => {
|
|
108
|
+
const tableName = (c as any).table || toSnakeCase(c.slug);
|
|
109
|
+
return tableName === table.name;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if (!matched) continue;
|
|
113
|
+
|
|
114
|
+
// Find columns belonging to this table from the schema
|
|
115
|
+
const tableColumns: string[] = [];
|
|
116
|
+
for (const schemaEntries of Object.values(schemas)) {
|
|
117
|
+
const tableInfo = schemaEntries.find(t => t.tableName === table.name);
|
|
118
|
+
if (tableInfo) {
|
|
119
|
+
tableColumns.push(...tableInfo.columns.map(c => c.name));
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Determine which result column holds the PK ("id") for this table.
|
|
125
|
+
// 1. Check parsed SELECT columns for an explicit "id" column from this table (by name or alias)
|
|
126
|
+
// 2. Fall back to checking if result columns contain "id"
|
|
127
|
+
let pkColumn: string | undefined;
|
|
128
|
+
|
|
129
|
+
// Look in the AST select columns for `table.id` or `alias.id`
|
|
130
|
+
const tableRef = table.alias || table.name;
|
|
131
|
+
const idSelectCol = selectColumns.find(
|
|
132
|
+
sc => sc.column === "id" && (!sc.table || sc.table === tableRef || sc.table === table.name)
|
|
133
|
+
);
|
|
134
|
+
if (idSelectCol) {
|
|
135
|
+
pkColumn = idSelectCol.alias || idSelectCol.column; // use alias if present
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// If we didn't find it from the AST (e.g. SELECT *), check if result columns have "id"
|
|
139
|
+
if (!pkColumn && resultColumns) {
|
|
140
|
+
if (resultColumns.includes("id")) {
|
|
141
|
+
pkColumn = "id";
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// If still not found, fall back to checking tableColumns
|
|
146
|
+
if (!pkColumn && tableColumns.includes("id")) {
|
|
147
|
+
pkColumn = "id";
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
results.push({
|
|
151
|
+
tableName: table.name,
|
|
152
|
+
tableAlias: table.alias,
|
|
153
|
+
collection: matched,
|
|
154
|
+
columns: tableColumns,
|
|
155
|
+
pkColumn
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return results;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export interface PKMapping {
|
|
163
|
+
/** The actual column name in the database table */
|
|
164
|
+
dbColumn: string;
|
|
165
|
+
/** The column name as it appears in the query result set (may be aliased) */
|
|
166
|
+
resultColumn: string;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface TableAndPKResult {
|
|
170
|
+
tableName?: string;
|
|
171
|
+
primaryKeys?: PKMapping[];
|
|
172
|
+
error?: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function determineTableAndPK(sqlString: string, columnKey: string, schemas: Record<string, TableInfo[]>): TableAndPKResult {
|
|
176
|
+
try {
|
|
177
|
+
const tables = extractTablesFromQuery(sqlString);
|
|
178
|
+
|
|
179
|
+
const ast = parseFirst(sqlString);
|
|
180
|
+
if (ast.type !== "select") {
|
|
181
|
+
return { error: "Inline editing is only supported for SELECT queries." };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (tables.length === 0) {
|
|
185
|
+
return { error: "Could not find any tables in the query." };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Parse SELECT columns to resolve aliases
|
|
189
|
+
const selectColumns: { table?: string; column: string; alias?: string }[] = [];
|
|
190
|
+
if (ast.columns) {
|
|
191
|
+
for (const col of ast.columns) {
|
|
192
|
+
if (col.expr?.type === "ref") {
|
|
193
|
+
selectColumns.push({
|
|
194
|
+
table: col.expr.table?.name,
|
|
195
|
+
column: col.expr.name,
|
|
196
|
+
alias: col.alias?.name
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Resolve which DB column `columnKey` refers to (it might be aliased)
|
|
203
|
+
const resolvedColumn = selectColumns.find(
|
|
204
|
+
sc => (sc.alias === columnKey) || (!sc.alias && sc.column === columnKey)
|
|
205
|
+
);
|
|
206
|
+
const actualDbColumnName = resolvedColumn?.column ?? columnKey;
|
|
207
|
+
const columnTableRef = resolvedColumn?.table; // e.g. "p" or "posts"
|
|
208
|
+
|
|
209
|
+
// Resolve the table for the edited column
|
|
210
|
+
let resolvedTableName: string | null = null;
|
|
211
|
+
|
|
212
|
+
if (tables.length === 1) {
|
|
213
|
+
resolvedTableName = tables[0].name;
|
|
214
|
+
} else {
|
|
215
|
+
// If the AST tells us which table, use that
|
|
216
|
+
if (columnTableRef) {
|
|
217
|
+
const matchedTable = tables.find(
|
|
218
|
+
t => t.alias === columnTableRef || t.name === columnTableRef
|
|
219
|
+
);
|
|
220
|
+
if (matchedTable) {
|
|
221
|
+
resolvedTableName = matchedTable.name;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Otherwise, look up which schema table has this column
|
|
226
|
+
if (!resolvedTableName) {
|
|
227
|
+
const matchedTables = tables.filter(t => {
|
|
228
|
+
for (const schema of Object.values(schemas)) {
|
|
229
|
+
const tableInfo = schema.find(ti => ti.tableName === t.name);
|
|
230
|
+
if (tableInfo && tableInfo.columns.some(c => c.name === actualDbColumnName)) {
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return false;
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
if (matchedTables.length === 1) {
|
|
238
|
+
resolvedTableName = matchedTables[0].name;
|
|
239
|
+
} else if (matchedTables.length > 1) {
|
|
240
|
+
return { error: `Ambiguous column "${columnKey}": Found in multiple queried tables.` };
|
|
241
|
+
} else {
|
|
242
|
+
return { error: `Could not find column "${columnKey}" in the queried tables.` };
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (!resolvedTableName) {
|
|
248
|
+
return { error: "Could not resolve the target table." };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Find the table's actual primary key columns from the schema
|
|
252
|
+
let pkDbColumns: string[] = [];
|
|
253
|
+
for (const schema of Object.values(schemas)) {
|
|
254
|
+
const tableInfo = schema.find(t => t.tableName === resolvedTableName);
|
|
255
|
+
if (tableInfo) {
|
|
256
|
+
pkDbColumns = tableInfo.columns
|
|
257
|
+
.filter(c => c.isPrimaryKey)
|
|
258
|
+
.map(c => c.name);
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (pkDbColumns.length === 0) {
|
|
264
|
+
return { error: `Table "${resolvedTableName}" has no primary key defined.` };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Find the table's alias in the query (for resolving PK result column names)
|
|
268
|
+
const tableEntry = tables.find(t => t.name === resolvedTableName);
|
|
269
|
+
const tableAlias = tableEntry?.alias;
|
|
270
|
+
|
|
271
|
+
// Map each PK db column to its result column name (resolving aliases)
|
|
272
|
+
const primaryKeys: PKMapping[] = pkDbColumns.map(dbCol => {
|
|
273
|
+
// Find the SELECT column for this PK
|
|
274
|
+
const selectCol = selectColumns.find(
|
|
275
|
+
sc => sc.column === dbCol &&
|
|
276
|
+
(!sc.table || sc.table === (tableAlias || resolvedTableName))
|
|
277
|
+
);
|
|
278
|
+
return {
|
|
279
|
+
dbColumn: dbCol,
|
|
280
|
+
resultColumn: selectCol?.alias || selectCol?.column || dbCol
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
return { tableName: resolvedTableName,
|
|
285
|
+
primaryKeys };
|
|
286
|
+
} catch (e: unknown) {
|
|
287
|
+
console.warn("Failed to parse SQL AST:", e);
|
|
288
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
289
|
+
return { error: `Could not safely parse query for inline editing: ${message}` };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|