spine-framework 0.1.0
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/.framework/README.md +129 -0
- package/.framework/cli/bin.cjs +14 -0
- package/.framework/cli/commands/agents.ts +153 -0
- package/.framework/cli/commands/auth.ts +94 -0
- package/.framework/cli/commands/create-app.ts +185 -0
- package/.framework/cli/commands/dev.ts +295 -0
- package/.framework/cli/commands/doctor.ts +442 -0
- package/.framework/cli/commands/generate.ts +332 -0
- package/.framework/cli/commands/init.ts +272 -0
- package/.framework/cli/commands/install-app.ts +391 -0
- package/.framework/cli/commands/items.ts +253 -0
- package/.framework/cli/commands/migrations.ts +141 -0
- package/.framework/cli/commands/pipelines.ts +166 -0
- package/.framework/cli/commands/status.ts +197 -0
- package/.framework/cli/commands/system.ts +184 -0
- package/.framework/cli/commands/test.ts +227 -0
- package/.framework/cli/commands/uninstall-app.ts +166 -0
- package/.framework/cli/context.ts +268 -0
- package/.framework/cli/env-loader.ts +36 -0
- package/.framework/cli/index.ts +106 -0
- package/.framework/cli/welcome.cjs +45 -0
- package/.framework/docs/API.md +384 -0
- package/.framework/docs/STABILITY.md +52 -0
- package/.framework/docs/admin-routes.md +76 -0
- package/.framework/docs/api-docs-progress.md +38 -0
- package/.framework/docs/api-governance.md +146 -0
- package/.framework/docs/api-testing-results.md +212 -0
- package/.framework/docs/apis/admin-configs.md +567 -0
- package/.framework/docs/apis/admin-data.md +272 -0
- package/.framework/docs/apis/index.md +231 -0
- package/.framework/docs/apis/internal.md +295 -0
- package/.framework/docs/apis/runtime.md +537 -0
- package/.framework/docs/assembly-launch-guide.md +138 -0
- package/.framework/docs/audit-results.md +590 -0
- package/.framework/docs/authorization-model.md +170 -0
- package/.framework/docs/db-api-inventory.md +95 -0
- package/.framework/docs/examples/custom-app/README.md +77 -0
- package/.framework/docs/examples/custom-function/README.md +27 -0
- package/.framework/docs/examples/custom-function/handler.ts +48 -0
- package/.framework/docs/examples/custom-webhook/README.md +68 -0
- package/.framework/docs/gap-remediation-backlog.md +103 -0
- package/.framework/docs/guides/cli-guide.md +224 -0
- package/.framework/docs/guides/getting-started.md +103 -0
- package/.framework/docs/guides/import-guide.md +193 -0
- package/.framework/docs/guides/testing-guide.md +229 -0
- package/.framework/docs/permission-examples.md +326 -0
- package/.framework/docs/ui-adoption-verification.md +111 -0
- package/.framework/docs/ui-api-coverage.md +84 -0
- package/.framework/docs/v2-compatibility-audit.md +228 -0
- package/.framework/functions/.gitkeep +1 -0
- package/.framework/functions/_shared/agent-runner.ts +1097 -0
- package/.framework/functions/_shared/app-manifest.ts +184 -0
- package/.framework/functions/_shared/audit.ts +150 -0
- package/.framework/functions/_shared/db.ts +174 -0
- package/.framework/functions/_shared/index.ts +382 -0
- package/.framework/functions/_shared/middleware.ts +490 -0
- package/.framework/functions/_shared/permissions.ts +1325 -0
- package/.framework/functions/_shared/pipeline-runner.ts +731 -0
- package/.framework/functions/_shared/principal.ts +760 -0
- package/.framework/functions/_shared/schema-utils.ts +967 -0
- package/.framework/functions/_shared/testing.ts +258 -0
- package/.framework/functions/_shared/trigger-engine.ts +425 -0
- package/.framework/functions/_shared/webhook-registration.ts +168 -0
- package/.framework/functions/_shared/webhook-registry.ts +129 -0
- package/.framework/functions/account-nodes.ts +111 -0
- package/.framework/functions/admin-data.ts +606 -0
- package/.framework/functions/ai-agents.ts +323 -0
- package/.framework/functions/api-keys.ts +376 -0
- package/.framework/functions/apps.ts +483 -0
- package/.framework/functions/auth.ts +196 -0
- package/.framework/functions/debug-auth.ts +107 -0
- package/.framework/functions/embeddings.ts +556 -0
- package/.framework/functions/integration-routes.ts +523 -0
- package/.framework/functions/integrations.ts +319 -0
- package/.framework/functions/item-progress.ts +272 -0
- package/.framework/functions/logs.ts +438 -0
- package/.framework/functions/observability.ts +275 -0
- package/.framework/functions/pipeline-executions.ts +494 -0
- package/.framework/functions/pipelines.ts +485 -0
- package/.framework/functions/prompt-configs.ts +339 -0
- package/.framework/functions/roles.ts +387 -0
- package/.framework/functions/system-cron.ts +742 -0
- package/.framework/functions/system.ts +323 -0
- package/.framework/functions/tests.ts +119 -0
- package/.framework/functions/timers.ts +357 -0
- package/.framework/functions/triggers.ts +563 -0
- package/.framework/functions/types.ts +604 -0
- package/.framework/migrations/000_foundation.sql +1256 -0
- package/.framework/migrations/001_seed.sql +92 -0
- package/.framework/migrations/002_seed_constraints.sql +13 -0
- package/.framework/migrations/003_auth_user_trigger.sql +59 -0
- package/.framework/src/App.tsx +126 -0
- package/.framework/src/apps/admin/index.tsx +173 -0
- package/.framework/src/components/AppWrapper.tsx +56 -0
- package/.framework/src/components/CustomAppLoader.tsx +116 -0
- package/.framework/src/components/admin/AdminListPage.tsx +151 -0
- package/.framework/src/components/admin/AdminSidebar.tsx +166 -0
- package/.framework/src/components/admin/AdminStatsCard.tsx +62 -0
- package/.framework/src/components/admin/SortableTableHeader.tsx +42 -0
- package/.framework/src/components/app-shell/GenericAppShell.tsx +181 -0
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +200 -0
- package/.framework/src/components/app-shell/GenericListPage.tsx +116 -0
- package/.framework/src/components/app-sidebar.tsx +228 -0
- package/.framework/src/components/auth/ProtectedRoute.tsx +88 -0
- package/.framework/src/components/layout/AppShell.tsx +91 -0
- package/.framework/src/components/layout/Header.tsx +88 -0
- package/.framework/src/components/layout/Layout.tsx +95 -0
- package/.framework/src/components/layout/Sidebar.tsx +329 -0
- package/.framework/src/components/runtime/DataDetailHeader.tsx +77 -0
- package/.framework/src/components/runtime/DataDetailPage.tsx +171 -0
- package/.framework/src/components/runtime/DataFilters.tsx +91 -0
- package/.framework/src/components/runtime/DataHeader.tsx +68 -0
- package/.framework/src/components/runtime/DataListPage.tsx +124 -0
- package/.framework/src/components/runtime/DataStats.tsx +70 -0
- package/.framework/src/components/runtime/DataTable.tsx +174 -0
- package/.framework/src/components/runtime/SchemaDetailForm.tsx +134 -0
- package/.framework/src/components/runtime/index.ts +18 -0
- package/.framework/src/components/search-form.tsx +29 -0
- package/.framework/src/components/shared/AgentView.tsx +213 -0
- package/.framework/src/components/shared/FieldRenderer.tsx +478 -0
- package/.framework/src/components/shared/SchemaFields.tsx +226 -0
- package/.framework/src/components/ui/DataTable.tsx +343 -0
- package/.framework/src/components/ui/Form.tsx +281 -0
- package/.framework/src/components/ui/ItemCard.tsx +296 -0
- package/.framework/src/components/ui/ItemListView.tsx +308 -0
- package/.framework/src/components/ui/LoadingSpinner.tsx +52 -0
- package/.framework/src/components/ui/Modal.tsx +61 -0
- package/.framework/src/components/ui/RichTextEditor.tsx +210 -0
- package/.framework/src/components/ui/accordion.tsx +82 -0
- package/.framework/src/components/ui/alert-dialog.tsx +197 -0
- package/.framework/src/components/ui/alert.tsx +76 -0
- package/.framework/src/components/ui/aspect-ratio.tsx +11 -0
- package/.framework/src/components/ui/avatar.tsx +110 -0
- package/.framework/src/components/ui/badge.tsx +49 -0
- package/.framework/src/components/ui/breadcrumb.tsx +122 -0
- package/.framework/src/components/ui/button-group.tsx +83 -0
- package/.framework/src/components/ui/button.tsx +65 -0
- package/.framework/src/components/ui/calendar.tsx +222 -0
- package/.framework/src/components/ui/card.tsx +100 -0
- package/.framework/src/components/ui/carousel.tsx +240 -0
- package/.framework/src/components/ui/chart.tsx +373 -0
- package/.framework/src/components/ui/checkbox.tsx +31 -0
- package/.framework/src/components/ui/collapsible.tsx +33 -0
- package/.framework/src/components/ui/combobox.tsx +299 -0
- package/.framework/src/components/ui/command.tsx +193 -0
- package/.framework/src/components/ui/context-menu.tsx +261 -0
- package/.framework/src/components/ui/dialog.tsx +165 -0
- package/.framework/src/components/ui/direction.tsx +22 -0
- package/.framework/src/components/ui/drawer.tsx +132 -0
- package/.framework/src/components/ui/dropdown-menu.tsx +269 -0
- package/.framework/src/components/ui/empty.tsx +104 -0
- package/.framework/src/components/ui/field.tsx +238 -0
- package/.framework/src/components/ui/hover-card.tsx +42 -0
- package/.framework/src/components/ui/input-group.tsx +153 -0
- package/.framework/src/components/ui/input-otp.tsx +87 -0
- package/.framework/src/components/ui/input.tsx +19 -0
- package/.framework/src/components/ui/item.tsx +196 -0
- package/.framework/src/components/ui/kbd.tsx +26 -0
- package/.framework/src/components/ui/label.tsx +22 -0
- package/.framework/src/components/ui/menubar.tsx +277 -0
- package/.framework/src/components/ui/native-select.tsx +61 -0
- package/.framework/src/components/ui/navigation-menu.tsx +164 -0
- package/.framework/src/components/ui/pagination.tsx +129 -0
- package/.framework/src/components/ui/popover.tsx +87 -0
- package/.framework/src/components/ui/progress.tsx +31 -0
- package/.framework/src/components/ui/radio-group.tsx +42 -0
- package/.framework/src/components/ui/resizable.tsx +50 -0
- package/.framework/src/components/ui/scroll-area.tsx +53 -0
- package/.framework/src/components/ui/select.tsx +195 -0
- package/.framework/src/components/ui/separator.tsx +26 -0
- package/.framework/src/components/ui/sheet.tsx +145 -0
- package/.framework/src/components/ui/sidebar.tsx +706 -0
- package/.framework/src/components/ui/skeleton.tsx +13 -0
- package/.framework/src/components/ui/slider.tsx +59 -0
- package/.framework/src/components/ui/sonner.tsx +47 -0
- package/.framework/src/components/ui/spinner.tsx +10 -0
- package/.framework/src/components/ui/switch.tsx +33 -0
- package/.framework/src/components/ui/table-primitives.tsx +141 -0
- package/.framework/src/components/ui/table.tsx +114 -0
- package/.framework/src/components/ui/tabs.tsx +90 -0
- package/.framework/src/components/ui/textarea.tsx +18 -0
- package/.framework/src/components/ui/toggle-group.tsx +89 -0
- package/.framework/src/components/ui/toggle.tsx +45 -0
- package/.framework/src/components/ui/tooltip.tsx +57 -0
- package/.framework/src/contexts/AppContext.tsx +133 -0
- package/.framework/src/contexts/AuthContext.tsx +371 -0
- package/.framework/src/hooks/use-mobile.ts +19 -0
- package/.framework/src/hooks/useApi.ts +526 -0
- package/.framework/src/hooks/useApps.ts +114 -0
- package/.framework/src/hooks/useEntityList.ts +190 -0
- package/.framework/src/hooks/useEntityRecord.ts +308 -0
- package/.framework/src/hooks/useForm.ts +307 -0
- package/.framework/src/hooks/useListSchema.ts +264 -0
- package/.framework/src/hooks/useSchemaRecord.ts +223 -0
- package/.framework/src/index.css +128 -0
- package/.framework/src/lib/api.ts +156 -0
- package/.framework/src/lib/supabase.ts +94 -0
- package/.framework/src/lib/utils.ts +317 -0
- package/.framework/src/main.tsx +27 -0
- package/.framework/src/pages/DashboardPage.tsx +181 -0
- package/.framework/src/pages/NotFoundPage.tsx +39 -0
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +161 -0
- package/.framework/src/pages/admin/AIAgentsPage.tsx +318 -0
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +199 -0
- package/.framework/src/pages/admin/APIKeysPage.tsx +303 -0
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +523 -0
- package/.framework/src/pages/admin/AppDetailPage.tsx +493 -0
- package/.framework/src/pages/admin/AppsPage.tsx +355 -0
- package/.framework/src/pages/admin/DesignedPage.tsx +491 -0
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +534 -0
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +424 -0
- package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +176 -0
- package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +109 -0
- package/.framework/src/pages/admin/IntegratedDashboard.tsx +402 -0
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +187 -0
- package/.framework/src/pages/admin/IntegrationsPage.tsx +301 -0
- package/.framework/src/pages/admin/LogsPage.tsx +283 -0
- package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +85 -0
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +470 -0
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +183 -0
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +279 -0
- package/.framework/src/pages/admin/PipelinesPage.tsx +390 -0
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +299 -0
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +292 -0
- package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +434 -0
- package/.framework/src/pages/admin/RoleDetailPage.tsx +273 -0
- package/.framework/src/pages/admin/RolesPage.tsx +292 -0
- package/.framework/src/pages/admin/SelectTestPage.tsx +61 -0
- package/.framework/src/pages/admin/ShadcnTestPage.tsx +588 -0
- package/.framework/src/pages/admin/SimpleDashboard.tsx +387 -0
- package/.framework/src/pages/admin/TestRunDetailPage.tsx +172 -0
- package/.framework/src/pages/admin/TestingDashboard.tsx +257 -0
- package/.framework/src/pages/admin/TimerDetailPage.tsx +151 -0
- package/.framework/src/pages/admin/TimersPage.tsx +376 -0
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +149 -0
- package/.framework/src/pages/admin/TriggersPage.tsx +381 -0
- package/.framework/src/pages/admin/TypeDetailPage.tsx +694 -0
- package/.framework/src/pages/admin/TypesPage.tsx +295 -0
- package/.framework/src/pages/auth/LoginPage.tsx +188 -0
- package/.framework/src/pages/auth/RegisterPage.tsx +163 -0
- package/.framework/src/pages/spine-framework/APIPage.tsx +17 -0
- package/.framework/src/pages/spine-framework/CLIPage.tsx +25 -0
- package/.framework/src/types/auth.ts +125 -0
- package/.framework/src/types/types.ts +407 -0
- package/STRUCTURE.md +150 -0
- package/config/components.json +25 -0
- package/config/deno.lock +108 -0
- package/config/package-lock.json +17183 -0
- package/config/postcss.config.cjs +10 -0
- package/config/tailwind.config.cjs +78 -0
- package/config/tsconfig.build.json +32 -0
- package/config/tsconfig.cli.json +18 -0
- package/config/tsconfig.json +41 -0
- package/config/tsconfig.node.json +17 -0
- package/config/tsconfig.node.tsbuildinfo +1 -0
- package/config/tsconfig.tsbuildinfo +1 -0
- package/config/typedoc.json +16 -0
- package/config/vite.config.d.ts +2 -0
- package/config/vite.config.ts +72 -0
- package/dist/cli/commands/agents.d.ts +39 -0
- package/dist/cli/commands/agents.d.ts.map +1 -0
- package/dist/cli/commands/auth.d.ts +36 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/create-app.d.ts +23 -0
- package/dist/cli/commands/create-app.d.ts.map +1 -0
- package/dist/cli/commands/dev.d.ts +39 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/doctor.d.ts +42 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/generate.d.ts +36 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/init.d.ts +30 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/install-app.d.ts +30 -0
- package/dist/cli/commands/install-app.d.ts.map +1 -0
- package/dist/cli/commands/items.d.ts +45 -0
- package/dist/cli/commands/items.d.ts.map +1 -0
- package/dist/cli/commands/migrations.d.ts +41 -0
- package/dist/cli/commands/migrations.d.ts.map +1 -0
- package/dist/cli/commands/pipelines.d.ts +40 -0
- package/dist/cli/commands/pipelines.d.ts.map +1 -0
- package/dist/cli/commands/status.d.ts +23 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/system.d.ts +29 -0
- package/dist/cli/commands/system.d.ts.map +1 -0
- package/dist/cli/commands/test.d.ts +46 -0
- package/dist/cli/commands/test.d.ts.map +1 -0
- package/dist/cli/commands/uninstall-app.d.ts +23 -0
- package/dist/cli/commands/uninstall-app.d.ts.map +1 -0
- package/dist/cli/context.d.ts +88 -0
- package/dist/cli/context.d.ts.map +1 -0
- package/dist/cli/env-loader.d.ts +14 -0
- package/dist/cli/env-loader.d.ts.map +1 -0
- package/dist/cli/index.d.ts +41 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/functions/_shared/agent-runner.d.ts +156 -0
- package/dist/functions/_shared/agent-runner.d.ts.map +1 -0
- package/dist/functions/_shared/app-manifest.d.ts +68 -0
- package/dist/functions/_shared/app-manifest.d.ts.map +1 -0
- package/dist/functions/_shared/audit.d.ts +91 -0
- package/dist/functions/_shared/audit.d.ts.map +1 -0
- package/dist/functions/_shared/db.d.ts +125 -0
- package/dist/functions/_shared/db.d.ts.map +1 -0
- package/dist/functions/_shared/index.d.ts +298 -0
- package/dist/functions/_shared/index.d.ts.map +1 -0
- package/dist/functions/_shared/middleware.d.ts +315 -0
- package/dist/functions/_shared/middleware.d.ts.map +1 -0
- package/dist/functions/_shared/permissions.d.ts +626 -0
- package/dist/functions/_shared/permissions.d.ts.map +1 -0
- package/dist/functions/_shared/pipeline-runner.d.ts +124 -0
- package/dist/functions/_shared/pipeline-runner.d.ts.map +1 -0
- package/dist/functions/_shared/principal.d.ts +284 -0
- package/dist/functions/_shared/principal.d.ts.map +1 -0
- package/dist/functions/_shared/schema-utils.d.ts +181 -0
- package/dist/functions/_shared/schema-utils.d.ts.map +1 -0
- package/dist/functions/_shared/testing.d.ts +172 -0
- package/dist/functions/_shared/testing.d.ts.map +1 -0
- package/dist/functions/_shared/trigger-engine.d.ts +140 -0
- package/dist/functions/_shared/trigger-engine.d.ts.map +1 -0
- package/dist/functions/_shared/webhook-registration.d.ts +81 -0
- package/dist/functions/_shared/webhook-registration.d.ts.map +1 -0
- package/dist/functions/_shared/webhook-registry.d.ts +57 -0
- package/dist/functions/_shared/webhook-registry.d.ts.map +1 -0
- package/dist/functions/account-nodes.d.ts +48 -0
- package/dist/functions/account-nodes.d.ts.map +1 -0
- package/dist/functions/admin-data.d.ts +178 -0
- package/dist/functions/admin-data.d.ts.map +1 -0
- package/dist/functions/ai-agents.d.ts +125 -0
- package/dist/functions/ai-agents.d.ts.map +1 -0
- package/dist/functions/api-keys.d.ts +140 -0
- package/dist/functions/api-keys.d.ts.map +1 -0
- package/dist/functions/apps.d.ts +163 -0
- package/dist/functions/apps.d.ts.map +1 -0
- package/dist/functions/auth.d.ts +74 -0
- package/dist/functions/auth.d.ts.map +1 -0
- package/dist/functions/debug-auth.d.ts +33 -0
- package/dist/functions/debug-auth.d.ts.map +1 -0
- package/dist/functions/embeddings.d.ts +205 -0
- package/dist/functions/embeddings.d.ts.map +1 -0
- package/dist/functions/integration-routes.d.ts +45 -0
- package/dist/functions/integration-routes.d.ts.map +1 -0
- package/dist/functions/integrations.d.ts +124 -0
- package/dist/functions/integrations.d.ts.map +1 -0
- package/dist/functions/item-progress.d.ts +41 -0
- package/dist/functions/item-progress.d.ts.map +1 -0
- package/dist/functions/logs.d.ts +162 -0
- package/dist/functions/logs.d.ts.map +1 -0
- package/dist/functions/observability.d.ts +123 -0
- package/dist/functions/observability.d.ts.map +1 -0
- package/dist/functions/pipeline-executions.d.ts +190 -0
- package/dist/functions/pipeline-executions.d.ts.map +1 -0
- package/dist/functions/pipelines.d.ts +171 -0
- package/dist/functions/pipelines.d.ts.map +1 -0
- package/dist/functions/prompt-configs.d.ts +125 -0
- package/dist/functions/prompt-configs.d.ts.map +1 -0
- package/dist/functions/roles.d.ts +118 -0
- package/dist/functions/roles.d.ts.map +1 -0
- package/dist/functions/system-cron.d.ts +65 -0
- package/dist/functions/system-cron.d.ts.map +1 -0
- package/dist/functions/system.d.ts +29 -0
- package/dist/functions/system.d.ts.map +1 -0
- package/dist/functions/tests.d.ts +28 -0
- package/dist/functions/tests.d.ts.map +1 -0
- package/dist/functions/timers.d.ts +139 -0
- package/dist/functions/timers.d.ts.map +1 -0
- package/dist/functions/triggers.d.ts +203 -0
- package/dist/functions/triggers.d.ts.map +1 -0
- package/dist/functions/types.d.ts +151 -0
- package/dist/functions/types.d.ts.map +1 -0
- package/dist/src/types/types.d.ts +364 -0
- package/dist/src/types/types.d.ts.map +1 -0
- package/package.json +192 -0
- package/scripts/app-install-cli.ts +286 -0
- package/scripts/assemble-frontend.sh +79 -0
- package/scripts/assemble-functions.sh +62 -0
- package/scripts/assemble.sh +35 -0
- package/scripts/boundary-check.sh +106 -0
- package/scripts/build-manifest.sh +80 -0
- package/scripts/check-core-integrity.sh +82 -0
- package/scripts/ingest-chunks.cjs +202 -0
- package/scripts/kb-chunk-parser.cjs +312 -0
- package/scripts/kb-chunk-parser.ts +330 -0
- package/scripts/load-test-app-install.ts +484 -0
- package/scripts/netlify-dev-wrapper.sh +22 -0
- package/scripts/verify-integrity.sh +69 -0
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module types
|
|
3
|
+
* @audience core-contributor
|
|
4
|
+
* @layer api-handler
|
|
5
|
+
* @stability stable
|
|
6
|
+
*
|
|
7
|
+
* CRUD API for the `types` table. Types are the schema configuration objects
|
|
8
|
+
* that define `design_schema`, `validation_schema`, field definitions, and
|
|
9
|
+
* view configurations for runtime entities (`items`, `people`, `accounts`).
|
|
10
|
+
*
|
|
11
|
+
* **Routed by:** `GET/POST/PATCH/DELETE /.netlify/functions/types`
|
|
12
|
+
*
|
|
13
|
+
* **Authorization model:**
|
|
14
|
+
* - All reads use `ctx.db` (RLS-scoped) and are sanitized via `sanitizeRecordData`.
|
|
15
|
+
* - All writes (`create`, `update`, `remove`) require `isSystemAdmin` AND the
|
|
16
|
+
* caller's account must be a master tenant (`accounts.parent_id IS NULL`).
|
|
17
|
+
* - Unauthenticated `list` calls receive minimal fields only (id, slug, name, kind).
|
|
18
|
+
*
|
|
19
|
+
* **Design schema auto-sync:** `update` regenerates `validation_schema` automatically
|
|
20
|
+
* via `generateValidationSchema` whenever `design_schema` changes.
|
|
21
|
+
*
|
|
22
|
+
* INVARIANT: type slugs must be unique per (kind, app_id) combination.
|
|
23
|
+
* INVARIANT: `ownership` of system types cannot be changed (to prevent constraint violations).
|
|
24
|
+
* INVARIANT: `app_id = null` for system types; must be provided for app/tenant types.
|
|
25
|
+
*
|
|
26
|
+
* @seeAlso middleware.ts (createHandler, CoreContext)
|
|
27
|
+
* @seeAlso permissions.ts (PermissionEngine, sanitizeRecordData, validateUpdatePermissions)
|
|
28
|
+
* @seeAlso schema-utils.ts (generateValidationSchema — auto-called on design_schema updates)
|
|
29
|
+
* @seeAlso audit.ts (emitLog for type.created / type.updated / type.deleted)
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { createHandler } from './_shared/middleware'
|
|
33
|
+
import { adminDb, joins } from './_shared/db'
|
|
34
|
+
import { emitLog } from './_shared/audit'
|
|
35
|
+
import { PermissionEngine, sanitizeRecordData } from './_shared/permissions'
|
|
36
|
+
import { generateValidationSchema } from './_shared/schema-utils'
|
|
37
|
+
|
|
38
|
+
const permissions = PermissionEngine as any
|
|
39
|
+
|
|
40
|
+
// ─── HANDLERS ─────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
// ─── CHUNK_START: TYPES_LIST ──────────────────────────────────────────────
|
|
43
|
+
/**
|
|
44
|
+
* @chunk-id TYPES_LIST_1_0_0
|
|
45
|
+
* @version 1.0.0
|
|
46
|
+
* @hash 0cfcf45a5a70d17002599b77e1c45bf7d3de456bc474e68a89ff44069d711b91
|
|
47
|
+
* @macro Types List Handler
|
|
48
|
+
* @micro Lists types with filtering, authentication-based response, and sanitization
|
|
49
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
50
|
+
* @inputs body: any — Request body (unused for GET)
|
|
51
|
+
* @outputs Array of type records with app join, sanitized and schema-aware
|
|
52
|
+
* @depends-on [createHandler, joins, sanitizeRecordData]
|
|
53
|
+
* @depended-by [Netlify function routing]
|
|
54
|
+
* @side-effects [DB queries, permission sanitization, schema preservation]
|
|
55
|
+
* @tags types, list, crud, filtering, authentication
|
|
56
|
+
*/
|
|
57
|
+
export const list = createHandler(async (ctx, body) => {
|
|
58
|
+
const { kind, app_id, ownership, limit = '50', offset = '0', include_schema } = ctx.query || {}
|
|
59
|
+
|
|
60
|
+
// RLS automatically filters to accessible accounts
|
|
61
|
+
let query = ctx.db
|
|
62
|
+
.from('types')
|
|
63
|
+
.select(`*, ${joins.app}`)
|
|
64
|
+
.eq('is_active', true)
|
|
65
|
+
.order('kind')
|
|
66
|
+
.order('name')
|
|
67
|
+
|
|
68
|
+
if (kind) {
|
|
69
|
+
query = query.eq('kind', kind)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (app_id) {
|
|
73
|
+
query = query.eq('app_id', app_id)
|
|
74
|
+
} else if (app_id === 'null') {
|
|
75
|
+
query = query.is('app_id', null)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (ownership) {
|
|
79
|
+
query = query.eq('ownership', ownership)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const { data, error: err } = await query
|
|
83
|
+
.range(parseInt(offset), parseInt(offset) + parseInt(limit) - 1)
|
|
84
|
+
|
|
85
|
+
if (err) throw err
|
|
86
|
+
|
|
87
|
+
// For authenticated users, always include design_schema for schema-driven UI
|
|
88
|
+
// System admin sees everything, others get sanitized data with design_schema preserved
|
|
89
|
+
// When include_schema=true, preserve full design_schema for all accessible types
|
|
90
|
+
const sanitized = []
|
|
91
|
+
for (const type of data || []) {
|
|
92
|
+
if (ctx.principal) {
|
|
93
|
+
// Authenticated user - get sanitized data but preserve design_schema
|
|
94
|
+
const sanitizedType = await sanitizeRecordData(ctx, type, 'type')
|
|
95
|
+
// Ensure design_schema is preserved for schema-driven UI
|
|
96
|
+
if (type.design_schema && !sanitizedType.design_schema) {
|
|
97
|
+
sanitizedType.design_schema = type.design_schema
|
|
98
|
+
}
|
|
99
|
+
// When include_schema=true, also preserve validation_schema
|
|
100
|
+
if (include_schema === 'true' && type.validation_schema) {
|
|
101
|
+
sanitizedType.validation_schema = type.validation_schema
|
|
102
|
+
}
|
|
103
|
+
sanitized.push(sanitizedType)
|
|
104
|
+
} else {
|
|
105
|
+
// Unauthenticated user - return minimal data
|
|
106
|
+
const minimal: any = {
|
|
107
|
+
id: type.id,
|
|
108
|
+
slug: type.slug,
|
|
109
|
+
name: type.name,
|
|
110
|
+
kind: type.kind
|
|
111
|
+
}
|
|
112
|
+
// When include_schema=true, unauthenticated users also get schema
|
|
113
|
+
if (include_schema === 'true' && type.design_schema) {
|
|
114
|
+
minimal.design_schema = type.design_schema
|
|
115
|
+
}
|
|
116
|
+
sanitized.push(minimal)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return sanitized
|
|
121
|
+
})
|
|
122
|
+
// ─── CHUNK_END: TYPES_LIST ────────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
// ─── CHUNK_START: TYPES_GET ──────────────────────────────────────────────
|
|
125
|
+
/**
|
|
126
|
+
* @chunk-id TYPES_GET_1_0_0
|
|
127
|
+
* @version 1.0.0
|
|
128
|
+
* @hash 435ff5a93d05f74e0299b515d32f50ceb5c526b517a6972ef65c3bdaa75c218f
|
|
129
|
+
* @macro Type Get Handler
|
|
130
|
+
* @micro Returns single type record with joins and sanitization
|
|
131
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
132
|
+
* @inputs body: any — Request body (unused for GET)
|
|
133
|
+
* @outputs Sanitized type record with app join
|
|
134
|
+
* @depends-on [createHandler, joins, sanitizeRecordData]
|
|
135
|
+
* @depended-by [Netlify function routing]
|
|
136
|
+
* @side-effects [DB single row query, permission sanitization]
|
|
137
|
+
* @tags types, get, crud, single-record
|
|
138
|
+
*/
|
|
139
|
+
export const get = createHandler(async (ctx, body) => {
|
|
140
|
+
const { id } = ctx.query || {}
|
|
141
|
+
|
|
142
|
+
if (!id) {
|
|
143
|
+
throw new Error('Type ID is required')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const { data, error: err } = await ctx.db
|
|
147
|
+
.from('types')
|
|
148
|
+
.select(`*, ${joins.app}`)
|
|
149
|
+
.eq('id', id)
|
|
150
|
+
.eq('is_active', true)
|
|
151
|
+
.single()
|
|
152
|
+
|
|
153
|
+
if (err) throw err
|
|
154
|
+
|
|
155
|
+
// Sanitize based on role permissions
|
|
156
|
+
return await sanitizeRecordData(ctx, data, 'type')
|
|
157
|
+
})
|
|
158
|
+
// ─── CHUNK_END: TYPES_GET ────────────────────────────────────────────────
|
|
159
|
+
|
|
160
|
+
// ─── CHUNK_START: TYPES_GET_BY_SLUG ──────────────────────────────────────────────
|
|
161
|
+
/**
|
|
162
|
+
* @chunk-id TYPES_GET_BY_SLUG_1_0_0
|
|
163
|
+
* @version 1.0.0
|
|
164
|
+
* @hash 6585200f3ecdef92e2912849afd5d72a3968fe13b464cdf43f33a888edda4163
|
|
165
|
+
* @macro Type Get By Slug Handler
|
|
166
|
+
* @micro Returns single type record by slug with joins and sanitization
|
|
167
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
168
|
+
* @inputs body: any — Request body (unused for GET)
|
|
169
|
+
* @outputs Sanitized type record with app join
|
|
170
|
+
* @depends-on [createHandler, joins, sanitizeRecordData]
|
|
171
|
+
* @depended-by [Netlify function routing]
|
|
172
|
+
* @side-effects [DB single row query, permission sanitization]
|
|
173
|
+
* @tags types, get, crud, single-record, slug
|
|
174
|
+
*/
|
|
175
|
+
export const getBySlug = createHandler(async (ctx, body) => {
|
|
176
|
+
const { slug } = ctx.query || {}
|
|
177
|
+
|
|
178
|
+
if (!slug) {
|
|
179
|
+
throw new Error('Type slug is required')
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const { data, error: err } = await ctx.db
|
|
183
|
+
.from('types')
|
|
184
|
+
.select(`*, ${joins.app}`)
|
|
185
|
+
.eq('slug', slug)
|
|
186
|
+
.eq('is_active', true)
|
|
187
|
+
.single()
|
|
188
|
+
|
|
189
|
+
if (err) throw err
|
|
190
|
+
|
|
191
|
+
// Sanitize based on role permissions
|
|
192
|
+
return await sanitizeRecordData(ctx, data, 'type')
|
|
193
|
+
})
|
|
194
|
+
// ─── CHUNK_END: TYPES_GET_BY_SLUG ────────────────────────────────────────────────
|
|
195
|
+
|
|
196
|
+
// ─── CHUNK_START: TYPES_GET_SCHEMA ──────────────────────────────────────────────
|
|
197
|
+
/**
|
|
198
|
+
* @chunk-id TYPES_GET_SCHEMA_1_0_0
|
|
199
|
+
* @version 1.0.0
|
|
200
|
+
* @hash 2cff73f363bb53c145d64b5b2fc788de592a94321000425f6b2ab82489c9a1e4
|
|
201
|
+
* @macro Type Schema Handler
|
|
202
|
+
* @micro Returns design_schema for a type via RPC
|
|
203
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
204
|
+
* @inputs body: any — Request body (unused for GET)
|
|
205
|
+
* @outputs {design_schema: object} — Type design schema
|
|
206
|
+
* @depends-on [createHandler]
|
|
207
|
+
* @depended-by [Netlify function routing]
|
|
208
|
+
* @side-effects [DB RPC call, RLS enforcement]
|
|
209
|
+
* @tags types, schema, rpc, design-schema
|
|
210
|
+
*/
|
|
211
|
+
export const getSchema = createHandler(async (ctx, body) => {
|
|
212
|
+
const { kind, slug, app_id } = ctx.query || {}
|
|
213
|
+
|
|
214
|
+
if (!kind || !slug) {
|
|
215
|
+
throw new Error('kind and slug are required')
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const { data, error: err } = await ctx.db
|
|
219
|
+
.rpc('get_type_schema', {
|
|
220
|
+
kind,
|
|
221
|
+
slug,
|
|
222
|
+
app_id: app_id || null
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
if (err) throw err
|
|
226
|
+
|
|
227
|
+
return { design_schema: data }
|
|
228
|
+
})
|
|
229
|
+
// ─── CHUNK_END: TYPES_GET_SCHEMA ────────────────────────────────────────────────
|
|
230
|
+
|
|
231
|
+
// ─── CHUNK_START: TYPES_CREATE ──────────────────────────────────────────────
|
|
232
|
+
/**
|
|
233
|
+
* @chunk-id TYPES_CREATE_1_0_0
|
|
234
|
+
* @version 1.0.0
|
|
235
|
+
* @hash c226d40910e332c0c89bfbb0d09f2b6a04b45a0a104c70d485ebd08c2c07cbe0
|
|
236
|
+
* @macro Type Create Handler
|
|
237
|
+
* @micro Creates type with system admin validation, schema generation, and audit logging
|
|
238
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
239
|
+
* @inputs body: object — Type data including kind, slug, name, and optional fields
|
|
240
|
+
* @outputs Inserted type record
|
|
241
|
+
* @depends-on [createHandler, adminDb, permissions, generateValidationSchema, emitLog]
|
|
242
|
+
* @depended-by [Netlify function routing]
|
|
243
|
+
* @side-effects [DB insert, master tenant validation, schema generation, audit logging]
|
|
244
|
+
* @tags types, create, crud, system-admin, schema-generation, audit
|
|
245
|
+
*/
|
|
246
|
+
export const create = createHandler(async (ctx, body) => {
|
|
247
|
+
const { app_id, kind, slug, name, description, icon, color, design_schema: bodySchema = {}, ownership } = body
|
|
248
|
+
|
|
249
|
+
if (!kind || !slug || !name) {
|
|
250
|
+
throw new Error('kind, slug, and name are required')
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (!ctx.principal || ctx.principal.id === 'anonymous' || !ctx.accountId) {
|
|
254
|
+
throw new Error('User context (person and account) required')
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Config mutations (types) are only allowed in master tenant by system admins
|
|
258
|
+
if (!permissions.isSystemAdmin(ctx)) {
|
|
259
|
+
throw new Error('Only system administrators can create type configurations')
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Verify the current account is a master tenant (parent_id IS NULL)
|
|
263
|
+
// Use adminDb for this check since we're verifying account structure
|
|
264
|
+
const { data: accountData } = await adminDb
|
|
265
|
+
.from('accounts')
|
|
266
|
+
.select('parent_id')
|
|
267
|
+
.eq('id', ctx.accountId!)
|
|
268
|
+
.single()
|
|
269
|
+
|
|
270
|
+
if (!accountData || accountData.parent_id !== null) {
|
|
271
|
+
throw new Error('Type configurations can only be created in master tenant accounts')
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Check if slug is unique within app/kind
|
|
275
|
+
let query = adminDb
|
|
276
|
+
.from('types')
|
|
277
|
+
.select('id')
|
|
278
|
+
.eq('kind', kind)
|
|
279
|
+
.eq('slug', slug)
|
|
280
|
+
|
|
281
|
+
if (app_id) {
|
|
282
|
+
query = query.eq('app_id', app_id)
|
|
283
|
+
} else {
|
|
284
|
+
query = query.is('app_id', null)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const { data: existing } = await query.single()
|
|
288
|
+
|
|
289
|
+
if (existing) {
|
|
290
|
+
throw new Error('Type slug already exists for this kind and app')
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Basic schema validation
|
|
294
|
+
if (bodySchema && typeof bodySchema === 'object') {
|
|
295
|
+
if (bodySchema.fields && (typeof bodySchema.fields !== 'object' || bodySchema.fields === null)) {
|
|
296
|
+
throw new Error('Schema fields must be an object')
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// For non-system ownership, we need an app_id to satisfy the constraint
|
|
301
|
+
let finalAppId = app_id
|
|
302
|
+
let finalOwnership = ownership
|
|
303
|
+
|
|
304
|
+
if (!app_id && ownership !== 'system') {
|
|
305
|
+
// Get the first available app for tenant/custom types
|
|
306
|
+
const { data: apps } = await adminDb
|
|
307
|
+
.from('apps')
|
|
308
|
+
.select('id')
|
|
309
|
+
.limit(1)
|
|
310
|
+
.single()
|
|
311
|
+
|
|
312
|
+
if (apps?.id) {
|
|
313
|
+
finalAppId = apps.id
|
|
314
|
+
finalOwnership = 'app' // Use 'app' ownership when associated with an app
|
|
315
|
+
} else {
|
|
316
|
+
throw new Error('Cannot create type without app_id for non-system ownership')
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Generate validation schema from design schema
|
|
321
|
+
const validationSchema = generateValidationSchema(bodySchema || {})
|
|
322
|
+
|
|
323
|
+
const { data, error: err } = await adminDb
|
|
324
|
+
.from('types')
|
|
325
|
+
.insert({
|
|
326
|
+
app_id: finalAppId,
|
|
327
|
+
kind,
|
|
328
|
+
slug,
|
|
329
|
+
name,
|
|
330
|
+
description,
|
|
331
|
+
icon,
|
|
332
|
+
color,
|
|
333
|
+
design_schema: bodySchema || {},
|
|
334
|
+
validation_schema: validationSchema,
|
|
335
|
+
ownership: finalOwnership || 'system',
|
|
336
|
+
is_active: true
|
|
337
|
+
})
|
|
338
|
+
.select()
|
|
339
|
+
.single()
|
|
340
|
+
|
|
341
|
+
if (err) throw err
|
|
342
|
+
|
|
343
|
+
await emitLog(ctx, 'type.created', { type: 'type', id: data.id }, { after: data })
|
|
344
|
+
|
|
345
|
+
return data
|
|
346
|
+
})
|
|
347
|
+
// ─── CHUNK_END: TYPES_CREATE ────────────────────────────────────────────────
|
|
348
|
+
|
|
349
|
+
// ─── CHUNK_START: TYPES_UPDATE ──────────────────────────────────────────────
|
|
350
|
+
/**
|
|
351
|
+
* @chunk-id TYPES_UPDATE_1_0_0
|
|
352
|
+
* @version 1.0.0
|
|
353
|
+
* @hash 7778ce63ecc3dd50fe50680c4f1199a59db4c4dae734cf7a2d58cc2de76b9a6a
|
|
354
|
+
* @macro Type Update Handler
|
|
355
|
+
* @micro Updates type with validation, schema regeneration, and audit logging
|
|
356
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
357
|
+
* @inputs body: object — Type updates including id and updatable fields
|
|
358
|
+
* @outputs Updated type record
|
|
359
|
+
* @depends-on [createHandler, adminDb, permissions, generateValidationSchema, emitLog]
|
|
360
|
+
* @depended-by [Netlify function routing]
|
|
361
|
+
* @side-effects [DB update, field validation, schema regeneration, audit logging]
|
|
362
|
+
* @tags types, update, crud, system-admin, schema-generation, audit
|
|
363
|
+
*/
|
|
364
|
+
export const update = createHandler(async (ctx, body) => {
|
|
365
|
+
const id = body?.id || ctx.query?.id
|
|
366
|
+
const { id: _bodyId, ...updates } = body || {}
|
|
367
|
+
|
|
368
|
+
if (!id) {
|
|
369
|
+
throw new Error('Type ID is required')
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (!ctx.principal || ctx.principal.id === 'anonymous' || !ctx.accountId) {
|
|
373
|
+
throw new Error('User context (person and account) required')
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Get current state for audit - RLS will filter to accessible types
|
|
377
|
+
const { data: current } = await adminDb
|
|
378
|
+
.from('types')
|
|
379
|
+
.select('*')
|
|
380
|
+
.eq('id', id)
|
|
381
|
+
.single()
|
|
382
|
+
|
|
383
|
+
if (!current) {
|
|
384
|
+
throw new Error('Type not found')
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Config mutations (types) are only allowed in master tenant by system admins
|
|
388
|
+
if (!permissions.isSystemAdmin(ctx)) {
|
|
389
|
+
throw new Error('Only system administrators can update type configurations')
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// System types can only be updated by system admins (already checked above)
|
|
393
|
+
// App types require app ownership verification
|
|
394
|
+
if (current.ownership === 'app' && current.app_id) {
|
|
395
|
+
// Verify user has access to this app
|
|
396
|
+
const { data: appAccess } = await adminDb
|
|
397
|
+
.from('apps')
|
|
398
|
+
.select('id')
|
|
399
|
+
.eq('id', current.app_id)
|
|
400
|
+
.eq('is_active', true)
|
|
401
|
+
.single()
|
|
402
|
+
|
|
403
|
+
if (!appAccess) {
|
|
404
|
+
throw new Error('App not found or inactive')
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Verify the current account is a master tenant (parent_id IS NULL)
|
|
409
|
+
// Use adminDb for this check since we're verifying account structure
|
|
410
|
+
const { data: accountData } = await adminDb
|
|
411
|
+
.from('accounts')
|
|
412
|
+
.select('parent_id')
|
|
413
|
+
.eq('id', ctx.accountId!)
|
|
414
|
+
.single()
|
|
415
|
+
|
|
416
|
+
if (!accountData || accountData.parent_id !== null) {
|
|
417
|
+
throw new Error('Type configurations can only be updated in master tenant accounts')
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Validate field-level permissions
|
|
421
|
+
const fieldValidation = await permissions.validateUpdatePermissions(
|
|
422
|
+
ctx,
|
|
423
|
+
updates,
|
|
424
|
+
current,
|
|
425
|
+
'type'
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
if (!fieldValidation.valid) {
|
|
429
|
+
throw new Error(fieldValidation.error)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Prevent ownership changes for system types to avoid constraint violations
|
|
433
|
+
if (current.ownership === 'system' && updates.ownership && updates.ownership !== 'system') {
|
|
434
|
+
// Remove ownership from updates to preserve system ownership
|
|
435
|
+
delete updates.ownership
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Handle app_id null conversion for system types
|
|
439
|
+
if (updates.app_id === '') {
|
|
440
|
+
updates.app_id = null
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Validate design_schema if being updated
|
|
444
|
+
if (updates.design_schema) {
|
|
445
|
+
if (typeof updates.design_schema !== 'object' || updates.design_schema === null) {
|
|
446
|
+
throw new Error('Design schema must be an object')
|
|
447
|
+
}
|
|
448
|
+
if (updates.design_schema.fields && (typeof updates.design_schema.fields !== 'object' || updates.design_schema.fields === null)) {
|
|
449
|
+
throw new Error('Design schema fields must be an object')
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Auto-generate validation_schema when design_schema changes
|
|
453
|
+
updates.validation_schema = generateValidationSchema(updates.design_schema)
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const { data, error: err } = await ctx.db
|
|
457
|
+
.from('types')
|
|
458
|
+
.update({
|
|
459
|
+
...updates,
|
|
460
|
+
updated_at: new Date().toISOString()
|
|
461
|
+
})
|
|
462
|
+
.eq('id', id)
|
|
463
|
+
.select()
|
|
464
|
+
.single()
|
|
465
|
+
|
|
466
|
+
if (err) throw err
|
|
467
|
+
|
|
468
|
+
await emitLog(ctx, 'type.updated', { type: 'type', id }, { before: current, after: data })
|
|
469
|
+
|
|
470
|
+
return data
|
|
471
|
+
})
|
|
472
|
+
// ─── CHUNK_END: TYPES_UPDATE ────────────────────────────────────────────────
|
|
473
|
+
|
|
474
|
+
// ─── CHUNK_START: TYPES_REMOVE ──────────────────────────────────────────────
|
|
475
|
+
/**
|
|
476
|
+
* @chunk-id TYPES_REMOVE_1_0_0
|
|
477
|
+
* @version 1.0.0
|
|
478
|
+
* @hash eef22a9e3f661c71c65ba5d69ce7e60c2c2bfccec986ad2309290217f5332455
|
|
479
|
+
* @macro Type Remove Handler
|
|
480
|
+
* @micro Soft-deletes type with system admin validation and audit logging
|
|
481
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
482
|
+
* @inputs body: object — Type id for deletion
|
|
483
|
+
* @outputs Updated type record with is_active: false
|
|
484
|
+
* @depends-on [createHandler, adminDb, permissions, emitLog]
|
|
485
|
+
* @depended-by [Netlify function routing]
|
|
486
|
+
* @side-effects [DB soft delete, master tenant validation, audit logging]
|
|
487
|
+
* @tags types, remove, soft-delete, system-admin, audit
|
|
488
|
+
*/
|
|
489
|
+
export const remove = createHandler(async (ctx, body) => {
|
|
490
|
+
const id = body?.id || ctx.query?.id
|
|
491
|
+
|
|
492
|
+
if (!id) {
|
|
493
|
+
throw new Error('Type ID is required')
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (!ctx.principal || ctx.principal.id === 'anonymous' || !ctx.accountId) {
|
|
497
|
+
throw new Error('User context (person and account) required')
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Get current state for audit - RLS will filter to accessible types
|
|
501
|
+
const { data: current } = await ctx.db
|
|
502
|
+
.from('types')
|
|
503
|
+
.select('*')
|
|
504
|
+
.eq('id', id)
|
|
505
|
+
.single()
|
|
506
|
+
|
|
507
|
+
if (!current) {
|
|
508
|
+
throw new Error('Type not found')
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Config mutations (types) are only allowed in master tenant by system admins
|
|
512
|
+
if (!permissions.isSystemAdmin(ctx)) {
|
|
513
|
+
throw new Error('Only system administrators can delete type configurations')
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// System types can only be deleted by system admins (already checked above)
|
|
517
|
+
// App types require app ownership verification
|
|
518
|
+
if (current.ownership === 'app' && current.app_id) {
|
|
519
|
+
// Verify user has access to this app
|
|
520
|
+
const { data: appAccess } = await ctx.db
|
|
521
|
+
.from('apps')
|
|
522
|
+
.select('id')
|
|
523
|
+
.eq('id', current.app_id)
|
|
524
|
+
.eq('is_active', true)
|
|
525
|
+
.single()
|
|
526
|
+
|
|
527
|
+
if (!appAccess) {
|
|
528
|
+
throw new Error('App not found or inactive')
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Verify the current account is a master tenant (parent_id IS NULL)
|
|
533
|
+
// Use adminDb for this check since we're verifying account structure
|
|
534
|
+
const { data: accountData } = await adminDb
|
|
535
|
+
.from('accounts')
|
|
536
|
+
.select('parent_id')
|
|
537
|
+
.eq('id', ctx.accountId!)
|
|
538
|
+
.single()
|
|
539
|
+
|
|
540
|
+
if (!accountData || accountData.parent_id !== null) {
|
|
541
|
+
throw new Error('Type configurations can only be deleted in master tenant accounts')
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const { data, error: err } = await ctx.db
|
|
545
|
+
.from('types')
|
|
546
|
+
.update({
|
|
547
|
+
is_active: false,
|
|
548
|
+
updated_at: new Date().toISOString()
|
|
549
|
+
})
|
|
550
|
+
.eq('id', id)
|
|
551
|
+
.select()
|
|
552
|
+
.single()
|
|
553
|
+
|
|
554
|
+
if (err) throw err
|
|
555
|
+
|
|
556
|
+
await emitLog(ctx, 'type.deleted', { type: 'type', id }, { before: current })
|
|
557
|
+
|
|
558
|
+
return data
|
|
559
|
+
})
|
|
560
|
+
// ─── CHUNK_END: TYPES_REMOVE ────────────────────────────────────────────────
|
|
561
|
+
|
|
562
|
+
// ─── MAIN HANDLER ────────────────────────────────────────────────────────────
|
|
563
|
+
|
|
564
|
+
// ─── CHUNK_START: TYPES_HANDLER ──────────────────────────────────────────────
|
|
565
|
+
/**
|
|
566
|
+
* @chunk-id TYPES_HANDLER_1_0_0
|
|
567
|
+
* @version 1.0.0
|
|
568
|
+
* @hash 70e9e120f669468bd7aa45d7a2b8b4cf9a13a2323f897a6fc2bd389d960f8adc
|
|
569
|
+
* @macro Types Router
|
|
570
|
+
* @micro Routes HTTP methods and actions to appropriate type handlers
|
|
571
|
+
* @inputs ctx: CoreContext — Request context with principal and database
|
|
572
|
+
* @inputs body: any — Request body for POST/PATCH/DELETE operations
|
|
573
|
+
* @outputs Varies — Depends on routed handler (list/get/getBySlug/getSchema/create/update/remove)
|
|
574
|
+
* @depends-on [createHandler, list, get, getBySlug, getSchema, create, update, remove]
|
|
575
|
+
* @depended-by [Netlify function routing]
|
|
576
|
+
* @side-effects [Delegates to appropriate handler]
|
|
577
|
+
* @tags types, router, crud, netlify-function
|
|
578
|
+
*/
|
|
579
|
+
export const handler = createHandler(async (ctx, body) => {
|
|
580
|
+
const method = ctx.query?.method || 'GET'
|
|
581
|
+
const action = ctx.query?.action
|
|
582
|
+
|
|
583
|
+
switch (method) {
|
|
584
|
+
case 'GET':
|
|
585
|
+
if (action === 'get' && ctx.query?.slug) {
|
|
586
|
+
return await getBySlug(ctx, body)
|
|
587
|
+
} else if (ctx.query?.id) {
|
|
588
|
+
return await get(ctx, body)
|
|
589
|
+
} else if (action === 'schema') {
|
|
590
|
+
return await getSchema(ctx, body)
|
|
591
|
+
} else {
|
|
592
|
+
return await list(ctx, body)
|
|
593
|
+
}
|
|
594
|
+
case 'POST':
|
|
595
|
+
return await create(ctx, body)
|
|
596
|
+
case 'PATCH':
|
|
597
|
+
return await update(ctx, body)
|
|
598
|
+
case 'DELETE':
|
|
599
|
+
return await remove(ctx, body)
|
|
600
|
+
default:
|
|
601
|
+
throw new Error(`Unsupported method: ${method}`)
|
|
602
|
+
}
|
|
603
|
+
})
|
|
604
|
+
// ─── CHUNK_END: TYPES_HANDLER ────────────────────────────────────────────────
|