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,626 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module permissions
|
|
3
|
+
* @audience both
|
|
4
|
+
* @layer shared-core
|
|
5
|
+
* @stability stable
|
|
6
|
+
*
|
|
7
|
+
* Single source of truth for all authorization in Spine. Exports one singleton —
|
|
8
|
+
* `PermissionEngine` — that routes every access check to one of three permission
|
|
9
|
+
* surfaces based on the table being accessed:
|
|
10
|
+
*
|
|
11
|
+
* First surface — runtime data (items, accounts, people, threads, messages…)
|
|
12
|
+
* Schema-driven: permissions are encoded in `design_schema.record_permissions`
|
|
13
|
+
* and `design_schema.fields[x].permissions` stamped on the record at creation.
|
|
14
|
+
*
|
|
15
|
+
* Second surface — config objects (apps, pipelines, triggers, roles, types…)
|
|
16
|
+
* Role-driven: system_admin full access, machine read, others denied.
|
|
17
|
+
*
|
|
18
|
+
* Third surface — system metadata (logs, pipeline_executions, link_types…)
|
|
19
|
+
* Ownership-driven: users read their own, system_admin sees all.
|
|
20
|
+
*
|
|
21
|
+
* INVARIANT: system_admin bypasses ALL surface checks. No other bypass exists.
|
|
22
|
+
* INVARIANT: missing or empty `design_schema` on a first-surface record is an
|
|
23
|
+
* explicit deny — not a free pass. RLS controls row access; design_schema
|
|
24
|
+
* controls what the principal can do with the row.
|
|
25
|
+
* INVARIANT: never import or instantiate `_PermissionEngineInternal` directly.
|
|
26
|
+
* Always import the `PermissionEngine` singleton or the named legacy exports.
|
|
27
|
+
*
|
|
28
|
+
* @seeAlso db.ts (adminDb used for schema and person lookups)
|
|
29
|
+
* @seeAlso principal.ts (Principal interface, isSystemAdmin, getPrincipalDb)
|
|
30
|
+
* @seeAlso middleware.ts (CoreContext shape, ctx.db, ctx.principal)
|
|
31
|
+
* @seeAlso schema-utils.ts (formatFieldData, sanitizeFieldData called during sanitization)
|
|
32
|
+
* @seeAlso index.ts (stable export surface for custom code)
|
|
33
|
+
*/
|
|
34
|
+
import { Principal } from './principal';
|
|
35
|
+
import { CoreContext } from './middleware';
|
|
36
|
+
/**
|
|
37
|
+
* Result of a permission resolution for a principal + record + action combination.
|
|
38
|
+
*
|
|
39
|
+
* Returned by `resolveFirstSurfacePermissions`. Captures both record-level CRUD
|
|
40
|
+
* flags and per-field read/write flags derived from `design_schema`.
|
|
41
|
+
*
|
|
42
|
+
* All flags default to `false` on any error or missing schema — never assume
|
|
43
|
+
* a missing flag means "allowed".
|
|
44
|
+
*
|
|
45
|
+
* @inputSpec none — this is a pure output type
|
|
46
|
+
* @outputSpec canCreate: boolean — principal may create records of this type
|
|
47
|
+
* @outputSpec canRead: boolean — principal may read this record
|
|
48
|
+
* @outputSpec canUpdate: boolean — principal may update this record
|
|
49
|
+
* @outputSpec canDelete: boolean — principal may delete this record
|
|
50
|
+
* @outputSpec fieldPermissions: Record<fieldName, {read, write}> — per-field flags
|
|
51
|
+
* derived from design_schema.fields[x].permissions merged across all roles
|
|
52
|
+
* @calledBy resolveFirstSurfacePermissions (producer), sanitizeFirstSurfaceRecordData,
|
|
53
|
+
* validateFirstSurfaceUpdatePermissions, canAccessFirstSurfaceRecord (consumers)
|
|
54
|
+
*/
|
|
55
|
+
export interface PermissionResult {
|
|
56
|
+
canCreate: boolean;
|
|
57
|
+
canRead: boolean;
|
|
58
|
+
canUpdate: boolean;
|
|
59
|
+
canDelete: boolean;
|
|
60
|
+
fieldPermissions: Record<string, {
|
|
61
|
+
read: boolean;
|
|
62
|
+
write: boolean;
|
|
63
|
+
}>;
|
|
64
|
+
}
|
|
65
|
+
type RequestContext = CoreContext;
|
|
66
|
+
/**
|
|
67
|
+
* The single permission engine for all authorization in Spine.
|
|
68
|
+
*
|
|
69
|
+
* Instantiated once as a module-level singleton (`PermissionEngine`). Routes
|
|
70
|
+
* every check through one of three surfaces based on table classification.
|
|
71
|
+
* All public methods are async and never throw — on any internal error they
|
|
72
|
+
* fall back to a deny result to avoid accidental permission grants.
|
|
73
|
+
*
|
|
74
|
+
* Do not instantiate directly. Import `PermissionEngine` or use the named
|
|
75
|
+
* legacy exports (`sanitizeRecordData`, `validateUpdatePermissions`, etc.).
|
|
76
|
+
*
|
|
77
|
+
* @audience both
|
|
78
|
+
* @stability stable
|
|
79
|
+
* @calledBy All 19 API handlers via sanitizeRecordData / validateUpdatePermissions
|
|
80
|
+
* @calledBy admin-data.ts (primary consumer for runtime data)
|
|
81
|
+
* @testUnit tests/unit/permissions.test.ts
|
|
82
|
+
* @testIntegration tests/integration/isolation.test.ts, admin-data-accounts.test.ts
|
|
83
|
+
*/
|
|
84
|
+
declare class _PermissionEngineInternal {
|
|
85
|
+
private static instance;
|
|
86
|
+
private readonly SECOND_SURFACE_TABLES;
|
|
87
|
+
private readonly THIRD_SURFACE_TABLES;
|
|
88
|
+
private constructor();
|
|
89
|
+
/**
|
|
90
|
+
* Classifies a table name into one of Spine's three permission surfaces.
|
|
91
|
+
*
|
|
92
|
+
* Surface membership is determined by static set membership — if a table is
|
|
93
|
+
* not in SECOND_SURFACE_TABLES or THIRD_SURFACE_TABLES, it defaults to first.
|
|
94
|
+
* This is intentionally conservative: unknown tables get the most restrictive
|
|
95
|
+
* surface (first), which requires a valid design_schema to grant any access.
|
|
96
|
+
*
|
|
97
|
+
* @param tableName - Table name string (e.g. 'items', 'pipelines', 'logs')
|
|
98
|
+
* @returns 'first' | 'second' | 'third' — surface classification
|
|
99
|
+
* @throws never
|
|
100
|
+
* @inputSpec tableName: string — any string; unknown names → 'first'
|
|
101
|
+
* @outputSpec 'first' | 'second' | 'third'
|
|
102
|
+
* @sideEffects none
|
|
103
|
+
* @calledBy canAccessRecord, sanitizeRecordData, validateUpdatePermissions
|
|
104
|
+
*/
|
|
105
|
+
private detectSurface;
|
|
106
|
+
/**
|
|
107
|
+
* Extracts a table/type name from a record to use for surface classification.
|
|
108
|
+
*
|
|
109
|
+
* Tries multiple fields in priority order: `record.table_name` (explicitly
|
|
110
|
+
* set by some handlers), `record.type`, `record.item_type`, then the
|
|
111
|
+
* `typeSlug` param. Falls back to `'unknown'` which routes to first surface.
|
|
112
|
+
*
|
|
113
|
+
* @param record - The record object being classified
|
|
114
|
+
* @param typeSlug - Optional caller-provided type slug (used as last resort)
|
|
115
|
+
* @returns string — table name used to classify the permission surface
|
|
116
|
+
* @throws never
|
|
117
|
+
* @inputSpec record: object — any record; missing fields are safely ignored
|
|
118
|
+
* @inputSpec typeSlug: string | undefined — optional fallback
|
|
119
|
+
* @outputSpec string — one of the known table names, or 'unknown'
|
|
120
|
+
* @sideEffects none
|
|
121
|
+
* @calledBy canAccessRecord, sanitizeRecordData, validateUpdatePermissions
|
|
122
|
+
*/
|
|
123
|
+
private extractTableName;
|
|
124
|
+
/**
|
|
125
|
+
* Returns the singleton instance. Called once at module load time to
|
|
126
|
+
* initialise `PermissionEngine`. Not for direct use outside this file.
|
|
127
|
+
*
|
|
128
|
+
* @returns _PermissionEngineInternal — the single shared instance
|
|
129
|
+
* @throws never
|
|
130
|
+
* @sideEffects creates instance on first call (subsequent calls return cached)
|
|
131
|
+
* @calledBy module initialisation (bottom of this file)
|
|
132
|
+
*/
|
|
133
|
+
static getInstance(): _PermissionEngineInternal;
|
|
134
|
+
/**
|
|
135
|
+
* Resolves record-level and field-level permissions for a human principal
|
|
136
|
+
* acting on a first-surface (runtime data) record.
|
|
137
|
+
*
|
|
138
|
+
* Resolution steps:
|
|
139
|
+
* 1. Load `design_schema` from the type record if not pre-stamped on the record
|
|
140
|
+
* 2. Look up the person's role via `people.role_id` FK (single DB query)
|
|
141
|
+
* 3. Evaluate `design_schema.record_permissions[role]` array for CRUD flags
|
|
142
|
+
* 4. Evaluate `design_schema.fields[x].permissions[role]` for field flags
|
|
143
|
+
* 5. Apply `'all'` wildcard role key if present (grants to all authenticated)
|
|
144
|
+
* 6. For fields with no explicit permission, inherit from record-level flags
|
|
145
|
+
*
|
|
146
|
+
* Returns all-deny `PermissionResult` on any error — never throws.
|
|
147
|
+
*
|
|
148
|
+
* @param personId - UUID of the person making the request (from principal.id)
|
|
149
|
+
* @param accountId - UUID of the account context for the operation
|
|
150
|
+
* @param typeSlug - Slug of the type to look up design_schema if not pre-stamped
|
|
151
|
+
* @param _action - CRUD action (currently used for context; merge logic is role-based)
|
|
152
|
+
* @param designSchema - Pre-loaded design_schema object (skips DB lookup if provided)
|
|
153
|
+
*
|
|
154
|
+
* @inputSpec personId: string — valid UUID, must exist in people table with is_active=true
|
|
155
|
+
* @inputSpec accountId: string — valid UUID of accessible account
|
|
156
|
+
* @inputSpec typeSlug: string — slug of a type in the types table with is_active=true
|
|
157
|
+
* @inputSpec designSchema: object | undefined — if provided, must have record_permissions
|
|
158
|
+
* @outputSpec PermissionResult — all flags false on error/missing schema
|
|
159
|
+
* @throws never — catches all errors, returns defaultResult
|
|
160
|
+
* @sideEffects DB read: types table (if schema not pre-stamped), people table (role lookup)
|
|
161
|
+
* @calledBy canAccessFirstSurfaceRecord, sanitizeFirstSurfaceRecordData,
|
|
162
|
+
* validateFirstSurfaceUpdatePermissions
|
|
163
|
+
* @calls adminDb.from('types'), adminDb.from('people')
|
|
164
|
+
* @testUnit tests/unit/permissions.test.ts — 'resolveFirstSurfacePermissions' describe block
|
|
165
|
+
*
|
|
166
|
+
* @example Import usage (v2-custom/)
|
|
167
|
+
* ```ts
|
|
168
|
+
* import { PermissionEngine } from '../_shared/index'
|
|
169
|
+
* const perms = await PermissionEngine.resolveFirstSurfacePermissions(
|
|
170
|
+
* ctx.principal.id, ctx.accountId, 'ticket', 'read'
|
|
171
|
+
* )
|
|
172
|
+
* if (!perms.canRead) return { error: 'Forbidden' }
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
resolveFirstSurfacePermissions(personId: string, accountId: string, typeSlug: string, _action: 'create' | 'read' | 'update' | 'delete', designSchema?: any): Promise<PermissionResult>;
|
|
176
|
+
/**
|
|
177
|
+
* Checks whether the principal in `ctx` may perform `action` on a second-surface
|
|
178
|
+
* config object (apps, pipelines, triggers, roles, types, etc.).
|
|
179
|
+
*
|
|
180
|
+
* Rules:
|
|
181
|
+
* - system_admin: full access to all actions
|
|
182
|
+
* - machine principal: read-only
|
|
183
|
+
* - all others: denied
|
|
184
|
+
*
|
|
185
|
+
* @param ctx - Request context containing principal
|
|
186
|
+
* @param action - CRUD action being attempted
|
|
187
|
+
* @returns boolean — true if access is allowed
|
|
188
|
+
* @throws never
|
|
189
|
+
* @inputSpec ctx.principal: Principal — must be resolved (not anonymous)
|
|
190
|
+
* @inputSpec action: 'create' | 'read' | 'update' | 'delete'
|
|
191
|
+
* @outputSpec boolean — true = allowed, false = denied
|
|
192
|
+
* @sideEffects none
|
|
193
|
+
* @calledBy canAccessRecord (surface='second'), validateConfigObjectPermissions
|
|
194
|
+
*/
|
|
195
|
+
private canAccessConfigObject;
|
|
196
|
+
/**
|
|
197
|
+
* Strips fields from a second-surface config record based on the principal's access.
|
|
198
|
+
*
|
|
199
|
+
* system_admin and machine principals receive the full record. All others
|
|
200
|
+
* receive only `{ id, created_at, updated_at }`. This is intentionally strict
|
|
201
|
+
* — config objects contain sensitive pipeline logic, schema definitions, and
|
|
202
|
+
* integration credentials that must not leak to end users.
|
|
203
|
+
*
|
|
204
|
+
* @param ctx - Request context
|
|
205
|
+
* @param record - The config record to sanitize
|
|
206
|
+
* @returns Sanitized record — full record or minimal stub
|
|
207
|
+
* @throws never
|
|
208
|
+
* @inputSpec ctx.principal: Principal — resolved principal
|
|
209
|
+
* @inputSpec record: object — must have id, created_at, updated_at at minimum
|
|
210
|
+
* @outputSpec object — full record for admin/machine, { id, created_at, updated_at } for others
|
|
211
|
+
* @sideEffects none
|
|
212
|
+
* @calledBy sanitizeRecordData (surface='second')
|
|
213
|
+
*/
|
|
214
|
+
private sanitizeConfigObject;
|
|
215
|
+
/**
|
|
216
|
+
* Validates whether the principal may perform `action` on a second-surface record.
|
|
217
|
+
* Thin wrapper around `canAccessConfigObject` that returns a typed result object
|
|
218
|
+
* suitable for returning directly from handler validation checks.
|
|
219
|
+
*
|
|
220
|
+
* @param ctx - Request context
|
|
221
|
+
* @param action - CRUD action being validated
|
|
222
|
+
* @returns { valid: boolean, error?: string }
|
|
223
|
+
* @throws never
|
|
224
|
+
* @inputSpec ctx.principal: Principal — resolved principal
|
|
225
|
+
* @inputSpec action: 'create' | 'read' | 'update' | 'delete'
|
|
226
|
+
* @outputSpec valid: boolean — true if action is permitted
|
|
227
|
+
* @outputSpec error: string | undefined — human-readable denial reason if !valid
|
|
228
|
+
* @sideEffects none
|
|
229
|
+
* @calledBy validateUpdatePermissions (surface='second')
|
|
230
|
+
*/
|
|
231
|
+
private validateConfigObjectPermissions;
|
|
232
|
+
/**
|
|
233
|
+
* Checks whether the principal may access a third-surface system metadata record
|
|
234
|
+
* (logs, pipeline_executions, trigger_executions, link_types, links).
|
|
235
|
+
*
|
|
236
|
+
* Rules:
|
|
237
|
+
* - system_admin: full access
|
|
238
|
+
* - machine principal: full access
|
|
239
|
+
* - human principal (read only):
|
|
240
|
+
* - owns the record (created_by === principal.id), OR
|
|
241
|
+
* - record is scoped to the principal's account (account_id === ctx.accountId), OR
|
|
242
|
+
* - record references the principal directly (person_id === principal.id)
|
|
243
|
+
* - human principal (create/update/delete): always denied
|
|
244
|
+
*
|
|
245
|
+
* @param ctx - Request context
|
|
246
|
+
* @param record - The system metadata record being accessed
|
|
247
|
+
* @param action - CRUD action being attempted
|
|
248
|
+
* @returns boolean — true if access is allowed
|
|
249
|
+
* @throws never
|
|
250
|
+
* @inputSpec ctx.principal: Principal — resolved principal
|
|
251
|
+
* @inputSpec record: object — must have at least one of: created_by, account_id, person_id
|
|
252
|
+
* @inputSpec action: 'create' | 'read' | 'update' | 'delete'
|
|
253
|
+
* @outputSpec boolean
|
|
254
|
+
* @sideEffects none
|
|
255
|
+
* @calledBy canAccessRecord (surface='third'), sanitizeSystemMetadata,
|
|
256
|
+
* validateSystemMetadataPermissions
|
|
257
|
+
*/
|
|
258
|
+
private canAccessSystemMetadata;
|
|
259
|
+
/**
|
|
260
|
+
* Strips fields from a third-surface system metadata record based on ownership.
|
|
261
|
+
*
|
|
262
|
+
* system_admin and machine principals receive the full record. Human principals
|
|
263
|
+
* who pass `canAccessSystemMetadata` receive the full record. All others
|
|
264
|
+
* receive only `{ id, created_at, updated_at }`.
|
|
265
|
+
*
|
|
266
|
+
* @param ctx - Request context
|
|
267
|
+
* @param record - The system metadata record to sanitize
|
|
268
|
+
* @returns Sanitized record
|
|
269
|
+
* @throws never
|
|
270
|
+
* @inputSpec record: object — must have id, created_at, updated_at
|
|
271
|
+
* @outputSpec object — full record for system_admin/machine/owner, minimal stub for others
|
|
272
|
+
* @sideEffects none
|
|
273
|
+
* @calledBy sanitizeRecordData (surface='third')
|
|
274
|
+
*/
|
|
275
|
+
private sanitizeSystemMetadata;
|
|
276
|
+
/**
|
|
277
|
+
* Validates whether the principal may perform `action` on a third-surface record.
|
|
278
|
+
* Delegates to `canAccessSystemMetadata` and wraps the result.
|
|
279
|
+
*
|
|
280
|
+
* @param ctx - Request context
|
|
281
|
+
* @param record - The system metadata record
|
|
282
|
+
* @param action - CRUD action being validated
|
|
283
|
+
* @returns { valid: boolean, error?: string }
|
|
284
|
+
* @throws never
|
|
285
|
+
* @inputSpec record: object — the record being written/read
|
|
286
|
+
* @outputSpec valid: boolean — true if action is permitted
|
|
287
|
+
* @outputSpec error: string | undefined — denial reason if !valid
|
|
288
|
+
* @sideEffects none
|
|
289
|
+
* @calledBy validateUpdatePermissions (surface='third')
|
|
290
|
+
*/
|
|
291
|
+
private validateSystemMetadataPermissions;
|
|
292
|
+
/**
|
|
293
|
+
* Returns true if the principal in `ctx` holds the `system_admin` role.
|
|
294
|
+
*
|
|
295
|
+
* This is the canonical system_admin check used by all three surfaces and
|
|
296
|
+
* the unified principal methods. It is the ONLY mechanism for bypassing
|
|
297
|
+
* surface-level permission checks — there is no other bypass in the engine.
|
|
298
|
+
*
|
|
299
|
+
* @param ctx - Request context with resolved principal
|
|
300
|
+
* @returns boolean — true if principal.roles includes 'system_admin'
|
|
301
|
+
* @throws never
|
|
302
|
+
* @inputSpec ctx.principal: Principal — principal.roles: string[]
|
|
303
|
+
* @outputSpec boolean — false if principal is null, anonymous, or has no roles
|
|
304
|
+
* @sideEffects none
|
|
305
|
+
* @calledBy canAccessRecord, sanitizeRecordData, validateUpdatePermissions,
|
|
306
|
+
* canAccessConfigObject, sanitizeConfigObject, sanitizeSystemMetadata,
|
|
307
|
+
* canPrincipalAccessRecord
|
|
308
|
+
* @testUnit tests/unit/permissions.test.ts — 'isSystemAdmin' describe block
|
|
309
|
+
*/
|
|
310
|
+
isSystemAdmin(ctx: RequestContext): boolean;
|
|
311
|
+
/**
|
|
312
|
+
* Checks whether the principal in `ctx` may perform `action` on `record`.
|
|
313
|
+
*
|
|
314
|
+
* Routes to the correct surface handler based on `record`'s table name:
|
|
315
|
+
* - second surface tables → `canAccessConfigObject`
|
|
316
|
+
* - third surface tables → `canAccessSystemMetadata`
|
|
317
|
+
* - everything else (first surface) → `canAccessFirstSurfaceRecord`
|
|
318
|
+
*
|
|
319
|
+
* system_admin always returns true before surface routing.
|
|
320
|
+
*
|
|
321
|
+
* @param ctx - Request context with resolved principal
|
|
322
|
+
* @param record - The record being accessed (used for surface detection only)
|
|
323
|
+
* @param action - CRUD action being attempted
|
|
324
|
+
* @returns Promise<boolean> — true if access is allowed
|
|
325
|
+
* @throws never — all surface handlers catch errors and return false
|
|
326
|
+
* @inputSpec ctx.principal: Principal — must be resolved
|
|
327
|
+
* @inputSpec record: object — used for table_name/type/item_type extraction
|
|
328
|
+
* @inputSpec action: 'create' | 'read' | 'update' | 'delete'
|
|
329
|
+
* @outputSpec boolean — false for anonymous principals, missing records, unknown types
|
|
330
|
+
* @sideEffects DB read (first surface only): types and people tables
|
|
331
|
+
* @calledBy API handlers where explicit access gate is needed (rare — most use sanitize)
|
|
332
|
+
* @testUnit tests/unit/permissions.test.ts — 'canAccessRecord' describe block
|
|
333
|
+
*/
|
|
334
|
+
canAccessRecord(ctx: RequestContext, record: any, action: 'create' | 'read' | 'update' | 'delete'): Promise<boolean>;
|
|
335
|
+
/**
|
|
336
|
+
* Access check for first-surface (runtime data) records.
|
|
337
|
+
*
|
|
338
|
+
* Delegates to `resolveFirstSurfacePermissions` to evaluate the design_schema
|
|
339
|
+
* permission model. For 'own' access level, additionally checks record ownership
|
|
340
|
+
* via `created_by === principal.id`.
|
|
341
|
+
*
|
|
342
|
+
* Returns false for anonymous principals and any principal without a valid accountId.
|
|
343
|
+
*
|
|
344
|
+
* @param ctx - Request context
|
|
345
|
+
* @param record - The first-surface record being accessed
|
|
346
|
+
* @param action - CRUD action
|
|
347
|
+
* @returns Promise<boolean>
|
|
348
|
+
* @throws never
|
|
349
|
+
* @inputSpec ctx.principal: not anonymous, ctx.accountId: non-empty string
|
|
350
|
+
* @inputSpec record: must have account_id or item_type/type for schema resolution
|
|
351
|
+
* @outputSpec boolean — false for anonymous, missing schema, insufficient permissions
|
|
352
|
+
* @sideEffects DB read: types and people tables (via resolveFirstSurfacePermissions)
|
|
353
|
+
* @calledBy canAccessRecord (surface='first'), canPrincipalAccessRecord (human branch)
|
|
354
|
+
*/
|
|
355
|
+
private canAccessFirstSurfaceRecord;
|
|
356
|
+
/**
|
|
357
|
+
* Strips and formats a record's fields based on the principal's permissions.
|
|
358
|
+
*
|
|
359
|
+
* This is the primary output filter called by every API handler before returning
|
|
360
|
+
* data to the client. Routes to the correct surface handler, which applies
|
|
361
|
+
* field-level filtering from the record's stamped `design_schema`.
|
|
362
|
+
*
|
|
363
|
+
* system_admin receives the full record unchanged.
|
|
364
|
+
*
|
|
365
|
+
* For first-surface records with missing `design_schema` or no `record_permissions`,
|
|
366
|
+
* returns `{ id }` only — explicit deny, not a pass-through.
|
|
367
|
+
*
|
|
368
|
+
* @param ctx - Request context with resolved principal
|
|
369
|
+
* @param record - The record to sanitize (should be the raw DB row)
|
|
370
|
+
* @param typeSlug - Type slug used to classify the surface and look up schema
|
|
371
|
+
* if not already stamped on the record. Optional for second/third surfaces.
|
|
372
|
+
* @returns Promise<object> — sanitized record safe to return to the client
|
|
373
|
+
* @throws never
|
|
374
|
+
* @inputSpec ctx.principal: Principal — resolved, may be anonymous
|
|
375
|
+
* @inputSpec record: object — raw DB row, must have id at minimum
|
|
376
|
+
* @inputSpec typeSlug: string | undefined — slug of the type (e.g. 'item', 'account')
|
|
377
|
+
* @outputSpec object — filtered record; field set depends on principal's role permissions
|
|
378
|
+
* @outputSpec system_admin: full record unchanged
|
|
379
|
+
* @outputSpec unauthenticated: { id, created_at, updated_at } only
|
|
380
|
+
* @outputSpec first surface, no schema: { id } only
|
|
381
|
+
* @sideEffects DB read (first surface): types and people tables via resolveFirstSurface
|
|
382
|
+
* @calledBy All 19 API handlers — this is the most-called method in the engine
|
|
383
|
+
* @calls sanitizeFirstSurfaceRecordData | sanitizeConfigObject | sanitizeSystemMetadata
|
|
384
|
+
* @testUnit tests/unit/permissions.test.ts — 'sanitizeRecordData' describe block
|
|
385
|
+
* @testIntegration tests/integration/admin-data-accounts.test.ts
|
|
386
|
+
*
|
|
387
|
+
* @example API handler usage
|
|
388
|
+
* ```ts
|
|
389
|
+
* const sanitized = await sanitizeRecordData(ctx, record, 'item')
|
|
390
|
+
* return { data: sanitized }
|
|
391
|
+
* ```
|
|
392
|
+
*
|
|
393
|
+
* @example Import usage (v2-custom/)
|
|
394
|
+
* ```ts
|
|
395
|
+
* import { sanitizeRecordData } from '../_shared/index'
|
|
396
|
+
* const safe = await sanitizeRecordData(ctx, rawRecord, 'ticket')
|
|
397
|
+
* ```
|
|
398
|
+
*/
|
|
399
|
+
sanitizeRecordData(ctx: RequestContext, record: any, typeSlug?: string): Promise<any>;
|
|
400
|
+
/**
|
|
401
|
+
* Field-level filter and formatter for first-surface (runtime data) records.
|
|
402
|
+
*
|
|
403
|
+
* Steps:
|
|
404
|
+
* 1. Return minimal stub for anonymous principals
|
|
405
|
+
* 2. Check `record.design_schema.record_permissions` — deny if missing
|
|
406
|
+
* 3. Resolve permissions via `resolveFirstSurfacePermissions`
|
|
407
|
+
* 4. Return minimal stub if `!perms.canRead`
|
|
408
|
+
* 5. For each field in `record.data`, include only if `fieldPerms.read === true`
|
|
409
|
+
* 6. Apply `formatFieldData` from schema-utils if validation_schema specifies a data_type
|
|
410
|
+
* 7. Strip `record.metadata` (legacy field, migrated to `data`)
|
|
411
|
+
*
|
|
412
|
+
* @param ctx - Request context
|
|
413
|
+
* @param record - First-surface DB row with design_schema and data fields
|
|
414
|
+
* @param typeSlug - Type slug for schema lookup
|
|
415
|
+
* @returns Promise<object> — filtered and formatted record
|
|
416
|
+
* @throws never
|
|
417
|
+
* @inputSpec record.design_schema: object with record_permissions — deny if missing
|
|
418
|
+
* @inputSpec record.data: object — JSONB data fields; only permitted fields returned
|
|
419
|
+
* @outputSpec object — sanitized record matching the principal's field permissions
|
|
420
|
+
* @sideEffects DB read: types and people via resolveFirstSurfacePermissions
|
|
421
|
+
* @calledBy sanitizeRecordData (surface='first')
|
|
422
|
+
* @calls resolveFirstSurfacePermissions, schema-utils.formatFieldData
|
|
423
|
+
*/
|
|
424
|
+
private sanitizeFirstSurfaceRecordData;
|
|
425
|
+
/**
|
|
426
|
+
* Validates that the principal has write permission for every field in `updateData`,
|
|
427
|
+
* and sanitizes the data using the validation schema before returning it.
|
|
428
|
+
*
|
|
429
|
+
* Routes to the correct surface handler. system_admin bypasses all checks and
|
|
430
|
+
* receives `updateData` unchanged (with `sanitizedData` set to `updateData`).
|
|
431
|
+
*
|
|
432
|
+
* For first-surface records:
|
|
433
|
+
* - Each field in `updateData.data` must have `fieldPerms.write === true`
|
|
434
|
+
* - Fields are sanitized via `sanitizeFieldData` from schema-utils
|
|
435
|
+
* - Returns `{ valid: false, error }` on the first denied or invalid field
|
|
436
|
+
*
|
|
437
|
+
* @param ctx - Request context with resolved principal
|
|
438
|
+
* @param updateData - The payload being written (may contain `data` and/or `metadata`)
|
|
439
|
+
* @param existingRecord - The current DB row (used for schema + account_id resolution)
|
|
440
|
+
* @param typeSlug - Type slug for surface classification and schema lookup
|
|
441
|
+
* @returns Promise<{ valid: boolean, error?: string, sanitizedData?: any }>
|
|
442
|
+
* @throws never
|
|
443
|
+
* @inputSpec ctx.principal: Principal — resolved, non-anonymous required for first surface
|
|
444
|
+
* @inputSpec updateData: object — payload with data: {} and/or metadata: {}
|
|
445
|
+
* @inputSpec existingRecord: object — must have design_schema, account_id
|
|
446
|
+
* @outputSpec valid: boolean — false on first permission or validation failure
|
|
447
|
+
* @outputSpec error: string | undefined — field name + reason when !valid
|
|
448
|
+
* @outputSpec sanitizedData: object | undefined — cleaned payload when valid
|
|
449
|
+
* @sideEffects DB read (first surface): types and people via resolveFirstSurfacePermissions
|
|
450
|
+
* @calledBy admin-data.ts (update handler), and any handler that accepts user writes
|
|
451
|
+
* @calls validateFirstSurfaceUpdatePermissions | validateConfigObjectPermissions |
|
|
452
|
+
* validateSystemMetadataPermissions
|
|
453
|
+
* @testUnit tests/unit/permissions.test.ts — 'validateUpdatePermissions' describe block
|
|
454
|
+
*
|
|
455
|
+
* @example API handler usage
|
|
456
|
+
* ```ts
|
|
457
|
+
* const { valid, error, sanitizedData } = await validateUpdatePermissions(
|
|
458
|
+
* ctx, body, existingRecord, 'item'
|
|
459
|
+
* )
|
|
460
|
+
* if (!valid) return { error }
|
|
461
|
+
* await ctx.db.from('items').update(sanitizedData).eq('id', id)
|
|
462
|
+
* ```
|
|
463
|
+
*/
|
|
464
|
+
validateUpdatePermissions(ctx: RequestContext, updateData: any, existingRecord: any, typeSlug?: string): Promise<{
|
|
465
|
+
valid: boolean;
|
|
466
|
+
error?: string;
|
|
467
|
+
}>;
|
|
468
|
+
/**
|
|
469
|
+
* Field-level write validation and sanitization for first-surface update payloads.
|
|
470
|
+
*
|
|
471
|
+
* Checks every field in `updateData.data` (and legacy `updateData.metadata`) against
|
|
472
|
+
* the principal's write permissions. Sanitizes each permitted field through
|
|
473
|
+
* `sanitizeFieldData` for type coercion and constraint validation. Returns on
|
|
474
|
+
* the first denied or invalid field — does not accumulate errors.
|
|
475
|
+
*
|
|
476
|
+
* @param ctx - Request context
|
|
477
|
+
* @param updateData - Incoming update payload
|
|
478
|
+
* @param existingRecord - Existing DB row with design_schema stamped at creation
|
|
479
|
+
* @param typeSlug - Type slug for schema lookup
|
|
480
|
+
* @returns Promise<{ valid: boolean, error?: string, sanitizedData?: any }>
|
|
481
|
+
* @throws never
|
|
482
|
+
* @inputSpec existingRecord.design_schema.record_permissions — deny if missing
|
|
483
|
+
* @inputSpec updateData.data: object — all fields must have fieldPerms.write=true
|
|
484
|
+
* @outputSpec sanitizedData: object — only present when valid=true
|
|
485
|
+
* @sideEffects DB read: types and people via resolveFirstSurfacePermissions
|
|
486
|
+
* @calledBy validateUpdatePermissions (surface='first')
|
|
487
|
+
* @calls resolveFirstSurfacePermissions, schema-utils.sanitizeFieldData
|
|
488
|
+
*/
|
|
489
|
+
private validateFirstSurfaceUpdatePermissions;
|
|
490
|
+
/**
|
|
491
|
+
* Unified permission check for all principal types (human, machine, cron, trigger).
|
|
492
|
+
*
|
|
493
|
+
* This is the preferred method when you have a `Principal` directly rather than
|
|
494
|
+
* a full `RequestContext`. Used by the Unified Principal Architecture to check
|
|
495
|
+
* access without constructing a fake context.
|
|
496
|
+
*
|
|
497
|
+
* Resolution:
|
|
498
|
+
* 1. system_admin (human with 'system_admin' role) → always true
|
|
499
|
+
* 2. machine principal → scope check via `checkMachineScope`
|
|
500
|
+
* 3. human principal with accountId → `canAccessFirstSurfaceRecord` (constructs minimal ctx)
|
|
501
|
+
* 4. all others → false
|
|
502
|
+
*
|
|
503
|
+
* @param principal - The fully resolved Principal from `resolvePrincipal()`
|
|
504
|
+
* @param record - The record being accessed; must include account_id and type for scope matching
|
|
505
|
+
* @param action - CRUD action being attempted
|
|
506
|
+
* @returns Promise<boolean> — true if the principal may perform the action
|
|
507
|
+
* @throws never
|
|
508
|
+
* @inputSpec principal: Principal — must be resolved (not ANONYMOUS_PRINCIPAL for useful results)
|
|
509
|
+
* @inputSpec record.account_id: string — required for human principals
|
|
510
|
+
* @inputSpec record.type: string | undefined — used for machine scope matching
|
|
511
|
+
* @outputSpec boolean
|
|
512
|
+
* @sideEffects DB read (human principal): types and people tables
|
|
513
|
+
* @calledBy Handlers that receive a Principal directly (e.g. CLI, import callers)
|
|
514
|
+
* @calls checkMachineScope, canAccessFirstSurfaceRecord
|
|
515
|
+
* @testUnit tests/unit/permissions.test.ts — 'canPrincipalAccessRecord' describe block
|
|
516
|
+
*
|
|
517
|
+
* @example Import usage (v2-custom/)
|
|
518
|
+
* ```ts
|
|
519
|
+
* import { PermissionEngine } from '../_shared/index'
|
|
520
|
+
* const allowed = await PermissionEngine.canPrincipalAccessRecord(
|
|
521
|
+
* principal, { account_id: accountId, type: 'item' }, 'create'
|
|
522
|
+
* )
|
|
523
|
+
* ```
|
|
524
|
+
*
|
|
525
|
+
* @example CLI usage
|
|
526
|
+
* ```bash
|
|
527
|
+
* # Access checks happen automatically when CLI constructs CoreContext
|
|
528
|
+
* spine items create --data '{"title":"Test"}'
|
|
529
|
+
* ```
|
|
530
|
+
*/
|
|
531
|
+
canPrincipalAccessRecord(principal: Principal, record: {
|
|
532
|
+
account_id: string;
|
|
533
|
+
type?: string;
|
|
534
|
+
[key: string]: any;
|
|
535
|
+
}, action: 'create' | 'read' | 'update' | 'delete'): Promise<boolean>;
|
|
536
|
+
/**
|
|
537
|
+
* Evaluates whether a machine principal's scopes permit the requested action.
|
|
538
|
+
*
|
|
539
|
+
* Scope matching supports three patterns (evaluated in order):
|
|
540
|
+
* 1. Exact match: `'items:read'` matches `'items:read'`
|
|
541
|
+
* 2. Wildcard action: `'items:*'` matches any action on `items`
|
|
542
|
+
* 3. Global wildcard: `'*:*'` matches any resource and any action
|
|
543
|
+
*
|
|
544
|
+
* The required scope is constructed as `<record.type>:<action>`. If `record.type`
|
|
545
|
+
* is absent, `'resource'` is used as the resource name.
|
|
546
|
+
*
|
|
547
|
+
* @param principal - Machine principal (principal.type must be 'machine')
|
|
548
|
+
* @param record - The record being accessed (record.type used as resource name)
|
|
549
|
+
* @param action - The CRUD action string
|
|
550
|
+
* @returns boolean — true if any scope in principal.scopes grants the action
|
|
551
|
+
* @throws never
|
|
552
|
+
* @inputSpec principal.type: 'machine' — returns false for non-machine principals
|
|
553
|
+
* @inputSpec principal.scopes: string[] — list of granted scope strings
|
|
554
|
+
* @inputSpec record.type: string | undefined — resource name portion of scope check
|
|
555
|
+
* @outputSpec boolean
|
|
556
|
+
* @sideEffects none
|
|
557
|
+
* @calledBy canPrincipalAccessRecord (machine branch)
|
|
558
|
+
* @testUnit tests/unit/permissions.test.ts — 'checkMachineScope' describe block
|
|
559
|
+
*/
|
|
560
|
+
private checkMachineScope;
|
|
561
|
+
/**
|
|
562
|
+
* Returns a structured summary of a principal's permission posture for use
|
|
563
|
+
* in audit log entries.
|
|
564
|
+
*
|
|
565
|
+
* Does not perform any access check — purely descriptive. The returned object
|
|
566
|
+
* is safe to serialize into the `metadata` column of the `logs` table.
|
|
567
|
+
*
|
|
568
|
+
* @param principal - The resolved principal to summarize
|
|
569
|
+
* @returns object — summary safe for audit log serialization
|
|
570
|
+
* @throws never
|
|
571
|
+
* @inputSpec principal: Principal — any resolved principal including ANONYMOUS
|
|
572
|
+
* @outputSpec { type, roles, is_system_admin } for human principals
|
|
573
|
+
* @outputSpec { type, machine_type, scopes, is_internal } for machine principals
|
|
574
|
+
* @outputSpec { type: 'unknown' } for all other types
|
|
575
|
+
* @sideEffects none
|
|
576
|
+
* @calledBy audit.ts (emitAudit), any handler that logs permission context
|
|
577
|
+
* @testUnit tests/unit/permissions.test.ts — 'getPrincipalPermissionSummary' describe block
|
|
578
|
+
*
|
|
579
|
+
* @example
|
|
580
|
+
* ```ts
|
|
581
|
+
* await emitAudit(ctx, 'record.read', record.id, {
|
|
582
|
+
* permissions: PermissionEngine.getPrincipalPermissionSummary(ctx.principal)
|
|
583
|
+
* })
|
|
584
|
+
* ```
|
|
585
|
+
*/
|
|
586
|
+
getPrincipalPermissionSummary(principal: Principal): object;
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* The single shared PermissionEngine instance.
|
|
590
|
+
*
|
|
591
|
+
* This is the ONLY export that should be used for permission checks. Import this
|
|
592
|
+
* directly or use the named legacy aliases below. Do not instantiate
|
|
593
|
+
* `_PermissionEngineInternal` yourself.
|
|
594
|
+
*
|
|
595
|
+
* @stability stable
|
|
596
|
+
* @audience both
|
|
597
|
+
* @calledBy All 19 API handlers, tests, and custom code in v2-custom/
|
|
598
|
+
*
|
|
599
|
+
* @example API handler
|
|
600
|
+
* ```ts
|
|
601
|
+
* import { PermissionEngine } from './_shared/permissions'
|
|
602
|
+
* const sanitized = await PermissionEngine.sanitizeRecordData(ctx, record, 'item')
|
|
603
|
+
* ```
|
|
604
|
+
*
|
|
605
|
+
* @example Import usage (v2-custom/)
|
|
606
|
+
* ```ts
|
|
607
|
+
* import { PermissionEngine } from '../_shared/index'
|
|
608
|
+
* const allowed = await PermissionEngine.canPrincipalAccessRecord(principal, record, 'read')
|
|
609
|
+
* ```
|
|
610
|
+
*/
|
|
611
|
+
export declare const PermissionEngine: _PermissionEngineInternal;
|
|
612
|
+
/**
|
|
613
|
+
* Legacy named exports — bound methods on the singleton for backward compatibility.
|
|
614
|
+
* Prefer importing `PermissionEngine` and calling methods on it directly.
|
|
615
|
+
* These will be removed in a future version.
|
|
616
|
+
*
|
|
617
|
+
* @deprecated Use `PermissionEngine.<methodName>()` instead.
|
|
618
|
+
* @stability internal
|
|
619
|
+
*/
|
|
620
|
+
export declare const resolveFirstSurfacePermissions: any;
|
|
621
|
+
export declare const isSystemAdmin: any;
|
|
622
|
+
export declare const canAccessRecord: any;
|
|
623
|
+
export declare const sanitizeRecordData: any;
|
|
624
|
+
export declare const validateUpdatePermissions: any;
|
|
625
|
+
export {};
|
|
626
|
+
//# sourceMappingURL=permissions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../../.framework/functions/_shared/permissions.ts"],"names":[],"mappings":"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"}
|