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.
Files changed (385) hide show
  1. package/.framework/README.md +129 -0
  2. package/.framework/cli/bin.cjs +14 -0
  3. package/.framework/cli/commands/agents.ts +153 -0
  4. package/.framework/cli/commands/auth.ts +94 -0
  5. package/.framework/cli/commands/create-app.ts +185 -0
  6. package/.framework/cli/commands/dev.ts +295 -0
  7. package/.framework/cli/commands/doctor.ts +442 -0
  8. package/.framework/cli/commands/generate.ts +332 -0
  9. package/.framework/cli/commands/init.ts +272 -0
  10. package/.framework/cli/commands/install-app.ts +391 -0
  11. package/.framework/cli/commands/items.ts +253 -0
  12. package/.framework/cli/commands/migrations.ts +141 -0
  13. package/.framework/cli/commands/pipelines.ts +166 -0
  14. package/.framework/cli/commands/status.ts +197 -0
  15. package/.framework/cli/commands/system.ts +184 -0
  16. package/.framework/cli/commands/test.ts +227 -0
  17. package/.framework/cli/commands/uninstall-app.ts +166 -0
  18. package/.framework/cli/context.ts +268 -0
  19. package/.framework/cli/env-loader.ts +36 -0
  20. package/.framework/cli/index.ts +106 -0
  21. package/.framework/cli/welcome.cjs +45 -0
  22. package/.framework/docs/API.md +384 -0
  23. package/.framework/docs/STABILITY.md +52 -0
  24. package/.framework/docs/admin-routes.md +76 -0
  25. package/.framework/docs/api-docs-progress.md +38 -0
  26. package/.framework/docs/api-governance.md +146 -0
  27. package/.framework/docs/api-testing-results.md +212 -0
  28. package/.framework/docs/apis/admin-configs.md +567 -0
  29. package/.framework/docs/apis/admin-data.md +272 -0
  30. package/.framework/docs/apis/index.md +231 -0
  31. package/.framework/docs/apis/internal.md +295 -0
  32. package/.framework/docs/apis/runtime.md +537 -0
  33. package/.framework/docs/assembly-launch-guide.md +138 -0
  34. package/.framework/docs/audit-results.md +590 -0
  35. package/.framework/docs/authorization-model.md +170 -0
  36. package/.framework/docs/db-api-inventory.md +95 -0
  37. package/.framework/docs/examples/custom-app/README.md +77 -0
  38. package/.framework/docs/examples/custom-function/README.md +27 -0
  39. package/.framework/docs/examples/custom-function/handler.ts +48 -0
  40. package/.framework/docs/examples/custom-webhook/README.md +68 -0
  41. package/.framework/docs/gap-remediation-backlog.md +103 -0
  42. package/.framework/docs/guides/cli-guide.md +224 -0
  43. package/.framework/docs/guides/getting-started.md +103 -0
  44. package/.framework/docs/guides/import-guide.md +193 -0
  45. package/.framework/docs/guides/testing-guide.md +229 -0
  46. package/.framework/docs/permission-examples.md +326 -0
  47. package/.framework/docs/ui-adoption-verification.md +111 -0
  48. package/.framework/docs/ui-api-coverage.md +84 -0
  49. package/.framework/docs/v2-compatibility-audit.md +228 -0
  50. package/.framework/functions/.gitkeep +1 -0
  51. package/.framework/functions/_shared/agent-runner.ts +1097 -0
  52. package/.framework/functions/_shared/app-manifest.ts +184 -0
  53. package/.framework/functions/_shared/audit.ts +150 -0
  54. package/.framework/functions/_shared/db.ts +174 -0
  55. package/.framework/functions/_shared/index.ts +382 -0
  56. package/.framework/functions/_shared/middleware.ts +490 -0
  57. package/.framework/functions/_shared/permissions.ts +1325 -0
  58. package/.framework/functions/_shared/pipeline-runner.ts +731 -0
  59. package/.framework/functions/_shared/principal.ts +760 -0
  60. package/.framework/functions/_shared/schema-utils.ts +967 -0
  61. package/.framework/functions/_shared/testing.ts +258 -0
  62. package/.framework/functions/_shared/trigger-engine.ts +425 -0
  63. package/.framework/functions/_shared/webhook-registration.ts +168 -0
  64. package/.framework/functions/_shared/webhook-registry.ts +129 -0
  65. package/.framework/functions/account-nodes.ts +111 -0
  66. package/.framework/functions/admin-data.ts +606 -0
  67. package/.framework/functions/ai-agents.ts +323 -0
  68. package/.framework/functions/api-keys.ts +376 -0
  69. package/.framework/functions/apps.ts +483 -0
  70. package/.framework/functions/auth.ts +196 -0
  71. package/.framework/functions/debug-auth.ts +107 -0
  72. package/.framework/functions/embeddings.ts +556 -0
  73. package/.framework/functions/integration-routes.ts +523 -0
  74. package/.framework/functions/integrations.ts +319 -0
  75. package/.framework/functions/item-progress.ts +272 -0
  76. package/.framework/functions/logs.ts +438 -0
  77. package/.framework/functions/observability.ts +275 -0
  78. package/.framework/functions/pipeline-executions.ts +494 -0
  79. package/.framework/functions/pipelines.ts +485 -0
  80. package/.framework/functions/prompt-configs.ts +339 -0
  81. package/.framework/functions/roles.ts +387 -0
  82. package/.framework/functions/system-cron.ts +742 -0
  83. package/.framework/functions/system.ts +323 -0
  84. package/.framework/functions/tests.ts +119 -0
  85. package/.framework/functions/timers.ts +357 -0
  86. package/.framework/functions/triggers.ts +563 -0
  87. package/.framework/functions/types.ts +604 -0
  88. package/.framework/migrations/000_foundation.sql +1256 -0
  89. package/.framework/migrations/001_seed.sql +92 -0
  90. package/.framework/migrations/002_seed_constraints.sql +13 -0
  91. package/.framework/migrations/003_auth_user_trigger.sql +59 -0
  92. package/.framework/src/App.tsx +126 -0
  93. package/.framework/src/apps/admin/index.tsx +173 -0
  94. package/.framework/src/components/AppWrapper.tsx +56 -0
  95. package/.framework/src/components/CustomAppLoader.tsx +116 -0
  96. package/.framework/src/components/admin/AdminListPage.tsx +151 -0
  97. package/.framework/src/components/admin/AdminSidebar.tsx +166 -0
  98. package/.framework/src/components/admin/AdminStatsCard.tsx +62 -0
  99. package/.framework/src/components/admin/SortableTableHeader.tsx +42 -0
  100. package/.framework/src/components/app-shell/GenericAppShell.tsx +181 -0
  101. package/.framework/src/components/app-shell/GenericDetailPage.tsx +200 -0
  102. package/.framework/src/components/app-shell/GenericListPage.tsx +116 -0
  103. package/.framework/src/components/app-sidebar.tsx +228 -0
  104. package/.framework/src/components/auth/ProtectedRoute.tsx +88 -0
  105. package/.framework/src/components/layout/AppShell.tsx +91 -0
  106. package/.framework/src/components/layout/Header.tsx +88 -0
  107. package/.framework/src/components/layout/Layout.tsx +95 -0
  108. package/.framework/src/components/layout/Sidebar.tsx +329 -0
  109. package/.framework/src/components/runtime/DataDetailHeader.tsx +77 -0
  110. package/.framework/src/components/runtime/DataDetailPage.tsx +171 -0
  111. package/.framework/src/components/runtime/DataFilters.tsx +91 -0
  112. package/.framework/src/components/runtime/DataHeader.tsx +68 -0
  113. package/.framework/src/components/runtime/DataListPage.tsx +124 -0
  114. package/.framework/src/components/runtime/DataStats.tsx +70 -0
  115. package/.framework/src/components/runtime/DataTable.tsx +174 -0
  116. package/.framework/src/components/runtime/SchemaDetailForm.tsx +134 -0
  117. package/.framework/src/components/runtime/index.ts +18 -0
  118. package/.framework/src/components/search-form.tsx +29 -0
  119. package/.framework/src/components/shared/AgentView.tsx +213 -0
  120. package/.framework/src/components/shared/FieldRenderer.tsx +478 -0
  121. package/.framework/src/components/shared/SchemaFields.tsx +226 -0
  122. package/.framework/src/components/ui/DataTable.tsx +343 -0
  123. package/.framework/src/components/ui/Form.tsx +281 -0
  124. package/.framework/src/components/ui/ItemCard.tsx +296 -0
  125. package/.framework/src/components/ui/ItemListView.tsx +308 -0
  126. package/.framework/src/components/ui/LoadingSpinner.tsx +52 -0
  127. package/.framework/src/components/ui/Modal.tsx +61 -0
  128. package/.framework/src/components/ui/RichTextEditor.tsx +210 -0
  129. package/.framework/src/components/ui/accordion.tsx +82 -0
  130. package/.framework/src/components/ui/alert-dialog.tsx +197 -0
  131. package/.framework/src/components/ui/alert.tsx +76 -0
  132. package/.framework/src/components/ui/aspect-ratio.tsx +11 -0
  133. package/.framework/src/components/ui/avatar.tsx +110 -0
  134. package/.framework/src/components/ui/badge.tsx +49 -0
  135. package/.framework/src/components/ui/breadcrumb.tsx +122 -0
  136. package/.framework/src/components/ui/button-group.tsx +83 -0
  137. package/.framework/src/components/ui/button.tsx +65 -0
  138. package/.framework/src/components/ui/calendar.tsx +222 -0
  139. package/.framework/src/components/ui/card.tsx +100 -0
  140. package/.framework/src/components/ui/carousel.tsx +240 -0
  141. package/.framework/src/components/ui/chart.tsx +373 -0
  142. package/.framework/src/components/ui/checkbox.tsx +31 -0
  143. package/.framework/src/components/ui/collapsible.tsx +33 -0
  144. package/.framework/src/components/ui/combobox.tsx +299 -0
  145. package/.framework/src/components/ui/command.tsx +193 -0
  146. package/.framework/src/components/ui/context-menu.tsx +261 -0
  147. package/.framework/src/components/ui/dialog.tsx +165 -0
  148. package/.framework/src/components/ui/direction.tsx +22 -0
  149. package/.framework/src/components/ui/drawer.tsx +132 -0
  150. package/.framework/src/components/ui/dropdown-menu.tsx +269 -0
  151. package/.framework/src/components/ui/empty.tsx +104 -0
  152. package/.framework/src/components/ui/field.tsx +238 -0
  153. package/.framework/src/components/ui/hover-card.tsx +42 -0
  154. package/.framework/src/components/ui/input-group.tsx +153 -0
  155. package/.framework/src/components/ui/input-otp.tsx +87 -0
  156. package/.framework/src/components/ui/input.tsx +19 -0
  157. package/.framework/src/components/ui/item.tsx +196 -0
  158. package/.framework/src/components/ui/kbd.tsx +26 -0
  159. package/.framework/src/components/ui/label.tsx +22 -0
  160. package/.framework/src/components/ui/menubar.tsx +277 -0
  161. package/.framework/src/components/ui/native-select.tsx +61 -0
  162. package/.framework/src/components/ui/navigation-menu.tsx +164 -0
  163. package/.framework/src/components/ui/pagination.tsx +129 -0
  164. package/.framework/src/components/ui/popover.tsx +87 -0
  165. package/.framework/src/components/ui/progress.tsx +31 -0
  166. package/.framework/src/components/ui/radio-group.tsx +42 -0
  167. package/.framework/src/components/ui/resizable.tsx +50 -0
  168. package/.framework/src/components/ui/scroll-area.tsx +53 -0
  169. package/.framework/src/components/ui/select.tsx +195 -0
  170. package/.framework/src/components/ui/separator.tsx +26 -0
  171. package/.framework/src/components/ui/sheet.tsx +145 -0
  172. package/.framework/src/components/ui/sidebar.tsx +706 -0
  173. package/.framework/src/components/ui/skeleton.tsx +13 -0
  174. package/.framework/src/components/ui/slider.tsx +59 -0
  175. package/.framework/src/components/ui/sonner.tsx +47 -0
  176. package/.framework/src/components/ui/spinner.tsx +10 -0
  177. package/.framework/src/components/ui/switch.tsx +33 -0
  178. package/.framework/src/components/ui/table-primitives.tsx +141 -0
  179. package/.framework/src/components/ui/table.tsx +114 -0
  180. package/.framework/src/components/ui/tabs.tsx +90 -0
  181. package/.framework/src/components/ui/textarea.tsx +18 -0
  182. package/.framework/src/components/ui/toggle-group.tsx +89 -0
  183. package/.framework/src/components/ui/toggle.tsx +45 -0
  184. package/.framework/src/components/ui/tooltip.tsx +57 -0
  185. package/.framework/src/contexts/AppContext.tsx +133 -0
  186. package/.framework/src/contexts/AuthContext.tsx +371 -0
  187. package/.framework/src/hooks/use-mobile.ts +19 -0
  188. package/.framework/src/hooks/useApi.ts +526 -0
  189. package/.framework/src/hooks/useApps.ts +114 -0
  190. package/.framework/src/hooks/useEntityList.ts +190 -0
  191. package/.framework/src/hooks/useEntityRecord.ts +308 -0
  192. package/.framework/src/hooks/useForm.ts +307 -0
  193. package/.framework/src/hooks/useListSchema.ts +264 -0
  194. package/.framework/src/hooks/useSchemaRecord.ts +223 -0
  195. package/.framework/src/index.css +128 -0
  196. package/.framework/src/lib/api.ts +156 -0
  197. package/.framework/src/lib/supabase.ts +94 -0
  198. package/.framework/src/lib/utils.ts +317 -0
  199. package/.framework/src/main.tsx +27 -0
  200. package/.framework/src/pages/DashboardPage.tsx +181 -0
  201. package/.framework/src/pages/NotFoundPage.tsx +39 -0
  202. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +161 -0
  203. package/.framework/src/pages/admin/AIAgentsPage.tsx +318 -0
  204. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +199 -0
  205. package/.framework/src/pages/admin/APIKeysPage.tsx +303 -0
  206. package/.framework/src/pages/admin/AlertsConfigPage.tsx +523 -0
  207. package/.framework/src/pages/admin/AppDetailPage.tsx +493 -0
  208. package/.framework/src/pages/admin/AppsPage.tsx +355 -0
  209. package/.framework/src/pages/admin/DesignedPage.tsx +491 -0
  210. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +534 -0
  211. package/.framework/src/pages/admin/EmbeddingsPage.tsx +424 -0
  212. package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +176 -0
  213. package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +109 -0
  214. package/.framework/src/pages/admin/IntegratedDashboard.tsx +402 -0
  215. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +187 -0
  216. package/.framework/src/pages/admin/IntegrationsPage.tsx +301 -0
  217. package/.framework/src/pages/admin/LogsPage.tsx +283 -0
  218. package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +85 -0
  219. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +470 -0
  220. package/.framework/src/pages/admin/PipelineDetailPage.tsx +183 -0
  221. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +279 -0
  222. package/.framework/src/pages/admin/PipelinesPage.tsx +390 -0
  223. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +299 -0
  224. package/.framework/src/pages/admin/PromptConfigsPage.tsx +292 -0
  225. package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +434 -0
  226. package/.framework/src/pages/admin/RoleDetailPage.tsx +273 -0
  227. package/.framework/src/pages/admin/RolesPage.tsx +292 -0
  228. package/.framework/src/pages/admin/SelectTestPage.tsx +61 -0
  229. package/.framework/src/pages/admin/ShadcnTestPage.tsx +588 -0
  230. package/.framework/src/pages/admin/SimpleDashboard.tsx +387 -0
  231. package/.framework/src/pages/admin/TestRunDetailPage.tsx +172 -0
  232. package/.framework/src/pages/admin/TestingDashboard.tsx +257 -0
  233. package/.framework/src/pages/admin/TimerDetailPage.tsx +151 -0
  234. package/.framework/src/pages/admin/TimersPage.tsx +376 -0
  235. package/.framework/src/pages/admin/TriggerDetailPage.tsx +149 -0
  236. package/.framework/src/pages/admin/TriggersPage.tsx +381 -0
  237. package/.framework/src/pages/admin/TypeDetailPage.tsx +694 -0
  238. package/.framework/src/pages/admin/TypesPage.tsx +295 -0
  239. package/.framework/src/pages/auth/LoginPage.tsx +188 -0
  240. package/.framework/src/pages/auth/RegisterPage.tsx +163 -0
  241. package/.framework/src/pages/spine-framework/APIPage.tsx +17 -0
  242. package/.framework/src/pages/spine-framework/CLIPage.tsx +25 -0
  243. package/.framework/src/types/auth.ts +125 -0
  244. package/.framework/src/types/types.ts +407 -0
  245. package/STRUCTURE.md +150 -0
  246. package/config/components.json +25 -0
  247. package/config/deno.lock +108 -0
  248. package/config/package-lock.json +17183 -0
  249. package/config/postcss.config.cjs +10 -0
  250. package/config/tailwind.config.cjs +78 -0
  251. package/config/tsconfig.build.json +32 -0
  252. package/config/tsconfig.cli.json +18 -0
  253. package/config/tsconfig.json +41 -0
  254. package/config/tsconfig.node.json +17 -0
  255. package/config/tsconfig.node.tsbuildinfo +1 -0
  256. package/config/tsconfig.tsbuildinfo +1 -0
  257. package/config/typedoc.json +16 -0
  258. package/config/vite.config.d.ts +2 -0
  259. package/config/vite.config.ts +72 -0
  260. package/dist/cli/commands/agents.d.ts +39 -0
  261. package/dist/cli/commands/agents.d.ts.map +1 -0
  262. package/dist/cli/commands/auth.d.ts +36 -0
  263. package/dist/cli/commands/auth.d.ts.map +1 -0
  264. package/dist/cli/commands/create-app.d.ts +23 -0
  265. package/dist/cli/commands/create-app.d.ts.map +1 -0
  266. package/dist/cli/commands/dev.d.ts +39 -0
  267. package/dist/cli/commands/dev.d.ts.map +1 -0
  268. package/dist/cli/commands/doctor.d.ts +42 -0
  269. package/dist/cli/commands/doctor.d.ts.map +1 -0
  270. package/dist/cli/commands/generate.d.ts +36 -0
  271. package/dist/cli/commands/generate.d.ts.map +1 -0
  272. package/dist/cli/commands/init.d.ts +30 -0
  273. package/dist/cli/commands/init.d.ts.map +1 -0
  274. package/dist/cli/commands/install-app.d.ts +30 -0
  275. package/dist/cli/commands/install-app.d.ts.map +1 -0
  276. package/dist/cli/commands/items.d.ts +45 -0
  277. package/dist/cli/commands/items.d.ts.map +1 -0
  278. package/dist/cli/commands/migrations.d.ts +41 -0
  279. package/dist/cli/commands/migrations.d.ts.map +1 -0
  280. package/dist/cli/commands/pipelines.d.ts +40 -0
  281. package/dist/cli/commands/pipelines.d.ts.map +1 -0
  282. package/dist/cli/commands/status.d.ts +23 -0
  283. package/dist/cli/commands/status.d.ts.map +1 -0
  284. package/dist/cli/commands/system.d.ts +29 -0
  285. package/dist/cli/commands/system.d.ts.map +1 -0
  286. package/dist/cli/commands/test.d.ts +46 -0
  287. package/dist/cli/commands/test.d.ts.map +1 -0
  288. package/dist/cli/commands/uninstall-app.d.ts +23 -0
  289. package/dist/cli/commands/uninstall-app.d.ts.map +1 -0
  290. package/dist/cli/context.d.ts +88 -0
  291. package/dist/cli/context.d.ts.map +1 -0
  292. package/dist/cli/env-loader.d.ts +14 -0
  293. package/dist/cli/env-loader.d.ts.map +1 -0
  294. package/dist/cli/index.d.ts +41 -0
  295. package/dist/cli/index.d.ts.map +1 -0
  296. package/dist/functions/_shared/agent-runner.d.ts +156 -0
  297. package/dist/functions/_shared/agent-runner.d.ts.map +1 -0
  298. package/dist/functions/_shared/app-manifest.d.ts +68 -0
  299. package/dist/functions/_shared/app-manifest.d.ts.map +1 -0
  300. package/dist/functions/_shared/audit.d.ts +91 -0
  301. package/dist/functions/_shared/audit.d.ts.map +1 -0
  302. package/dist/functions/_shared/db.d.ts +125 -0
  303. package/dist/functions/_shared/db.d.ts.map +1 -0
  304. package/dist/functions/_shared/index.d.ts +298 -0
  305. package/dist/functions/_shared/index.d.ts.map +1 -0
  306. package/dist/functions/_shared/middleware.d.ts +315 -0
  307. package/dist/functions/_shared/middleware.d.ts.map +1 -0
  308. package/dist/functions/_shared/permissions.d.ts +626 -0
  309. package/dist/functions/_shared/permissions.d.ts.map +1 -0
  310. package/dist/functions/_shared/pipeline-runner.d.ts +124 -0
  311. package/dist/functions/_shared/pipeline-runner.d.ts.map +1 -0
  312. package/dist/functions/_shared/principal.d.ts +284 -0
  313. package/dist/functions/_shared/principal.d.ts.map +1 -0
  314. package/dist/functions/_shared/schema-utils.d.ts +181 -0
  315. package/dist/functions/_shared/schema-utils.d.ts.map +1 -0
  316. package/dist/functions/_shared/testing.d.ts +172 -0
  317. package/dist/functions/_shared/testing.d.ts.map +1 -0
  318. package/dist/functions/_shared/trigger-engine.d.ts +140 -0
  319. package/dist/functions/_shared/trigger-engine.d.ts.map +1 -0
  320. package/dist/functions/_shared/webhook-registration.d.ts +81 -0
  321. package/dist/functions/_shared/webhook-registration.d.ts.map +1 -0
  322. package/dist/functions/_shared/webhook-registry.d.ts +57 -0
  323. package/dist/functions/_shared/webhook-registry.d.ts.map +1 -0
  324. package/dist/functions/account-nodes.d.ts +48 -0
  325. package/dist/functions/account-nodes.d.ts.map +1 -0
  326. package/dist/functions/admin-data.d.ts +178 -0
  327. package/dist/functions/admin-data.d.ts.map +1 -0
  328. package/dist/functions/ai-agents.d.ts +125 -0
  329. package/dist/functions/ai-agents.d.ts.map +1 -0
  330. package/dist/functions/api-keys.d.ts +140 -0
  331. package/dist/functions/api-keys.d.ts.map +1 -0
  332. package/dist/functions/apps.d.ts +163 -0
  333. package/dist/functions/apps.d.ts.map +1 -0
  334. package/dist/functions/auth.d.ts +74 -0
  335. package/dist/functions/auth.d.ts.map +1 -0
  336. package/dist/functions/debug-auth.d.ts +33 -0
  337. package/dist/functions/debug-auth.d.ts.map +1 -0
  338. package/dist/functions/embeddings.d.ts +205 -0
  339. package/dist/functions/embeddings.d.ts.map +1 -0
  340. package/dist/functions/integration-routes.d.ts +45 -0
  341. package/dist/functions/integration-routes.d.ts.map +1 -0
  342. package/dist/functions/integrations.d.ts +124 -0
  343. package/dist/functions/integrations.d.ts.map +1 -0
  344. package/dist/functions/item-progress.d.ts +41 -0
  345. package/dist/functions/item-progress.d.ts.map +1 -0
  346. package/dist/functions/logs.d.ts +162 -0
  347. package/dist/functions/logs.d.ts.map +1 -0
  348. package/dist/functions/observability.d.ts +123 -0
  349. package/dist/functions/observability.d.ts.map +1 -0
  350. package/dist/functions/pipeline-executions.d.ts +190 -0
  351. package/dist/functions/pipeline-executions.d.ts.map +1 -0
  352. package/dist/functions/pipelines.d.ts +171 -0
  353. package/dist/functions/pipelines.d.ts.map +1 -0
  354. package/dist/functions/prompt-configs.d.ts +125 -0
  355. package/dist/functions/prompt-configs.d.ts.map +1 -0
  356. package/dist/functions/roles.d.ts +118 -0
  357. package/dist/functions/roles.d.ts.map +1 -0
  358. package/dist/functions/system-cron.d.ts +65 -0
  359. package/dist/functions/system-cron.d.ts.map +1 -0
  360. package/dist/functions/system.d.ts +29 -0
  361. package/dist/functions/system.d.ts.map +1 -0
  362. package/dist/functions/tests.d.ts +28 -0
  363. package/dist/functions/tests.d.ts.map +1 -0
  364. package/dist/functions/timers.d.ts +139 -0
  365. package/dist/functions/timers.d.ts.map +1 -0
  366. package/dist/functions/triggers.d.ts +203 -0
  367. package/dist/functions/triggers.d.ts.map +1 -0
  368. package/dist/functions/types.d.ts +151 -0
  369. package/dist/functions/types.d.ts.map +1 -0
  370. package/dist/src/types/types.d.ts +364 -0
  371. package/dist/src/types/types.d.ts.map +1 -0
  372. package/package.json +192 -0
  373. package/scripts/app-install-cli.ts +286 -0
  374. package/scripts/assemble-frontend.sh +79 -0
  375. package/scripts/assemble-functions.sh +62 -0
  376. package/scripts/assemble.sh +35 -0
  377. package/scripts/boundary-check.sh +106 -0
  378. package/scripts/build-manifest.sh +80 -0
  379. package/scripts/check-core-integrity.sh +82 -0
  380. package/scripts/ingest-chunks.cjs +202 -0
  381. package/scripts/kb-chunk-parser.cjs +312 -0
  382. package/scripts/kb-chunk-parser.ts +330 -0
  383. package/scripts/load-test-app-install.ts +484 -0
  384. package/scripts/netlify-dev-wrapper.sh +22 -0
  385. package/scripts/verify-integrity.sh +69 -0
@@ -0,0 +1,478 @@
1
+ /**
2
+ * @module src/components/shared/FieldRenderer
3
+ * @audience installer
4
+ * @layer frontend-component
5
+ * @stability stable
6
+ *
7
+ * Low-level field rendering primitive. Given a `FieldDefinition` and a
8
+ * current value, renders the appropriate input widget (edit mode) or
9
+ * display element (read-only mode).
10
+ *
11
+ * **Render type resolution** (`resolveRenderType`):
12
+ * 1. If `displayType` is set (from the view config), it maps to a canonical
13
+ * render type first — this lets a view override how a field looks without
14
+ * changing the underlying `FieldDefinition`.
15
+ * 2. Falls back to `field.data_type`.
16
+ *
17
+ * **Supported render types:**
18
+ * `text`, `textarea`, `email`, `phone`, `url`, `number`, `date`,
19
+ * `datetime`, `select`, `multiselect`, `radio`, `checkbox`, `json`,
20
+ * `file`, `color`, `range`
21
+ *
22
+ * **Error display:** when `error` is set, a red helper text is shown below
23
+ * the field and `border-red-500` is applied where applicable.
24
+ *
25
+ * **Label + description:** rendered above / below the input respectively,
26
+ * with a red `*` suffix when `field.required` is true.
27
+ *
28
+ * @seeAlso src/components/shared/SchemaFields.tsx (mounts this component)
29
+ * @seeAlso src/types/types.ts (FieldDefinition)
30
+ */
31
+
32
+ import React from 'react'
33
+ import { FieldDefinition } from '../../types/types'
34
+
35
+ /**
36
+ * Props for `FieldRenderer`.
37
+ *
38
+ * @prop field - Full field definition including data_type, options, validation
39
+ * @prop value - Current controlled value
40
+ * @prop onChange - Value change callback (omit or pass `undefined` for read-only)
41
+ * @prop onBlur - Optional blur callback for validation triggers
42
+ * @prop readonly - If true, renders a display element instead of an input
43
+ * @prop error - Validation error message to display below the field
44
+ * @prop displayType - View-config widget override (e.g. `'textarea'`, `'select'`)
45
+ */
46
+ interface FieldRendererProps {
47
+ field: FieldDefinition
48
+ value: any
49
+ onChange?: (value: any) => void
50
+ onBlur?: () => void
51
+ readonly?: boolean
52
+ error?: string
53
+ displayType?: string // From view config — controls rendering without polluting field data contract
54
+ }
55
+
56
+ /**
57
+ * Resolves the canonical render type for a field.
58
+ *
59
+ * `displayType` (from view config) takes precedence over `field.data_type`
60
+ * so view authors can choose a different widget without changing the
61
+ * underlying schema.
62
+ *
63
+ * @param field - Field definition
64
+ * @param displayType - Optional view-config widget override
65
+ * @returns Canonical render type string (e.g. `'text'`, `'select'`, `'checkbox'`)
66
+ */
67
+ function resolveRenderType(field: FieldDefinition, displayType?: string): string {
68
+ // displayType from view config overrides data_type for rendering decisions
69
+ if (displayType) {
70
+ switch (displayType) {
71
+ case 'textarea':
72
+ case 'rich_text':
73
+ return 'textarea'
74
+ case 'select':
75
+ return 'select'
76
+ case 'multiselect':
77
+ return 'multiselect'
78
+ case 'radio':
79
+ return 'radio'
80
+ case 'checkbox':
81
+ case 'switch':
82
+ return 'checkbox'
83
+ case 'date_picker':
84
+ return 'date'
85
+ case 'datetime_picker':
86
+ return 'datetime'
87
+ case 'color_picker':
88
+ return 'color'
89
+ case 'range_slider':
90
+ return 'range'
91
+ case 'file_upload':
92
+ case 'image_upload':
93
+ return 'file'
94
+ }
95
+ }
96
+ // Fall back to data_type
97
+ switch (field.data_type) {
98
+ case 'textarea':
99
+ case 'rich_text':
100
+ return 'textarea'
101
+ case 'email':
102
+ return 'email'
103
+ case 'phone':
104
+ return 'phone'
105
+ case 'url':
106
+ return 'url'
107
+ case 'number':
108
+ case 'currency':
109
+ case 'range':
110
+ return 'number'
111
+ case 'date':
112
+ return 'date'
113
+ case 'datetime':
114
+ return 'datetime'
115
+ case 'boolean':
116
+ case 'checkbox':
117
+ return 'checkbox'
118
+ case 'select':
119
+ return 'select'
120
+ case 'multiselect':
121
+ return 'multiselect'
122
+ case 'radio':
123
+ return 'radio'
124
+ case 'color':
125
+ return 'color'
126
+ case 'file':
127
+ case 'image':
128
+ return 'file'
129
+ case 'json':
130
+ return 'json'
131
+ default:
132
+ return 'text'
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Renders a single schema field as an input widget or read-only display.
138
+ *
139
+ * @param props - `FieldRendererProps`
140
+ * @returns Label + field widget + description + error message
141
+ * @sideEffects none (delegates changes to `onChange`)
142
+ */
143
+ export function FieldRenderer({ field, value, onChange, readonly = false, error, displayType }: FieldRendererProps) {
144
+ const renderType = resolveRenderType(field, displayType)
145
+
146
+ const renderField = () => {
147
+ switch (renderType) {
148
+ case 'text':
149
+ return readonly ? (
150
+ <div className={`text-slate-900 ${error ? 'border-red-500' : ''}`}>
151
+ {value || <span className="text-slate-400 italic">—</span>}
152
+ </div>
153
+ ) : (
154
+ <input
155
+ type="text"
156
+ value={value || ''}
157
+ onChange={(e) => onChange?.(e.target.value)}
158
+ className={`input ${error ? 'border-red-500' : ''}`}
159
+ placeholder={field.placeholder}
160
+ />
161
+ )
162
+
163
+ case 'textarea':
164
+ return readonly ? (
165
+ <div className={`text-slate-900 whitespace-pre-wrap ${error ? 'border-red-500' : ''}`}>
166
+ {value || <span className="text-slate-400 italic">—</span>}
167
+ </div>
168
+ ) : (
169
+ <textarea
170
+ value={value || ''}
171
+ onChange={(e) => onChange?.(e.target.value)}
172
+ className={`textarea ${error ? 'border-red-500' : ''}`}
173
+ placeholder={field.placeholder}
174
+ rows={field.rows || 3}
175
+ />
176
+ )
177
+
178
+ case 'email':
179
+ return readonly ? (
180
+ <div className={`text-slate-900 ${error ? 'border-red-500' : ''}`}>
181
+ {value ? <a href={`mailto:${value}`} className="text-blue-600 hover:text-blue-800">{value}</a> : <span className="text-slate-400 italic">—</span>}
182
+ </div>
183
+ ) : (
184
+ <input
185
+ type="email"
186
+ value={value || ''}
187
+ onChange={(e) => onChange?.(e.target.value)}
188
+ className={`input ${error ? 'border-red-500' : ''}`}
189
+ placeholder={field.placeholder}
190
+ />
191
+ )
192
+
193
+ case 'phone':
194
+ return readonly ? (
195
+ <div className={`text-slate-900 ${error ? 'border-red-500' : ''}`}>
196
+ {value || <span className="text-slate-400 italic">—</span>}
197
+ </div>
198
+ ) : (
199
+ <input
200
+ type="tel"
201
+ value={value || ''}
202
+ onChange={(e) => onChange?.(e.target.value)}
203
+ className={`input ${error ? 'border-red-500' : ''}`}
204
+ placeholder={field.placeholder}
205
+ />
206
+ )
207
+
208
+ case 'number':
209
+ return (
210
+ <input
211
+ type="number"
212
+ value={value ?? ''}
213
+ onChange={(e) => onChange?.(e.target.value ? Number(e.target.value) : null)}
214
+ readOnly={readonly}
215
+ className={`input ${error ? 'border-red-500' : ''}`}
216
+ placeholder={field.placeholder}
217
+ min={field.min ?? field.validation?.min}
218
+ max={field.max ?? field.validation?.max}
219
+ step={field.step ?? field.validation?.step}
220
+ />
221
+ )
222
+
223
+ case 'date':
224
+ return (
225
+ <input
226
+ type="date"
227
+ value={value ? new Date(value).toISOString().split('T')[0] : ''}
228
+ onChange={(e) => onChange?.(e.target.value || null)}
229
+ readOnly={readonly}
230
+ className={`input ${error ? 'border-red-500' : ''}`}
231
+ />
232
+ )
233
+
234
+ case 'datetime':
235
+ return (
236
+ <input
237
+ type="datetime-local"
238
+ value={value ? new Date(value).toISOString().slice(0, 16) : ''}
239
+ onChange={(e) => onChange?.(e.target.value || null)}
240
+ readOnly={readonly}
241
+ className={`input ${error ? 'border-red-500' : ''}`}
242
+ />
243
+ )
244
+
245
+ case 'select':
246
+ return readonly ? (
247
+ <div className={`text-slate-900 ${error ? 'border-red-500' : ''}`}>
248
+ {(() => {
249
+ if (!value) return <span className="text-slate-400 italic">—</span>
250
+ const option = field.options?.find((opt) => {
251
+ const optVal = typeof opt === 'string' ? opt : opt.value
252
+ return optVal === value
253
+ })
254
+ return typeof option === 'string' ? option : option?.label || value
255
+ })()}
256
+ </div>
257
+ ) : (
258
+ <select
259
+ value={value || ''}
260
+ onChange={(e) => onChange?.(e.target.value)}
261
+ className={`select ${error ? 'border-red-500' : ''}`}
262
+ >
263
+ <option value="">Select...</option>
264
+ {field.options?.map((option) => {
265
+ const optVal = typeof option === 'string' ? option : option.value
266
+ const optLabel = typeof option === 'string' ? option : option.label
267
+ return (
268
+ <option key={optVal} value={optVal}>
269
+ {optLabel}
270
+ </option>
271
+ )
272
+ })}
273
+ </select>
274
+ )
275
+
276
+ case 'multiselect':
277
+ return (
278
+ <div className="space-y-2">
279
+ {field.options?.map((option) => {
280
+ const optionValue = typeof option === 'string' ? option : option.value
281
+ const optionLabel = typeof option === 'string' ? option : option.label
282
+ return (
283
+ <label key={optionValue} className="flex items-center">
284
+ <input
285
+ type="checkbox"
286
+ checked={Array.isArray(value) && value.includes(optionValue)}
287
+ onChange={(e) => {
288
+ const currentValue = Array.isArray(value) ? value : []
289
+ if (e.target.checked) {
290
+ onChange?.([...currentValue, optionValue])
291
+ } else {
292
+ onChange?.(currentValue.filter((v: string) => v !== optionValue))
293
+ }
294
+ }}
295
+ disabled={readonly}
296
+ className="mr-2"
297
+ />
298
+ <span>{optionLabel}</span>
299
+ </label>
300
+ )
301
+ })}
302
+ </div>
303
+ )
304
+
305
+ case 'radio':
306
+ return (
307
+ <div className="space-y-2">
308
+ {field.options?.map((option) => {
309
+ const optionValue = typeof option === 'string' ? option : option.value
310
+ const optionLabel = typeof option === 'string' ? option : option.label
311
+ return (
312
+ <label key={optionValue} className="flex items-center">
313
+ <input
314
+ type="radio"
315
+ name={field.name}
316
+ value={optionValue}
317
+ checked={value === optionValue}
318
+ onChange={(e) => onChange?.(e.target.value)}
319
+ disabled={readonly}
320
+ className="mr-2"
321
+ />
322
+ <span>{optionLabel}</span>
323
+ </label>
324
+ )
325
+ })}
326
+ </div>
327
+ )
328
+
329
+ case 'checkbox':
330
+ return readonly ? (
331
+ <div className={`text-slate-900 ${error ? 'border-red-500' : ''}`}>
332
+ {Boolean(value) ? 'Yes' : 'No'}
333
+ </div>
334
+ ) : (
335
+ <label className="flex items-center">
336
+ <input
337
+ type="checkbox"
338
+ checked={Boolean(value)}
339
+ onChange={(e) => onChange?.(e.target.checked)}
340
+ className="mr-2"
341
+ />
342
+ <span>{field.label}</span>
343
+ </label>
344
+ )
345
+
346
+ case 'json':
347
+ return readonly ? (
348
+ <pre className="bg-slate-100 p-3 rounded-md text-sm overflow-x-auto">
349
+ {JSON.stringify(value, null, 2)}
350
+ </pre>
351
+ ) : (
352
+ <textarea
353
+ value={typeof value === 'string' ? value : JSON.stringify(value, null, 2)}
354
+ onChange={(e) => {
355
+ try {
356
+ onChange?.(JSON.parse(e.target.value))
357
+ } catch {
358
+ // Invalid JSON, don't update
359
+ }
360
+ }}
361
+ className={`textarea font-mono text-sm ${error ? 'border-red-500' : ''}`}
362
+ rows={field.rows || 6}
363
+ placeholder="Enter valid JSON"
364
+ />
365
+ )
366
+
367
+ case 'url':
368
+ return readonly ? (
369
+ <div className={`text-slate-900 ${error ? 'border-red-500' : ''}`}>
370
+ {value ? <a href={value} target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:text-blue-800">{value}</a> : <span className="text-slate-400 italic">—</span>}
371
+ </div>
372
+ ) : (
373
+ <input
374
+ type="url"
375
+ value={value || ''}
376
+ onChange={(e) => onChange?.(e.target.value)}
377
+ className={`input ${error ? 'border-red-500' : ''}`}
378
+ placeholder={field.placeholder || 'https://example.com'}
379
+ />
380
+ )
381
+
382
+ case 'file':
383
+ return readonly ? (
384
+ value ? (
385
+ <div className="text-sm">
386
+ <a href={value} target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:text-blue-800">
387
+ View File
388
+ </a>
389
+ </div>
390
+ ) : (
391
+ <span className="text-slate-500">No file</span>
392
+ )
393
+ ) : (
394
+ <input
395
+ type="file"
396
+ onChange={(e) => {
397
+ const file = e.target.files?.[0]
398
+ if (file) onChange?.(file.name)
399
+ }}
400
+ className={`input ${error ? 'border-red-500' : ''}`}
401
+ />
402
+ )
403
+
404
+ case 'color':
405
+ return (
406
+ <div className="flex items-center space-x-2">
407
+ <input
408
+ type="color"
409
+ value={value || '#000000'}
410
+ onChange={(e) => onChange?.(e.target.value)}
411
+ disabled={readonly}
412
+ className="h-10 w-20"
413
+ />
414
+ <input
415
+ type="text"
416
+ value={value || ''}
417
+ onChange={(e) => onChange?.(e.target.value)}
418
+ readOnly={readonly}
419
+ className={`input flex-1 ${error ? 'border-red-500' : ''}`}
420
+ placeholder="#000000"
421
+ />
422
+ </div>
423
+ )
424
+
425
+ case 'range':
426
+ return (
427
+ <div className="space-y-2">
428
+ <input
429
+ type="range"
430
+ value={value ?? field.min ?? field.validation?.min ?? 0}
431
+ onChange={(e) => onChange?.(Number(e.target.value))}
432
+ disabled={readonly}
433
+ className="w-full"
434
+ min={field.min ?? field.validation?.min}
435
+ max={field.max ?? field.validation?.max}
436
+ step={field.step ?? field.validation?.step}
437
+ />
438
+ <div className="text-sm text-slate-600">
439
+ Value: {value ?? field.min ?? 0}
440
+ </div>
441
+ </div>
442
+ )
443
+
444
+ default:
445
+ return (
446
+ <input
447
+ type="text"
448
+ value={value || ''}
449
+ onChange={(e) => onChange?.(e.target.value)}
450
+ readOnly={readonly}
451
+ className={`input ${error ? 'border-red-500' : ''}`}
452
+ placeholder={field.placeholder}
453
+ />
454
+ )
455
+ }
456
+ }
457
+
458
+ return (
459
+ <div className="space-y-1">
460
+ {field.label && (
461
+ <label className="label">
462
+ {field.label}
463
+ {field.required && <span className="text-red-500 ml-1">*</span>}
464
+ </label>
465
+ )}
466
+
467
+ {renderField()}
468
+
469
+ {field.description && (
470
+ <p className="text-xs text-slate-500">{field.description}</p>
471
+ )}
472
+
473
+ {error && (
474
+ <p className="text-xs text-red-600">{error}</p>
475
+ )}
476
+ </div>
477
+ )
478
+ }
@@ -0,0 +1,226 @@
1
+ /**
2
+ * @module src/components/shared/SchemaFields
3
+ * @audience installer
4
+ * @layer frontend-component
5
+ * @stability stable
6
+ *
7
+ * Schema-driven field grid. Renders an ordered list of `FieldDefinition`
8
+ * entries via `FieldRenderer`, managing the name→value mapping and
9
+ * propagating `onChange` calls back to the parent.
10
+ *
11
+ * **Value source resolution** (per field):
12
+ * - `system` flag set → `data[name]` (top-level column)
13
+ * - `system` flag unset → `data.data?.[name] ?? data[name]` (JSONB field
14
+ * with column fallback)
15
+ *
16
+ * **Layout:** two-column responsive grid by default (`twoColumn=true`);
17
+ * pass `twoColumn=false` for a single-column stacked layout.
18
+ *
19
+ * **Exports:**
20
+ * - `SchemaFields` — full editable/read-only field grid
21
+ * - `SchemaFieldDisplay` — read-only single-field key:value display row
22
+ * - `SchemaField` (unexported) — internal wrapper binding name to value
23
+ *
24
+ * @seeAlso src/components/shared/FieldRenderer.tsx
25
+ * @seeAlso src/types/types.ts (FieldDefinition)
26
+ * @seeAlso src/components/runtime/SchemaDetailForm.tsx (primary consumer)
27
+ */
28
+
29
+ import React from 'react'
30
+ import { FieldDefinition } from '../../types/types'
31
+ import { FieldRenderer } from './FieldRenderer'
32
+
33
+ /**
34
+ * Props for `SchemaFields`.
35
+ *
36
+ * @prop fields - Ordered array of field definitions to render
37
+ * @prop data - Record containing current field values; may be flat or
38
+ * nested under a `.data` key for JSONB fields
39
+ * @prop onChange - `(name, value)` callback; omit for pure read-only display
40
+ * @prop readonly - Passes read-only mode down to every `FieldRenderer`
41
+ * @prop errors - Per-field validation error messages
42
+ * @prop twoColumn - Two-column responsive grid (default: `true`)
43
+ * @prop displayTypes - Widget override map keyed by field name (from view config)
44
+ */
45
+ interface SchemaFieldsProps {
46
+ fields: FieldDefinition[]
47
+ data: Record<string, any>
48
+ onChange?: (name: string, value: any) => void
49
+ readonly?: boolean
50
+ errors?: Record<string, string>
51
+ /** Render fields in a two-column grid (default: true) */
52
+ twoColumn?: boolean
53
+ /** display_type per field key, sourced from view config — never from field definitions */
54
+ displayTypes?: Record<string, string>
55
+ }
56
+
57
+ /**
58
+ * Renders a full schema field grid.
59
+ *
60
+ * @param props - `SchemaFieldsProps`
61
+ * @returns Two-column (or single-column) field grid, or an empty-state message
62
+ * @sideEffects none (delegates changes to `onChange`)
63
+ */
64
+ export function SchemaFields({
65
+ fields,
66
+ data,
67
+ onChange,
68
+ readonly = false,
69
+ errors = {},
70
+ twoColumn = true,
71
+ displayTypes = {}
72
+ }: SchemaFieldsProps) {
73
+ if (!fields || fields.length === 0) {
74
+ return (
75
+ <p className="text-sm text-slate-500 italic">No schema fields defined for this type.</p>
76
+ )
77
+ }
78
+
79
+ return (
80
+ <div className={twoColumn ? 'grid grid-cols-1 md:grid-cols-2 gap-4' : 'space-y-4'}>
81
+ {fields.filter(f => !!f.name).map((field) => {
82
+ const name = field.name!
83
+ return (
84
+ <SchemaField
85
+ key={name}
86
+ field={field}
87
+ value={field.system ? data[name] : (data.data?.[name] ?? data[name])}
88
+ onChange={onChange}
89
+ readonly={readonly || field.readonly}
90
+ error={errors[name]}
91
+ displayType={displayTypes[name]}
92
+ />
93
+ )
94
+ })}
95
+ </div>
96
+ )
97
+ }
98
+
99
+ /** Internal props for the `SchemaField` name-binding wrapper. */
100
+ interface SchemaFieldProps {
101
+ field: FieldDefinition
102
+ value: any
103
+ onChange?: (name: string, value: any) => void
104
+ readonly?: boolean
105
+ error?: string
106
+ displayType?: string
107
+ }
108
+
109
+ function SchemaField({ field, value, onChange, readonly, error, displayType }: SchemaFieldProps) {
110
+ return (
111
+ <FieldRenderer
112
+ field={field}
113
+ value={value}
114
+ onChange={readonly ? undefined : (val) => field.name && onChange?.(field.name, val)}
115
+ readonly={readonly}
116
+ error={error}
117
+ displayType={displayType}
118
+ />
119
+ )
120
+ }
121
+
122
+ /**
123
+ * Read-only display of a single schema field value as a label:value row.
124
+ * Useful for compact detail views outside the full form layout.
125
+ *
126
+ * @param field - Field definition (used for label and data_type formatting)
127
+ * @param value - Raw value to display
128
+ * @returns A `<div>` with label on the left and formatted value on the right
129
+ * @sideEffects none (pure rendering)
130
+ */
131
+ export function SchemaFieldDisplay({
132
+ field,
133
+ value
134
+ }: {
135
+ field: FieldDefinition
136
+ value: any
137
+ }) {
138
+ const displayValue = formatFieldValue(field, value)
139
+
140
+ return (
141
+ <div className="flex justify-between items-start py-2">
142
+ <dt className="text-xs text-slate-500 font-medium flex-shrink-0 mr-4">
143
+ {field.label || field.name}:
144
+ </dt>
145
+ <dd className="text-sm text-slate-900 text-right">
146
+ {displayValue}
147
+ </dd>
148
+ </div>
149
+ )
150
+ }
151
+
152
+ /**
153
+ * Formats a raw field value for read-only display, applying type-specific
154
+ * transformations:
155
+ * - `boolean`/`checkbox` → `'Yes'` / `'No'`
156
+ * - `date` / `datetime` → locale string
157
+ * - `select` → resolves option label from `field.options`
158
+ * - `multiselect` → comma-joined option labels
159
+ * - `json` → `<pre>` code block
160
+ * - `url` → `<a>` link
161
+ * - Null/undefined/empty → em-dash placeholder
162
+ *
163
+ * @param field - Field definition
164
+ * @param value - Raw value
165
+ * @returns Formatted `ReactNode`
166
+ */
167
+ function formatFieldValue(field: FieldDefinition, value: any): React.ReactNode {
168
+ if (value === null || value === undefined || value === '') {
169
+ return <span className="text-slate-400 italic">—</span>
170
+ }
171
+
172
+ switch (field.data_type) {
173
+ case 'boolean':
174
+ case 'checkbox':
175
+ return value ? 'Yes' : 'No'
176
+
177
+ case 'date':
178
+ try {
179
+ return new Date(value).toLocaleDateString()
180
+ } catch {
181
+ return String(value)
182
+ }
183
+
184
+ case 'datetime':
185
+ try {
186
+ return new Date(value).toLocaleString()
187
+ } catch {
188
+ return String(value)
189
+ }
190
+
191
+ case 'select': {
192
+ const option = field.options?.find(o => typeof o === 'object' && o.value === value)
193
+ return option && typeof option === 'object' ? option.label : String(value)
194
+ }
195
+
196
+ case 'multiselect': {
197
+ if (!Array.isArray(value)) return String(value)
198
+ return value.map(v => {
199
+ const option = field.options?.find(o => typeof o === 'object' && o.value === v)
200
+ return option && typeof option === 'object' ? option.label : v
201
+ }).join(', ')
202
+ }
203
+
204
+ case 'json':
205
+ return (
206
+ <pre className="text-xs bg-slate-50 rounded p-2 max-w-xs overflow-x-auto">
207
+ {JSON.stringify(value, null, 2)}
208
+ </pre>
209
+ )
210
+
211
+ case 'url':
212
+ return (
213
+ <a
214
+ href={value}
215
+ target="_blank"
216
+ rel="noopener noreferrer"
217
+ className="text-blue-600 hover:text-blue-800 underline"
218
+ >
219
+ {value}
220
+ </a>
221
+ )
222
+
223
+ default:
224
+ return String(value)
225
+ }
226
+ }