spine-framework 0.3.145 → 0.4.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 (451) hide show
  1. package/.env.example +76 -0
  2. package/.framework/.spine-manifest.json +19 -0
  3. package/.framework/cli/commands/agents.ts +61 -33
  4. package/.framework/cli/commands/auth.ts +60 -28
  5. package/.framework/cli/commands/create-app.ts +212 -80
  6. package/.framework/cli/commands/dev.ts +184 -68
  7. package/.framework/cli/commands/doctor.ts +324 -152
  8. package/.framework/cli/commands/generate.ts +119 -68
  9. package/.framework/cli/commands/init.ts +141 -242
  10. package/.framework/cli/commands/install-app.ts +1336 -314
  11. package/.framework/cli/commands/items.ts +31 -29
  12. package/.framework/cli/commands/migrate.ts +299 -156
  13. package/.framework/cli/commands/migrations.ts +62 -30
  14. package/.framework/cli/commands/pipelines.ts +31 -29
  15. package/.framework/cli/commands/status.ts +105 -45
  16. package/.framework/cli/commands/system.ts +47 -42
  17. package/.framework/cli/commands/test.ts +161 -102
  18. package/.framework/cli/commands/upgrade.ts +260 -0
  19. package/.framework/cli/commands/validate-app.ts +517 -161
  20. package/.framework/cli/context.ts +117 -85
  21. package/.framework/cli/env-loader.ts +74 -43
  22. package/.framework/cli/index.ts +70 -46
  23. package/.framework/cli/lib/project-files.ts +404 -0
  24. package/.framework/docs/guides/testing-guide.md +1 -1
  25. package/.framework/docs/runtime-types.md +72 -72
  26. package/.framework/functions/_shared/access.ts +84 -0
  27. package/.framework/functions/_shared/agent-runner.ts +5051 -1026
  28. package/.framework/functions/_shared/app-manifest.ts +214 -94
  29. package/.framework/functions/_shared/ast.ts +214 -0
  30. package/.framework/functions/_shared/audit.ts +91 -55
  31. package/.framework/functions/_shared/cil.ts +368 -127
  32. package/.framework/functions/_shared/db.ts +337 -68
  33. package/.framework/functions/_shared/funnel.ts +228 -0
  34. package/.framework/functions/_shared/geo.ts +105 -0
  35. package/.framework/functions/_shared/hash-api-key.ts +94 -17
  36. package/.framework/functions/_shared/index.ts +22 -224
  37. package/.framework/functions/_shared/kb.ts +832 -58
  38. package/.framework/functions/_shared/llm.ts +118 -18
  39. package/.framework/functions/_shared/logging.ts +317 -19
  40. package/.framework/functions/_shared/middleware.ts +701 -131
  41. package/.framework/functions/_shared/notify.ts +70 -0
  42. package/.framework/functions/_shared/outcomes.ts +34 -19
  43. package/.framework/functions/_shared/permissions.ts +501 -315
  44. package/.framework/functions/_shared/pipeline-runner.ts +704 -155
  45. package/.framework/functions/_shared/principal.ts +436 -263
  46. package/.framework/functions/_shared/rate-limit.ts +233 -0
  47. package/.framework/functions/_shared/resolve-credentials.ts +86 -19
  48. package/.framework/functions/_shared/resolve-ids.ts +203 -139
  49. package/.framework/functions/_shared/retry-deliveries.ts +51 -16
  50. package/.framework/functions/_shared/schema-utils.ts +602 -521
  51. package/.framework/functions/_shared/security.ts +345 -0
  52. package/.framework/functions/_shared/testing.ts +197 -172
  53. package/.framework/functions/_shared/tracing.ts +351 -21
  54. package/.framework/functions/_shared/trigger-engine.ts +425 -136
  55. package/.framework/functions/_shared/webhook-registration.ts +71 -70
  56. package/.framework/functions/_shared/webhook-registry.ts +126 -83
  57. package/.framework/functions/account-nodes.ts +30 -30
  58. package/.framework/functions/adapters/generic-webhook-inbound.ts +49 -16
  59. package/.framework/functions/adapters/generic-webhook-outbound.ts +17 -17
  60. package/.framework/functions/adapters/openai-outbound.ts +15 -16
  61. package/.framework/functions/admin-data.ts +596 -173
  62. package/.framework/functions/admin-funnel.ts +64 -60
  63. package/.framework/functions/admin-install-funnel.ts +84 -66
  64. package/.framework/functions/admin-signals.ts +69 -51
  65. package/.framework/functions/ai-agents.ts +431 -106
  66. package/.framework/functions/ai-interactions.ts +186 -112
  67. package/.framework/functions/api-keys.ts +107 -107
  68. package/.framework/functions/apps.ts +154 -136
  69. package/.framework/functions/auth.ts +83 -65
  70. package/.framework/functions/cil-agent-ingest.ts +189 -232
  71. package/.framework/functions/cil-agent.ts +433 -177
  72. package/.framework/functions/cil-callmap.ts +642 -430
  73. package/.framework/functions/cil-config.ts +139 -135
  74. package/.framework/functions/cil-context.ts +214 -127
  75. package/.framework/functions/cil-doc.ts +587 -458
  76. package/.framework/functions/cil-embeddings.ts +453 -364
  77. package/.framework/functions/cil-graph.ts +427 -142
  78. package/.framework/functions/cil-ingest.ts +1418 -1154
  79. package/.framework/functions/cil-pipeline.ts +276 -21
  80. package/.framework/functions/cil-project.ts +659 -132
  81. package/.framework/functions/cil-validate.ts +198 -164
  82. package/.framework/functions/debug-auth.ts +57 -55
  83. package/.framework/functions/deliveries.ts +80 -21
  84. package/.framework/functions/embeddings.ts +1023 -318
  85. package/.framework/functions/eval-harness.ts +146 -108
  86. package/.framework/functions/integration-outbound.ts +205 -75
  87. package/.framework/functions/integration-routes.ts +128 -23
  88. package/.framework/functions/integrations.ts +125 -100
  89. package/.framework/functions/invites.ts +1095 -460
  90. package/.framework/functions/item-progress.ts +158 -102
  91. package/.framework/functions/kb.ts +34 -10
  92. package/.framework/functions/logs.ts +133 -133
  93. package/.framework/functions/observability.ts +152 -122
  94. package/.framework/functions/pipeline-executions.ts +147 -132
  95. package/.framework/functions/pipelines.ts +134 -119
  96. package/.framework/functions/prompt-configs.ts +96 -80
  97. package/.framework/functions/roles.ts +112 -82
  98. package/.framework/functions/security.ts +450 -0
  99. package/.framework/functions/system-cron.ts +117 -80
  100. package/.framework/functions/system.ts +158 -98
  101. package/.framework/functions/tests.ts +29 -29
  102. package/.framework/functions/timers.ts +94 -95
  103. package/.framework/functions/trigger-scheduler.ts +10 -0
  104. package/.framework/functions/triggers.ts +179 -149
  105. package/.framework/functions/types.ts +137 -125
  106. package/.framework/mcp/index.ts +133 -0
  107. package/.framework/mcp/load-env.ts +60 -0
  108. package/.framework/mcp/shared/app-manifest.ts +275 -0
  109. package/.framework/mcp/shared/db.ts +477 -0
  110. package/.framework/mcp/shared/kb.ts +118 -0
  111. package/.framework/mcp/tools/architect.ts +431 -0
  112. package/.framework/mcp/tools/cil/context.ts +345 -0
  113. package/.framework/mcp/tools/cil/graph.ts +242 -0
  114. package/.framework/mcp/tools/cil/ingest.ts +1217 -0
  115. package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +693 -0
  116. package/.framework/mcp/tools/cil/pipeline.ts +767 -0
  117. package/.framework/mcp/tools/cil/project.ts +791 -0
  118. package/.framework/mcp/tools/cil/retrieval.ts +336 -0
  119. package/.framework/mcp/tools/cil/search-core.ts +454 -0
  120. package/.framework/mcp/tools/cil/search.ts +97 -0
  121. package/.framework/mcp/tools/cil/suggest.ts +186 -0
  122. package/.framework/mcp/tools/cil/validate.ts +1369 -0
  123. package/.framework/mcp/tools/kb/delete.ts +139 -0
  124. package/.framework/mcp/tools/kb/index.ts +52 -0
  125. package/.framework/mcp/tools/kb/ingest.ts +84 -0
  126. package/.framework/mcp/tools/kb/list-types.ts +102 -0
  127. package/.framework/mcp/tools/kb/publish.ts +100 -0
  128. package/.framework/mcp/tools/kb/reindex.ts +86 -0
  129. package/.framework/mcp/tools/kb/revoke.ts +85 -0
  130. package/.framework/mcp/tools/kb/search.ts +101 -0
  131. package/.framework/mcp/tools/kb/upsert.ts +125 -0
  132. package/.framework/mcp/tools/observability.ts +510 -0
  133. package/.framework/migrations/001_core_schema.sql +15 -0
  134. package/.framework/migrations/002_core_rls_and_functions.sql +15 -0
  135. package/.framework/migrations/003_core_system_data.sql +16 -1
  136. package/.framework/migrations/004_core_rpc_and_broker.sql +17 -1
  137. package/.framework/migrations/005_core_observability.sql +17 -1
  138. package/.framework/migrations/006_security_hardening_rls.sql +15 -0
  139. package/.framework/migrations/007_kb_article_list.sql +17 -1
  140. package/.framework/migrations/008_items_target.sql +15 -0
  141. package/.framework/migrations/009_roles_slug_unique.sql +15 -0
  142. package/.framework/migrations/010_auth_user_trigger.sql +15 -0
  143. package/.framework/migrations/011_spine_system_account.sql +16 -0
  144. package/.framework/migrations/012_service_role_grants.sql +16 -0
  145. package/.framework/migrations/013_get_account_apps_role_filter.sql +15 -0
  146. package/.framework/migrations/014_apps_required_roles.sql +16 -0
  147. package/.framework/migrations/015_types_kind_slug_unique.sql +15 -0
  148. package/.framework/migrations/016_cil_types.sql +281 -0
  149. package/.framework/migrations/017_cil_stable_ids.sql +37 -0
  150. package/.framework/migrations/018_runtime_tables.sql +129 -0
  151. package/.framework/migrations/019_agent_prompt_columns.sql +56 -0
  152. package/.framework/migrations/020_schema_reconciliation.sql +67 -0
  153. package/.framework/migrations/021_kb_embedding_lifecycle.sql +31 -0
  154. package/.framework/migrations/032_cil_status_migration.sql +73 -0
  155. package/.framework/migrations/033_cil_versioning.sql +73 -0
  156. package/.framework/migrations/034_match_embeddings_knn.sql +115 -0
  157. package/.framework/migrations/035_rate_limits.sql +55 -0
  158. package/.framework/migrations/036_demo_record_flags.sql +72 -0
  159. package/.framework/migrations/037_kb_types_canonical.sql +244 -0
  160. package/.framework/migrations/038_drop_demo_record_flags.sql +38 -0
  161. package/.framework/migrations/042_registration_hierarchy.sql +309 -0
  162. package/.framework/migrations/043_policy_actor_cleanup.sql +39 -0
  163. package/.framework/migrations/044_match_embeddings_filtered_knn.sql +151 -0
  164. package/.framework/migrations/045_security_critical_rls.sql +139 -0
  165. package/.framework/migrations/046_security_missing_rls.sql +79 -0
  166. package/.framework/migrations/047_people_updated_by.sql +27 -0
  167. package/.framework/migrations/048_watcher_last_seen_at.sql +24 -0
  168. package/.framework/migrations/049_security_blocklist.sql +148 -0
  169. package/.framework/migrations/050_notifications.sql +49 -0
  170. package/.framework/migrations/051_message_agent_fields.sql +74 -0
  171. package/.framework/migrations/052_registration_security.sql +421 -0
  172. package/.framework/migrations/053_builtin_action_seeds.sql +66 -0
  173. package/.framework/migrations/054_kb_ingest_lease.sql +157 -0
  174. package/.framework/migrations/055_type_id_drift_reconciliation.sql +33 -0
  175. package/.framework/migrations/056_slug_column_reconciliation.sql +38 -0
  176. package/.framework/migrations/057_pipelines_slug_identity.sql +14 -0
  177. package/.framework/migrations/058_account_data_field_declarations.sql +53 -0
  178. package/.framework/migrations/059_registration_placement_fixes.sql +269 -0
  179. package/.framework/migrations/060_account_person_record_permissions.sql +38 -0
  180. package/.framework/migrations/061_fk_dedup.sql +32 -0
  181. package/.framework/public/spine-logo.svg +10 -3
  182. package/.framework/scripts/app-install-cli.ts +190 -93
  183. package/.framework/scripts/assemble-frontend.sh +127 -44
  184. package/.framework/scripts/assemble-functions.sh +128 -30
  185. package/.framework/scripts/boundary-check.sh +6 -2
  186. package/.framework/scripts/build-manifest.sh +24 -68
  187. package/.framework/scripts/check-core-integrity.sh +15 -76
  188. package/.framework/scripts/checksum-manifest.mts +343 -0
  189. package/.framework/scripts/cil-migrate-markers.ts +403 -0
  190. package/.framework/scripts/fix-cil-markers.mjs +176 -0
  191. package/.framework/scripts/verify-integrity.sh +21 -57
  192. package/.framework/scripts/verify-manifest.mts +305 -0
  193. package/.framework/seeds/types/funnel_signal_use.json +74 -21
  194. package/.framework/seeds/types/spine_instance.json +73 -21
  195. package/.framework/src/App.tsx +118 -72
  196. package/.framework/src/apps/admin/index.tsx +855 -58
  197. package/.framework/src/components/AppWrapper.tsx +43 -45
  198. package/.framework/src/components/CustomAppLoader.tsx +160 -116
  199. package/.framework/src/components/admin/AdminListPage.tsx +46 -44
  200. package/.framework/src/components/admin/AdminSidebar.tsx +57 -47
  201. package/.framework/src/components/admin/AdminStatsCard.tsx +46 -44
  202. package/.framework/src/components/admin/PageInstructions.tsx +98 -2
  203. package/.framework/src/components/admin/SortableTableHeader.tsx +44 -45
  204. package/.framework/src/components/app-shell/GenericAppShell.tsx +64 -65
  205. package/.framework/src/components/app-shell/GenericDetailPage.tsx +44 -47
  206. package/.framework/src/components/app-shell/GenericListPage.tsx +45 -49
  207. package/.framework/src/components/app-sidebar.tsx +42 -45
  208. package/.framework/src/components/auth/ProtectedRoute.tsx +48 -41
  209. package/.framework/src/components/auth/TurnstileWidget.tsx +27 -0
  210. package/.framework/src/components/kb/KBArticleEditor.tsx +231 -22
  211. package/.framework/src/components/kb/KBArticleList.tsx +251 -10
  212. package/.framework/src/components/layout/AppShell.tsx +51 -45
  213. package/.framework/src/components/layout/Header.tsx +42 -43
  214. package/.framework/src/components/layout/Layout.tsx +46 -46
  215. package/.framework/src/components/layout/Sidebar.tsx +196 -96
  216. package/.framework/src/components/runtime/DataDetailHeader.tsx +55 -30
  217. package/.framework/src/components/runtime/DataDetailPage.tsx +31 -269
  218. package/.framework/src/components/runtime/DataFilters.tsx +49 -74
  219. package/.framework/src/components/runtime/DataHeader.tsx +53 -32
  220. package/.framework/src/components/runtime/DataListPage.tsx +51 -155
  221. package/.framework/src/components/runtime/DataStats.tsx +66 -44
  222. package/.framework/src/components/runtime/DataTable.tsx +49 -149
  223. package/.framework/src/components/runtime/SchemaDetailForm.tsx +45 -75
  224. package/.framework/src/components/runtime/index.ts +119 -16
  225. package/.framework/src/components/search-form.tsx +46 -29
  226. package/.framework/src/components/shared/AgentView.tsx +65 -48
  227. package/.framework/src/components/shared/FieldRenderer.tsx +144 -50
  228. package/.framework/src/components/shared/InviteManager.tsx +207 -97
  229. package/.framework/src/components/shared/MemberList.tsx +97 -0
  230. package/.framework/src/components/shared/SchemaFields.tsx +107 -86
  231. package/.framework/src/components/ui/DataTable.tsx +80 -68
  232. package/.framework/src/components/ui/Form.tsx +157 -146
  233. package/.framework/src/components/ui/ItemCard.tsx +74 -67
  234. package/.framework/src/components/ui/ItemListView.tsx +73 -68
  235. package/.framework/src/components/ui/LoadingSpinner.tsx +43 -48
  236. package/.framework/src/components/ui/Modal.tsx +78 -68
  237. package/.framework/src/components/ui/RichTextEditor.tsx +89 -69
  238. package/.framework/src/components/ui/accordion.tsx +85 -86
  239. package/.framework/src/components/ui/alert-dialog.tsx +197 -184
  240. package/.framework/src/components/ui/alert.tsx +105 -94
  241. package/.framework/src/components/ui/aspect-ratio.tsx +57 -43
  242. package/.framework/src/components/ui/avatar.tsx +122 -107
  243. package/.framework/src/components/ui/badge.tsx +60 -55
  244. package/.framework/src/components/ui/breadcrumb.tsx +127 -128
  245. package/.framework/src/components/ui/button-group.tsx +86 -72
  246. package/.framework/src/components/ui/button.tsx +71 -50
  247. package/.framework/src/components/ui/calendar.tsx +72 -57
  248. package/.framework/src/components/ui/card.tsx +136 -129
  249. package/.framework/src/components/ui/carousel.tsx +226 -107
  250. package/.framework/src/components/ui/chart.tsx +252 -147
  251. package/.framework/src/components/ui/checkbox.tsx +43 -44
  252. package/.framework/src/components/ui/collapsible.tsx +86 -71
  253. package/.framework/src/components/ui/combobox.tsx +282 -249
  254. package/.framework/src/components/ui/command.tsx +158 -146
  255. package/.framework/src/components/ui/context-menu.tsx +239 -240
  256. package/.framework/src/components/ui/dialog.tsx +174 -157
  257. package/.framework/src/components/ui/direction.tsx +72 -56
  258. package/.framework/src/components/ui/drawer.tsx +168 -169
  259. package/.framework/src/components/ui/dropdown-menu.tsx +254 -223
  260. package/.framework/src/components/ui/empty.tsx +131 -120
  261. package/.framework/src/components/ui/field.tsx +205 -173
  262. package/.framework/src/components/ui/hover-card.tsx +71 -72
  263. package/.framework/src/components/ui/input-group.tsx +148 -133
  264. package/.framework/src/components/ui/input-otp.tsx +99 -86
  265. package/.framework/src/components/ui/input.tsx +43 -41
  266. package/.framework/src/components/ui/item.tsx +207 -185
  267. package/.framework/src/components/ui/kbd.tsx +57 -56
  268. package/.framework/src/components/ui/label.tsx +45 -41
  269. package/.framework/src/components/ui/menubar.tsx +268 -236
  270. package/.framework/src/components/ui/native-select.tsx +85 -86
  271. package/.framework/src/components/ui/navigation-menu.tsx +166 -156
  272. package/.framework/src/components/ui/pagination.tsx +131 -141
  273. package/.framework/src/components/ui/popover.tsx +125 -128
  274. package/.framework/src/components/ui/progress.tsx +47 -44
  275. package/.framework/src/components/ui/radio-group.tsx +60 -55
  276. package/.framework/src/components/ui/resizable.tsx +85 -72
  277. package/.framework/src/components/ui/scroll-area.tsx +57 -58
  278. package/.framework/src/components/ui/select.tsx +182 -169
  279. package/.framework/src/components/ui/separator.tsx +42 -43
  280. package/.framework/src/components/ui/sheet.tsx +170 -159
  281. package/.framework/src/components/ui/sidebar.tsx +395 -398
  282. package/.framework/src/components/ui/skeleton.tsx +42 -43
  283. package/.framework/src/components/ui/slider.tsx +61 -44
  284. package/.framework/src/components/ui/sonner.tsx +44 -44
  285. package/.framework/src/components/ui/spinner.tsx +47 -42
  286. package/.framework/src/components/ui/switch.tsx +56 -42
  287. package/.framework/src/components/ui/table-primitives.tsx +272 -132
  288. package/.framework/src/components/ui/table.tsx +140 -142
  289. package/.framework/src/components/ui/tabs.tsx +110 -93
  290. package/.framework/src/components/ui/textarea.tsx +43 -44
  291. package/.framework/src/components/ui/toggle-group.tsx +87 -71
  292. package/.framework/src/components/ui/toggle.tsx +56 -56
  293. package/.framework/src/components/ui/tooltip.tsx +99 -85
  294. package/.framework/src/contexts/AppContext.tsx +166 -114
  295. package/.framework/src/contexts/AuthContext.tsx +232 -137
  296. package/.framework/src/hooks/use-mobile.ts +43 -44
  297. package/.framework/src/hooks/useApi.ts +185 -172
  298. package/.framework/src/hooks/useAppPath.ts +31 -29
  299. package/.framework/src/hooks/useApps.ts +77 -42
  300. package/.framework/src/hooks/useEntityList.ts +66 -29
  301. package/.framework/src/hooks/useEntityRecord.ts +76 -32
  302. package/.framework/src/hooks/useForm.ts +62 -30
  303. package/.framework/src/hooks/useListSchema.ts +60 -43
  304. package/.framework/src/hooks/useSchemaRecord.ts +115 -95
  305. package/.framework/src/lib/api.ts +258 -134
  306. package/.framework/src/lib/registration.ts +181 -0
  307. package/.framework/src/lib/supabase.ts +118 -81
  308. package/.framework/src/lib/utils.ts +266 -266
  309. package/.framework/src/main.tsx +43 -44
  310. package/.framework/src/pages/DashboardPage.tsx +44 -43
  311. package/.framework/src/pages/NoAppPage.tsx +50 -0
  312. package/.framework/src/pages/NotFoundPage.tsx +32 -29
  313. package/.framework/src/pages/SetupPage.tsx +34 -33
  314. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +69 -58
  315. package/.framework/src/pages/admin/AIAgentsPage.tsx +43 -40
  316. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +63 -55
  317. package/.framework/src/pages/admin/APIKeysPage.tsx +44 -42
  318. package/.framework/src/pages/admin/AlertsConfigPage.tsx +117 -94
  319. package/.framework/src/pages/admin/AppDetailPage.tsx +48 -42
  320. package/.framework/src/pages/admin/AppsPage.tsx +45 -42
  321. package/.framework/src/pages/admin/DeliveriesPage.tsx +49 -17
  322. package/.framework/src/pages/admin/DesignedPage.tsx +31 -30
  323. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +58 -0
  324. package/.framework/src/pages/admin/EmbeddingsPage.tsx +49 -0
  325. package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +31 -29
  326. package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +46 -29
  327. package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -29
  328. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +68 -55
  329. package/.framework/src/pages/admin/IntegrationsPage.tsx +45 -42
  330. package/.framework/src/pages/admin/InvitesPage.tsx +60 -44
  331. package/.framework/src/pages/admin/LogsPage.tsx +87 -137
  332. package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +31 -29
  333. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +150 -89
  334. package/.framework/src/pages/admin/PipelineDetailPage.tsx +77 -104
  335. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +44 -43
  336. package/.framework/src/pages/admin/PipelinesPage.tsx +59 -145
  337. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +53 -42
  338. package/.framework/src/pages/admin/PromptConfigsPage.tsx +47 -117
  339. package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +31 -44
  340. package/.framework/src/pages/admin/RoleDetailPage.tsx +49 -92
  341. package/.framework/src/pages/admin/RolesPage.tsx +47 -164
  342. package/.framework/src/pages/admin/SecurityPage.tsx +310 -0
  343. package/.framework/src/pages/admin/SelectTestPage.tsx +30 -32
  344. package/.framework/src/pages/admin/ShadcnTestPage.tsx +37 -44
  345. package/.framework/src/pages/admin/SimpleDashboard.tsx +32 -44
  346. package/.framework/src/pages/admin/TestRunDetailPage.tsx +97 -44
  347. package/.framework/src/pages/admin/TestingDashboard.tsx +126 -71
  348. package/.framework/src/pages/admin/TimerDetailPage.tsx +67 -55
  349. package/.framework/src/pages/admin/TimersPage.tsx +44 -42
  350. package/.framework/src/pages/admin/TriggerDetailPage.tsx +47 -42
  351. package/.framework/src/pages/admin/TriggersPage.tsx +58 -57
  352. package/.framework/src/pages/admin/TypeDetailPage.tsx +91 -55
  353. package/.framework/src/pages/admin/TypesPage.tsx +45 -42
  354. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +258 -128
  355. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +127 -123
  356. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +308 -235
  357. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +159 -110
  358. package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +432 -0
  359. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +200 -178
  360. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +286 -239
  361. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +211 -90
  362. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +156 -143
  363. package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +379 -0
  364. package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +337 -0
  365. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +219 -214
  366. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
  367. package/.framework/src/pages/auth/AuthConfirmPage.tsx +135 -65
  368. package/.framework/src/pages/auth/ForgotPasswordPage.tsx +39 -31
  369. package/.framework/src/pages/auth/LoginPage.tsx +38 -32
  370. package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -28
  371. package/.framework/src/pages/auth/RegisterPage.tsx +429 -155
  372. package/.framework/src/pages/spine-framework/APIPage.tsx +33 -30
  373. package/.framework/src/pages/spine-framework/CLIPage.tsx +32 -30
  374. package/.framework/src/types/auth.ts +101 -93
  375. package/.framework/src/types/types.ts +283 -262
  376. package/.framework/templates/project.json +87 -0
  377. package/.framework/templates/spine.config.neutral.json +7 -0
  378. package/.framework/tests/api/api-surface.test.ts +381 -0
  379. package/.framework/tests/fixtures/seed.ts +427 -0
  380. package/.framework/tests/fixtures/teardown.ts +228 -0
  381. package/.framework/tests/reporter.ts +351 -0
  382. package/.framework/tests/setup.ts +135 -0
  383. package/.framework/tests/ui/ui-sweep.ts +278 -0
  384. package/.framework/tests/unit/admin-data-logger.test.ts +310 -0
  385. package/.framework/tests/unit/agent-runner.test.ts +2389 -0
  386. package/.framework/tests/unit/ai-interactions.test.ts +81 -0
  387. package/.framework/tests/unit/app-manifest.test.ts +359 -0
  388. package/.framework/tests/unit/assembly-scripts.test.ts +172 -0
  389. package/.framework/tests/unit/cil-determinism.test.ts +85 -0
  390. package/.framework/tests/unit/core-isolation.test.ts +153 -0
  391. package/.framework/tests/unit/funnel-signals.test.ts +135 -0
  392. package/.framework/tests/unit/install-app.test.ts +1759 -0
  393. package/.framework/tests/unit/invites-security.test.ts +572 -0
  394. package/.framework/tests/unit/kb-ingest-lease.test.ts +287 -0
  395. package/.framework/tests/unit/kb-search.test.ts +119 -0
  396. package/.framework/tests/unit/migrate.test.ts +509 -0
  397. package/.framework/tests/unit/notify.test.ts +136 -0
  398. package/.framework/tests/unit/permissions.test.ts +269 -0
  399. package/.framework/tests/unit/pipeline-runner.test.ts +755 -0
  400. package/.framework/tests/unit/principal-cache.test.ts +138 -0
  401. package/.framework/tests/unit/retrieval-golden.test.ts +520 -0
  402. package/.framework/tests/unit/runtime-isolation.test.ts +642 -0
  403. package/.framework/tests/unit/schema-utils.test.ts +225 -0
  404. package/.framework/tests/unit/trigger-engine.test.ts +513 -0
  405. package/.framework/tests/unit/validate-app.test.ts +527 -0
  406. package/README.md +34 -3
  407. package/bin/spine-framework.cjs +1 -0
  408. package/config/tailwind.config.cjs +7 -0
  409. package/config/tsconfig.functions.json +42 -0
  410. package/config/tsconfig.json +37 -9
  411. package/config/vite.config.ts +31 -2
  412. package/netlify.toml +31 -3
  413. package/package.json +46 -29
  414. package/scripts/assemble-frontend.sh +127 -44
  415. package/scripts/assemble-functions.sh +127 -29
  416. package/scripts/boundary-check.sh +6 -2
  417. package/scripts/build-manifest.sh +24 -68
  418. package/scripts/checksum-manifest.mts +345 -0
  419. package/scripts/dev-clean.sh +86 -0
  420. package/scripts/dev-netlify.sh +49 -0
  421. package/scripts/netlify-dev-wrapper.sh +2 -0
  422. package/scripts/verify-install.ts +358 -0
  423. package/scripts/verify-integrity.sh +21 -57
  424. package/scripts/verify-manifest.mts +305 -0
  425. package/templates/project/gitignore +45 -0
  426. package/{config → templates/project}/package-lock.json +22335 -9366
  427. package/vitest.config.ts +46 -0
  428. package/.framework/public/spine.config.json +0 -91
  429. package/config/deno.lock +0 -108
  430. package/config/tsconfig.node.tsbuildinfo +0 -1
  431. package/config/tsconfig.tsbuildinfo +0 -1
  432. package/config/vite.config.d.ts +0 -2
  433. package/custom/.devin/AGENT.md +0 -357
  434. package/custom/.devin/skills/install-app/SKILL.md +0 -39
  435. package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +0 -39
  436. package/custom/.devin/skills/spine-workbench-idea/SKILL.md +0 -36
  437. package/custom/.devin/skills/spine-workbench-implement/SKILL.md +0 -46
  438. package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +0 -28
  439. package/custom/.devin/skills/spine-workbench-plan/SKILL.md +0 -38
  440. package/custom/.devin/workflows/app-install.yaml +0 -83
  441. package/custom/README.md +0 -79
  442. package/docs/README.md +0 -15
  443. package/scripts/app-install-cli.ts +0 -286
  444. package/scripts/check-core-integrity.sh +0 -82
  445. package/scripts/ingest-chunks.cjs +0 -202
  446. package/scripts/kb-chunk-parser.cjs +0 -312
  447. package/scripts/kb-chunk-parser.ts +0 -330
  448. package/scripts/kb-ingest-docs.ts +0 -203
  449. package/scripts/load-test-app-install.ts +0 -484
  450. package/scripts/test-login.js +0 -33
  451. package/spine.config.json +0 -91
@@ -1,203 +0,0 @@
1
- import fs from 'fs/promises'
2
- import path from 'path'
3
- import yaml from 'js-yaml'
4
- import { create as adminCreate, update as adminUpdate } from '../.framework/functions/admin-data.ts'
5
- import { kbIngest } from '../.framework/functions/_shared/kb.ts'
6
- import { adminDb } from '../.framework/functions/_shared/db.ts'
7
-
8
- const DOCS_ROOT = 'docs/spine-framework-documentation'
9
- const MANIFEST_PATH = path.join(DOCS_ROOT, 'manifest.json')
10
- const TARGET_TYPE_SLUG = 'kb-spine-internal'
11
- const ACCOUNT_SLUG = 'spine-system'
12
- const CREATED_BY_PERSON_ID = '69fbb941-344a-446b-85ad-50a73cab9051'
13
-
14
- const VALID_TAGS = new Set(['support', 'how-to', 'faq', 'tutorial'])
15
- const VALID_CATEGORIES = new Set(['how-to', 'runbook', 'policy', 'faq', 'tutorial'])
16
-
17
- const LAYER_TO_CATEGORY: Record<string, string> = {
18
- overview: 'how-to',
19
- concept: 'how-to',
20
- reference: 'runbook',
21
- guide: 'tutorial',
22
- quickstart: 'tutorial',
23
- 'task-guide': 'tutorial',
24
- operations: 'runbook',
25
- }
26
-
27
- function normaliseTags(input: string[]): string[] {
28
- return input
29
- .map(t => {
30
- const lower = String(t).toLowerCase()
31
- if (lower.includes('faq')) return 'faq'
32
- if (lower.includes('how-to') || lower.includes('howto') || lower.includes('guide')) return 'how-to'
33
- if (lower.includes('tutorial')) return 'tutorial'
34
- if (lower.includes('support')) return 'support'
35
- return lower
36
- })
37
- .filter(t => VALID_TAGS.has(t))
38
- }
39
-
40
- function parseArrayValue(value: string): string[] {
41
- const inside = value.replace(/^\[/, '').replace(/\]$/, '').trim()
42
- if (!inside) return []
43
- return inside
44
- .split(/,\s*/)
45
- .map(s => s.trim().replace(/^["']|["']$/g, ''))
46
- .filter(Boolean)
47
- }
48
-
49
- function parseFrontmatter(raw: string): any {
50
- const frontmatter: any = {}
51
- const lines = raw.split('\n')
52
- for (const line of lines) {
53
- const idx = line.indexOf(':')
54
- if (idx === -1) continue
55
- const key = line.slice(0, idx).trim()
56
- const value = line.slice(idx + 1).trim()
57
- if (value.startsWith('[') && value.endsWith(']')) {
58
- frontmatter[key] = parseArrayValue(value)
59
- } else if (value === 'true' || value === 'false') {
60
- frontmatter[key] = value === 'true'
61
- } else {
62
- frontmatter[key] = value.replace(/^["']|["']$/g, '')
63
- }
64
- }
65
- return frontmatter
66
- }
67
-
68
- function parseMarkdown(filePath: string, content: string): { frontmatter: any; body: string } {
69
- const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/)
70
- if (!match) {
71
- return { frontmatter: {}, body: content.trim() }
72
- }
73
- const frontmatter = parseFrontmatter(match[1])
74
- const body = match[2].trim()
75
- return { frontmatter, body }
76
- }
77
-
78
- async function buildCtx(accountId: string) {
79
- return {
80
- requestId: `kb-ingest-${Date.now()}`,
81
- accountId,
82
- appId: null,
83
- principal: {
84
- id: CREATED_BY_PERSON_ID,
85
- type: 'machine' as const,
86
- roles: [{ slug: 'system_admin', is_system: true }],
87
- },
88
- db: adminDb,
89
- query: {},
90
- requestPath: '/admin-data',
91
- } as any
92
- }
93
-
94
- async function main() {
95
- const { data: account } = await adminDb
96
- .from('accounts')
97
- .select('id')
98
- .eq('slug', ACCOUNT_SLUG)
99
- .single()
100
-
101
- if (!account) throw new Error(`Account not found: ${ACCOUNT_SLUG}`)
102
- const accountId = account.id
103
-
104
- const { data: typeRecord } = await adminDb
105
- .from('types')
106
- .select('id')
107
- .eq('slug', TARGET_TYPE_SLUG)
108
- .eq('kind', 'item')
109
- .single()
110
-
111
- if (!typeRecord) throw new Error(`Type not found: ${TARGET_TYPE_SLUG}`)
112
- const typeId = typeRecord.id
113
-
114
- const manifest = JSON.parse(await fs.readFile(MANIFEST_PATH, 'utf8'))
115
- const pages: Array<{ path: string }> = []
116
-
117
- function collectPages(obj: any) {
118
- if (Array.isArray(obj)) {
119
- for (const item of obj) collectPages(item)
120
- } else if (typeof obj === 'object' && obj) {
121
- if (obj.path) pages.push({ path: obj.path })
122
- for (const key of Object.keys(obj)) collectPages(obj[key])
123
- }
124
- }
125
- collectPages(manifest)
126
-
127
- const stats = { created: 0, updated: 0, failed: 0, skipped: 0, errors: [] as string[] }
128
-
129
- for (const page of pages) {
130
- if (!page.path.endsWith('.md')) continue
131
- const filePath = path.join(DOCS_ROOT, page.path)
132
- const sourceFile = filePath.replace(/^docs\//, '')
133
-
134
- try {
135
- const raw = await fs.readFile(filePath, 'utf8')
136
- const { frontmatter, body } = parseMarkdown(filePath, raw)
137
-
138
- const title = frontmatter.title || path.basename(page.path, '.md')
139
- const description = frontmatter.description || ''
140
- const tags = normaliseTags(frontmatter.tags || [])
141
- const layer = frontmatter.layer || 'overview'
142
- const category = [LAYER_TO_CATEGORY[layer] || 'how-to'].filter(c => VALID_CATEGORIES.has(c))
143
- const product = 'spine-framework'
144
-
145
- const existing = await adminDb
146
- .from('items')
147
- .select('id')
148
- .eq('type_id', typeId)
149
- .eq('data->>source_file', sourceFile)
150
- .eq('is_active', true)
151
- .maybeSingle()
152
-
153
- const ctx = await buildCtx(accountId)
154
- const recordData: any = {
155
- entity: 'items',
156
- title,
157
- description,
158
- status: 'published',
159
- data: {
160
- content: body,
161
- tags,
162
- category,
163
- product,
164
- source_file: sourceFile,
165
- version: frontmatter.version || '',
166
- is_ai_generated: false,
167
- },
168
- }
169
-
170
- let record: any
171
- if (existing?.data?.id) {
172
- recordData.id = existing.data.id
173
- record = await adminUpdate(
174
- { ...ctx, query: { entity: 'items', id: existing.data.id, method: 'PATCH' } },
175
- recordData
176
- )
177
- stats.updated++
178
- } else {
179
- recordData.type_slug = TARGET_TYPE_SLUG
180
- record = await adminCreate(
181
- { ...ctx, query: { entity: 'items', method: 'POST' } },
182
- recordData
183
- )
184
- stats.created++
185
- }
186
-
187
- await kbIngest(ctx, { id: record.id })
188
- console.log(`[${existing?.data?.id ? 'updated' : 'created'}] ${sourceFile} -> ${record.id}`)
189
- } catch (err: any) {
190
- stats.failed++
191
- const msg = `${sourceFile}: ${err?.message || err}`
192
- stats.errors.push(msg)
193
- console.error(`[failed] ${msg}`)
194
- }
195
- }
196
-
197
- console.log(JSON.stringify({ stats }, null, 2))
198
- }
199
-
200
- main().catch(e => {
201
- console.error(e)
202
- process.exit(1)
203
- })
@@ -1,484 +0,0 @@
1
- #!/usr/bin/env tsx
2
-
3
- /**
4
- * Load Testing for App Installation System
5
- * Tests performance with large installations and concurrent operations
6
- */
7
-
8
- import { readFileSync, existsSync } from 'fs'
9
- import { resolve, dirname } from 'path'
10
- import { program } from 'commander'
11
- import yaml from 'js-yaml'
12
- import { performance } from 'perf_hooks'
13
-
14
- // Fix __dirname for ES modules
15
- import { fileURLToPath } from 'url'
16
- const __filename = fileURLToPath(import.meta.url)
17
- const __dirname = dirname(__filename)
18
-
19
- interface LoadTestConfig {
20
- concurrentInstalls: number
21
- totalApps: number
22
- appComplexity: 'simple' | 'medium' | 'complex'
23
- testDuration: number // seconds
24
- reportInterval: number // seconds
25
- }
26
-
27
- interface TestResult {
28
- appId: string
29
- startTime: number
30
- endTime: number
31
- duration: number
32
- success: boolean
33
- error?: string
34
- steps: {
35
- validation: number
36
- packInstallation: number
37
- appCreation: number
38
- migrations: number
39
- verification: number
40
- }
41
- }
42
-
43
- interface LoadTestReport {
44
- config: LoadTestConfig
45
- results: TestResult[]
46
- summary: {
47
- totalTests: number
48
- successfulTests: number
49
- failedTests: number
50
- averageDuration: number
51
- minDuration: number
52
- maxDuration: number
53
- throughput: number // tests per second
54
- errors: Array<{ error: string; count: number }>
55
- }
56
- performance: {
57
- validation: { avg: number; min: number; max: number }
58
- packInstallation: { avg: number; min: number; max: number }
59
- appCreation: { avg: number; min: number; max: number }
60
- migrations: { avg: number; min: number; max: number }
61
- verification: { avg: number; min: number; max: number }
62
- }
63
- }
64
-
65
- class LoadTestRunner {
66
- private config: LoadTestConfig
67
- private results: TestResult[] = []
68
- private running = false
69
- private startTime = 0
70
-
71
- constructor(config: LoadTestConfig) {
72
- this.config = config
73
- }
74
-
75
- /**
76
- * Generate test app installation YAML
77
- */
78
- private generateTestApp(index: number): string {
79
- const complexity = this.config.appComplexity
80
- const baseApp = {
81
- app: {
82
- name: `Load Test App ${index}`,
83
- slug: `load-test-app-${index}`,
84
- version: "1.0.0",
85
- external_app_id: `load-test-${index}`,
86
- description: `Load testing app ${index}`,
87
- author: "Load Test Runner"
88
- },
89
- installation: {
90
- pack_installation: {
91
- account_id: "${TARGET_ACCOUNT_ID}",
92
- external_app_id: `load-test-${index}`,
93
- external_app_version: "1.0.0",
94
- install_mode: "full",
95
- manifest_version: 1
96
- },
97
- app_creation: [
98
- {
99
- slug: `load-test-app-${index}`,
100
- name: `Load Test App ${index}`,
101
- installed_pack_id: "${PACK_INSTALLATION_ID}",
102
- min_role: "member",
103
- nav_items: [
104
- {
105
- key: "dashboard",
106
- to: `/load-test-${index}`,
107
- label: "Dashboard",
108
- icon: "layout-dashboard",
109
- min_role: "member"
110
- }
111
- ]
112
- }
113
- ],
114
- migrations: [],
115
- verification: [
116
- { check: "app_definition_exists", slug: `load-test-app-${index}` }
117
- ]
118
- }
119
- }
120
-
121
- // Add complexity-based migrations
122
- if (complexity === 'medium') {
123
- baseApp.installation.migrations = [
124
- {
125
- file: `migrations/001_app_${index}_setup.sql`,
126
- description: `Create app ${index} item types and views`,
127
- rollback_file: `migrations/rollback_001_app_${index}_setup.sql`
128
- },
129
- {
130
- file: `migrations/002_app_${index}_data.sql`,
131
- description: `Seed app ${index} categories and templates`,
132
- dependencies: [`001_app_${index}_setup`]
133
- }
134
- ]
135
- baseApp.installation.verification.push(
136
- { check: "item_type_exists", slug: `app_${index}_item` },
137
- { check: "view_exists", slug: `app_${index}_dashboard` }
138
- )
139
- } else if (complexity === 'complex') {
140
- baseApp.installation.migrations = [
141
- {
142
- file: `migrations/001_app_${index}_setup.sql`,
143
- description: `Create app ${index} item types and views`,
144
- rollback_file: `migrations/rollback_001_app_${index}_setup.sql`
145
- },
146
- {
147
- file: `migrations/002_app_${index}_tables.sql`,
148
- description: `Create app ${index} custom tables`,
149
- dependencies: [`001_app_${index}_setup`]
150
- },
151
- {
152
- file: `migrations/003_app_${index}_data.sql`,
153
- description: `Seed app ${index} categories and templates`,
154
- dependencies: [`001_app_${index}_setup`, `002_app_${index}_tables`]
155
- },
156
- {
157
- file: `migrations/004_app_${index}_automations.sql`,
158
- description: `Create app ${index} automation rules`,
159
- dependencies: [`002_app_${index}_tables`, `003_app_${index}_data`]
160
- }
161
- ]
162
- baseApp.installation.verification.push(
163
- { check: "item_type_exists", slug: `app_${index}_item` },
164
- { check: "view_exists", slug: `app_${index}_dashboard` },
165
- { check: "view_exists", slug: `app_${index}_list` },
166
- { check: "integration_exists", integration_id: `app_${index}_integration` }
167
- )
168
- baseApp.installation.app_creation[0].nav_items.push(
169
- { key: "list", to: `/load-test-${index}/list`, label: "List", icon: "list", min_role: "member" },
170
- { key: "settings", to: `/load-test-${index}/settings`, label: "Settings", icon: "settings", min_role: "admin" }
171
- )
172
- }
173
-
174
- return yaml.dump(baseApp)
175
- }
176
-
177
- /**
178
- * Simulate app installation steps
179
- */
180
- private async simulateInstallation(appIndex: number): Promise<TestResult> {
181
- const result: TestResult = {
182
- appId: `load-test-app-${appIndex}`,
183
- startTime: performance.now(),
184
- endTime: 0,
185
- duration: 0,
186
- success: false,
187
- steps: {
188
- validation: 0,
189
- packInstallation: 0,
190
- appCreation: 0,
191
- migrations: 0,
192
- verification: 0
193
- }
194
- }
195
-
196
- try {
197
- // Generate test app YAML
198
- const appYaml = this.generateTestApp(appIndex)
199
-
200
- // Step 1: Validation (simulate schema validation)
201
- const validationStart = performance.now()
202
- await this.simulateValidation(appYaml)
203
- result.steps.validation = performance.now() - validationStart
204
-
205
- // Step 2: Pack Installation
206
- const packStart = performance.now()
207
- await this.simulatePackInstallation(result.appId)
208
- result.steps.packInstallation = performance.now() - packStart
209
-
210
- // Step 3: App Creation
211
- const appStart = performance.now()
212
- await this.simulateAppCreation(result.appId)
213
- result.steps.appCreation = performance.now() - appStart
214
-
215
- // Step 4: Migrations
216
- const migrationsStart = performance.now()
217
- const migrationCount = this.config.appComplexity === 'simple' ? 0 :
218
- this.config.appComplexity === 'medium' ? 2 : 4
219
- await this.simulateMigrations(result.appId, migrationCount)
220
- result.steps.migrations = performance.now() - migrationsStart
221
-
222
- // Step 5: Verification
223
- const verificationStart = performance.now()
224
- const verificationCount = this.config.appComplexity === 'simple' ? 1 :
225
- this.config.appComplexity === 'medium' ? 3 : 5
226
- await this.simulateVerification(result.appId, verificationCount)
227
- result.steps.verification = performance.now() - verificationStart
228
-
229
- result.success = true
230
- } catch (error) {
231
- result.error = error instanceof Error ? error.message : String(error)
232
- }
233
-
234
- result.endTime = performance.now()
235
- result.duration = result.endTime - result.startTime
236
-
237
- return result
238
- }
239
-
240
- /**
241
- * Simulate validation step
242
- */
243
- private async simulateValidation(appYaml: string): Promise<void> {
244
- // Simulate YAML parsing and schema validation
245
- const complexity = this.config.appComplexity
246
- const delay = complexity === 'simple' ? 10 : complexity === 'medium' ? 25 : 50
247
-
248
- await this.sleep(delay)
249
-
250
- // Parse YAML to simulate actual validation work
251
- yaml.load(appYaml)
252
- }
253
-
254
- /**
255
- * Simulate pack installation
256
- */
257
- private async simulatePackInstallation(appId: string): Promise<void> {
258
- // Simulate database insert for installed_packs
259
- const complexity = this.config.appComplexity
260
- const delay = complexity === 'simple' ? 20 : complexity === 'medium' ? 40 : 80
261
-
262
- await this.sleep(delay)
263
- }
264
-
265
- /**
266
- * Simulate app creation
267
- */
268
- private async simulateAppCreation(appId: string): Promise<void> {
269
- // Simulate app_definitions creation
270
- const complexity = this.config.appComplexity
271
- const delay = complexity === 'simple' ? 15 : complexity === 'medium' ? 30 : 60
272
-
273
- await this.sleep(delay)
274
- }
275
-
276
- /**
277
- * Simulate migrations
278
- */
279
- private async simulateMigrations(appId: string, count: number): Promise<void> {
280
- // Simulate running SQL migrations
281
- for (let i = 0; i < count; i++) {
282
- const delay = this.config.appComplexity === 'simple' ? 10 :
283
- this.config.appComplexity === 'medium' ? 25 : 50
284
- await this.sleep(delay)
285
- }
286
- }
287
-
288
- /**
289
- * Simulate verification steps
290
- */
291
- private async simulateVerification(appId: string, count: number): Promise<void> {
292
- // Simulate verification checks
293
- for (let i = 0; i < count; i++) {
294
- await this.sleep(5)
295
- }
296
- }
297
-
298
- /**
299
- * Sleep helper
300
- */
301
- private sleep(ms: number): Promise<void> {
302
- return new Promise(resolve => setTimeout(resolve, ms))
303
- }
304
-
305
- /**
306
- * Run concurrent load test
307
- */
308
- async runLoadTest(): Promise<LoadTestReport> {
309
- console.log(`🚀 Starting load test...`)
310
- console.log(`Configuration:`, this.config)
311
-
312
- this.running = true
313
- this.startTime = performance.now()
314
- this.results = []
315
-
316
- const concurrentBatches = Math.ceil(this.config.totalApps / this.config.concurrentInstalls)
317
-
318
- for (let batch = 0; batch < concurrentBatches; batch++) {
319
- const startIndex = batch * this.config.concurrentInstalls
320
- const endIndex = Math.min(startIndex + this.config.concurrentInstalls, this.config.totalApps)
321
- const batchSize = endIndex - startIndex
322
-
323
- console.log(`📦 Running batch ${batch + 1}/${concurrentBatches} (${batchSize} apps)`)
324
-
325
- // Run installations concurrently
326
- const batchPromises = []
327
- for (let i = startIndex; i < endIndex; i++) {
328
- batchPromises.push(this.simulateInstallation(i))
329
- }
330
-
331
- const batchResults = await Promise.all(batchPromises)
332
- this.results.push(...batchResults)
333
-
334
- // Report progress
335
- const completed = this.results.length
336
- const successRate = (this.results.filter(r => r.success).length / this.results.length * 100).toFixed(1)
337
- console.log(`✅ Completed ${completed}/${this.config.totalApps} apps (${successRate}% success rate)`)
338
-
339
- // Check if we should stop due to time limit
340
- const elapsed = (performance.now() - this.startTime) / 1000
341
- if (elapsed >= this.config.testDuration) {
342
- console.log(`⏰ Time limit reached, stopping test`)
343
- break
344
- }
345
- }
346
-
347
- this.running = false
348
- return this.generateReport()
349
- }
350
-
351
- /**
352
- * Generate comprehensive report
353
- */
354
- private generateReport(): LoadTestReport {
355
- const successfulTests = this.results.filter(r => r.success)
356
- const failedTests = this.results.filter(r => !r.success)
357
-
358
- const durations = successfulTests.map(r => r.duration)
359
- const avgDuration = durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0
360
- const minDuration = durations.length > 0 ? Math.min(...durations) : 0
361
- const maxDuration = durations.length > 0 ? Math.max(...durations) : 0
362
-
363
- const totalElapsed = (performance.now() - this.startTime) / 1000
364
- const throughput = this.results.length / totalElapsed
365
-
366
- // Error aggregation
367
- const errorMap = new Map<string, number>()
368
- for (const test of failedTests) {
369
- const error = test.error || 'Unknown error'
370
- errorMap.set(error, (errorMap.get(error) || 0) + 1)
371
- }
372
-
373
- const errors = Array.from(errorMap.entries()).map(([error, count]) => ({ error, count }))
374
-
375
- // Step performance analysis
376
- const stepPerformance = {
377
- validation: this.calculateStepPerformance('validation'),
378
- packInstallation: this.calculateStepPerformance('packInstallation'),
379
- appCreation: this.calculateStepPerformance('appCreation'),
380
- migrations: this.calculateStepPerformance('migrations'),
381
- verification: this.calculateStepPerformance('verification')
382
- }
383
-
384
- return {
385
- config: this.config,
386
- results: this.results,
387
- summary: {
388
- totalTests: this.results.length,
389
- successfulTests: successfulTests.length,
390
- failedTests: failedTests.length,
391
- averageDuration: avgDuration,
392
- minDuration,
393
- maxDuration,
394
- throughput,
395
- errors
396
- },
397
- performance: stepPerformance
398
- }
399
- }
400
-
401
- /**
402
- * Calculate performance metrics for a specific step
403
- */
404
- private calculateStepPerformance(step: keyof TestResult['steps']) {
405
- const values = this.results.filter(r => r.success).map(r => r.steps[step])
406
- return {
407
- avg: values.length > 0 ? values.reduce((a, b) => a + b, 0) / values.length : 0,
408
- min: values.length > 0 ? Math.min(...values) : 0,
409
- max: values.length > 0 ? Math.max(...values) : 0
410
- }
411
- }
412
- }
413
-
414
- /**
415
- * Pretty print report
416
- */
417
- function printReport(report: LoadTestReport): void {
418
- console.log('\n' + '='.repeat(80))
419
- console.log('📊 LOAD TEST REPORT')
420
- console.log('='.repeat(80))
421
-
422
- console.log('\n🔧 Configuration:')
423
- console.log(` Concurrent Installs: ${report.config.concurrentInstalls}`)
424
- console.log(` Total Apps: ${report.config.totalApps}`)
425
- console.log(` App Complexity: ${report.config.appComplexity}`)
426
- console.log(` Test Duration: ${report.config.testDuration}s`)
427
-
428
- console.log('\n📈 Summary:')
429
- console.log(` Total Tests: ${report.summary.totalTests}`)
430
- console.log(` Successful: ${report.summary.successfulTests} (${(report.summary.successfulTests / report.summary.totalTests * 100).toFixed(1)}%)`)
431
- console.log(` Failed: ${report.summary.failedTests} (${(report.summary.failedTests / report.summary.totalTests * 100).toFixed(1)}%)`)
432
- console.log(` Average Duration: ${report.summary.averageDuration.toFixed(2)}ms`)
433
- console.log(` Min Duration: ${report.summary.minDuration.toFixed(2)}ms`)
434
- console.log(` Max Duration: ${report.summary.maxDuration.toFixed(2)}ms`)
435
- console.log(` Throughput: ${report.summary.throughput.toFixed(2)} tests/second`)
436
-
437
- if (report.summary.errors.length > 0) {
438
- console.log('\n❌ Errors:')
439
- report.summary.errors.forEach(({ error, count }) => {
440
- console.log(` ${error}: ${count}`)
441
- })
442
- }
443
-
444
- console.log('\n⚡ Step Performance:')
445
- Object.entries(report.performance).forEach(([step, perf]) => {
446
- console.log(` ${step}:`)
447
- console.log(` Avg: ${perf.avg.toFixed(2)}ms`)
448
- console.log(` Min: ${perf.min.toFixed(2)}ms`)
449
- console.log(` Max: ${perf.max.toFixed(2)}ms`)
450
- })
451
-
452
- console.log('\n' + '='.repeat(80))
453
- }
454
-
455
- // CLI setup
456
- program
457
- .name('load-test-app-install')
458
- .description('Load testing for Spine app installation system')
459
- .version('1.0.0')
460
- .option('-c, --concurrent <number>', 'Number of concurrent installations', '10')
461
- .option('-t, --total <number>', 'Total number of apps to install', '100')
462
- .option('-x, --complexity <type>', 'App complexity (simple, medium, complex)', 'medium')
463
- .option('-d, --duration <seconds>', 'Maximum test duration in seconds', '300')
464
- .option('-r, --report-interval <seconds>', 'Progress report interval', '10')
465
- .action(async (options) => {
466
- const config: LoadTestConfig = {
467
- concurrentInstalls: parseInt(options.concurrent),
468
- totalApps: parseInt(options.total),
469
- appComplexity: options.complexity as 'simple' | 'medium' | 'complex',
470
- testDuration: parseInt(options.duration),
471
- reportInterval: parseInt(options.reportInterval)
472
- }
473
-
474
- const runner = new LoadTestRunner(config)
475
- const report = await runner.runLoadTest()
476
- printReport(report)
477
-
478
- // Exit with error code if there were failures
479
- if (report.summary.failedTests > 0) {
480
- process.exit(1)
481
- }
482
- })
483
-
484
- program.parse()