insforge 0.3.1
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/.dockerignore +58 -0
- package/.env.example +49 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +83 -0
- package/.github/ISSUE_TEMPLATE/config.yml +11 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +79 -0
- package/.github/copilot-instructions.md +147 -0
- package/.github/workflows/build-image.yml +65 -0
- package/.github/workflows/ci-premerge-check.yml +24 -0
- package/.github/workflows/deploy-aws.yml +130 -0
- package/.github/workflows/lint-and-format.yml +33 -0
- package/.prettierignore +65 -0
- package/.prettierrc +9 -0
- package/CHANGELOG.md +3 -0
- package/CONTRIBUTING.md +126 -0
- package/Dockerfile +27 -0
- package/GITHUB_OAUTH_SETUP.md +49 -0
- package/GOOGLE_OAUTH_SETUP.md +148 -0
- package/LICENSE +201 -0
- package/README.md +134 -0
- package/assets/Dark.svg +23 -0
- package/assets/archDiagram.png +0 -0
- package/assets/banner.png +0 -0
- package/assets/mcpInstallv2.png +0 -0
- package/assets/sampleResponse.png +0 -0
- package/assets/signin.png +0 -0
- package/assets/userflow.png +0 -0
- package/backend/migrations/000_create-base-tables.sql +142 -0
- package/backend/migrations/001_create-helper-functions.sql +41 -0
- package/backend/migrations/002_rename-auth-tables.sql +30 -0
- package/backend/migrations/003_create-users-table.sql +56 -0
- package/backend/migrations/004_add-reload-postgrest-func.sql +24 -0
- package/backend/migrations/005_enable-project-admin-modify-users.sql +30 -0
- package/backend/migrations/006_modify-ai-usage-table.sql +25 -0
- package/backend/migrations/007_drop-metadata-table.sql +2 -0
- package/backend/migrations/008_add-system-tables.sql +77 -0
- package/backend/migrations/009_add-function-secrets.sql +24 -0
- package/backend/migrations/010_modify-ai-config-modalities.sql +93 -0
- package/backend/migrations/011_refactor-secrets-table.sql +15 -0
- package/backend/migrations/012_add-storage-uploaded-by.sql +8 -0
- package/backend/package.json +75 -0
- package/backend/src/api/middleware/auth.ts +240 -0
- package/backend/src/api/middleware/error.ts +231 -0
- package/backend/src/api/middleware/upload.ts +59 -0
- package/backend/src/api/routes/agent.ts +29 -0
- package/backend/src/api/routes/ai.ts +472 -0
- package/backend/src/api/routes/auth.oauth.ts +482 -0
- package/backend/src/api/routes/auth.ts +386 -0
- package/backend/src/api/routes/database.advance.ts +275 -0
- package/backend/src/api/routes/database.records.ts +246 -0
- package/backend/src/api/routes/database.tables.ts +161 -0
- package/backend/src/api/routes/docs.ts +66 -0
- package/backend/src/api/routes/functions.ts +183 -0
- package/backend/src/api/routes/logs.ts +150 -0
- package/backend/src/api/routes/metadata.ts +160 -0
- package/backend/src/api/routes/openapi.ts +82 -0
- package/backend/src/api/routes/secrets.ts +199 -0
- package/backend/src/api/routes/storage.ts +547 -0
- package/backend/src/api/routes/usage.ts +96 -0
- package/backend/src/core/ai/chat.ts +207 -0
- package/backend/src/core/ai/client.ts +242 -0
- package/backend/src/core/ai/config.ts +187 -0
- package/backend/src/core/ai/image.ts +156 -0
- package/backend/src/core/ai/model.ts +117 -0
- package/backend/src/core/ai/usage.ts +290 -0
- package/backend/src/core/auth/auth.ts +781 -0
- package/backend/src/core/auth/oauth.ts +398 -0
- package/backend/src/core/database/advance.ts +1074 -0
- package/backend/src/core/database/manager.ts +178 -0
- package/backend/src/core/database/table.ts +772 -0
- package/backend/src/core/documentation/agent.ts +689 -0
- package/backend/src/core/documentation/openapi.ts +856 -0
- package/backend/src/core/functions/functions.ts +310 -0
- package/backend/src/core/logs/analytics.ts +76 -0
- package/backend/src/core/logs/audit.ts +255 -0
- package/backend/src/core/logs/providers/base.provider.ts +83 -0
- package/backend/src/core/logs/providers/cloudwatch.provider.ts +510 -0
- package/backend/src/core/logs/providers/localdb.provider.ts +246 -0
- package/backend/src/core/secrets/encryption.ts +58 -0
- package/backend/src/core/secrets/secrets.ts +410 -0
- package/backend/src/core/socket/socket.ts +388 -0
- package/backend/src/core/socket/types.ts +79 -0
- package/backend/src/core/storage/storage.ts +923 -0
- package/backend/src/server.ts +288 -0
- package/backend/src/types/ai.ts +46 -0
- package/backend/src/types/auth.ts +90 -0
- package/backend/src/types/database.ts +136 -0
- package/backend/src/types/error-constants.ts +86 -0
- package/backend/src/types/logs.ts +47 -0
- package/backend/src/types/profile.ts +55 -0
- package/backend/src/types/storage.ts +23 -0
- package/backend/src/utils/cloud-token.ts +39 -0
- package/backend/src/utils/constants.ts +1 -0
- package/backend/src/utils/environment.ts +35 -0
- package/backend/src/utils/helpers.ts +49 -0
- package/backend/src/utils/logger.ts +13 -0
- package/backend/src/utils/response.ts +62 -0
- package/backend/src/utils/seed.ts +205 -0
- package/backend/src/utils/sql-parser.ts +63 -0
- package/backend/src/utils/uuid.ts +9 -0
- package/backend/src/utils/validations.ts +129 -0
- package/backend/tests/README.md +134 -0
- package/backend/tests/cleanup-all-test-data.sh +231 -0
- package/backend/tests/cloud/test-s3-multitenant.sh +132 -0
- package/backend/tests/local/comprehensive-curl-tests.sh +156 -0
- package/backend/tests/local/test-auth-router.sh +144 -0
- package/backend/tests/local/test-database-router.sh +222 -0
- package/backend/tests/local/test-e2e.sh +241 -0
- package/backend/tests/local/test-fk-errors.sh +97 -0
- package/backend/tests/local/test-id-field.sh +201 -0
- package/backend/tests/local/test-public-bucket.sh +265 -0
- package/backend/tests/local/test-secrets.sh +248 -0
- package/backend/tests/local/test-serverless-functions.sh.disabled +325 -0
- package/backend/tests/local/test-traditional-rest.sh +209 -0
- package/backend/tests/manual/README.md +51 -0
- package/backend/tests/manual/create-large-table-simple.sql +11 -0
- package/backend/tests/manual/seed-large-table.sql +101 -0
- package/backend/tests/manual/setup-large-table-extras.sql +34 -0
- package/backend/tests/manual/test-better-auth.sh +303 -0
- package/backend/tests/manual/test-bulk-upsert.sh +410 -0
- package/backend/tests/manual/test-database-advance.sh +297 -0
- package/backend/tests/manual/test-postgrest-stability.sh +192 -0
- package/backend/tests/manual/test-rawsql-export-import.sh +412 -0
- package/backend/tests/manual/test-universal-storage.sh +264 -0
- package/backend/tests/manual/test-users.sql +18 -0
- package/backend/tests/run-all-tests.sh +140 -0
- package/backend/tests/setup.ts +22 -0
- package/backend/tests/test-config.sh +303 -0
- package/backend/tsconfig.json +23 -0
- package/backend/tsup.config.ts +18 -0
- package/backend/vitest.config.ts +22 -0
- package/docker-compose.prod.yml +145 -0
- package/docker-compose.yml +167 -0
- package/docker-init/db/db-init.sql +125 -0
- package/docker-init/db/jwt.sql +5 -0
- package/docker-init/db/logs.sql +9 -0
- package/docker-init/db/postgresql.conf +17 -0
- package/docs/deprecated/insforge-auth-api.md +215 -0
- package/docs/deprecated/insforge-auth-sdk.md +100 -0
- package/docs/deprecated/insforge-db-api.md +359 -0
- package/docs/deprecated/insforge-db-sdk.md +140 -0
- package/docs/deprecated/insforge-debug-sdk.md +157 -0
- package/docs/deprecated/insforge-debug.md +65 -0
- package/docs/deprecated/insforge-instructions.md +124 -0
- package/docs/deprecated/insforge-project.md +118 -0
- package/docs/deprecated/insforge-storage-api.md +279 -0
- package/docs/deprecated/insforge-storage-sdk.md +159 -0
- package/docs/insforge-instructions-sdk.md +407 -0
- package/eslint.config.js +317 -0
- package/examples/oauth/frontend-oauth-example.html +251 -0
- package/examples/response-examples.md +444 -0
- package/frontend/README.md +112 -0
- package/frontend/components.json +17 -0
- package/frontend/index.html +13 -0
- package/frontend/package.json +63 -0
- package/frontend/public/favicon.ico +0 -0
- package/frontend/src/App.tsx +106 -0
- package/frontend/src/assets/icons/checkbox_checked.svg +6 -0
- package/frontend/src/assets/icons/checkbox_undetermined.svg +6 -0
- package/frontend/src/assets/icons/checked.svg +3 -0
- package/frontend/src/assets/icons/error.svg +3 -0
- package/frontend/src/assets/icons/pencil.svg +4 -0
- package/frontend/src/assets/icons/refresh.svg +4 -0
- package/frontend/src/assets/icons/step_active.svg +3 -0
- package/frontend/src/assets/icons/step_inactive.svg +11 -0
- package/frontend/src/assets/icons/warning.svg +3 -0
- package/frontend/src/assets/logos/amazon.svg +1 -0
- package/frontend/src/assets/logos/claude_code.svg +3 -0
- package/frontend/src/assets/logos/cline.svg +6 -0
- package/frontend/src/assets/logos/cursor.svg +20 -0
- package/frontend/src/assets/logos/discord.svg +9 -0
- package/frontend/src/assets/logos/gemini.svg +19 -0
- package/frontend/src/assets/logos/github.svg +5 -0
- package/frontend/src/assets/logos/google.svg +13 -0
- package/frontend/src/assets/logos/grok.svg +10 -0
- package/frontend/src/assets/logos/insforge_dark.svg +15 -0
- package/frontend/src/assets/logos/insforge_light.svg +15 -0
- package/frontend/src/assets/logos/openai.svg +10 -0
- package/frontend/src/assets/logos/roo_code.svg +9 -0
- package/frontend/src/assets/logos/trae.svg +3 -0
- package/frontend/src/assets/logos/windsurf.svg +10 -0
- package/frontend/src/components/ButtonWithLoading.tsx +27 -0
- package/frontend/src/components/Checkbox.tsx +61 -0
- package/frontend/src/components/CodeBlock.tsx +32 -0
- package/frontend/src/components/ConfirmDialog.tsx +96 -0
- package/frontend/src/components/CopyButton.tsx +69 -0
- package/frontend/src/components/DeleteActionButton.tsx +42 -0
- package/frontend/src/components/EmptyState.tsx +41 -0
- package/frontend/src/components/ErrorState.tsx +35 -0
- package/frontend/src/components/FeatureSidebar.tsx +126 -0
- package/frontend/src/components/FeatureSidebarItem.tsx +101 -0
- package/frontend/src/components/JsonHighlight.tsx +61 -0
- package/frontend/src/components/LoadingState.tsx +16 -0
- package/frontend/src/components/PaginationControls.tsx +54 -0
- package/frontend/src/components/PromptDialog.tsx +68 -0
- package/frontend/src/components/SearchInput.tsx +90 -0
- package/frontend/src/components/SelectionClearButton.tsx +26 -0
- package/frontend/src/components/Stepper.tsx +139 -0
- package/frontend/src/components/ThemeToggle.tsx +58 -0
- package/frontend/src/components/TypeBadge.tsx +20 -0
- package/frontend/src/components/datagrid/DataGrid.tsx +264 -0
- package/frontend/src/components/datagrid/DefaultCellRenderer.tsx +114 -0
- package/frontend/src/components/datagrid/IdCell.tsx +44 -0
- package/frontend/src/components/datagrid/SortableHeader.tsx +74 -0
- package/frontend/src/components/datagrid/cell-editors/BooleanCellEditor.tsx +54 -0
- package/frontend/src/components/datagrid/cell-editors/DateCellEditor.tsx +483 -0
- package/frontend/src/components/datagrid/cell-editors/JsonCellEditor.tsx +362 -0
- package/frontend/src/components/datagrid/cell-editors/TextCellEditor.tsx +38 -0
- package/frontend/src/components/datagrid/cell-editors/index.ts +14 -0
- package/frontend/src/components/datagrid/cell-editors/types.ts +43 -0
- package/frontend/src/components/datagrid/datagridTypes.tsx +72 -0
- package/frontend/src/components/datagrid/index.tsx +20 -0
- package/frontend/src/components/index.ts +39 -0
- package/frontend/src/components/layout/AppHeader.tsx +146 -0
- package/frontend/src/components/layout/AppSidebar.tsx +190 -0
- package/frontend/src/components/layout/CloudLayout.tsx +95 -0
- package/frontend/src/components/layout/Layout.tsx +43 -0
- package/frontend/src/components/radix/Alert.tsx +45 -0
- package/frontend/src/components/radix/AlertDialog.tsx +115 -0
- package/frontend/src/components/radix/Avatar.tsx +45 -0
- package/frontend/src/components/radix/Badge.tsx +33 -0
- package/frontend/src/components/radix/Button.tsx +50 -0
- package/frontend/src/components/radix/Card.tsx +58 -0
- package/frontend/src/components/radix/Dialog.tsx +98 -0
- package/frontend/src/components/radix/DropdownMenu.tsx +185 -0
- package/frontend/src/components/radix/Form.tsx +167 -0
- package/frontend/src/components/radix/Input.tsx +22 -0
- package/frontend/src/components/radix/Label.tsx +19 -0
- package/frontend/src/components/radix/Popover.tsx +29 -0
- package/frontend/src/components/radix/ScrollArea.tsx +44 -0
- package/frontend/src/components/radix/Select.tsx +151 -0
- package/frontend/src/components/radix/Separator.tsx +26 -0
- package/frontend/src/components/radix/Sheet.tsx +119 -0
- package/frontend/src/components/radix/Skeleton.tsx +7 -0
- package/frontend/src/components/radix/Switch.tsx +29 -0
- package/frontend/src/components/radix/Tabs.tsx +50 -0
- package/frontend/src/components/radix/Textarea.tsx +21 -0
- package/frontend/src/components/radix/Tooltip.tsx +28 -0
- package/frontend/src/features/ai/components/AIConfigCard.tsx +154 -0
- package/frontend/src/features/ai/components/AIConfigDialog.tsx +76 -0
- package/frontend/src/features/ai/components/AIConfigForm.tsx +222 -0
- package/frontend/src/features/ai/components/AIEmptyState.tsx +18 -0
- package/frontend/src/features/ai/components/fields/ModalityField.tsx +87 -0
- package/frontend/src/features/ai/components/fields/ModelSelectionField.tsx +134 -0
- package/frontend/src/features/ai/components/fields/SystemPromptField.tsx +33 -0
- package/frontend/src/features/ai/helpers.ts +155 -0
- package/frontend/src/features/ai/hooks/useAIConfigs.ts +221 -0
- package/frontend/src/features/ai/hooks/useAIUsage.ts +77 -0
- package/frontend/src/features/ai/page/AIPage.tsx +178 -0
- package/frontend/src/features/ai/services/ai.service.ts +148 -0
- package/frontend/src/features/auth/components/AddOAuthDialog.tsx +106 -0
- package/frontend/src/features/auth/components/AuthMethodTab.tsx +238 -0
- package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +303 -0
- package/frontend/src/features/auth/components/OAuthEmptyState.tsx +15 -0
- package/frontend/src/features/auth/components/UserFormDialog.tsx +248 -0
- package/frontend/src/features/auth/components/UsersDataGrid.tsx +183 -0
- package/frontend/src/features/auth/components/UsersTab.tsx +114 -0
- package/frontend/src/features/auth/hooks/useOAuthConfig.ts +129 -0
- package/frontend/src/features/auth/hooks/useUsers.ts +57 -0
- package/frontend/src/features/auth/index.ts +9 -0
- package/frontend/src/features/auth/page/AuthenticationPage.tsx +169 -0
- package/frontend/src/features/auth/services/auth.service.ts +112 -0
- package/frontend/src/features/auth/services/oauth.service.ts +49 -0
- package/frontend/src/features/dashboard/page/DashboardPage.tsx +194 -0
- package/frontend/src/features/database/components/ColumnTypeSelect.tsx +64 -0
- package/frontend/src/features/database/components/DatabaseDataGrid.tsx +282 -0
- package/frontend/src/features/database/components/ForeignKeyCell.tsx +187 -0
- package/frontend/src/features/database/components/ForeignKeyPopover.tsx +378 -0
- package/frontend/src/features/database/components/LinkRecordModal.tsx +288 -0
- package/frontend/src/features/database/components/RecordFormDialog.tsx +164 -0
- package/frontend/src/features/database/components/RecordFormField.tsx +568 -0
- package/frontend/src/features/database/components/TableEmptyState.tsx +21 -0
- package/frontend/src/features/database/components/TableForm.tsx +656 -0
- package/frontend/src/features/database/components/TableFormColumn.tsx +137 -0
- package/frontend/src/features/database/components/TableListSkeleton.tsx +9 -0
- package/frontend/src/features/database/components/TableSidebar.tsx +47 -0
- package/frontend/src/features/database/constants.ts +26 -0
- package/frontend/src/features/database/helpers.ts +125 -0
- package/frontend/src/features/database/hooks/UseLinkModal.tsx +78 -0
- package/frontend/src/features/database/index.ts +12 -0
- package/frontend/src/features/database/page/DatabasePage.tsx +626 -0
- package/frontend/src/features/database/schema.ts +25 -0
- package/frontend/src/features/database/services/database.service.ts +216 -0
- package/frontend/src/features/functions/components/FunctionEmptyState.tsx +15 -0
- package/frontend/src/features/functions/components/FunctionRow.tsx +71 -0
- package/frontend/src/features/functions/components/FunctionViewer.tsx +46 -0
- package/frontend/src/features/functions/components/FunctionsContent.tsx +88 -0
- package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -0
- package/frontend/src/features/functions/components/SecretEmptyState.tsx +23 -0
- package/frontend/src/features/functions/components/SecretRow.tsx +68 -0
- package/frontend/src/features/functions/components/SecretsContent.tsx +120 -0
- package/frontend/src/features/functions/hooks/useFunctions.ts +106 -0
- package/frontend/src/features/functions/page/FunctionsPage.tsx +28 -0
- package/frontend/src/features/functions/services/functions.service.ts +48 -0
- package/frontend/src/features/login/components/AuthErrorBoundary.tsx +87 -0
- package/frontend/src/features/login/components/PrivateRoute.tsx +24 -0
- package/frontend/src/features/login/page/CloudLoginPage.tsx +93 -0
- package/frontend/src/features/login/page/LoginPage.tsx +174 -0
- package/frontend/src/features/logs/components/AnalyticsLogsTable.tsx +313 -0
- package/frontend/src/features/logs/components/LogsTable.tsx +199 -0
- package/frontend/src/features/logs/hooks/useAuditLogs.ts +39 -0
- package/frontend/src/features/logs/index.ts +5 -0
- package/frontend/src/features/logs/page/AnalyticsLogsPage.tsx +530 -0
- package/frontend/src/features/logs/page/AuditsPage.tsx +192 -0
- package/frontend/src/features/logs/services/log.service.ts +171 -0
- package/frontend/src/features/metadata/hooks/useMetadata.ts +53 -0
- package/frontend/src/features/metadata/index.ts +0 -0
- package/frontend/src/features/metadata/page/MetadataPage.tsx +136 -0
- package/frontend/src/features/metadata/services/metadata.service.ts +17 -0
- package/frontend/src/features/onboard/components/CompletionCard.tsx +41 -0
- package/frontend/src/features/onboard/components/OnboardButton.tsx +84 -0
- package/frontend/src/features/onboard/components/StepContent.tsx +91 -0
- package/frontend/src/features/onboard/components/TestConnectionStep.tsx +53 -0
- package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +35 -0
- package/frontend/src/features/onboard/components/mcp/McpInstallation.tsx +144 -0
- package/frontend/src/features/onboard/components/mcp/index.ts +4 -0
- package/frontend/src/features/onboard/components/mcp/mcp-helper.tsx +98 -0
- package/frontend/src/features/onboard/index.ts +3 -0
- package/frontend/src/features/onboard/page/OnBoardPage.tsx +104 -0
- package/frontend/src/features/onboard/types.ts +8 -0
- package/frontend/src/features/secrets/hooks/useSecrets.ts +139 -0
- package/frontend/src/features/secrets/services/secrets.service.ts +57 -0
- package/frontend/src/features/storage/components/BucketEmptyState.tsx +19 -0
- package/frontend/src/features/storage/components/BucketFormDialog.tsx +194 -0
- package/frontend/src/features/storage/components/BucketListSkeleton.tsx +17 -0
- package/frontend/src/features/storage/components/FilePreviewDialog.tsx +287 -0
- package/frontend/src/features/storage/components/StorageDataGrid.tsx +239 -0
- package/frontend/src/features/storage/components/StorageManager.tsx +236 -0
- package/frontend/src/features/storage/components/StorageSidebar.tsx +44 -0
- package/frontend/src/features/storage/components/UploadToast.tsx +46 -0
- package/frontend/src/features/storage/index.ts +3 -0
- package/frontend/src/features/storage/page/StoragePage.tsx +553 -0
- package/frontend/src/features/storage/services/storage.service.ts +144 -0
- package/frontend/src/features/visualizer/components/AuthNode.tsx +107 -0
- package/frontend/src/features/visualizer/components/BucketNode.tsx +34 -0
- package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +359 -0
- package/frontend/src/features/visualizer/components/TableNode.tsx +152 -0
- package/frontend/src/features/visualizer/components/VisualizerSkeleton.tsx +24 -0
- package/frontend/src/features/visualizer/components/index.ts +5 -0
- package/frontend/src/features/visualizer/page/VisualizerPage.tsx +127 -0
- package/frontend/src/index.css +248 -0
- package/frontend/src/lib/api/client.ts +163 -0
- package/frontend/src/lib/contexts/AuthContext.tsx +157 -0
- package/frontend/src/lib/contexts/OnboardStepContext.tsx +68 -0
- package/frontend/src/lib/contexts/SocketContext.tsx +303 -0
- package/frontend/src/lib/contexts/ThemeContext.tsx +125 -0
- package/frontend/src/lib/hooks/useAuth.ts +4 -0
- package/frontend/src/lib/hooks/useConfirm.ts +55 -0
- package/frontend/src/lib/hooks/useInterval.ts +27 -0
- package/frontend/src/lib/hooks/useMediaQuery.ts +59 -0
- package/frontend/src/lib/hooks/useOnboardingCompletion.ts +29 -0
- package/frontend/src/lib/hooks/usePagination.ts +27 -0
- package/frontend/src/lib/hooks/useTimeout.ts +27 -0
- package/frontend/src/lib/hooks/useToast.tsx +229 -0
- package/frontend/src/lib/utils/constants.ts +38 -0
- package/frontend/src/lib/utils/utils.ts +165 -0
- package/frontend/src/lib/utils/validation-schemas.ts +126 -0
- package/frontend/src/main.tsx +16 -0
- package/frontend/src/rdg.css +194 -0
- package/frontend/src/vite-env.d.ts +12 -0
- package/frontend/tailwind.config.js +97 -0
- package/frontend/tsconfig.json +26 -0
- package/frontend/tsconfig.node.json +10 -0
- package/frontend/vite.config.ts +37 -0
- package/frontend/vitest.config.ts +36 -0
- package/functions/deno.json +25 -0
- package/functions/server.ts +290 -0
- package/functions/worker-template.js +126 -0
- package/openapi/ai.yaml +689 -0
- package/openapi/auth.yaml +563 -0
- package/openapi/functions.yaml +476 -0
- package/openapi/health.yaml +30 -0
- package/openapi/logs.yaml +224 -0
- package/openapi/metadata.yaml +178 -0
- package/openapi/records.yaml +382 -0
- package/openapi/secrets.yaml +371 -0
- package/openapi/storage.yaml +876 -0
- package/openapi/tables.yaml +464 -0
- package/package.json +88 -0
- package/shared-schemas/package.json +31 -0
- package/shared-schemas/src/ai-api.schema.ts +167 -0
- package/shared-schemas/src/ai.schema.ts +54 -0
- package/shared-schemas/src/auth-api.schema.ts +193 -0
- package/shared-schemas/src/auth.schema.ts +94 -0
- package/shared-schemas/src/database-api.schema.ts +259 -0
- package/shared-schemas/src/database.schema.ts +69 -0
- package/shared-schemas/src/functions-api.schema.ts +25 -0
- package/shared-schemas/src/functions.schema.ts +16 -0
- package/shared-schemas/src/index.ts +13 -0
- package/shared-schemas/src/logs-api.schema.ts +49 -0
- package/shared-schemas/src/logs.schema.ts +14 -0
- package/shared-schemas/src/metadata.schema.ts +56 -0
- package/shared-schemas/src/storage-api.schema.ts +65 -0
- package/shared-schemas/src/storage.schema.ts +19 -0
- package/shared-schemas/tsconfig.json +21 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useEffect, useLayoutEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useTimeout(callback: () => void, delay: number | null): void {
|
|
4
|
+
const savedCallback = useRef(callback);
|
|
5
|
+
|
|
6
|
+
// Remember the latest callback if it changes.
|
|
7
|
+
useLayoutEffect(() => {
|
|
8
|
+
savedCallback.current = callback;
|
|
9
|
+
}, [callback]);
|
|
10
|
+
|
|
11
|
+
// Set up the timeout.
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
// Don't schedule if no delay is specified.
|
|
14
|
+
// Note: 0 is a valid value for delay.
|
|
15
|
+
if (!delay && delay !== 0) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const id = setTimeout(() => {
|
|
20
|
+
savedCallback.current();
|
|
21
|
+
}, delay);
|
|
22
|
+
|
|
23
|
+
return () => {
|
|
24
|
+
clearTimeout(id);
|
|
25
|
+
};
|
|
26
|
+
}, [delay]);
|
|
27
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import React, { createContext, useContext, useState, useCallback } from 'react';
|
|
2
|
+
import { useTimeout } from './useTimeout';
|
|
3
|
+
import { generateUUID } from '@/lib/utils/utils';
|
|
4
|
+
|
|
5
|
+
interface Toast {
|
|
6
|
+
id: string;
|
|
7
|
+
message: string;
|
|
8
|
+
type?: 'success' | 'error' | 'info' | 'warn' | 'upload';
|
|
9
|
+
customIcon?: React.ReactNode;
|
|
10
|
+
duration: number;
|
|
11
|
+
progress?: number;
|
|
12
|
+
onCancel?: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface ToastContextType {
|
|
16
|
+
showToast: (
|
|
17
|
+
message: string,
|
|
18
|
+
type?: 'success' | 'error' | 'info' | 'warn' | 'upload',
|
|
19
|
+
customIcon?: React.ReactNode,
|
|
20
|
+
duration?: number
|
|
21
|
+
) => string;
|
|
22
|
+
updateToast: (id: string, updates: Partial<Toast>) => void;
|
|
23
|
+
removeToast: (id: string) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const DEFAULT_TOAST_DURATION = 3000; // 3 seconds
|
|
27
|
+
|
|
28
|
+
const ToastContext = createContext<ToastContextType | undefined>(undefined);
|
|
29
|
+
|
|
30
|
+
interface ToastItemProps {
|
|
31
|
+
toast: Toast;
|
|
32
|
+
onRemove: () => void;
|
|
33
|
+
getDefaultIcon: (type: string) => React.ReactNode;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function ToastItem({ toast, onRemove, getDefaultIcon }: ToastItemProps) {
|
|
37
|
+
// Auto remove after duration (except for upload toasts)
|
|
38
|
+
useTimeout(onRemove, toast.type === 'upload' ? null : toast.duration);
|
|
39
|
+
|
|
40
|
+
const baseClasses =
|
|
41
|
+
'relative p-3 rounded-[8px] font-medium animate-in fade-in duration-300 flex items-center overflow-hidden border-1';
|
|
42
|
+
|
|
43
|
+
const typeClasses = {
|
|
44
|
+
success: 'bg-green-300 text-green-700 border-green-400',
|
|
45
|
+
error: 'bg-red-300 text-red-700 border-red-400',
|
|
46
|
+
info: 'bg-blue-300 text-blue-700 border-blue-400',
|
|
47
|
+
warn: 'bg-yellow-300 text-yellow-700 border-yellow-400',
|
|
48
|
+
upload: 'bg-neutral-100 text-zinc-950 border-neutral-800',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const progressBarClasses = {
|
|
52
|
+
success: 'bg-green-700',
|
|
53
|
+
error: 'bg-red-700',
|
|
54
|
+
info: 'bg-blue-700',
|
|
55
|
+
warn: 'bg-yellow-700',
|
|
56
|
+
upload: 'bg-neutral-700',
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const toastType = toast.type || 'info';
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div className={`${baseClasses} ${typeClasses[toastType]}`}>
|
|
63
|
+
<div className="flex items-center gap-2 flex-1">
|
|
64
|
+
{toast.customIcon || getDefaultIcon(toastType)}
|
|
65
|
+
<span className="flex-1 text-sm font-medium">{toast.message}</span>
|
|
66
|
+
{toast.type === 'upload' && toast.onCancel && (
|
|
67
|
+
<button
|
|
68
|
+
onClick={toast.onCancel}
|
|
69
|
+
className="text-zinc-500 hover:text-zinc-700 text-sm font-medium"
|
|
70
|
+
>
|
|
71
|
+
Cancel
|
|
72
|
+
</button>
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
{/* Progress bar */}
|
|
77
|
+
{toast.type === 'upload' ? (
|
|
78
|
+
<div className="absolute bottom-0 left-0 w-full h-1 bg-neutral-300 overflow-hidden">
|
|
79
|
+
<div
|
|
80
|
+
className={`h-full ${progressBarClasses[toastType]} transition-all duration-300 ease-out`}
|
|
81
|
+
style={{
|
|
82
|
+
width: `${toast.progress || 0}%`,
|
|
83
|
+
}}
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
) : (
|
|
87
|
+
<div className="absolute bottom-0 left-0 w-full h-1 bg-transparent overflow-hidden">
|
|
88
|
+
<div
|
|
89
|
+
className={`h-full w-full ${progressBarClasses[toastType]} animate-toast-progress origin-left`}
|
|
90
|
+
style={{
|
|
91
|
+
animationDuration: `${toast.duration}ms`,
|
|
92
|
+
}}
|
|
93
|
+
/>
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function ToastProvider({ children }: { children: React.ReactNode }) {
|
|
101
|
+
const [toasts, setToasts] = useState<Toast[]>([]);
|
|
102
|
+
|
|
103
|
+
const showToast = useCallback(
|
|
104
|
+
(
|
|
105
|
+
message: string,
|
|
106
|
+
type: 'success' | 'error' | 'info' | 'warn' | 'upload' = 'info',
|
|
107
|
+
customIcon?: React.ReactNode,
|
|
108
|
+
duration: number = DEFAULT_TOAST_DURATION
|
|
109
|
+
) => {
|
|
110
|
+
const id = generateUUID();
|
|
111
|
+
// Set duration to 2 seconds for success toasts
|
|
112
|
+
const toastDuration = type === 'success' ? 2000 : duration;
|
|
113
|
+
const newToast: Toast = { id, message, type, customIcon, duration: toastDuration };
|
|
114
|
+
|
|
115
|
+
setToasts((prev) => [...prev, newToast]);
|
|
116
|
+
return id;
|
|
117
|
+
},
|
|
118
|
+
[]
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const updateToast = useCallback((id: string, updates: Partial<Toast>) => {
|
|
122
|
+
setToasts((prev) => prev.map((toast) => (toast.id === id ? { ...toast, ...updates } : toast)));
|
|
123
|
+
}, []);
|
|
124
|
+
|
|
125
|
+
const removeToast = useCallback((id: string) => {
|
|
126
|
+
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
127
|
+
}, []);
|
|
128
|
+
|
|
129
|
+
const getDefaultIcon = (type: string) => {
|
|
130
|
+
switch (type) {
|
|
131
|
+
case 'success':
|
|
132
|
+
return (
|
|
133
|
+
<svg className="w-5 h-5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
134
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
135
|
+
</svg>
|
|
136
|
+
);
|
|
137
|
+
case 'error':
|
|
138
|
+
return (
|
|
139
|
+
<svg className="w-5 h-5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
140
|
+
<path
|
|
141
|
+
strokeLinecap="round"
|
|
142
|
+
strokeLinejoin="round"
|
|
143
|
+
strokeWidth={2}
|
|
144
|
+
d="M6 18L18 6M6 6l12 12"
|
|
145
|
+
/>
|
|
146
|
+
</svg>
|
|
147
|
+
);
|
|
148
|
+
case 'warn':
|
|
149
|
+
return (
|
|
150
|
+
<svg className="w-5 h-5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
151
|
+
<path
|
|
152
|
+
strokeLinecap="round"
|
|
153
|
+
strokeLinejoin="round"
|
|
154
|
+
strokeWidth={2}
|
|
155
|
+
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.464 0L4.35 16.5c-.77.833.192 2.5 1.732 2.5z"
|
|
156
|
+
/>
|
|
157
|
+
</svg>
|
|
158
|
+
);
|
|
159
|
+
case 'upload':
|
|
160
|
+
return (
|
|
161
|
+
<svg
|
|
162
|
+
className="w-5 h-5 shrink-0 animate-spin"
|
|
163
|
+
fill="none"
|
|
164
|
+
viewBox="0 0 24 24"
|
|
165
|
+
stroke="currentColor"
|
|
166
|
+
strokeWidth={2}
|
|
167
|
+
>
|
|
168
|
+
<path
|
|
169
|
+
strokeLinecap="round"
|
|
170
|
+
strokeLinejoin="round"
|
|
171
|
+
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
|
|
172
|
+
/>
|
|
173
|
+
</svg>
|
|
174
|
+
);
|
|
175
|
+
case 'info':
|
|
176
|
+
default:
|
|
177
|
+
return (
|
|
178
|
+
<svg className="w-5 h-5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
179
|
+
<path
|
|
180
|
+
strokeLinecap="round"
|
|
181
|
+
strokeLinejoin="round"
|
|
182
|
+
strokeWidth={2}
|
|
183
|
+
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
184
|
+
/>
|
|
185
|
+
</svg>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<ToastContext.Provider value={{ showToast, updateToast, removeToast }}>
|
|
192
|
+
{children}
|
|
193
|
+
{/* Regular toasts - top center */}
|
|
194
|
+
<div className="fixed top-7.5 dark:top-20 left-1/2 transform -translate-x-1/2 z-[9999] flex flex-col gap-3 w-full max-w-[480px]">
|
|
195
|
+
{toasts
|
|
196
|
+
.filter((t) => t.type !== 'upload')
|
|
197
|
+
.map((toast) => (
|
|
198
|
+
<ToastItem
|
|
199
|
+
key={toast.id}
|
|
200
|
+
toast={toast}
|
|
201
|
+
onRemove={() => removeToast(toast.id)}
|
|
202
|
+
getDefaultIcon={getDefaultIcon}
|
|
203
|
+
/>
|
|
204
|
+
))}
|
|
205
|
+
</div>
|
|
206
|
+
{/* Upload toasts - bottom right */}
|
|
207
|
+
<div className="fixed bottom-4 right-4 z-[9999] w-[276px] flex flex-col">
|
|
208
|
+
{toasts
|
|
209
|
+
.filter((t) => t.type === 'upload')
|
|
210
|
+
.map((toast) => (
|
|
211
|
+
<ToastItem
|
|
212
|
+
key={toast.id}
|
|
213
|
+
toast={toast}
|
|
214
|
+
onRemove={() => removeToast(toast.id)}
|
|
215
|
+
getDefaultIcon={getDefaultIcon}
|
|
216
|
+
/>
|
|
217
|
+
))}
|
|
218
|
+
</div>
|
|
219
|
+
</ToastContext.Provider>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function useToast() {
|
|
224
|
+
const context = useContext(ToastContext);
|
|
225
|
+
if (!context) {
|
|
226
|
+
throw new Error('useToast must be used within a ToastProvider');
|
|
227
|
+
}
|
|
228
|
+
return context;
|
|
229
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// UI constants
|
|
2
|
+
export const BREAKPOINTS = {
|
|
3
|
+
xs: 475,
|
|
4
|
+
sm: 640,
|
|
5
|
+
md: 768,
|
|
6
|
+
lg: 1024,
|
|
7
|
+
xl: 1280,
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
// Colors for stat cards
|
|
11
|
+
export const STAT_COLORS = {
|
|
12
|
+
blue: {
|
|
13
|
+
icon: 'text-blue-400',
|
|
14
|
+
bg: 'bg-blue-500/10',
|
|
15
|
+
},
|
|
16
|
+
green: {
|
|
17
|
+
icon: 'text-green-400',
|
|
18
|
+
bg: 'bg-green-500/10',
|
|
19
|
+
},
|
|
20
|
+
purple: {
|
|
21
|
+
icon: 'text-purple-400',
|
|
22
|
+
bg: 'bg-purple-500/10',
|
|
23
|
+
},
|
|
24
|
+
emerald: {
|
|
25
|
+
icon: 'text-emerald-400',
|
|
26
|
+
bg: 'bg-emerald-500/10',
|
|
27
|
+
},
|
|
28
|
+
yellow: {
|
|
29
|
+
icon: 'text-yellow-400',
|
|
30
|
+
bg: 'bg-yellow-500/10',
|
|
31
|
+
},
|
|
32
|
+
} as const;
|
|
33
|
+
|
|
34
|
+
// Storage keys
|
|
35
|
+
export enum StorageKeys {
|
|
36
|
+
AUTH_TOKEN = 'auth_token',
|
|
37
|
+
USER_PREFERENCES = 'user_preferences',
|
|
38
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { ColumnType } from '@insforge/shared-schemas';
|
|
2
|
+
import { type ClassValue, clsx } from 'clsx';
|
|
3
|
+
import { twMerge } from 'tailwind-merge';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { format, parse, isValid, parseISO } from 'date-fns';
|
|
6
|
+
import {
|
|
7
|
+
uuidSchema,
|
|
8
|
+
integerSchema,
|
|
9
|
+
floatSchema,
|
|
10
|
+
booleanSchema,
|
|
11
|
+
dateSchema,
|
|
12
|
+
dateTimeSchema,
|
|
13
|
+
jsonSchema,
|
|
14
|
+
stringSchema,
|
|
15
|
+
} from './validation-schemas';
|
|
16
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
17
|
+
import type { ConvertedValue, DisplayValue, ValueConversionResult } from '@/components/datagrid';
|
|
18
|
+
|
|
19
|
+
export function cn(...inputs: ClassValue[]) {
|
|
20
|
+
return twMerge(clsx(inputs));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Convert and validate a string value based on the specified ColumnType
|
|
25
|
+
*/
|
|
26
|
+
export function convertValueForColumn(
|
|
27
|
+
type: ColumnType | string,
|
|
28
|
+
value: string | null | undefined
|
|
29
|
+
): ValueConversionResult {
|
|
30
|
+
try {
|
|
31
|
+
let convertedValue;
|
|
32
|
+
|
|
33
|
+
switch (type) {
|
|
34
|
+
case ColumnType.UUID:
|
|
35
|
+
convertedValue = uuidSchema.parse(value);
|
|
36
|
+
break;
|
|
37
|
+
case ColumnType.INTEGER:
|
|
38
|
+
convertedValue = integerSchema.parse(value);
|
|
39
|
+
break;
|
|
40
|
+
case ColumnType.FLOAT:
|
|
41
|
+
convertedValue = floatSchema.parse(value);
|
|
42
|
+
break;
|
|
43
|
+
case ColumnType.BOOLEAN:
|
|
44
|
+
convertedValue = booleanSchema.parse(value);
|
|
45
|
+
break;
|
|
46
|
+
case ColumnType.DATE:
|
|
47
|
+
convertedValue = dateSchema.parse(value);
|
|
48
|
+
break;
|
|
49
|
+
case ColumnType.DATETIME:
|
|
50
|
+
convertedValue = dateTimeSchema.parse(value);
|
|
51
|
+
break;
|
|
52
|
+
case ColumnType.JSON:
|
|
53
|
+
convertedValue = jsonSchema.parse(value);
|
|
54
|
+
break;
|
|
55
|
+
case ColumnType.STRING:
|
|
56
|
+
convertedValue = stringSchema.parse(value);
|
|
57
|
+
break;
|
|
58
|
+
default:
|
|
59
|
+
return {
|
|
60
|
+
success: false,
|
|
61
|
+
error: `Unsupported column type: ${type}`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
success: true,
|
|
67
|
+
value: convertedValue,
|
|
68
|
+
};
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (error instanceof z.ZodError) {
|
|
71
|
+
return {
|
|
72
|
+
success: false,
|
|
73
|
+
error: error.errors[0]?.message || 'Validation failed',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
success: false,
|
|
78
|
+
error: error instanceof Error ? error.message : 'Unknown conversion error',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Generate a UUID v4 using the uuid library
|
|
85
|
+
* Works in all browsers and contexts (secure and non-secure)
|
|
86
|
+
* Uses crypto.getRandomValues when available, falls back to Math.random
|
|
87
|
+
*/
|
|
88
|
+
export function generateUUID(): string {
|
|
89
|
+
return uuidv4();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Centralized value formatter that handles all data types consistently
|
|
94
|
+
* Converts database values to formatted display strings for UI components
|
|
95
|
+
*/
|
|
96
|
+
export function formatValueForDisplay(value: ConvertedValue, type?: ColumnType): DisplayValue {
|
|
97
|
+
// Handle null/undefined values
|
|
98
|
+
if (isEmptyValue(value)) {
|
|
99
|
+
return 'null';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Handle different column types
|
|
103
|
+
switch (type) {
|
|
104
|
+
case ColumnType.BOOLEAN:
|
|
105
|
+
return value ? 'True' : 'False';
|
|
106
|
+
|
|
107
|
+
case ColumnType.DATE: {
|
|
108
|
+
const date = parse(String(value), 'yyyy-MM-dd', new Date());
|
|
109
|
+
if (!isValid(date)) {
|
|
110
|
+
return String(value);
|
|
111
|
+
}
|
|
112
|
+
const displayValue = format(date, 'MMM dd, yyyy');
|
|
113
|
+
return displayValue;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
case ColumnType.DATETIME: {
|
|
117
|
+
const date = parseISO(String(value));
|
|
118
|
+
if (!isValid(date)) {
|
|
119
|
+
return String(value);
|
|
120
|
+
}
|
|
121
|
+
const displayValue = format(date, 'MMM dd, yyyy, hh:mm a');
|
|
122
|
+
return displayValue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
case ColumnType.JSON: {
|
|
126
|
+
try {
|
|
127
|
+
const parsed = typeof value === 'string' ? JSON.parse(value) : value;
|
|
128
|
+
const formatted =
|
|
129
|
+
parsed && typeof parsed === 'object' ? JSON.stringify(parsed) : String(parsed);
|
|
130
|
+
|
|
131
|
+
return formatted;
|
|
132
|
+
} catch {
|
|
133
|
+
return 'Invalid JSON';
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
case ColumnType.INTEGER:
|
|
138
|
+
case ColumnType.FLOAT: {
|
|
139
|
+
return String(value);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
case ColumnType.UUID:
|
|
143
|
+
case ColumnType.STRING:
|
|
144
|
+
default: {
|
|
145
|
+
// Convert to string and optionally truncate
|
|
146
|
+
return String(value);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Check if a value is considered empty for database purposes
|
|
153
|
+
*/
|
|
154
|
+
export function isEmptyValue(value: unknown): boolean {
|
|
155
|
+
return value === null || value === undefined || value === '';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export const isInsForgeCloudProject = () => {
|
|
159
|
+
return window.location.hostname.endsWith('.insforge.app');
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export const getBackendUrl = () => {
|
|
163
|
+
const isHttp = window.location.protocol === 'http:';
|
|
164
|
+
return isHttp ? 'http://localhost:7130' : window.location.origin;
|
|
165
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const uuidSchema = z
|
|
4
|
+
.string()
|
|
5
|
+
.uuid({ message: 'Please enter a valid UUID' })
|
|
6
|
+
.or(z.literal('').transform(() => null))
|
|
7
|
+
.or(z.null());
|
|
8
|
+
|
|
9
|
+
export const integerSchema = z
|
|
10
|
+
.union([
|
|
11
|
+
z
|
|
12
|
+
.string()
|
|
13
|
+
.regex(/^-?\d+$/, { message: 'Please enter a valid integer' })
|
|
14
|
+
.transform((val) => {
|
|
15
|
+
const num = parseInt(val, 10);
|
|
16
|
+
if (num < -2147483648 || num > 2147483647) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
'Integer value out of range. Please enter a value between -2,147,483,648 and 2,147,483,647'
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
return num;
|
|
22
|
+
}),
|
|
23
|
+
z.number().int().min(-2147483648).max(2147483647),
|
|
24
|
+
z.literal('').transform(() => null),
|
|
25
|
+
z.null(),
|
|
26
|
+
])
|
|
27
|
+
.catch(() => {
|
|
28
|
+
throw new Error('Please enter a valid integer');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export const floatSchema = z
|
|
32
|
+
.union([
|
|
33
|
+
z
|
|
34
|
+
.string()
|
|
35
|
+
.regex(/^-?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/, {
|
|
36
|
+
message: 'Please enter a valid number',
|
|
37
|
+
})
|
|
38
|
+
.transform((val) => {
|
|
39
|
+
const num = parseFloat(val);
|
|
40
|
+
if (!isFinite(num)) {
|
|
41
|
+
throw new Error('Number value out of range');
|
|
42
|
+
}
|
|
43
|
+
if (Math.abs(num) > 1.7976931348623157e308) {
|
|
44
|
+
throw new Error('Number value exceeds double precision range');
|
|
45
|
+
}
|
|
46
|
+
return num;
|
|
47
|
+
}),
|
|
48
|
+
z.number().finite(),
|
|
49
|
+
z.literal('').transform(() => null),
|
|
50
|
+
z.null(),
|
|
51
|
+
])
|
|
52
|
+
.catch(() => {
|
|
53
|
+
throw new Error('Please enter a valid number');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export const booleanSchema = z
|
|
57
|
+
.union([
|
|
58
|
+
z.boolean(),
|
|
59
|
+
z.string().transform((val) => {
|
|
60
|
+
const lower = val.toLowerCase();
|
|
61
|
+
if (lower === 'true') {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
if (lower === 'false') {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if (lower === 'null' || lower === '') {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
throw new Error('Please enter a valid boolean value: true, false, or leave empty');
|
|
71
|
+
}),
|
|
72
|
+
z.literal('').transform(() => null),
|
|
73
|
+
z.null(),
|
|
74
|
+
])
|
|
75
|
+
.catch(() => {
|
|
76
|
+
throw new Error('Please enter a valid boolean value');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export const dateSchema = z
|
|
80
|
+
.union([z.string().date(), z.literal('').transform(() => null), z.null()])
|
|
81
|
+
.catch(() => {
|
|
82
|
+
throw new Error('Please enter a valid date');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
export const dateTimeSchema = z
|
|
86
|
+
.union([
|
|
87
|
+
// ISO 8601 datetime with timezone (Z or ±HH:MM)
|
|
88
|
+
z.string().datetime({ offset: true }),
|
|
89
|
+
z.literal('').transform(() => null),
|
|
90
|
+
z.null(),
|
|
91
|
+
])
|
|
92
|
+
.catch(() => {
|
|
93
|
+
throw new Error('Please enter a valid datetime');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
export const jsonSchema = z
|
|
97
|
+
.union([
|
|
98
|
+
z.string().transform((val) => {
|
|
99
|
+
if (val === '' || val === 'null') {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
return JSON.parse(val);
|
|
104
|
+
} catch {
|
|
105
|
+
throw new Error('Please enter valid JSON');
|
|
106
|
+
}
|
|
107
|
+
}),
|
|
108
|
+
z.object({}).passthrough(),
|
|
109
|
+
z.array(z.any()),
|
|
110
|
+
z.null(),
|
|
111
|
+
])
|
|
112
|
+
.catch(() => {
|
|
113
|
+
throw new Error('Please enter valid JSON');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
export const stringSchema = z.union([z.string(), z.null()]);
|
|
117
|
+
|
|
118
|
+
export const emailSchema = z.string().email('Invalid email address');
|
|
119
|
+
export const passwordSchema = z.string().min(8, 'Password must be at least 8 characters');
|
|
120
|
+
|
|
121
|
+
export const loginFormSchema = z.object({
|
|
122
|
+
email: emailSchema,
|
|
123
|
+
password: passwordSchema,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
export type LoginFormData = z.infer<typeof loginFormSchema>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom/client';
|
|
3
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
4
|
+
import App from './App.tsx';
|
|
5
|
+
import './index.css';
|
|
6
|
+
|
|
7
|
+
const rootElement = document.getElementById('root');
|
|
8
|
+
if (rootElement) {
|
|
9
|
+
ReactDOM.createRoot(rootElement).render(
|
|
10
|
+
<React.StrictMode>
|
|
11
|
+
<BrowserRouter>
|
|
12
|
+
<App />
|
|
13
|
+
</BrowserRouter>
|
|
14
|
+
</React.StrictMode>
|
|
15
|
+
);
|
|
16
|
+
}
|