spine-framework 0.3.124 → 0.3.126

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.
Files changed (319) hide show
  1. package/.framework/cli/commands/agents.ts +35 -37
  2. package/.framework/cli/commands/auth.ts +35 -34
  3. package/.framework/cli/commands/create-app.ts +104 -40
  4. package/.framework/cli/commands/dev.ts +104 -69
  5. package/.framework/cli/commands/doctor.ts +179 -69
  6. package/.framework/cli/commands/generate.ts +83 -143
  7. package/.framework/cli/commands/init.ts +72 -34
  8. package/.framework/cli/commands/install-app.ts +260 -125
  9. package/.framework/cli/commands/items.ts +33 -44
  10. package/.framework/cli/commands/migrate.ts +122 -40
  11. package/.framework/cli/commands/migrations.ts +33 -40
  12. package/.framework/cli/commands/pipelines.ts +31 -39
  13. package/.framework/cli/commands/status.ts +47 -23
  14. package/.framework/cli/commands/system.ts +47 -37
  15. package/.framework/cli/commands/test.ts +112 -68
  16. package/.framework/cli/commands/validate-app.ts +215 -121
  17. package/.framework/cli/context.ts +112 -130
  18. package/.framework/cli/env-loader.ts +47 -18
  19. package/.framework/cli/index.ts +81 -42
  20. package/.framework/docs/runtime-types.md +1024 -0
  21. package/.framework/functions/_shared/agent-runner.ts +1296 -961
  22. package/.framework/functions/_shared/app-manifest.ts +82 -68
  23. package/.framework/functions/_shared/audit.ts +135 -83
  24. package/.framework/functions/_shared/{cil-state.ts → cil.ts} +158 -56
  25. package/.framework/functions/_shared/db.ts +80 -126
  26. package/.framework/functions/_shared/hash-api-key.ts +63 -0
  27. package/.framework/functions/_shared/index.ts +224 -335
  28. package/.framework/functions/_shared/kb.ts +319 -0
  29. package/.framework/functions/_shared/llm.ts +92 -0
  30. package/.framework/functions/_shared/logging.ts +288 -0
  31. package/.framework/functions/_shared/middleware.ts +329 -365
  32. package/.framework/functions/_shared/outcomes.ts +36 -0
  33. package/.framework/functions/_shared/permissions.ts +359 -643
  34. package/.framework/functions/_shared/pipeline-runner.ts +243 -316
  35. package/.framework/functions/_shared/principal.ts +376 -468
  36. package/.framework/functions/_shared/resolve-credentials.ts +102 -0
  37. package/.framework/functions/_shared/resolve-ids.ts +188 -16
  38. package/.framework/functions/_shared/retry-deliveries.ts +104 -0
  39. package/.framework/functions/_shared/schema-utils.ts +611 -373
  40. package/.framework/functions/_shared/testing.ts +207 -137
  41. package/.framework/functions/_shared/tracing.ts +276 -0
  42. package/.framework/functions/_shared/trigger-engine.ts +159 -211
  43. package/.framework/functions/_shared/webhook-registration.ts +95 -81
  44. package/.framework/functions/_shared/webhook-registry.ts +153 -75
  45. package/.framework/functions/account-nodes.ts +31 -53
  46. package/.framework/functions/adapters/generic-webhook-inbound.ts +56 -0
  47. package/.framework/functions/adapters/generic-webhook-outbound.ts +43 -0
  48. package/.framework/functions/adapters/openai-outbound.ts +40 -0
  49. package/.framework/functions/admin-data.ts +685 -537
  50. package/.framework/functions/admin-funnel.ts +31 -20
  51. package/.framework/functions/admin-install-funnel.ts +47 -28
  52. package/.framework/functions/admin-signals.ts +47 -29
  53. package/.framework/functions/ai-agents.ts +122 -147
  54. package/.framework/functions/ai-interactions.ts +180 -90
  55. package/.framework/functions/api-keys.ts +250 -192
  56. package/.framework/functions/apps.ts +203 -197
  57. package/.framework/functions/auth.ts +98 -91
  58. package/.framework/functions/cil-agent-ingest.ts +773 -0
  59. package/.framework/functions/cil-agent.ts +592 -0
  60. package/.framework/functions/cil-callmap.ts +1227 -0
  61. package/.framework/functions/cil-config.ts +583 -0
  62. package/.framework/functions/cil-context.ts +445 -0
  63. package/.framework/functions/cil-doc.ts +1752 -0
  64. package/.framework/functions/cil-embeddings.ts +1202 -0
  65. package/.framework/functions/cil-graph.ts +585 -0
  66. package/.framework/functions/cil-ingest.ts +2416 -0
  67. package/.framework/functions/cil-pipeline.ts +318 -0
  68. package/.framework/functions/cil-project.ts +499 -0
  69. package/.framework/functions/cil-validate.ts +469 -0
  70. package/.framework/functions/debug-auth.ts +73 -53
  71. package/.framework/functions/deliveries.ts +117 -0
  72. package/.framework/functions/embeddings.ts +528 -516
  73. package/.framework/functions/eval-harness.ts +158 -106
  74. package/.framework/functions/integration-outbound.ts +311 -0
  75. package/.framework/functions/integration-routes.ts +240 -466
  76. package/.framework/functions/integrations.ts +132 -140
  77. package/.framework/functions/invites.ts +401 -201
  78. package/.framework/functions/item-progress.ts +113 -53
  79. package/.framework/functions/kb.ts +28 -0
  80. package/.framework/functions/logs.ts +239 -222
  81. package/.framework/functions/observability.ts +308 -259
  82. package/.framework/functions/pipeline-executions.ts +179 -235
  83. package/.framework/functions/pipelines.ts +163 -205
  84. package/.framework/functions/prompt-configs.ts +109 -156
  85. package/.framework/functions/roles.ts +97 -147
  86. package/.framework/functions/system-cron.ts +132 -218
  87. package/.framework/functions/system.ts +112 -52
  88. package/.framework/functions/tests.ts +32 -28
  89. package/.framework/functions/timers.ts +162 -210
  90. package/.framework/functions/triggers.ts +282 -346
  91. package/.framework/functions/types.ts +283 -400
  92. package/.framework/migrations/001_core_schema.sql +630 -0
  93. package/.framework/migrations/002_core_rls_and_functions.sql +532 -0
  94. package/.framework/migrations/{003_system_data.sql → 003_core_system_data.sql} +26 -153
  95. package/.framework/migrations/004_core_rpc_and_broker.sql +421 -0
  96. package/.framework/migrations/005_core_observability.sql +598 -0
  97. package/.framework/migrations/006_security_hardening_rls.sql +97 -0
  98. package/.framework/migrations/007_kb_article_list.sql +237 -0
  99. package/.framework/migrations/008_items_target.sql +8 -0
  100. package/.framework/migrations/009_roles_slug_unique.sql +11 -0
  101. package/.framework/public/spine.config.json +91 -0
  102. package/.framework/scripts/app-install-cli.ts +112 -98
  103. package/.framework/src/App.tsx +67 -20
  104. package/.framework/src/apps/admin/index.tsx +97 -27
  105. package/.framework/src/components/AppWrapper.tsx +50 -11
  106. package/.framework/src/components/CustomAppLoader.tsx +131 -22
  107. package/.framework/src/components/admin/AdminListPage.tsx +75 -8
  108. package/.framework/src/components/admin/AdminSidebar.tsx +148 -36
  109. package/.framework/src/components/admin/AdminStatsCard.tsx +48 -29
  110. package/.framework/src/components/admin/PageInstructions.tsx +49 -0
  111. package/.framework/src/components/admin/SortableTableHeader.tsx +49 -2
  112. package/.framework/src/components/app-shell/GenericAppShell.tsx +69 -11
  113. package/.framework/src/components/app-shell/GenericDetailPage.tsx +55 -10
  114. package/.framework/src/components/app-shell/GenericListPage.tsx +76 -9
  115. package/.framework/src/components/app-sidebar.tsx +50 -3
  116. package/.framework/src/components/auth/ProtectedRoute.tsx +53 -45
  117. package/.framework/src/components/kb/KBArticleEditor.tsx +409 -0
  118. package/.framework/src/components/kb/KBArticleList.tsx +557 -0
  119. package/.framework/src/components/layout/AppShell.tsx +54 -16
  120. package/.framework/src/components/layout/Header.tsx +50 -38
  121. package/.framework/src/components/layout/Layout.tsx +48 -32
  122. package/.framework/src/components/layout/Sidebar.tsx +167 -16
  123. package/.framework/src/components/runtime/DataDetailHeader.tsx +33 -1
  124. package/.framework/src/components/runtime/DataDetailPage.tsx +289 -4
  125. package/.framework/src/components/runtime/DataFilters.tsx +78 -1
  126. package/.framework/src/components/runtime/DataHeader.tsx +32 -29
  127. package/.framework/src/components/runtime/DataListPage.tsx +161 -2
  128. package/.framework/src/components/runtime/DataStats.tsx +48 -1
  129. package/.framework/src/components/runtime/DataTable.tsx +153 -1
  130. package/.framework/src/components/runtime/SchemaDetailForm.tsx +77 -46
  131. package/.framework/src/components/runtime/index.ts +16 -9
  132. package/.framework/src/components/search-form.tsx +33 -1
  133. package/.framework/src/components/shared/AgentView.tsx +48 -51
  134. package/.framework/src/components/shared/FieldRenderer.tsx +84 -67
  135. package/.framework/src/components/shared/InviteManager.tsx +113 -52
  136. package/.framework/src/components/shared/MemberList.tsx +182 -0
  137. package/.framework/src/components/shared/SchemaFields.tsx +96 -78
  138. package/.framework/src/components/ui/DataTable.tsx +86 -56
  139. package/.framework/src/components/ui/Form.tsx +188 -109
  140. package/.framework/src/components/ui/ItemCard.tsx +86 -58
  141. package/.framework/src/components/ui/ItemListView.tsx +88 -61
  142. package/.framework/src/components/ui/LoadingSpinner.tsx +52 -27
  143. package/.framework/src/components/ui/Modal.tsx +81 -4
  144. package/.framework/src/components/ui/RichTextEditor.tsx +101 -9
  145. package/.framework/src/components/ui/accordion.tsx +97 -5
  146. package/.framework/src/components/ui/alert-dialog.tsx +225 -13
  147. package/.framework/src/components/ui/alert.tsx +113 -6
  148. package/.framework/src/components/ui/aspect-ratio.tsx +49 -2
  149. package/.framework/src/components/ui/avatar.tsx +129 -7
  150. package/.framework/src/components/ui/badge.tsx +65 -3
  151. package/.framework/src/components/ui/breadcrumb.tsx +145 -8
  152. package/.framework/src/components/ui/button-group.tsx +81 -4
  153. package/.framework/src/components/ui/button.tsx +49 -2
  154. package/.framework/src/components/ui/calendar.tsx +65 -3
  155. package/.framework/src/components/ui/card.tsx +145 -8
  156. package/.framework/src/components/ui/carousel.tsx +129 -7
  157. package/.framework/src/components/ui/chart.tsx +161 -10
  158. package/.framework/src/components/ui/checkbox.tsx +49 -2
  159. package/.framework/src/components/ui/collapsible.tsx +81 -5
  160. package/.framework/src/components/ui/combobox.tsx +305 -20
  161. package/.framework/src/components/ui/command.tsx +177 -11
  162. package/.framework/src/components/ui/context-menu.tsx +273 -17
  163. package/.framework/src/components/ui/dialog.tsx +209 -27
  164. package/.framework/src/components/ui/direction.tsx +64 -4
  165. package/.framework/src/components/ui/drawer.tsx +193 -12
  166. package/.framework/src/components/ui/dropdown-menu.tsx +273 -18
  167. package/.framework/src/components/ui/empty.tsx +145 -9
  168. package/.framework/src/components/ui/field.tsx +209 -14
  169. package/.framework/src/components/ui/hover-card.tsx +81 -4
  170. package/.framework/src/components/ui/input-group.tsx +162 -10
  171. package/.framework/src/components/ui/input-otp.tsx +98 -5
  172. package/.framework/src/components/ui/input.tsx +49 -2
  173. package/.framework/src/components/ui/item.tsx +225 -13
  174. package/.framework/src/components/ui/kbd.tsx +65 -3
  175. package/.framework/src/components/ui/label.tsx +49 -2
  176. package/.framework/src/components/ui/menubar.tsx +289 -17
  177. package/.framework/src/components/ui/native-select.tsx +97 -5
  178. package/.framework/src/components/ui/navigation-menu.tsx +177 -10
  179. package/.framework/src/components/ui/pagination.tsx +161 -9
  180. package/.framework/src/components/ui/popover.tsx +145 -8
  181. package/.framework/src/components/ui/progress.tsx +49 -2
  182. package/.framework/src/components/ui/radio-group.tsx +65 -3
  183. package/.framework/src/components/ui/resizable.tsx +82 -4
  184. package/.framework/src/components/ui/scroll-area.tsx +65 -3
  185. package/.framework/src/components/ui/select.tsx +193 -11
  186. package/.framework/src/components/ui/separator.tsx +49 -2
  187. package/.framework/src/components/ui/sheet.tsx +193 -11
  188. package/.framework/src/components/ui/sidebar.tsx +481 -38
  189. package/.framework/src/components/ui/skeleton.tsx +49 -2
  190. package/.framework/src/components/ui/slider.tsx +49 -2
  191. package/.framework/src/components/ui/sonner.tsx +49 -2
  192. package/.framework/src/components/ui/spinner.tsx +49 -2
  193. package/.framework/src/components/ui/switch.tsx +49 -2
  194. package/.framework/src/components/ui/table-primitives.tsx +160 -33
  195. package/.framework/src/components/ui/table.tsx +161 -9
  196. package/.framework/src/components/ui/tabs.tsx +113 -6
  197. package/.framework/src/components/ui/textarea.tsx +49 -2
  198. package/.framework/src/components/ui/toggle-group.tsx +81 -4
  199. package/.framework/src/components/ui/toggle.tsx +65 -3
  200. package/.framework/src/components/ui/tooltip.tsx +97 -5
  201. package/.framework/src/contexts/AppContext.tsx +129 -45
  202. package/.framework/src/contexts/AuthContext.tsx +187 -144
  203. package/.framework/src/hooks/use-mobile.ts +48 -2
  204. package/.framework/src/hooks/useApi.ts +208 -159
  205. package/.framework/src/hooks/useAppPath.ts +32 -23
  206. package/.framework/src/hooks/useApps.ts +51 -20
  207. package/.framework/src/hooks/useEntityList.ts +32 -78
  208. package/.framework/src/hooks/useEntityRecord.ts +35 -96
  209. package/.framework/src/hooks/useForm.ts +32 -92
  210. package/.framework/src/hooks/useListSchema.ts +49 -78
  211. package/.framework/src/hooks/useSchemaRecord.ts +114 -105
  212. package/.framework/src/lib/api.ts +195 -116
  213. package/.framework/src/lib/supabase.ts +98 -64
  214. package/.framework/src/lib/utils.ts +287 -151
  215. package/.framework/src/main.tsx +49 -3
  216. package/.framework/src/pages/DashboardPage.tsx +52 -29
  217. package/.framework/src/pages/NotFoundPage.tsx +32 -10
  218. package/.framework/src/pages/SetupPage.tsx +32 -12
  219. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +119 -10
  220. package/.framework/src/pages/admin/AIAgentsPage.tsx +48 -19
  221. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +187 -17
  222. package/.framework/src/pages/admin/APIKeysPage.tsx +98 -22
  223. package/.framework/src/pages/admin/AlertsConfigPage.tsx +113 -26
  224. package/.framework/src/pages/admin/AppDetailPage.tsx +125 -8
  225. package/.framework/src/pages/admin/AppsPage.tsx +49 -2
  226. package/.framework/src/pages/admin/DeliveriesPage.tsx +196 -0
  227. package/.framework/src/pages/admin/DesignedPage.tsx +32 -18
  228. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +215 -484
  229. package/.framework/src/pages/admin/EmbeddingsPage.tsx +116 -319
  230. package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +34 -9
  231. package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +34 -9
  232. package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -16
  233. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +183 -26
  234. package/.framework/src/pages/admin/IntegrationsPage.tsx +48 -19
  235. package/.framework/src/pages/admin/InvitesPage.tsx +48 -14
  236. package/.framework/src/pages/admin/LogsPage.tsx +422 -75
  237. package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +34 -9
  238. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +113 -24
  239. package/.framework/src/pages/admin/PipelineDetailPage.tsx +206 -34
  240. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +50 -17
  241. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -21
  242. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +378 -88
  243. package/.framework/src/pages/admin/PromptConfigsPage.tsx +125 -16
  244. package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +49 -10
  245. package/.framework/src/pages/admin/RoleDetailPage.tsx +130 -10
  246. package/.framework/src/pages/admin/RolesPage.tsx +171 -18
  247. package/.framework/src/pages/admin/SelectTestPage.tsx +34 -9
  248. package/.framework/src/pages/admin/ShadcnTestPage.tsx +49 -11
  249. package/.framework/src/pages/admin/SimpleDashboard.tsx +49 -10
  250. package/.framework/src/pages/admin/TestRunDetailPage.tsx +49 -4
  251. package/.framework/src/pages/admin/TestingDashboard.tsx +98 -28
  252. package/.framework/src/pages/admin/TimerDetailPage.tsx +143 -33
  253. package/.framework/src/pages/admin/TimersPage.tsx +48 -20
  254. package/.framework/src/pages/admin/TriggerDetailPage.tsx +118 -30
  255. package/.framework/src/pages/admin/TriggersPage.tsx +64 -19
  256. package/.framework/src/pages/admin/TypeDetailPage.tsx +138 -15
  257. package/.framework/src/pages/admin/TypesPage.tsx +48 -20
  258. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +465 -0
  259. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +787 -0
  260. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +699 -0
  261. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +647 -0
  262. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +616 -0
  263. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +1402 -0
  264. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +614 -0
  265. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +571 -0
  266. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +731 -0
  267. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
  268. package/.framework/src/pages/auth/AuthConfirmPage.tsx +52 -51
  269. package/.framework/src/pages/auth/ForgotPasswordPage.tsx +31 -22
  270. package/.framework/src/pages/auth/LoginPage.tsx +50 -23
  271. package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -15
  272. package/.framework/src/pages/auth/RegisterPage.tsx +281 -317
  273. package/.framework/src/pages/spine-framework/APIPage.tsx +33 -0
  274. package/.framework/src/pages/spine-framework/CLIPage.tsx +33 -0
  275. package/.framework/src/types/auth.ts +128 -71
  276. package/.framework/src/types/types.ts +373 -157
  277. package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +39 -0
  278. package/custom/.devin/skills/spine-workbench-idea/SKILL.md +36 -0
  279. package/custom/.devin/skills/spine-workbench-implement/SKILL.md +46 -0
  280. package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +28 -0
  281. package/custom/.devin/skills/spine-workbench-plan/SKILL.md +38 -0
  282. package/docs/README.md +9 -130
  283. package/netlify.toml +5 -2
  284. package/package.json +13 -25
  285. package/scripts/kb-ingest-docs.ts +203 -0
  286. package/spine.config.json +65 -4
  287. package/.framework/migrations/001_schema.sql +0 -425
  288. package/.framework/migrations/002_rls_and_functions.sql +0 -763
  289. package/.framework/migrations/004_enhancements.sql +0 -43
  290. package/.framework/migrations/005_auth_user_trigger.sql +0 -105
  291. package/.framework/migrations/006_fix_current_actor_id.sql +0 -21
  292. package/.framework/migrations/016_invites.sql +0 -27
  293. package/.framework/migrations/017_fix_get_account_apps.sql +0 -78
  294. package/.framework/migrations/018_apps_required_roles.sql +0 -17
  295. package/.framework/migrations/019_triggers_unique_constraint.sql +0 -13
  296. package/.framework/migrations/020_get_account_apps_role_filter.sql +0 -117
  297. package/.framework/migrations/021_app_domains.sql +0 -112
  298. package/.framework/migrations/022_route_prefix_domain_unique.sql +0 -21
  299. package/.framework/migrations/023_match_embeddings_rpc.sql +0 -78
  300. package/.framework/migrations/024_messages_account_id.sql +0 -9
  301. package/.framework/migrations/025_retype_threads_messages.sql +0 -240
  302. package/.framework/migrations/026_fix_rls_policies.sql +0 -47
  303. package/.framework/migrations/027_embedding_lifecycle.sql +0 -176
  304. package/.framework/migrations/028_fix_message_data_permissions.sql +0 -49
  305. package/.framework/migrations/029_tighten_support_triage_prompt.sql +0 -41
  306. package/.framework/migrations/030_messages_updated_by.sql +0 -6
  307. package/.framework/migrations/031_seed_upsert_constraints.sql +0 -35
  308. package/docs/APPS.md +0 -232
  309. package/docs/ARCHITECTURE.md +0 -246
  310. package/docs/INSTALL.md +0 -155
  311. package/docs/design-schema-spec.md +0 -1837
  312. package/docs/dev-guides/01-create-an-app.md +0 -183
  313. package/docs/dev-guides/02-create-a-function.md +0 -178
  314. package/docs/dev-guides/03-create-a-component.md +0 -178
  315. package/docs/dev-guides/04-webhook-handlers.md +0 -129
  316. package/docs/dev-guides/05-testing.md +0 -168
  317. package/docs/dev-guides/README.md +0 -40
  318. package/docs/npm-packages-plan.md +0 -288
  319. package/docs/spine-core--api-reference.md +0 -822
@@ -1,66 +1,56 @@
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
1
 
2
+
3
+
4
+
5
+
6
+
7
+
8
+ // CIL[BEGIN chunk_id="types_module_header_1783931956" type="module_header" identity="module_header" parent="none"]
9
+ // CIL[DOC-MACRO purpose="Import dependencies for types CRUD API"]
10
+ // CIL[DOC-RESPONSIBILITY responsibility="Provide middleware, database, permissions and schema utilities"]
11
+ // CIL[DOC-CONTEXT module="types" layer="api-handler" related_files="[\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/functions/_shared/principal.ts\",\".framework/functions/_shared/schema-utils.ts\",\"custom/apps/portal/pages/ContentPage.tsx\",\"\\\"]"]
12
+ // CIL[DOC-CONTRACT inputs="none" outputs="imported modules and permissions instance" errors="none" side_effects="none"]
13
+ // CIL[DOC-MICRO logic_summary="Imports all required modules for type management operations" notable_decisions="[]"]
14
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
15
+ // CIL[DOC-STABILITY stability="stable"]
16
+ // CIL[DOC-AUDIENCE audience="developer"]
17
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, .framework/functions/_shared/principal.ts, .framework/functions/_shared/schema-utils.ts, custom/apps/portal/pages/ContentPage.tsx, \."]
18
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
19
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
20
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
21
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
22
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
32
23
  import { createHandler } from './_shared/middleware'
33
24
  import { adminDb, joins } from './_shared/db'
34
25
  import { emitLog } from './_shared/audit'
26
+ import { isSystemAdmin } from './_shared/principal'
35
27
  import { PermissionEngine, sanitizeRecordData } from './_shared/permissions'
36
28
  import { generateValidationSchema } from './_shared/schema-utils'
37
29
 
38
30
  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
- */
31
+ // CIL[END chunk_id="types_module_header_1783931956"]
32
+ // CIL[BEGIN chunk_id="types_list_1783931956" type="function" identity="list" parent="none"]
33
+ // CIL[DOC-MACRO purpose="List types with filtering and sanitization"]
34
+ // CIL[DOC-RESPONSIBILITY responsibility="Handle type listing with authentication-based response"]
35
+ // CIL[DOC-CONTEXT module="types" layer="api-handler" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/components/ui/dialog.tsx\",\".framework/functions/_shared/principal.ts\",\"\\\"]"]
36
+ // CIL[DOC-CONTRACT inputs="none" outputs="sanitized type array" errors="throws" side_effects="database queries"]
37
+ // CIL[DOC-MICRO logic_summary="Filters types by kind/app/ownership, sanitizes based on auth, preserves schemas" notable_decisions="[]"]
38
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
39
+ // CIL[DOC-STABILITY stability="stable"]
40
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/RolesPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/components/ui/dialog.tsx, .framework/functions/_shared/principal.ts, \."]
41
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
42
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
43
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
44
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
45
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
57
46
  export const list = createHandler(async (ctx, body) => {
58
47
  const { kind, app_id, ownership, limit = '50', offset = '0', include_schema } = ctx.query || {}
59
48
 
60
- // RLS automatically filters to accessible accounts
61
- let query = ctx.db
49
+ const db = isSystemAdmin(ctx.principal) ? adminDb : ctx.db
50
+
51
+ let query = db
62
52
  .from('types')
63
- .select(`*, ${joins.app}`)
53
+ .select('*')
64
54
  .eq('is_active', true)
65
55
  .order('kind')
66
56
  .order('name')
@@ -84,32 +74,24 @@ export const list = createHandler(async (ctx, body) => {
84
74
 
85
75
  if (err) throw err
86
76
 
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
77
  const sanitized = []
91
78
  for (const type of data || []) {
92
79
  if (ctx.principal) {
93
- // Authenticated user - get sanitized data but preserve design_schema
94
80
  const sanitizedType = await sanitizeRecordData(ctx, type, 'type')
95
- // Ensure design_schema is preserved for schema-driven UI
96
81
  if (type.design_schema && !sanitizedType.design_schema) {
97
82
  sanitizedType.design_schema = type.design_schema
98
83
  }
99
- // When include_schema=true, also preserve validation_schema
100
84
  if (include_schema === 'true' && type.validation_schema) {
101
85
  sanitizedType.validation_schema = type.validation_schema
102
86
  }
103
87
  sanitized.push(sanitizedType)
104
88
  } else {
105
- // Unauthenticated user - return minimal data
106
89
  const minimal: any = {
107
90
  id: type.id,
108
91
  slug: type.slug,
109
92
  name: type.name,
110
93
  kind: type.kind
111
94
  }
112
- // When include_schema=true, unauthenticated users also get schema
113
95
  if (include_schema === 'true' && type.design_schema) {
114
96
  minimal.design_schema = type.design_schema
115
97
  }
@@ -119,23 +101,21 @@ export const list = createHandler(async (ctx, body) => {
119
101
 
120
102
  return sanitized
121
103
  })
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
- */
104
+ // CIL[END chunk_id="types_list_1783931956"]
105
+ // CIL[BEGIN chunk_id="types_get_1783931956" type="function" identity="get" parent="none"]
106
+ // CIL[DOC-MACRO purpose="Get single type by ID with sanitization"]
107
+ // CIL[DOC-RESPONSIBILITY responsibility="Retrieve and sanitize individual type records"]
108
+ // CIL[DOC-CONTEXT module="types" layer="api-handler" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/functions/_shared/schema-utils.ts\",\".framework/src/pages/admin/RolesPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"\\\"]"]
109
+ // CIL[DOC-CONTRACT inputs="none" outputs="sanitized type object" errors="throws" side_effects="database query"]
110
+ // CIL[DOC-MICRO logic_summary="Queries type by ID with app join, sanitizes based on permissions" notable_decisions="[]"]
111
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
112
+ // CIL[DOC-STABILITY stability="stable"]
113
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, .framework/functions/_shared/schema-utils.ts, .framework/src/pages/admin/RolesPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, \."]
114
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
115
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
116
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
117
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
118
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
139
119
  export const get = createHandler(async (ctx, body) => {
140
120
  const { id } = ctx.query || {}
141
121
 
@@ -143,35 +123,34 @@ export const get = createHandler(async (ctx, body) => {
143
123
  throw new Error('Type ID is required')
144
124
  }
145
125
 
146
- const { data, error: err } = await ctx.db
126
+ const db = isSystemAdmin(ctx.principal) ? adminDb : ctx.db
127
+
128
+ const { data, error: err } = await db
147
129
  .from('types')
148
- .select(`*, ${joins.app}`)
130
+ .select('*')
149
131
  .eq('id', id)
150
132
  .eq('is_active', true)
151
133
  .single()
152
134
 
153
135
  if (err) throw err
154
136
 
155
- // Sanitize based on role permissions
156
137
  return await sanitizeRecordData(ctx, data, 'type')
157
138
  })
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
- */
139
+ // CIL[END chunk_id="types_get_1783931956"]
140
+ // CIL[BEGIN chunk_id="types_getbyslug_1783931956" type="function" identity="getBySlug" parent="none"]
141
+ // CIL[DOC-MACRO purpose="Get single type by slug with sanitization"]
142
+ // CIL[DOC-RESPONSIBILITY responsibility="Retrieve type records using slug identifier"]
143
+ // CIL[DOC-CONTEXT module="types" layer="api-handler" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/functions/_shared/schema-utils.ts\",\"\\\"]"]
144
+ // CIL[DOC-CONTRACT inputs="none" outputs="sanitized type object" errors="throws" side_effects="database query"]
145
+ // CIL[DOC-MICRO logic_summary="Queries type by slug with app join, sanitizes based on permissions" notable_decisions="[]"]
146
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
147
+ // CIL[DOC-STABILITY stability="stable"]
148
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, .framework/src/pages/admin/RolesPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/functions/_shared/schema-utils.ts, \."]
149
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
150
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
151
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
152
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
153
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
175
154
  export const getBySlug = createHandler(async (ctx, body) => {
176
155
  const { slug } = ctx.query || {}
177
156
 
@@ -188,26 +167,23 @@ export const getBySlug = createHandler(async (ctx, body) => {
188
167
 
189
168
  if (err) throw err
190
169
 
191
- // Sanitize based on role permissions
192
170
  return await sanitizeRecordData(ctx, data, 'type')
193
171
  })
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
- */
172
+ // CIL[END chunk_id="types_getbyslug_1783931956"]
173
+ // CIL[BEGIN chunk_id="types_getschema_1783931956" type="function" identity="getSchema" parent="none"]
174
+ // CIL[DOC-MACRO purpose="Get type design schema via RPC"]
175
+ // CIL[DOC-RESPONSIBILITY responsibility="Retrieve design schema for specific type"]
176
+ // CIL[DOC-CONTEXT module="types" layer="api-handler" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/functions/_shared/schema-utils.ts\",\".framework/src/pages/admin/RolesPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\"]"]
177
+ // CIL[DOC-CONTRACT inputs="none" outputs="design_schema object" errors="throws" side_effects="database RPC call"]
178
+ // CIL[DOC-MICRO logic_summary="Calls get_type_schema RPC with kind, slug, and optional app_id" notable_decisions="[]"]
179
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="external_symbols_unknown"]
180
+ // CIL[DOC-STABILITY stability="stable"]
181
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, .framework/functions/_shared/schema-utils.ts, .framework/src/pages/admin/RolesPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx."]
182
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
183
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
184
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
185
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
186
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
211
187
  export const getSchema = createHandler(async (ctx, body) => {
212
188
  const { kind, slug, app_id } = ctx.query || {}
213
189
 
@@ -226,379 +202,286 @@ export const getSchema = createHandler(async (ctx, body) => {
226
202
 
227
203
  return { design_schema: data }
228
204
  })
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
- */
205
+ // CIL[END chunk_id="types_getschema_1783931956"]
206
+ // CIL[BEGIN chunk_id="types_create_1783931956" type="function" identity="create" parent="none"]
207
+ // CIL[DOC-LEARNING topic="runtime-type:generic/validation" content="The types API stores design_schema and auto-generates validation_schema from it via generateValidationSchema. design_schema must define record_permissions, fields, and views to drive the rest of the framework."]
208
+ // CIL[DOC-NOTE type="design_schema_caveat" content="Hand-editing validation_schema is unsafe; it is overwritten by the API. design_schema changes do not retroactively re-stamp existing runtime records."]
209
+ // CIL[DOC-MACRO purpose="Create new type with validation and audit"]
210
+ // CIL[DOC-RESPONSIBILITY responsibility="Handle type creation with permissions and validation"]
211
+ // CIL[DOC-CONTEXT module="types" layer="api-handler" related_files="[]"]
212
+ // CIL[DOC-CONTRACT inputs="none" outputs="created type record" errors="throws" side_effects="database insert, audit log"]
213
+ // CIL[DOC-MICRO logic_summary="Validates permissions, checks uniqueness, generates validation schema, creates type" notable_decisions="[]"]
214
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
215
+ // CIL[DOC-STABILITY stability="stable"]
216
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: none."]
217
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
218
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
219
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
220
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
221
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
246
222
  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')
223
+ if (!permissions.isSystemAdmin(ctx)) {
224
+ throw new Error('System admin required')
255
225
  }
256
226
 
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')
227
+ if (!body || typeof body !== 'object') {
228
+ throw new Error('Request body is required')
260
229
  }
261
230
 
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()
231
+ const {
232
+ name,
233
+ slug,
234
+ kind,
235
+ ownership = 'system',
236
+ app_id = null,
237
+ design_schema = {},
238
+ validation_schema = null,
239
+ view_config = {},
240
+ description = null,
241
+ icon = null,
242
+ color = null
243
+ } = body
269
244
 
270
- if (!accountData || accountData.parent_id !== null) {
271
- throw new Error('Type configurations can only be created in master tenant accounts')
245
+ if (!name || !slug || !kind) {
246
+ throw new Error('name, slug, and kind are required')
272
247
  }
273
248
 
274
- // Check if slug is unique within app/kind
275
- let query = adminDb
249
+ const { data: existing } = await adminDb
276
250
  .from('types')
277
251
  .select('id')
278
- .eq('kind', kind)
279
252
  .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()
253
+ .eq('kind', kind)
254
+ .eq('app_id', app_id)
255
+ .eq('is_active', true)
256
+ .single()
288
257
 
289
258
  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
- }
259
+ throw new Error(`Type with slug '${slug}' already exists for kind '${kind}'`)
318
260
  }
319
261
 
320
- // Generate validation schema from design schema
321
- const validationSchema = generateValidationSchema(bodySchema || {})
262
+ const validation = validation_schema || generateValidationSchema(design_schema)
322
263
 
323
- const { data, error: err } = await adminDb
264
+ const { data, error } = await adminDb
324
265
  .from('types')
325
266
  .insert({
326
- app_id: finalAppId,
327
- kind,
328
- slug,
329
267
  name,
268
+ slug,
269
+ kind,
270
+ ownership,
271
+ app_id,
272
+ design_schema,
273
+ validation_schema: validation,
274
+ view_config,
330
275
  description,
331
276
  icon,
332
277
  color,
333
- design_schema: bodySchema || {},
334
- validation_schema: validationSchema,
335
- ownership: finalOwnership || 'system',
336
278
  is_active: true
337
279
  })
338
280
  .select()
339
281
  .single()
340
282
 
341
- if (err) throw err
283
+ if (error) throw error
342
284
 
343
- await emitLog(ctx, 'type.created', { type: 'type', id: data.id }, { after: data })
285
+ await emitLog(ctx, 'type.created', {
286
+ type_id: data.id,
287
+ type_slug: data.slug,
288
+ type_kind: data.kind
289
+ })
344
290
 
345
291
  return data
346
292
  })
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
- */
293
+ // CIL[END chunk_id="types_create_1783931956"]
294
+ // CIL[BEGIN chunk_id="types_update_1783931956" type="function" identity="update" parent="none"]
295
+ // CIL[DOC-MACRO purpose="Update existing type with validation"]
296
+ // CIL[DOC-RESPONSIBILITY responsibility="Handle type updates with permissions and schema sync"]
297
+ // CIL[DOC-CONTEXT module="types" layer="api-handler" related_files="[]"]
298
+ // CIL[DOC-CONTRACT inputs="none" outputs="updated type record" errors="throws" side_effects="database update, audit log"]
299
+ // CIL[DOC-MICRO logic_summary="Validates permissions, protects system types, regenerates validation schema on design changes" notable_decisions="[]"]
300
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
301
+ // CIL[DOC-STABILITY stability="stable"]
302
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: none."]
303
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
304
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
305
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
306
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
307
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
364
308
  export const update = createHandler(async (ctx, body) => {
365
- const id = body?.id || ctx.query?.id
366
- const { id: _bodyId, ...updates } = body || {}
309
+ if (!permissions.isSystemAdmin(ctx)) {
310
+ throw new Error('System admin required')
311
+ }
367
312
 
313
+ const { id } = ctx.query || {}
314
+
368
315
  if (!id) {
369
316
  throw new Error('Type ID is required')
370
317
  }
371
318
 
372
- if (!ctx.principal || ctx.principal.id === 'anonymous' || !ctx.accountId) {
373
- throw new Error('User context (person and account) required')
319
+ if (!body || typeof body !== 'object') {
320
+ throw new Error('Request body is required')
374
321
  }
375
322
 
376
- // Get current state for audit - RLS will filter to accessible types
377
- const { data: current } = await adminDb
323
+ const { data: existing } = await adminDb
378
324
  .from('types')
379
325
  .select('*')
380
326
  .eq('id', id)
327
+ .eq('is_active', true)
381
328
  .single()
382
329
 
383
- if (!current) {
330
+ if (!existing) {
384
331
  throw new Error('Type not found')
385
332
  }
386
333
 
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
- }
334
+ if (existing.ownership === 'system' && body.ownership && body.ownership !== 'system') {
335
+ throw new Error('Cannot change ownership of system types')
406
336
  }
407
337
 
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()
338
+ const {
339
+ name,
340
+ slug,
341
+ design_schema,
342
+ view_config,
343
+ description,
344
+ icon,
345
+ color,
346
+ is_active,
347
+ kind,
348
+ app_id,
349
+ ownership
350
+ } = body
415
351
 
416
- if (!accountData || accountData.parent_id !== null) {
417
- throw new Error('Type configurations can only be updated in master tenant accounts')
418
- }
352
+ const updates: any = {}
419
353
 
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
- }
354
+ if (name !== undefined) updates.name = name
355
+ if (slug !== undefined) updates.slug = slug
356
+ if (view_config !== undefined) updates.view_config = view_config
357
+ if (description !== undefined) updates.description = description
358
+ if (icon !== undefined) updates.icon = icon
359
+ if (color !== undefined) updates.color = color
360
+ if (is_active !== undefined) updates.is_active = is_active
361
+ if (kind !== undefined) updates.kind = kind
362
+ if (app_id !== undefined) updates.app_id = app_id
363
+ if (ownership !== undefined) updates.ownership = ownership
431
364
 
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)
365
+ if (design_schema !== undefined) {
366
+ updates.design_schema = design_schema
367
+ updates.validation_schema = generateValidationSchema(design_schema)
454
368
  }
455
369
 
456
- const { data, error: err } = await ctx.db
370
+ const { data, error } = await adminDb
457
371
  .from('types')
458
- .update({
459
- ...updates,
460
- updated_at: new Date().toISOString()
461
- })
372
+ .update(updates)
462
373
  .eq('id', id)
463
374
  .select()
464
375
  .single()
465
376
 
466
- if (err) throw err
377
+ if (error) throw error
467
378
 
468
- await emitLog(ctx, 'type.updated', { type: 'type', id }, { before: current, after: data })
379
+ await emitLog(ctx, 'type.updated', {
380
+ type_id: data.id,
381
+ type_slug: data.slug,
382
+ type_kind: data.kind
383
+ })
469
384
 
470
385
  return data
471
386
  })
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
- */
387
+ // CIL[END chunk_id="types_update_1783931956"]
388
+ // CIL[BEGIN chunk_id="types_remove_1783931956" type="function" identity="remove" parent="none"]
389
+ // CIL[DOC-MACRO purpose="Soft delete type with validation"]
390
+ // CIL[DOC-RESPONSIBILITY responsibility="Handle type deletion with permissions and protection"]
391
+ // CIL[DOC-CONTEXT module="types" layer="api-handler" related_files="[]"]
392
+ // CIL[DOC-CONTRACT inputs="none" outputs="success indicator" errors="throws" side_effects="database update, audit log"]
393
+ // CIL[DOC-MICRO logic_summary="Validates permissions, protects system types, soft deletes by setting is_active=false" notable_decisions="[]"]
394
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
395
+ // CIL[DOC-STABILITY stability="stable"]
396
+ // CIL[DOC-VECTOR type="context" content="Layer: api-handler. Direct callers: 0. Direct calls: 0. Related files: none."]
397
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
398
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
399
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
400
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
401
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
489
402
  export const remove = createHandler(async (ctx, body) => {
490
- const id = body?.id || ctx.query?.id
403
+ if (!permissions.isSystemAdmin(ctx)) {
404
+ throw new Error('System admin required')
405
+ }
491
406
 
407
+ const { id } = ctx.query || {}
408
+
492
409
  if (!id) {
493
410
  throw new Error('Type ID is required')
494
411
  }
495
412
 
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
413
+ const { data: existing } = await adminDb
502
414
  .from('types')
503
415
  .select('*')
504
416
  .eq('id', id)
417
+ .eq('is_active', true)
505
418
  .single()
506
419
 
507
- if (!current) {
420
+ if (!existing) {
508
421
  throw new Error('Type not found')
509
422
  }
510
423
 
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')
424
+ if (existing.ownership === 'system') {
425
+ throw new Error('Cannot delete system types')
514
426
  }
515
427
 
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
428
+ const { error } = await adminDb
545
429
  .from('types')
546
- .update({
547
- is_active: false,
548
- updated_at: new Date().toISOString()
430
+ .update({
431
+ is_active: false
549
432
  })
550
433
  .eq('id', id)
551
- .select()
552
- .single()
553
434
 
554
- if (err) throw err
435
+ if (error) throw error
555
436
 
556
- await emitLog(ctx, 'type.deleted', { type: 'type', id }, { before: current })
437
+ await emitLog(ctx, 'type.deleted', {
438
+ type_id: existing.id,
439
+ type_slug: existing.slug,
440
+ type_kind: existing.kind
441
+ })
557
442
 
558
- return data
443
+ return { success: true }
559
444
  })
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
- */
445
+ // CIL[END chunk_id="types_remove_1783931956"]
446
+ // CIL[BEGIN chunk_id="types_handler_1783931956" type="function" identity="handler" parent="none"]
447
+ // CIL[DOC-MACRO purpose="Netlify function entrypoint for types API"]
448
+ // CIL[DOC-RESPONSIBILITY responsibility="Dispatch incoming requests to list, get, getBySlug, getSchema, create, update, or remove based on HTTP method and query parameters"]
449
+ // CIL[DOC-CONTEXT module="types" layer="api-handler" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/src/hooks/useSchemaRecord.ts\",\".framework/src/hooks/useListSchema.ts\"]"]
450
+ // CIL[DOC-CONTRACT inputs="http context and optional body" outputs="routed handler result" errors="throws on unsupported action or method" side_effects="none"]
451
+ // CIL[DOC-MICRO logic_summary="Routes GET by action/id/slug to list, get, getBySlug, or getSchema; POST to create; PATCH to update; DELETE to remove" notable_decisions="[]"]
452
+ // CIL[DOC-CONFIDENCE pass="first" score="0.95" flags="none"]
453
+ // CIL[DOC-STABILITY stability="stable"]
454
+ // CIL[DOC-AUDIENCE audience="developer"]
579
455
  export const handler = createHandler(async (ctx, body) => {
456
+ const { action, id, slug, kind } = ctx.query || {}
580
457
  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}`)
458
+
459
+ if (action === 'schema') {
460
+ return await getSchema(ctx, body)
461
+ }
462
+
463
+ if (action === 'list' || (method === 'GET' && !id && !slug && !kind)) {
464
+ return await list(ctx, body)
465
+ }
466
+
467
+ if (method === 'GET') {
468
+ if (slug) return await getBySlug(ctx, body)
469
+ if (id || (kind && action !== 'schema')) return await get(ctx, body)
470
+ return await list(ctx, body)
471
+ }
472
+
473
+ if (method === 'POST') {
474
+ return await create(ctx, body)
475
+ }
476
+
477
+ if (method === 'PATCH') {
478
+ return await update(ctx, body)
602
479
  }
480
+
481
+ if (method === 'DELETE') {
482
+ return await remove(ctx, body)
483
+ }
484
+
485
+ throw new Error('Invalid action or method')
603
486
  })
604
- // ─── CHUNK_END: TYPES_HANDLER ────────────────────────────────────────────────
487
+ // CIL[END chunk_id="types_handler_1783931956"]