spine-framework 0.3.144 → 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.
- package/.env.example +76 -0
- package/.framework/.spine-manifest.json +19 -0
- package/.framework/cli/commands/agents.ts +61 -33
- package/.framework/cli/commands/auth.ts +60 -28
- package/.framework/cli/commands/create-app.ts +212 -80
- package/.framework/cli/commands/dev.ts +184 -68
- package/.framework/cli/commands/doctor.ts +324 -152
- package/.framework/cli/commands/generate.ts +119 -68
- package/.framework/cli/commands/init.ts +141 -242
- package/.framework/cli/commands/install-app.ts +1336 -314
- package/.framework/cli/commands/items.ts +31 -29
- package/.framework/cli/commands/migrate.ts +299 -156
- package/.framework/cli/commands/migrations.ts +62 -30
- package/.framework/cli/commands/pipelines.ts +31 -29
- package/.framework/cli/commands/status.ts +105 -45
- package/.framework/cli/commands/system.ts +47 -42
- package/.framework/cli/commands/test.ts +161 -102
- package/.framework/cli/commands/upgrade.ts +260 -0
- package/.framework/cli/commands/validate-app.ts +517 -161
- package/.framework/cli/context.ts +117 -85
- package/.framework/cli/env-loader.ts +74 -43
- package/.framework/cli/index.ts +70 -46
- package/.framework/cli/lib/project-files.ts +404 -0
- package/.framework/docs/guides/testing-guide.md +1 -1
- package/.framework/docs/runtime-types.md +72 -72
- package/.framework/functions/_shared/access.ts +84 -0
- package/.framework/functions/_shared/agent-runner.ts +5051 -1026
- package/.framework/functions/_shared/app-manifest.ts +214 -94
- package/.framework/functions/_shared/ast.ts +214 -0
- package/.framework/functions/_shared/audit.ts +91 -55
- package/.framework/functions/_shared/cil.ts +368 -127
- package/.framework/functions/_shared/db.ts +337 -68
- package/.framework/functions/_shared/funnel.ts +228 -0
- package/.framework/functions/_shared/geo.ts +105 -0
- package/.framework/functions/_shared/hash-api-key.ts +94 -17
- package/.framework/functions/_shared/index.ts +22 -224
- package/.framework/functions/_shared/kb.ts +832 -58
- package/.framework/functions/_shared/llm.ts +118 -18
- package/.framework/functions/_shared/logging.ts +317 -19
- package/.framework/functions/_shared/middleware.ts +701 -131
- package/.framework/functions/_shared/notify.ts +70 -0
- package/.framework/functions/_shared/outcomes.ts +34 -19
- package/.framework/functions/_shared/permissions.ts +501 -315
- package/.framework/functions/_shared/pipeline-runner.ts +704 -155
- package/.framework/functions/_shared/principal.ts +436 -263
- package/.framework/functions/_shared/rate-limit.ts +233 -0
- package/.framework/functions/_shared/resolve-credentials.ts +86 -19
- package/.framework/functions/_shared/resolve-ids.ts +203 -139
- package/.framework/functions/_shared/retry-deliveries.ts +51 -16
- package/.framework/functions/_shared/schema-utils.ts +602 -521
- package/.framework/functions/_shared/security.ts +345 -0
- package/.framework/functions/_shared/testing.ts +197 -172
- package/.framework/functions/_shared/tracing.ts +351 -21
- package/.framework/functions/_shared/trigger-engine.ts +425 -136
- package/.framework/functions/_shared/webhook-registration.ts +71 -70
- package/.framework/functions/_shared/webhook-registry.ts +126 -83
- package/.framework/functions/account-nodes.ts +30 -30
- package/.framework/functions/adapters/generic-webhook-inbound.ts +49 -16
- package/.framework/functions/adapters/generic-webhook-outbound.ts +17 -17
- package/.framework/functions/adapters/openai-outbound.ts +15 -16
- package/.framework/functions/admin-data.ts +596 -173
- package/.framework/functions/admin-funnel.ts +64 -60
- package/.framework/functions/admin-install-funnel.ts +84 -66
- package/.framework/functions/admin-signals.ts +69 -51
- package/.framework/functions/ai-agents.ts +431 -106
- package/.framework/functions/ai-interactions.ts +186 -112
- package/.framework/functions/api-keys.ts +107 -107
- package/.framework/functions/apps.ts +154 -136
- package/.framework/functions/auth.ts +83 -65
- package/.framework/functions/cil-agent-ingest.ts +189 -232
- package/.framework/functions/cil-agent.ts +433 -177
- package/.framework/functions/cil-callmap.ts +642 -430
- package/.framework/functions/cil-config.ts +139 -135
- package/.framework/functions/cil-context.ts +214 -127
- package/.framework/functions/cil-doc.ts +587 -458
- package/.framework/functions/cil-embeddings.ts +453 -364
- package/.framework/functions/cil-graph.ts +427 -142
- package/.framework/functions/cil-ingest.ts +1418 -1154
- package/.framework/functions/cil-pipeline.ts +276 -21
- package/.framework/functions/cil-project.ts +659 -132
- package/.framework/functions/cil-validate.ts +198 -164
- package/.framework/functions/debug-auth.ts +57 -55
- package/.framework/functions/deliveries.ts +80 -21
- package/.framework/functions/embeddings.ts +1023 -318
- package/.framework/functions/eval-harness.ts +146 -108
- package/.framework/functions/integration-outbound.ts +205 -75
- package/.framework/functions/integration-routes.ts +128 -23
- package/.framework/functions/integrations.ts +125 -100
- package/.framework/functions/invites.ts +1095 -460
- package/.framework/functions/item-progress.ts +158 -102
- package/.framework/functions/kb.ts +34 -10
- package/.framework/functions/logs.ts +133 -133
- package/.framework/functions/observability.ts +152 -122
- package/.framework/functions/pipeline-executions.ts +147 -132
- package/.framework/functions/pipelines.ts +134 -119
- package/.framework/functions/prompt-configs.ts +96 -80
- package/.framework/functions/roles.ts +112 -82
- package/.framework/functions/security.ts +450 -0
- package/.framework/functions/system-cron.ts +117 -80
- package/.framework/functions/system.ts +158 -98
- package/.framework/functions/tests.ts +29 -29
- package/.framework/functions/timers.ts +94 -95
- package/.framework/functions/trigger-scheduler.ts +10 -0
- package/.framework/functions/triggers.ts +179 -149
- package/.framework/functions/types.ts +137 -125
- package/.framework/mcp/index.ts +133 -0
- package/.framework/mcp/load-env.ts +60 -0
- package/.framework/mcp/shared/app-manifest.ts +275 -0
- package/.framework/mcp/shared/db.ts +477 -0
- package/.framework/mcp/shared/kb.ts +118 -0
- package/.framework/mcp/tools/architect.ts +431 -0
- package/.framework/mcp/tools/cil/context.ts +345 -0
- package/.framework/mcp/tools/cil/graph.ts +242 -0
- package/.framework/mcp/tools/cil/ingest.ts +1217 -0
- package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +693 -0
- package/.framework/mcp/tools/cil/pipeline.ts +767 -0
- package/.framework/mcp/tools/cil/project.ts +791 -0
- package/.framework/mcp/tools/cil/retrieval.ts +336 -0
- package/.framework/mcp/tools/cil/search-core.ts +454 -0
- package/.framework/mcp/tools/cil/search.ts +97 -0
- package/.framework/mcp/tools/cil/suggest.ts +186 -0
- package/.framework/mcp/tools/cil/validate.ts +1369 -0
- package/.framework/mcp/tools/kb/delete.ts +139 -0
- package/.framework/mcp/tools/kb/index.ts +52 -0
- package/.framework/mcp/tools/kb/ingest.ts +84 -0
- package/.framework/mcp/tools/kb/list-types.ts +102 -0
- package/.framework/mcp/tools/kb/publish.ts +100 -0
- package/.framework/mcp/tools/kb/reindex.ts +86 -0
- package/.framework/mcp/tools/kb/revoke.ts +85 -0
- package/.framework/mcp/tools/kb/search.ts +101 -0
- package/.framework/mcp/tools/kb/upsert.ts +125 -0
- package/.framework/mcp/tools/observability.ts +510 -0
- package/.framework/migrations/001_core_schema.sql +15 -0
- package/.framework/migrations/002_core_rls_and_functions.sql +15 -0
- package/.framework/migrations/003_core_system_data.sql +16 -1
- package/.framework/migrations/004_core_rpc_and_broker.sql +17 -1
- package/.framework/migrations/005_core_observability.sql +17 -1
- package/.framework/migrations/006_security_hardening_rls.sql +15 -0
- package/.framework/migrations/007_kb_article_list.sql +17 -1
- package/.framework/migrations/008_items_target.sql +15 -0
- package/.framework/migrations/009_roles_slug_unique.sql +15 -0
- package/.framework/migrations/010_auth_user_trigger.sql +15 -0
- package/.framework/migrations/011_spine_system_account.sql +16 -0
- package/.framework/migrations/012_service_role_grants.sql +16 -0
- package/.framework/migrations/013_get_account_apps_role_filter.sql +15 -0
- package/.framework/migrations/014_apps_required_roles.sql +16 -0
- package/.framework/migrations/015_types_kind_slug_unique.sql +20 -0
- package/.framework/migrations/016_cil_types.sql +281 -0
- package/.framework/migrations/017_cil_stable_ids.sql +37 -0
- package/.framework/migrations/018_runtime_tables.sql +129 -0
- package/.framework/migrations/019_agent_prompt_columns.sql +56 -0
- package/.framework/migrations/020_schema_reconciliation.sql +67 -0
- package/.framework/migrations/021_kb_embedding_lifecycle.sql +31 -0
- package/.framework/migrations/032_cil_status_migration.sql +73 -0
- package/.framework/migrations/033_cil_versioning.sql +73 -0
- package/.framework/migrations/034_match_embeddings_knn.sql +115 -0
- package/.framework/migrations/035_rate_limits.sql +55 -0
- package/.framework/migrations/036_demo_record_flags.sql +72 -0
- package/.framework/migrations/037_kb_types_canonical.sql +244 -0
- package/.framework/migrations/038_drop_demo_record_flags.sql +38 -0
- package/.framework/migrations/042_registration_hierarchy.sql +309 -0
- package/.framework/migrations/043_policy_actor_cleanup.sql +39 -0
- package/.framework/migrations/044_match_embeddings_filtered_knn.sql +151 -0
- package/.framework/migrations/045_security_critical_rls.sql +139 -0
- package/.framework/migrations/046_security_missing_rls.sql +79 -0
- package/.framework/migrations/047_people_updated_by.sql +27 -0
- package/.framework/migrations/048_watcher_last_seen_at.sql +24 -0
- package/.framework/migrations/049_security_blocklist.sql +148 -0
- package/.framework/migrations/050_notifications.sql +49 -0
- package/.framework/migrations/051_message_agent_fields.sql +74 -0
- package/.framework/migrations/052_registration_security.sql +421 -0
- package/.framework/migrations/053_builtin_action_seeds.sql +66 -0
- package/.framework/migrations/054_kb_ingest_lease.sql +157 -0
- package/.framework/migrations/055_type_id_drift_reconciliation.sql +33 -0
- package/.framework/migrations/056_slug_column_reconciliation.sql +38 -0
- package/.framework/migrations/057_pipelines_slug_identity.sql +14 -0
- package/.framework/migrations/058_account_data_field_declarations.sql +53 -0
- package/.framework/migrations/059_registration_placement_fixes.sql +269 -0
- package/.framework/migrations/060_account_person_record_permissions.sql +38 -0
- package/.framework/migrations/061_fk_dedup.sql +32 -0
- package/.framework/public/spine-logo.svg +10 -3
- package/.framework/scripts/app-install-cli.ts +190 -93
- package/.framework/scripts/assemble-frontend.sh +127 -44
- package/.framework/scripts/assemble-functions.sh +128 -30
- package/.framework/scripts/boundary-check.sh +6 -2
- package/.framework/scripts/build-manifest.sh +24 -68
- package/.framework/scripts/check-core-integrity.sh +15 -76
- package/.framework/scripts/checksum-manifest.mts +343 -0
- package/.framework/scripts/cil-migrate-markers.ts +403 -0
- package/.framework/scripts/fix-cil-markers.mjs +176 -0
- package/.framework/scripts/verify-integrity.sh +21 -57
- package/.framework/scripts/verify-manifest.mts +305 -0
- package/.framework/seeds/types/funnel_signal_use.json +74 -21
- package/.framework/seeds/types/spine_instance.json +73 -21
- package/.framework/src/App.tsx +118 -72
- package/.framework/src/apps/admin/index.tsx +855 -58
- package/.framework/src/components/AppWrapper.tsx +43 -45
- package/.framework/src/components/CustomAppLoader.tsx +160 -116
- package/.framework/src/components/admin/AdminListPage.tsx +46 -44
- package/.framework/src/components/admin/AdminSidebar.tsx +57 -47
- package/.framework/src/components/admin/AdminStatsCard.tsx +46 -44
- package/.framework/src/components/admin/PageInstructions.tsx +98 -2
- package/.framework/src/components/admin/SortableTableHeader.tsx +44 -45
- package/.framework/src/components/app-shell/GenericAppShell.tsx +64 -65
- package/.framework/src/components/app-shell/GenericDetailPage.tsx +44 -47
- package/.framework/src/components/app-shell/GenericListPage.tsx +45 -49
- package/.framework/src/components/app-sidebar.tsx +42 -45
- package/.framework/src/components/auth/ProtectedRoute.tsx +48 -41
- package/.framework/src/components/auth/TurnstileWidget.tsx +27 -0
- package/.framework/src/components/kb/KBArticleEditor.tsx +231 -22
- package/.framework/src/components/kb/KBArticleList.tsx +251 -10
- package/.framework/src/components/layout/AppShell.tsx +51 -45
- package/.framework/src/components/layout/Header.tsx +42 -43
- package/.framework/src/components/layout/Layout.tsx +46 -46
- package/.framework/src/components/layout/Sidebar.tsx +196 -96
- package/.framework/src/components/runtime/DataDetailHeader.tsx +55 -30
- package/.framework/src/components/runtime/DataDetailPage.tsx +31 -269
- package/.framework/src/components/runtime/DataFilters.tsx +49 -74
- package/.framework/src/components/runtime/DataHeader.tsx +53 -32
- package/.framework/src/components/runtime/DataListPage.tsx +51 -155
- package/.framework/src/components/runtime/DataStats.tsx +66 -44
- package/.framework/src/components/runtime/DataTable.tsx +49 -149
- package/.framework/src/components/runtime/SchemaDetailForm.tsx +45 -75
- package/.framework/src/components/runtime/index.ts +119 -16
- package/.framework/src/components/search-form.tsx +46 -29
- package/.framework/src/components/shared/AgentView.tsx +65 -48
- package/.framework/src/components/shared/FieldRenderer.tsx +144 -50
- package/.framework/src/components/shared/InviteManager.tsx +207 -97
- package/.framework/src/components/shared/MemberList.tsx +97 -0
- package/.framework/src/components/shared/SchemaFields.tsx +107 -86
- package/.framework/src/components/ui/DataTable.tsx +80 -68
- package/.framework/src/components/ui/Form.tsx +157 -146
- package/.framework/src/components/ui/ItemCard.tsx +74 -67
- package/.framework/src/components/ui/ItemListView.tsx +73 -68
- package/.framework/src/components/ui/LoadingSpinner.tsx +43 -48
- package/.framework/src/components/ui/Modal.tsx +78 -68
- package/.framework/src/components/ui/RichTextEditor.tsx +89 -69
- package/.framework/src/components/ui/accordion.tsx +85 -86
- package/.framework/src/components/ui/alert-dialog.tsx +197 -184
- package/.framework/src/components/ui/alert.tsx +105 -94
- package/.framework/src/components/ui/aspect-ratio.tsx +57 -43
- package/.framework/src/components/ui/avatar.tsx +122 -107
- package/.framework/src/components/ui/badge.tsx +60 -55
- package/.framework/src/components/ui/breadcrumb.tsx +127 -128
- package/.framework/src/components/ui/button-group.tsx +86 -72
- package/.framework/src/components/ui/button.tsx +71 -50
- package/.framework/src/components/ui/calendar.tsx +72 -57
- package/.framework/src/components/ui/card.tsx +136 -129
- package/.framework/src/components/ui/carousel.tsx +226 -107
- package/.framework/src/components/ui/chart.tsx +252 -147
- package/.framework/src/components/ui/checkbox.tsx +43 -44
- package/.framework/src/components/ui/collapsible.tsx +86 -71
- package/.framework/src/components/ui/combobox.tsx +282 -249
- package/.framework/src/components/ui/command.tsx +158 -146
- package/.framework/src/components/ui/context-menu.tsx +239 -240
- package/.framework/src/components/ui/dialog.tsx +174 -157
- package/.framework/src/components/ui/direction.tsx +72 -56
- package/.framework/src/components/ui/drawer.tsx +168 -169
- package/.framework/src/components/ui/dropdown-menu.tsx +254 -223
- package/.framework/src/components/ui/empty.tsx +131 -120
- package/.framework/src/components/ui/field.tsx +205 -173
- package/.framework/src/components/ui/hover-card.tsx +71 -72
- package/.framework/src/components/ui/input-group.tsx +148 -133
- package/.framework/src/components/ui/input-otp.tsx +99 -86
- package/.framework/src/components/ui/input.tsx +43 -41
- package/.framework/src/components/ui/item.tsx +207 -185
- package/.framework/src/components/ui/kbd.tsx +57 -56
- package/.framework/src/components/ui/label.tsx +45 -41
- package/.framework/src/components/ui/menubar.tsx +268 -236
- package/.framework/src/components/ui/native-select.tsx +85 -86
- package/.framework/src/components/ui/navigation-menu.tsx +166 -156
- package/.framework/src/components/ui/pagination.tsx +131 -141
- package/.framework/src/components/ui/popover.tsx +125 -128
- package/.framework/src/components/ui/progress.tsx +47 -44
- package/.framework/src/components/ui/radio-group.tsx +60 -55
- package/.framework/src/components/ui/resizable.tsx +85 -72
- package/.framework/src/components/ui/scroll-area.tsx +57 -58
- package/.framework/src/components/ui/select.tsx +182 -169
- package/.framework/src/components/ui/separator.tsx +42 -43
- package/.framework/src/components/ui/sheet.tsx +170 -159
- package/.framework/src/components/ui/sidebar.tsx +395 -398
- package/.framework/src/components/ui/skeleton.tsx +42 -43
- package/.framework/src/components/ui/slider.tsx +61 -44
- package/.framework/src/components/ui/sonner.tsx +44 -44
- package/.framework/src/components/ui/spinner.tsx +47 -42
- package/.framework/src/components/ui/switch.tsx +56 -42
- package/.framework/src/components/ui/table-primitives.tsx +272 -132
- package/.framework/src/components/ui/table.tsx +140 -142
- package/.framework/src/components/ui/tabs.tsx +110 -93
- package/.framework/src/components/ui/textarea.tsx +43 -44
- package/.framework/src/components/ui/toggle-group.tsx +87 -71
- package/.framework/src/components/ui/toggle.tsx +56 -56
- package/.framework/src/components/ui/tooltip.tsx +99 -85
- package/.framework/src/contexts/AppContext.tsx +166 -114
- package/.framework/src/contexts/AuthContext.tsx +232 -137
- package/.framework/src/hooks/use-mobile.ts +43 -44
- package/.framework/src/hooks/useApi.ts +185 -172
- package/.framework/src/hooks/useAppPath.ts +31 -29
- package/.framework/src/hooks/useApps.ts +77 -42
- package/.framework/src/hooks/useEntityList.ts +66 -29
- package/.framework/src/hooks/useEntityRecord.ts +76 -32
- package/.framework/src/hooks/useForm.ts +62 -30
- package/.framework/src/hooks/useListSchema.ts +60 -43
- package/.framework/src/hooks/useSchemaRecord.ts +115 -95
- package/.framework/src/lib/api.ts +258 -134
- package/.framework/src/lib/registration.ts +181 -0
- package/.framework/src/lib/supabase.ts +118 -81
- package/.framework/src/lib/utils.ts +266 -266
- package/.framework/src/main.tsx +43 -44
- package/.framework/src/pages/DashboardPage.tsx +44 -43
- package/.framework/src/pages/NoAppPage.tsx +50 -0
- package/.framework/src/pages/NotFoundPage.tsx +32 -29
- package/.framework/src/pages/SetupPage.tsx +34 -33
- package/.framework/src/pages/admin/AIAgentDetailPage.tsx +69 -58
- package/.framework/src/pages/admin/AIAgentsPage.tsx +43 -40
- package/.framework/src/pages/admin/APIKeyDetailPage.tsx +63 -55
- package/.framework/src/pages/admin/APIKeysPage.tsx +44 -42
- package/.framework/src/pages/admin/AlertsConfigPage.tsx +117 -94
- package/.framework/src/pages/admin/AppDetailPage.tsx +48 -42
- package/.framework/src/pages/admin/AppsPage.tsx +45 -42
- package/.framework/src/pages/admin/DeliveriesPage.tsx +49 -17
- package/.framework/src/pages/admin/DesignedPage.tsx +31 -30
- package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +58 -0
- package/.framework/src/pages/admin/EmbeddingsPage.tsx +49 -0
- package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +31 -29
- package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +46 -29
- package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -29
- package/.framework/src/pages/admin/IntegrationDetailPage.tsx +68 -55
- package/.framework/src/pages/admin/IntegrationsPage.tsx +45 -42
- package/.framework/src/pages/admin/InvitesPage.tsx +60 -44
- package/.framework/src/pages/admin/LogsPage.tsx +87 -137
- package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +31 -29
- package/.framework/src/pages/admin/ObservabilityDashboard.tsx +150 -89
- package/.framework/src/pages/admin/PipelineDetailPage.tsx +77 -104
- package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +44 -43
- package/.framework/src/pages/admin/PipelinesPage.tsx +59 -145
- package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +53 -42
- package/.framework/src/pages/admin/PromptConfigsPage.tsx +47 -117
- package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +31 -44
- package/.framework/src/pages/admin/RoleDetailPage.tsx +49 -92
- package/.framework/src/pages/admin/RolesPage.tsx +47 -164
- package/.framework/src/pages/admin/SecurityPage.tsx +310 -0
- package/.framework/src/pages/admin/SelectTestPage.tsx +30 -32
- package/.framework/src/pages/admin/ShadcnTestPage.tsx +37 -44
- package/.framework/src/pages/admin/SimpleDashboard.tsx +32 -44
- package/.framework/src/pages/admin/TestRunDetailPage.tsx +97 -44
- package/.framework/src/pages/admin/TestingDashboard.tsx +126 -71
- package/.framework/src/pages/admin/TimerDetailPage.tsx +67 -55
- package/.framework/src/pages/admin/TimersPage.tsx +44 -42
- package/.framework/src/pages/admin/TriggerDetailPage.tsx +47 -42
- package/.framework/src/pages/admin/TriggersPage.tsx +58 -57
- package/.framework/src/pages/admin/TypeDetailPage.tsx +91 -55
- package/.framework/src/pages/admin/TypesPage.tsx +45 -42
- package/.framework/src/pages/admin/cil/CILAgentPage.tsx +258 -128
- package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +127 -123
- package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +308 -235
- package/.framework/src/pages/admin/cil/CILFileListPage.tsx +159 -110
- package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +432 -0
- package/.framework/src/pages/admin/cil/CILGraphPage.tsx +200 -178
- package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +286 -239
- package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +211 -90
- package/.framework/src/pages/admin/cil/CILSearchPage.tsx +156 -143
- package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +379 -0
- package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +337 -0
- package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +219 -214
- package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
- package/.framework/src/pages/auth/AuthConfirmPage.tsx +135 -65
- package/.framework/src/pages/auth/ForgotPasswordPage.tsx +39 -31
- package/.framework/src/pages/auth/LoginPage.tsx +38 -32
- package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -28
- package/.framework/src/pages/auth/RegisterPage.tsx +429 -155
- package/.framework/src/pages/spine-framework/APIPage.tsx +33 -30
- package/.framework/src/pages/spine-framework/CLIPage.tsx +32 -30
- package/.framework/src/types/auth.ts +101 -93
- package/.framework/src/types/types.ts +283 -262
- package/.framework/templates/project.json +87 -0
- package/.framework/templates/spine.config.neutral.json +7 -0
- package/.framework/tests/api/api-surface.test.ts +381 -0
- package/.framework/tests/fixtures/seed.ts +427 -0
- package/.framework/tests/fixtures/teardown.ts +228 -0
- package/.framework/tests/reporter.ts +351 -0
- package/.framework/tests/setup.ts +135 -0
- package/.framework/tests/ui/ui-sweep.ts +278 -0
- package/.framework/tests/unit/admin-data-logger.test.ts +310 -0
- package/.framework/tests/unit/agent-runner.test.ts +2389 -0
- package/.framework/tests/unit/ai-interactions.test.ts +81 -0
- package/.framework/tests/unit/app-manifest.test.ts +359 -0
- package/.framework/tests/unit/assembly-scripts.test.ts +172 -0
- package/.framework/tests/unit/cil-determinism.test.ts +85 -0
- package/.framework/tests/unit/core-isolation.test.ts +153 -0
- package/.framework/tests/unit/funnel-signals.test.ts +135 -0
- package/.framework/tests/unit/install-app.test.ts +1759 -0
- package/.framework/tests/unit/invites-security.test.ts +572 -0
- package/.framework/tests/unit/kb-ingest-lease.test.ts +287 -0
- package/.framework/tests/unit/kb-search.test.ts +119 -0
- package/.framework/tests/unit/migrate.test.ts +509 -0
- package/.framework/tests/unit/notify.test.ts +136 -0
- package/.framework/tests/unit/permissions.test.ts +269 -0
- package/.framework/tests/unit/pipeline-runner.test.ts +755 -0
- package/.framework/tests/unit/principal-cache.test.ts +138 -0
- package/.framework/tests/unit/retrieval-golden.test.ts +520 -0
- package/.framework/tests/unit/runtime-isolation.test.ts +642 -0
- package/.framework/tests/unit/schema-utils.test.ts +225 -0
- package/.framework/tests/unit/trigger-engine.test.ts +513 -0
- package/.framework/tests/unit/validate-app.test.ts +527 -0
- package/README.md +34 -3
- package/bin/spine-framework.cjs +1 -0
- package/config/tailwind.config.cjs +7 -0
- package/config/tsconfig.functions.json +42 -0
- package/config/tsconfig.json +37 -9
- package/config/vite.config.ts +31 -2
- package/netlify.toml +31 -3
- package/package.json +46 -29
- package/scripts/assemble-frontend.sh +127 -44
- package/scripts/assemble-functions.sh +127 -29
- package/scripts/boundary-check.sh +6 -2
- package/scripts/build-manifest.sh +24 -68
- package/scripts/checksum-manifest.mts +345 -0
- package/scripts/dev-clean.sh +86 -0
- package/scripts/dev-netlify.sh +49 -0
- package/scripts/netlify-dev-wrapper.sh +2 -0
- package/scripts/verify-install.ts +358 -0
- package/scripts/verify-integrity.sh +21 -57
- package/scripts/verify-manifest.mts +305 -0
- package/templates/project/gitignore +45 -0
- package/{config → templates/project}/package-lock.json +22335 -9366
- package/vitest.config.ts +46 -0
- package/.framework/public/spine.config.json +0 -91
- package/config/deno.lock +0 -108
- package/config/tsconfig.node.tsbuildinfo +0 -1
- package/config/tsconfig.tsbuildinfo +0 -1
- package/config/vite.config.d.ts +0 -2
- package/custom/.devin/AGENT.md +0 -357
- package/custom/.devin/skills/install-app/SKILL.md +0 -39
- package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +0 -39
- package/custom/.devin/skills/spine-workbench-idea/SKILL.md +0 -36
- package/custom/.devin/skills/spine-workbench-implement/SKILL.md +0 -46
- package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +0 -28
- package/custom/.devin/skills/spine-workbench-plan/SKILL.md +0 -38
- package/custom/.devin/workflows/app-install.yaml +0 -83
- package/custom/README.md +0 -79
- package/docs/README.md +0 -15
- package/scripts/app-install-cli.ts +0 -286
- package/scripts/check-core-integrity.sh +0 -82
- package/scripts/ingest-chunks.cjs +0 -202
- package/scripts/kb-chunk-parser.cjs +0 -312
- package/scripts/kb-chunk-parser.ts +0 -330
- package/scripts/kb-ingest-docs.ts +0 -203
- package/scripts/load-test-app-install.ts +0 -484
- package/scripts/test-login.js +0 -33
- package/spine.config.json +0 -91
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/tests/unit/validate-app.test.ts"]
|
|
2
|
+
// CIL[DOC-MACRO purpose="Import Vitest, filesystem fixture utilities, Commander, and validator entry points."]
|
|
3
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Import Vitest, filesystem fixture utilities, Commander, and validator entry points."]
|
|
4
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
5
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
6
|
+
// CIL[DOC-MICRO logic_summary="Import Vitest, filesystem fixture utilities, Commander, and validator entry points." notable_decisions=""]
|
|
7
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
8
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
9
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
10
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
11
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, symlinkSync } from 'node:fs'
|
|
12
|
+
import { tmpdir } from 'node:os'
|
|
13
|
+
import { join, dirname } from 'node:path'
|
|
14
|
+
import { Command } from 'commander'
|
|
15
|
+
import { validateAppStructure, registerValidateAppCommands } from '../../cli/commands/validate-app.ts'
|
|
16
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:module_header:module_header"]
|
|
17
|
+
|
|
18
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:root:variable" type="variable" identity="root" parent="none"]
|
|
19
|
+
// CIL[DOC-MACRO purpose="Declare the temporary repository root used by validator fixtures."]
|
|
20
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Declare the temporary repository root used by validator fixtures."]
|
|
21
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
22
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="Mutable root path" errors="Assertion or fixture failures propagate to Vitest" side_effects="Declares module-local fixture state"]
|
|
23
|
+
// CIL[DOC-MICRO logic_summary="Declare the temporary repository root used by validator fixtures." notable_decisions=""]
|
|
24
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
25
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
26
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
27
|
+
let root: string
|
|
28
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:root:variable"]
|
|
29
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:app:variable" type="variable" identity="app" parent="none"]
|
|
30
|
+
// CIL[DOC-MACRO purpose="Declare the current temporary app directory."]
|
|
31
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Declare the current temporary app directory."]
|
|
32
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
33
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="Mutable app path" errors="Assertion or fixture failures propagate to Vitest" side_effects="Declares module-local fixture state"]
|
|
34
|
+
// CIL[DOC-MICRO logic_summary="Declare the current temporary app directory." notable_decisions=""]
|
|
35
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
36
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
37
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
38
|
+
let app: string
|
|
39
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:app:variable"]
|
|
40
|
+
|
|
41
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:fixture_setup:block" type="block" identity="fixture_setup" parent="none"]
|
|
42
|
+
// CIL[DOC-MACRO purpose="Register fixture setup that creates a temporary root and mocks console output."]
|
|
43
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Register fixture setup that creates a temporary root and mocks console output."]
|
|
44
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
45
|
+
// CIL[DOC-CONTRACT inputs="Vitest beforeEach lifecycle" outputs="Fixture paths and console spies" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers a hook that creates a temporary directory and installs console spies"]
|
|
46
|
+
// CIL[DOC-MICRO logic_summary="Register fixture setup that creates a temporary root and mocks console output." notable_decisions=""]
|
|
47
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
48
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
49
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
root = mkdtempSync(join(tmpdir(), 'spine-validate-app-'))
|
|
52
|
+
app = join(root, 'test-app')
|
|
53
|
+
vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
54
|
+
vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
55
|
+
})
|
|
56
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:fixture_setup:block"]
|
|
57
|
+
|
|
58
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:fixture_cleanup:block" type="block" identity="fixture_cleanup" parent="none"]
|
|
59
|
+
// CIL[DOC-MACRO purpose="Register fixture cleanup that removes the created root and restores spies."]
|
|
60
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Register fixture cleanup that removes the created root and restores spies."]
|
|
61
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
62
|
+
// CIL[DOC-CONTRACT inputs="Fixture root and Vitest mocks" outputs="Cleaned fixture and restored mocks" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers a cleanup hook"]
|
|
63
|
+
// CIL[DOC-MICRO logic_summary="Register fixture cleanup that removes the created root and restores spies." notable_decisions=""]
|
|
64
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
65
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
66
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
67
|
+
afterEach(() => {
|
|
68
|
+
rmSync(root, { recursive: true, force: true })
|
|
69
|
+
vi.restoreAllMocks()
|
|
70
|
+
})
|
|
71
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:fixture_cleanup:block"]
|
|
72
|
+
|
|
73
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:fixtureoptions:interface" type="interface" identity="FixtureOptions" parent="none"]
|
|
74
|
+
// CIL[DOC-MACRO purpose="Describe optional manifest, directory, file, and seed overrides for a fixture."]
|
|
75
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Describe optional manifest, directory, file, and seed overrides for a fixture."]
|
|
76
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
77
|
+
// CIL[DOC-CONTRACT inputs="Fixture override values" outputs="FixtureOptions type" errors="Assertion or fixture failures propagate to Vitest" side_effects="none"]
|
|
78
|
+
// CIL[DOC-MICRO logic_summary="Describe optional manifest, directory, file, and seed overrides for a fixture." notable_decisions=""]
|
|
79
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
80
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
81
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
82
|
+
interface FixtureOptions {
|
|
83
|
+
manifest?: Record<string, any>
|
|
84
|
+
manifestText?: string
|
|
85
|
+
dirs?: string[]
|
|
86
|
+
files?: Record<string, string>
|
|
87
|
+
seedJson?: Record<string, string>
|
|
88
|
+
}
|
|
89
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:fixtureoptions:interface"]
|
|
90
|
+
|
|
91
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:makeapp:function" type="function" identity="makeApp" parent="none"]
|
|
92
|
+
// CIL[DOC-MACRO purpose="Write a minimal valid temporary app with optional manifest, directory, file, and seed overrides."]
|
|
93
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Write a minimal valid temporary app with optional manifest, directory, file, and seed overrides."]
|
|
94
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
95
|
+
// CIL[DOC-CONTRACT inputs="opts: FixtureOptions default empty object" outputs="void" errors="Assertion or fixture failures propagate to Vitest" side_effects="Creates temporary app directories and writes fixture files"]
|
|
96
|
+
// CIL[DOC-MICRO logic_summary="Write a minimal valid temporary app with optional manifest, directory, file, and seed overrides." notable_decisions=""]
|
|
97
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
98
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
99
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
100
|
+
function makeApp(opts: FixtureOptions = {}): void {
|
|
101
|
+
mkdirSync(app, { recursive: true })
|
|
102
|
+
const manifest = opts.manifest ?? {
|
|
103
|
+
name: 'Test App',
|
|
104
|
+
slug: 'test-app',
|
|
105
|
+
version: '0.1.0',
|
|
106
|
+
app_type: 'full',
|
|
107
|
+
route_prefix: '/test-app',
|
|
108
|
+
entry_point: 'index.tsx',
|
|
109
|
+
seed: [{ file: 'seed/roles.json', table: 'roles', conflict: 'slug' }],
|
|
110
|
+
config: { knowledge: { account: true } },
|
|
111
|
+
registration: { enabled: true },
|
|
112
|
+
}
|
|
113
|
+
writeFileSync(join(app, 'manifest.json'), opts.manifestText ?? JSON.stringify(manifest))
|
|
114
|
+
writeFileSync(join(app, 'package.json'), JSON.stringify({ name: 'test-app', version: '0.1.0', private: true }))
|
|
115
|
+
writeFileSync(join(app, 'index.tsx'), 'export default () => null\n')
|
|
116
|
+
for (const d of ['pages', 'functions', 'seed', ...(opts.dirs ?? [])]) {
|
|
117
|
+
mkdirSync(join(app, d), { recursive: true })
|
|
118
|
+
}
|
|
119
|
+
writeFileSync(join(app, 'seed/roles.json'), JSON.stringify([{ slug: 'member' }]))
|
|
120
|
+
for (const [rel, content] of Object.entries(opts.files ?? {})) {
|
|
121
|
+
mkdirSync(dirname(join(app, rel)), { recursive: true })
|
|
122
|
+
writeFileSync(join(app, rel), content)
|
|
123
|
+
}
|
|
124
|
+
for (const [rel, content] of Object.entries(opts.seedJson ?? {})) {
|
|
125
|
+
writeFileSync(join(app, rel), content)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:makeapp:function"]
|
|
129
|
+
|
|
130
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:basemanifest:function" type="function" identity="baseManifest" parent="none"]
|
|
131
|
+
// CIL[DOC-MACRO purpose="Build the standard validator fixture manifest with supplied overrides."]
|
|
132
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Build the standard validator fixture manifest with supplied overrides."]
|
|
133
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
134
|
+
// CIL[DOC-CONTRACT inputs="overrides: Record<string, any> default empty object" outputs="Manifest fixture object" errors="Assertion or fixture failures propagate to Vitest" side_effects="none"]
|
|
135
|
+
// CIL[DOC-MICRO logic_summary="Build the standard validator fixture manifest with supplied overrides." notable_decisions=""]
|
|
136
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
137
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
138
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
139
|
+
function baseManifest(overrides: Record<string, any> = {}): Record<string, any> {
|
|
140
|
+
return {
|
|
141
|
+
name: 'Test App',
|
|
142
|
+
slug: 'test-app',
|
|
143
|
+
version: '0.1.0',
|
|
144
|
+
app_type: 'full',
|
|
145
|
+
route_prefix: '/test-app',
|
|
146
|
+
entry_point: 'index.tsx',
|
|
147
|
+
seed: [{ file: 'seed/roles.json', table: 'roles', conflict: 'slug' }],
|
|
148
|
+
config: { knowledge: { account: true } },
|
|
149
|
+
registration: { enabled: true },
|
|
150
|
+
...overrides,
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:basemanifest:function"]
|
|
154
|
+
|
|
155
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:acceptance_suite:block" type="block" identity="acceptance_suite" parent="none"]
|
|
156
|
+
// CIL[DOC-MACRO purpose="Register successful validation cases for current JSON seed paths, private packages, legacy app_type, source directories, and empty seeds."]
|
|
157
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Register successful validation cases for current JSON seed paths, private packages, legacy app_type, source directories, and empty seeds."]
|
|
158
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
159
|
+
// CIL[DOC-CONTRACT inputs="makeApp, baseManifest, validateAppStructure" outputs="Vitest acceptance assertions" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers test cases using temporary fixture files"]
|
|
160
|
+
// CIL[DOC-MICRO logic_summary="Register successful validation cases for current JSON seed paths, private packages, legacy app_type, source directories, and empty seeds." notable_decisions=""]
|
|
161
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
162
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
163
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
164
|
+
describe('validateAppStructure — acceptance', () => {
|
|
165
|
+
it('accepts a valid app fixture using current seed/ JSON paths', () => {
|
|
166
|
+
makeApp()
|
|
167
|
+
expect(() => validateAppStructure(app)).not.toThrow()
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('accepts a private internal package', () => {
|
|
171
|
+
makeApp()
|
|
172
|
+
expect(() => validateAppStructure(app)).not.toThrow()
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('normalizes nested registration.app_type when root app_type is absent', () => {
|
|
176
|
+
const m = baseManifest()
|
|
177
|
+
delete m.app_type
|
|
178
|
+
m.registration = { enabled: true, app_type: 'full' }
|
|
179
|
+
makeApp({ manifest: m })
|
|
180
|
+
expect(() => validateAppStructure(app)).not.toThrow()
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it('accepts the expanded allowed source directories', () => {
|
|
184
|
+
makeApp({ dirs: ['lib', 'views', 'docs', 'mcp', 'migrations', 'migrations-archive', 'tests'] })
|
|
185
|
+
expect(() => validateAppStructure(app)).not.toThrow()
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('accepts an empty JSON array seed file', () => {
|
|
189
|
+
makeApp({ manifest: baseManifest({ seed: [{ file: 'seed/empty.json', table: 'roles', conflict: 'slug' }] }), seedJson: { 'seed/empty.json': '[]' } })
|
|
190
|
+
expect(() => validateAppStructure(app)).not.toThrow()
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:acceptance_suite:block"]
|
|
194
|
+
|
|
195
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:manifest_rules_suite:block" type="block" identity="manifest_rules_suite" parent="none"]
|
|
196
|
+
// CIL[DOC-MACRO purpose="Register malformed and missing manifest, app_type precedence, invalid roots, and whitespace-name rejection cases."]
|
|
197
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Register malformed and missing manifest, app_type precedence, invalid roots, and whitespace-name rejection cases."]
|
|
198
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
199
|
+
// CIL[DOC-CONTRACT inputs="Fixture helpers and validateAppStructure" outputs="Vitest manifest validation assertions" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers tests that mutate or remove their own fixture files"]
|
|
200
|
+
// CIL[DOC-MICRO logic_summary="Register malformed and missing manifest, app_type precedence, invalid roots, and whitespace-name rejection cases." notable_decisions=""]
|
|
201
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
202
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
203
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
204
|
+
describe('validateAppStructure — manifest rules', () => {
|
|
205
|
+
it('rejects a missing manifest.json', () => {
|
|
206
|
+
mkdirSync(app, { recursive: true })
|
|
207
|
+
expect(() => validateAppStructure(app)).toThrowError(/manifest\.json is required/)
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
it('rejects malformed manifest.json', () => {
|
|
211
|
+
makeApp({ manifestText: '{nope' })
|
|
212
|
+
expect(() => validateAppStructure(app)).toThrowError(/not valid JSON/)
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('rejects an invalid explicit app_type', () => {
|
|
216
|
+
makeApp({ manifest: baseManifest({ app_type: 'banana' }) })
|
|
217
|
+
expect(() => validateAppStructure(app)).toThrowError(/app_type/)
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it('rejects an invalid nested registration.app_type', () => {
|
|
221
|
+
const m = baseManifest()
|
|
222
|
+
delete m.app_type
|
|
223
|
+
m.registration = { app_type: 'banana' }
|
|
224
|
+
makeApp({ manifest: m })
|
|
225
|
+
expect(() => validateAppStructure(app)).toThrowError(/app_type/)
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('still requires app_type when neither root nor nested value is present', () => {
|
|
229
|
+
const m = baseManifest()
|
|
230
|
+
delete m.app_type
|
|
231
|
+
makeApp({ manifest: m })
|
|
232
|
+
expect(() => validateAppStructure(app)).toThrowError(/app_type/)
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it('lets a root app_type take precedence over the nested registration value', () => {
|
|
236
|
+
const m = baseManifest({ app_type: 'backend', entry_point: undefined })
|
|
237
|
+
delete m.entry_point
|
|
238
|
+
m.registration = { app_type: 'full' }
|
|
239
|
+
makeApp({ manifest: m })
|
|
240
|
+
rmSync(join(app, 'index.tsx'))
|
|
241
|
+
rmSync(join(app, 'pages'), { recursive: true })
|
|
242
|
+
expect(() => validateAppStructure(app)).not.toThrow()
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
it('rejects a manifest.json root that is null, an array, or a primitive', () => {
|
|
246
|
+
for (const body of ['null', '[1]', '"x"', '7']) {
|
|
247
|
+
makeApp({ manifestText: body })
|
|
248
|
+
expect(() => validateAppStructure(app), body).toThrowError(/JSON object/)
|
|
249
|
+
rmSync(app, { recursive: true, force: true })
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it('rejects a whitespace-only manifest name', () => {
|
|
254
|
+
makeApp({ manifest: baseManifest({ name: ' ' }) })
|
|
255
|
+
expect(() => validateAppStructure(app)).toThrowError(/name/)
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:manifest_rules_suite:block"]
|
|
259
|
+
|
|
260
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:directory_rules_suite:block" type="block" identity="directory_rules_suite" parent="none"]
|
|
261
|
+
// CIL[DOC-MACRO purpose="Register rejection of an unknown app source directory."]
|
|
262
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Register rejection of an unknown app source directory."]
|
|
263
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
264
|
+
// CIL[DOC-CONTRACT inputs="makeApp and validateAppStructure" outputs="Vitest directory validation assertion" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers a temporary fixture test"]
|
|
265
|
+
// CIL[DOC-MICRO logic_summary="Register rejection of an unknown app source directory." notable_decisions=""]
|
|
266
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
267
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
268
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
269
|
+
describe('validateAppStructure — directory rules', () => {
|
|
270
|
+
it('rejects unknown directories', () => {
|
|
271
|
+
makeApp({ dirs: ['junk-drawer'] })
|
|
272
|
+
expect(() => validateAppStructure(app)).toThrowError(/Unknown directories/)
|
|
273
|
+
})
|
|
274
|
+
})
|
|
275
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:directory_rules_suite:block"]
|
|
276
|
+
|
|
277
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:seed_rules_suite:block" type="block" identity="seed_rules_suite" parent="none"]
|
|
278
|
+
// CIL[DOC-MACRO purpose="Register missing, malformed, non-object, absolute, escaping, and externally linked JSON seed rejection cases."]
|
|
279
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Register missing, malformed, non-object, absolute, escaping, and externally linked JSON seed rejection cases."]
|
|
280
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
281
|
+
// CIL[DOC-CONTRACT inputs="Fixture helpers and validateAppStructure" outputs="Vitest seed validation assertions" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers tests that create, rewrite, and remove temporary seed fixtures"]
|
|
282
|
+
// CIL[DOC-MICRO logic_summary="Register missing, malformed, non-object, absolute, escaping, and externally linked JSON seed rejection cases." notable_decisions=""]
|
|
283
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
284
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
285
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
286
|
+
describe('validateAppStructure — seed rules', () => {
|
|
287
|
+
it('rejects a missing seed file', () => {
|
|
288
|
+
makeApp({ manifest: baseManifest({ seed: [{ file: 'seed/nope.json', table: 'roles', conflict: 'slug' }] }) })
|
|
289
|
+
expect(() => validateAppStructure(app)).toThrowError(/not found/)
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('rejects a non-.json seed file', () => {
|
|
293
|
+
makeApp({
|
|
294
|
+
manifest: baseManifest({ seed: [{ file: 'seed/x.sql', table: 'roles', conflict: 'slug' }] }),
|
|
295
|
+
seedJson: { 'seed/x.sql': 'INSERT INTO roles VALUES (1);' },
|
|
296
|
+
})
|
|
297
|
+
expect(() => validateAppStructure(app)).toThrowError(/\.json/)
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
it('rejects a seed file that is not valid JSON', () => {
|
|
301
|
+
makeApp({ seedJson: { 'seed/roles.json': '{oops' } })
|
|
302
|
+
expect(() => validateAppStructure(app)).toThrowError(/valid JSON/)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('rejects a seed JSON root that is an object not an array', () => {
|
|
306
|
+
makeApp({ seedJson: { 'seed/roles.json': '{"a":1}' } })
|
|
307
|
+
expect(() => validateAppStructure(app)).toThrowError(/array/)
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
it('rejects null, primitive, or array seed rows', () => {
|
|
311
|
+
for (const body of ['[null]', '[1]', '["x"]', '[[1]]']) {
|
|
312
|
+
makeApp({ seedJson: { 'seed/roles.json': body } })
|
|
313
|
+
expect(() => validateAppStructure(app), body).toThrowError(/non-null object/)
|
|
314
|
+
rmSync(app, { recursive: true, force: true })
|
|
315
|
+
}
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
it('rejects a seed entry with missing file/table/conflict', () => {
|
|
319
|
+
makeApp({ manifest: baseManifest({ seed: [{ file: 'seed/roles.json', conflict: 'slug' }] }) })
|
|
320
|
+
expect(() => validateAppStructure(app)).toThrowError(/file, table, and conflict/)
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it('rejects a whitespace-only file/table/conflict', () => {
|
|
324
|
+
makeApp({ manifest: baseManifest({ seed: [{ file: ' ', table: 'roles', conflict: 'slug' }] }) })
|
|
325
|
+
expect(() => validateAppStructure(app)).toThrowError(/file, table, and conflict/)
|
|
326
|
+
rmSync(app, { recursive: true, force: true })
|
|
327
|
+
makeApp({ manifest: baseManifest({ seed: [{ file: 'seed/roles.json', table: ' ', conflict: 'slug' }] }) })
|
|
328
|
+
expect(() => validateAppStructure(app)).toThrowError(/file, table, and conflict/)
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
it('rejects null, array, or primitive seed entries', () => {
|
|
332
|
+
for (const entry of [null, 7, 'x', ['seed/roles.json']]) {
|
|
333
|
+
makeApp({ manifest: baseManifest({ seed: [entry] }) })
|
|
334
|
+
expect(() => validateAppStructure(app), JSON.stringify(entry)).toThrowError(/seed entries must be objects/)
|
|
335
|
+
rmSync(app, { recursive: true, force: true })
|
|
336
|
+
}
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
it('rejects an absolute seed path', () => {
|
|
340
|
+
makeApp({ manifest: baseManifest({ seed: [{ file: join(app, 'seed/roles.json'), table: 'roles', conflict: 'slug' }] }) })
|
|
341
|
+
expect(() => validateAppStructure(app)).toThrowError()
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
it('rejects a traversal seed path escaping the app root', () => {
|
|
345
|
+
writeFileSync(join(root, 'outside.json'), '[]')
|
|
346
|
+
makeApp({ manifest: baseManifest({ seed: [{ file: '../outside.json', table: 'roles', conflict: 'slug' }] }) })
|
|
347
|
+
expect(() => validateAppStructure(app)).toThrowError()
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
it('rejects a seed file that symlinks outside the app root', () => {
|
|
351
|
+
const external = join(root, 'external-seed.json')
|
|
352
|
+
writeFileSync(external, '[]')
|
|
353
|
+
makeApp({
|
|
354
|
+
manifest: baseManifest({ seed: [{ file: 'seed/external.json', table: 'roles', conflict: 'slug' }] }),
|
|
355
|
+
})
|
|
356
|
+
rmSync(join(app, 'seed/external.json'), { force: true })
|
|
357
|
+
symlinkSync(external, join(app, 'seed/external.json'))
|
|
358
|
+
expect(() => validateAppStructure(app)).toThrowError()
|
|
359
|
+
})
|
|
360
|
+
})
|
|
361
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:seed_rules_suite:block"]
|
|
362
|
+
|
|
363
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:component_paths_suite:block" type="block" identity="component_paths_suite" parent="none"]
|
|
364
|
+
// CIL[DOC-MACRO purpose="Register entry and sidebar path confinement checks plus successful contained paths."]
|
|
365
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Register entry and sidebar path confinement checks plus successful contained paths."]
|
|
366
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
367
|
+
// CIL[DOC-CONTRACT inputs="Fixture helpers and validateAppStructure" outputs="Vitest component path assertions" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers temporary file and symlink fixture tests"]
|
|
368
|
+
// CIL[DOC-MICRO logic_summary="Register entry and sidebar path confinement checks plus successful contained paths." notable_decisions=""]
|
|
369
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
370
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
371
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
372
|
+
describe('validateAppStructure — component path confinement', () => {
|
|
373
|
+
it('rejects an entry_point escaping the app root', () => {
|
|
374
|
+
writeFileSync(join(root, 'escape.tsx'), 'export default () => null\n')
|
|
375
|
+
makeApp({ manifest: baseManifest({ entry_point: '../escape.tsx' }) })
|
|
376
|
+
expect(() => validateAppStructure(app)).toThrowError(/entry_point/)
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
it('rejects an absolute entry_point outside the app root', () => {
|
|
380
|
+
makeApp({ manifest: baseManifest({ entry_point: join(root, 'escape.tsx') }) })
|
|
381
|
+
expect(() => validateAppStructure(app)).toThrowError(/entry_point/)
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
it('rejects a sidebar_component that symlinks outside the app root', () => {
|
|
385
|
+
const external = join(root, 'external-sidebar.tsx')
|
|
386
|
+
writeFileSync(external, 'export default () => null\n')
|
|
387
|
+
makeApp({ manifest: baseManifest({ sidebar_component: 'components/Sidebar.tsx' }) })
|
|
388
|
+
mkdirSync(join(app, 'components'), { recursive: true })
|
|
389
|
+
symlinkSync(external, join(app, 'components/Sidebar.tsx'))
|
|
390
|
+
expect(() => validateAppStructure(app)).toThrowError(/sidebar_component/)
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
it('accepts entry_point and sidebar_component inside the app', () => {
|
|
394
|
+
makeApp({
|
|
395
|
+
manifest: baseManifest({ entry_point: './index.tsx', sidebar_component: 'components/Sidebar.tsx' }),
|
|
396
|
+
files: { 'components/Sidebar.tsx': 'export default () => null\n' },
|
|
397
|
+
})
|
|
398
|
+
expect(() => validateAppStructure(app)).not.toThrow()
|
|
399
|
+
})
|
|
400
|
+
})
|
|
401
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:component_paths_suite:block"]
|
|
402
|
+
|
|
403
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:isolation_suite:block" type="block" identity="isolation_suite" parent="none"]
|
|
404
|
+
// CIL[DOC-MACRO purpose="Register successful validation while cwd differs from the repository and restore cwd afterward."]
|
|
405
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Register successful validation while cwd differs from the repository and restore cwd afterward."]
|
|
406
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
407
|
+
// CIL[DOC-CONTRACT inputs="Fixture app and validateAppStructure" outputs="Vitest cwd-independence assertion" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers a test that temporarily changes and restores process cwd"]
|
|
408
|
+
// CIL[DOC-MICRO logic_summary="Register successful validation while cwd differs from the repository and restore cwd afterward." notable_decisions=""]
|
|
409
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
410
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
411
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
412
|
+
describe('validateAppStructure — isolation', () => {
|
|
413
|
+
it('validates without repo .env or cwd dependence and no network use', () => {
|
|
414
|
+
makeApp()
|
|
415
|
+
const cwd = process.cwd()
|
|
416
|
+
try {
|
|
417
|
+
process.chdir(root)
|
|
418
|
+
expect(() => validateAppStructure(app)).not.toThrow()
|
|
419
|
+
} finally {
|
|
420
|
+
process.chdir(cwd)
|
|
421
|
+
}
|
|
422
|
+
})
|
|
423
|
+
})
|
|
424
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:isolation_suite:block"]
|
|
425
|
+
|
|
426
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:command_registration_suite:block" type="block" identity="command_registration_suite" parent="none"]
|
|
427
|
+
// CIL[DOC-MACRO purpose="Register a Commander assertion that validate-app appears in the command list and help."]
|
|
428
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Register a Commander assertion that validate-app appears in the command list and help."]
|
|
429
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
430
|
+
// CIL[DOC-CONTRACT inputs="Command and registerValidateAppCommands" outputs="Vitest command registration assertions" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers an in-memory Commander test"]
|
|
431
|
+
// CIL[DOC-MICRO logic_summary="Register a Commander assertion that validate-app appears in the command list and help." notable_decisions=""]
|
|
432
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
433
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
434
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
435
|
+
describe('registerValidateAppCommands', () => {
|
|
436
|
+
it('registers a validate-app command that appears in CLI help', () => {
|
|
437
|
+
const program = new Command()
|
|
438
|
+
registerValidateAppCommands(program)
|
|
439
|
+
expect(program.commands.map((c) => c.name())).toContain('validate-app')
|
|
440
|
+
expect(program.helpInformation()).toContain('validate-app')
|
|
441
|
+
})
|
|
442
|
+
})
|
|
443
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:command_registration_suite:block"]
|
|
444
|
+
|
|
445
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:owned_types_suite:block" type="block" identity="owned_types_suite" parent="none"]
|
|
446
|
+
// CIL[DOC-MACRO purpose="Register owned_types shape and type-seed resolution validation cases."]
|
|
447
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Register owned_types shape and type-seed resolution validation cases."]
|
|
448
|
+
// CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
|
|
449
|
+
// CIL[DOC-CONTRACT inputs="Fixture helpers and validateAppStructure" outputs="Vitest owned_types assertions" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers tests that write temporary seed fixtures"]
|
|
450
|
+
// CIL[DOC-MICRO logic_summary="Register owned_types shape and type-seed resolution validation cases." notable_decisions=""]
|
|
451
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
|
|
452
|
+
// CIL[DOC-STABILITY stability="experimental"]
|
|
453
|
+
// CIL[DOC-AUDIENCE audience="framework contributors"]
|
|
454
|
+
describe('validateAppStructure — owned_types', () => {
|
|
455
|
+
const typesSeedEntry = { file: 'seed/types.json', table: 'types', conflict: 'kind,slug', inject_app_id: false }
|
|
456
|
+
|
|
457
|
+
it('treats an absent owned_types as a valid optional field', () => {
|
|
458
|
+
makeApp()
|
|
459
|
+
expect(() => validateAppStructure(app)).not.toThrow()
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
it('accepts owned_types slugs that resolve to the types seed', () => {
|
|
463
|
+
makeApp({
|
|
464
|
+
manifest: baseManifest({
|
|
465
|
+
owned_types: ['demo_thing', 'demo_other'],
|
|
466
|
+
seed: [{ file: 'seed/roles.json', table: 'roles', conflict: 'slug' }, typesSeedEntry],
|
|
467
|
+
}),
|
|
468
|
+
seedJson: {
|
|
469
|
+
'seed/types.json': JSON.stringify([
|
|
470
|
+
{ kind: 'item', slug: 'demo_thing' },
|
|
471
|
+
{ kind: 'item', slug: 'demo_other' },
|
|
472
|
+
]),
|
|
473
|
+
},
|
|
474
|
+
})
|
|
475
|
+
expect(() => validateAppStructure(app)).not.toThrow()
|
|
476
|
+
})
|
|
477
|
+
|
|
478
|
+
it('accepts owned_types resolved by a declared non-patch types seed file', () => {
|
|
479
|
+
makeApp({
|
|
480
|
+
manifest: baseManifest({
|
|
481
|
+
owned_types: ['demo_thing'],
|
|
482
|
+
seed: [
|
|
483
|
+
{ file: 'seed/roles.json', table: 'roles', conflict: 'slug' },
|
|
484
|
+
{ file: 'seed/more-types.json', table: 'types', conflict: 'kind,slug', inject_app_id: false },
|
|
485
|
+
],
|
|
486
|
+
}),
|
|
487
|
+
seedJson: { 'seed/more-types.json': JSON.stringify([{ kind: 'item', slug: 'demo_thing' }]) },
|
|
488
|
+
})
|
|
489
|
+
expect(() => validateAppStructure(app)).not.toThrow()
|
|
490
|
+
})
|
|
491
|
+
|
|
492
|
+
it.each([
|
|
493
|
+
'not-an-array',
|
|
494
|
+
42,
|
|
495
|
+
{ slug: 'x' },
|
|
496
|
+
['ok', ''],
|
|
497
|
+
['ok', ' '],
|
|
498
|
+
['ok', 7],
|
|
499
|
+
[null],
|
|
500
|
+
])('rejects malformed owned_types %j', (owned_types) => {
|
|
501
|
+
makeApp({
|
|
502
|
+
manifest: baseManifest({
|
|
503
|
+
owned_types,
|
|
504
|
+
seed: [{ file: 'seed/roles.json', table: 'roles', conflict: 'slug' }, typesSeedEntry],
|
|
505
|
+
}),
|
|
506
|
+
seedJson: { 'seed/types.json': JSON.stringify([{ kind: 'item', slug: 'ok' }]) },
|
|
507
|
+
})
|
|
508
|
+
expect(() => validateAppStructure(app)).toThrowError(/owned_types/)
|
|
509
|
+
})
|
|
510
|
+
|
|
511
|
+
it('rejects an owned slug absent from the types seed', () => {
|
|
512
|
+
makeApp({
|
|
513
|
+
manifest: baseManifest({
|
|
514
|
+
owned_types: ['ghost'],
|
|
515
|
+
seed: [{ file: 'seed/roles.json', table: 'roles', conflict: 'slug' }, typesSeedEntry],
|
|
516
|
+
}),
|
|
517
|
+
seedJson: { 'seed/types.json': JSON.stringify([{ kind: 'item', slug: 'real' }]) },
|
|
518
|
+
})
|
|
519
|
+
expect(() => validateAppStructure(app)).toThrowError(/ghost/)
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
it('rejects owned_types when the app ships no types seed', () => {
|
|
523
|
+
makeApp({ manifest: baseManifest({ owned_types: ['anything'] }) })
|
|
524
|
+
expect(() => validateAppStructure(app)).toThrowError(/owned_types.*anything|anything.*types seed/)
|
|
525
|
+
})
|
|
526
|
+
})
|
|
527
|
+
// CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:owned_types_suite:block"]
|
package/README.md
CHANGED
|
@@ -8,11 +8,19 @@ A complete enterprise application platform built on Supabase + Netlify + React f
|
|
|
8
8
|
|
|
9
9
|
### Prerequisites
|
|
10
10
|
|
|
11
|
-
- Node.js
|
|
11
|
+
- Node.js 22+
|
|
12
12
|
- A [Supabase](https://supabase.com) project
|
|
13
13
|
- A [Netlify](https://netlify.com) account
|
|
14
14
|
|
|
15
|
-
###
|
|
15
|
+
### Start with an app
|
|
16
|
+
|
|
17
|
+
The fastest way to a running system is an app package that scaffolds the framework plus itself:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm create @spine-framework/answer-once my-app
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Or scaffold the framework alone
|
|
16
24
|
|
|
17
25
|
```bash
|
|
18
26
|
mkdir my-app && cd my-app
|
|
@@ -20,7 +28,7 @@ npm install spine-framework@latest
|
|
|
20
28
|
npx spine-framework init
|
|
21
29
|
```
|
|
22
30
|
|
|
23
|
-
This
|
|
31
|
+
This copies the framework files, writes `package.json` with an exact `spine-framework` pin, a verified `package-lock.json`, a neutral `spine.config.json`, `.env.example`, and a frozen `.spine-manifest.json` integrity manifest.
|
|
24
32
|
|
|
25
33
|
### 2. Configure your environment
|
|
26
34
|
|
|
@@ -65,6 +73,29 @@ All commands run via `npx spine-framework <command>`.
|
|
|
65
73
|
| `migrate --dry-run` | Preview pending migrations without applying |
|
|
66
74
|
| `migrate --db-password <pw>` | Pass DB password directly instead of reading from `.env` |
|
|
67
75
|
| `status` | Show installed apps and framework version |
|
|
76
|
+
| `upgrade` | Sync framework-owned files to the installed `spine-framework` version |
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Upgrading
|
|
81
|
+
|
|
82
|
+
`.framework/`, `config/`, `scripts/` and `bin/` are a managed copy of the
|
|
83
|
+
`spine-framework` package — don't edit them (the frozen `.spine-manifest.json`
|
|
84
|
+
integrity check will fail). To move to a newer release:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm install spine-framework@latest
|
|
88
|
+
npx spine-framework upgrade --dry-run # review what would change
|
|
89
|
+
npx spine-framework upgrade
|
|
90
|
+
npm install
|
|
91
|
+
npm run verify
|
|
92
|
+
npx spine-framework migrate # apply any new migrations
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Upgrade refuses to overwrite locally modified framework files (rerun with
|
|
96
|
+
`--force` to accept the overwrite), preserves your `custom/` code and app-owned
|
|
97
|
+
`netlify.toml` sections, and lists new migrations before you apply them.
|
|
98
|
+
App packages upgrade themselves the same way — see the app's own docs.
|
|
68
99
|
|
|
69
100
|
---
|
|
70
101
|
|
package/bin/spine-framework.cjs
CHANGED
|
@@ -14,6 +14,7 @@ if (process.argv.length === 2) {
|
|
|
14
14
|
console.log('Usage: spine-framework <command>\n')
|
|
15
15
|
console.log('Commands:')
|
|
16
16
|
console.log(' init Scaffold a new Spine Framework project')
|
|
17
|
+
console.log(' upgrade Sync framework files to the installed spine-framework version')
|
|
17
18
|
console.log(' migrate Apply database migrations')
|
|
18
19
|
console.log(' install-app Install a Spine Framework app from npm')
|
|
19
20
|
console.log(' status Show installed apps and framework version')
|
|
@@ -63,6 +63,12 @@ module.exports = {
|
|
|
63
63
|
'4': 'hsl(var(--chart-4))',
|
|
64
64
|
'5': 'hsl(var(--chart-5))',
|
|
65
65
|
},
|
|
66
|
+
brand: {
|
|
67
|
+
navy: '#0B1F44',
|
|
68
|
+
DEFAULT: '#0066FF',
|
|
69
|
+
light: '#33A1FF',
|
|
70
|
+
gray: '#F4F7FB',
|
|
71
|
+
},
|
|
66
72
|
},
|
|
67
73
|
borderRadius: {
|
|
68
74
|
lg: 'var(--radius)',
|
|
@@ -73,6 +79,7 @@ module.exports = {
|
|
|
73
79
|
},
|
|
74
80
|
plugins: [
|
|
75
81
|
require('tailwindcss-animate'),
|
|
82
|
+
require('@tailwindcss/typography'),
|
|
76
83
|
],
|
|
77
84
|
}
|
|
78
85
|
|