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,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module funnel
|
|
3
|
+
* @audience internal
|
|
4
|
+
* @layer shared-core
|
|
5
|
+
* @stability stable
|
|
6
|
+
*
|
|
7
|
+
* Generic typed-signal emission + account data-flag reads
|
|
8
|
+
*
|
|
9
|
+
* @seeAlso db (provides database access for signal insertion).
|
|
10
|
+
* @seeAlso trigger-engine (handles the creation of triggers based on emitted signals).
|
|
11
|
+
* @seeAlso middleware (defines the core context used in signal processing).
|
|
12
|
+
*
|
|
13
|
+
* @cil-generated
|
|
14
|
+
*/
|
|
15
|
+
// CIL[BEGIN stable_id="~/.framework/functions/_shared/funnel.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/functions/_shared/funnel.ts"]
|
|
16
|
+
// CIL[DOC-MACRO purpose="Generic typed-signal emission for app-defined journey milestones plus account data-flag reads"]
|
|
17
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Write signal items under a caller-declared type_slug for journey events (demo_started, workflow_completed, install_attempted, install_verified, feedback_submitted, escalation/completion signals, etc.)"]
|
|
18
|
+
// CIL[DOC-CONTEXT module="funnel" layer="shared-core" related_files=".framework/functions/_shared/db.ts,.framework/functions/_shared/middleware.ts,.framework/functions/_shared/trigger-engine.ts"]
|
|
19
|
+
// CIL[DOC-CONTRACT inputs="ctx, SignalInput {type_slug, action, data?, dedupe?, dedupe_scope?}" outputs="created item or null on skip" errors="never throws — failures are logged and swallowed" side_effects="items insert"]
|
|
20
|
+
// CIL[DOC-MICRO logic_summary="Resolve the declared item type once per call, dedupe on (account_id, action_type) or (account_id, action_type, dedupe_scope) when a scope is provided, insert the signal" notable_decisions="Milestones are once-per-account by default; dedupe_scope narrows dedupe to one-per-run; dedupe:false disables it entirely"]
|
|
21
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
22
|
+
// CIL[DOC-AUDIENCE audience="internal"]
|
|
23
|
+
|
|
24
|
+
import { adminDb } from './db'
|
|
25
|
+
import type { CoreContext } from './middleware'
|
|
26
|
+
import { fireCreateTriggers } from './trigger-engine'
|
|
27
|
+
// CIL[END stable_id="~/.framework/functions/_shared/funnel.ts:module_header:module_header"]
|
|
28
|
+
|
|
29
|
+
// CIL[DOC-MACRO purpose="SignalInput defines the typed request shape for emitSignal: which item type slug to write under, which action name to record, and the dedupe policy."]
|
|
30
|
+
// CIL[DOC-RESPONSIBILITY responsibility="SignalInput defines the typed request shape for emitSignal. It lets each caller namespace its own signal item type (e.g. funnel_signal, demo_signal) and choose per-account or run-scoped deduplication."]
|
|
31
|
+
// CIL[DOC-CONTEXT module="funnel" layer="app-specific" related_files=".framework/functions/_shared/db,.framework/functions/_shared/middleware,.framework/functions/_shared/trigger-engine"]
|
|
32
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
33
|
+
// CIL[DOC-MICRO logic_summary="SignalInput is a plain options object: type_slug selects the item type, action is stored as data.action_type, data merges into the row, dedupe toggles deduplication, dedupe_scope narrows the dedupe key to data->>dedupe_scope." notable_decisions="dedupe_scope is stamped after ...data so the stored value can never disagree with the lookup key"]
|
|
34
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
35
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
36
|
+
/**
|
|
37
|
+
* SignalInput describes a typed signal emission request: which item type to
|
|
38
|
+
* write under, which action name to record, and whether to dedupe per account.
|
|
39
|
+
*
|
|
40
|
+
* @sideEffects none
|
|
41
|
+
* @stability stable
|
|
42
|
+
* @cil-generated
|
|
43
|
+
*/
|
|
44
|
+
// CIL[BEGIN stable_id="~/.framework/functions/_shared/funnel.ts:SignalInput:type" type="type" identity="SignalInput" parent="~/.framework/functions/_shared/funnel.ts"]
|
|
45
|
+
// CIL[DOC-MACRO purpose="SignalInput type"]
|
|
46
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Implements the SignalInput type."]
|
|
47
|
+
// CIL[DOC-CONTEXT module=".framework/functions/_shared/funnel.ts" layer="app-specific" related_files=""]
|
|
48
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
49
|
+
// CIL[DOC-MICRO logic_summary="Implements the SignalInput type." notable_decisions="[]"]
|
|
50
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.8" flags="none"]
|
|
51
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
52
|
+
export type SignalInput = {
|
|
53
|
+
/** Item type slug (kind='item') the signal is written under. */
|
|
54
|
+
type_slug: string
|
|
55
|
+
/** Caller-defined action name, recorded as data.action_type. */
|
|
56
|
+
action: string
|
|
57
|
+
/** Extra payload merged into the item's data. */
|
|
58
|
+
data?: Record<string, any>
|
|
59
|
+
/** When true (default), skip the write if this account already recorded the same action. */
|
|
60
|
+
dedupe?: boolean
|
|
61
|
+
/**
|
|
62
|
+
* Optional dedupe scope. When set, the dedupe lookup additionally matches
|
|
63
|
+
* data->>dedupe_scope and the inserted row carries the value — e.g. one
|
|
64
|
+
* milestone per demo run instead of once per account.
|
|
65
|
+
*/
|
|
66
|
+
dedupe_scope?: string
|
|
67
|
+
}
|
|
68
|
+
// CIL[END stable_id="~/.framework/functions/_shared/funnel.ts:SignalInput:type"]
|
|
69
|
+
// CIL[DOC-VECTOR type="context" content="purpose='SignalInput defines the typed request shape for emitSignal: item type slug, action name, data, and dedupe policy.'; No verified external callers" status="ready"]
|
|
70
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Consumed by emitSignal and every caller emitting typed signals (demo triage/case-analysis/journey, ai-interactions)" status="ready"]
|
|
71
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified" status="ready"]
|
|
72
|
+
// CIL[DOC-VECTOR type="constraint" content="dedupe_scope, when present, must equal data->>dedupe_scope on the inserted row so later lookups dedupe correctly" status="ready"]
|
|
73
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
74
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
75
|
+
|
|
76
|
+
// CIL[DOC-MACRO purpose="Records a caller-defined action as a signal item under the declared type, ensuring that milestones are only recorded once per account (or per dedupe_scope) to prevent inflation of verified states."]
|
|
77
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Records a caller-defined action as a signal item under the declared type_slug, ensuring that milestones are only recorded once per account by default to prevent inflation of verified states. This function is designed to never throw, maintaining the integrity of the product path."]
|
|
78
|
+
// CIL[DOC-CONTEXT module="funnel" layer="app-specific" related_files=".framework/functions/_shared/db,.framework/functions/_shared/middleware,.framework/functions/_shared/trigger-engine"]
|
|
79
|
+
// CIL[DOC-CONTRACT inputs="- ctx: CoreContext (required) — the context containing account information.\n- opts: SignalInput (required) — type_slug, action, optional data, dedupe flag, and dedupe_scope." outputs="Promise<any | null> — resolves to the created signal item or null if no action was taken." errors="none" side_effects="Writes to the database and triggers external event dispatch." dependencies="adminDb.from('types'), adminDb.from('items'), fireCreateTriggers"]
|
|
80
|
+
// CIL[DOC-MICRO logic_summary="Resolve the caller-declared type_slug to an item type; if dedupe is on, look up an existing item for (type_id, account_id, action_type) — additionally matching data->>dedupe_scope when provided — and return null on a hit; otherwise insert the signal item with dedupe_scope stamped into data after ...data so the stored key always matches the lookup." notable_decisions="logs warnings on insert and trigger dispatch failures without throwing; dedupe_scope is stamped post-spread so a caller-supplied data.dedupe_scope cannot disagree with the lookup key"]
|
|
81
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
82
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
83
|
+
/**
|
|
84
|
+
* Records a caller-defined action as a signal item under the declared type, ensuring that milestones are only recorded once per account by default to prevent inflation of verified states. This function is designed to never throw, maintaining the integrity of the product path.
|
|
85
|
+
*
|
|
86
|
+
* @inputSpec
|
|
87
|
+
* - ctx: CoreContext (required) — the context containing account information.
|
|
88
|
+
* - opts: SignalInput (required) — type_slug, action, optional data and dedupe flag.
|
|
89
|
+
* @outputSpec Promise<any | null> — resolves to the created signal item or null if no action was taken.
|
|
90
|
+
* @sideEffects Writes to the database and triggers external event dispatch.
|
|
91
|
+
* @stability stable
|
|
92
|
+
* @calls adminDb.from('types'), adminDb.from('items'), fireCreateTriggers
|
|
93
|
+
* @errorHandling logs warnings on insert and trigger dispatch failures without throwing
|
|
94
|
+
* @cil-generated
|
|
95
|
+
*/
|
|
96
|
+
/**
|
|
97
|
+
* Record a caller-defined action as a signal item under the declared type.
|
|
98
|
+
* Signals are once-per-account by default: if this account already recorded
|
|
99
|
+
* the same action_type the call is a no-op, so "verified" states can't be
|
|
100
|
+
* inflated by retries. Never throws — a signal write must never break the
|
|
101
|
+
* product path.
|
|
102
|
+
*/
|
|
103
|
+
// CIL[BEGIN stable_id="~/.framework/functions/_shared/funnel.ts:emitSignal:function" type="function" identity="emitSignal" parent="~/.framework/functions/_shared/funnel.ts"]
|
|
104
|
+
// CIL[DOC-MACRO purpose="emitSignal function"]
|
|
105
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Implements the emitSignal function."]
|
|
106
|
+
// CIL[DOC-CONTEXT module=".framework/functions/_shared/funnel.ts" layer="app-specific" related_files=""]
|
|
107
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
108
|
+
// CIL[DOC-MICRO logic_summary="Implements the emitSignal function." notable_decisions="[]"]
|
|
109
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.8" flags="none"]
|
|
110
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
111
|
+
// CIL[DOC-NOTE type="invariant" content="Signal items must be stamped with the type's design_schema/validation_schema (and created_by for human principals) — records without stamped schemas fail admin-data's 'No permissions defined' gate. Found as a fresh-install defect: demo_signal items were created unstamped."]
|
|
112
|
+
export async function emitSignal(
|
|
113
|
+
ctx: CoreContext,
|
|
114
|
+
opts: SignalInput
|
|
115
|
+
): Promise<any | null> {
|
|
116
|
+
const { type_slug, action, data = {}, dedupe = true, dedupe_scope } = opts
|
|
117
|
+
try {
|
|
118
|
+
const { data: typeRow } = await adminDb
|
|
119
|
+
.from('types').select('id, design_schema, validation_schema').eq('kind', 'item').eq('slug', type_slug).maybeSingle()
|
|
120
|
+
if (!typeRow?.id) return null
|
|
121
|
+
|
|
122
|
+
if (dedupe) {
|
|
123
|
+
let existingQ = adminDb
|
|
124
|
+
.from('items').select('id')
|
|
125
|
+
.eq('type_id', typeRow.id)
|
|
126
|
+
.eq('account_id', ctx.accountId)
|
|
127
|
+
.eq('data->>action_type', action)
|
|
128
|
+
if (dedupe_scope !== undefined) {
|
|
129
|
+
existingQ = existingQ.eq('data->>dedupe_scope', dedupe_scope)
|
|
130
|
+
}
|
|
131
|
+
const { data: existing } = await existingQ.limit(1)
|
|
132
|
+
if (existing?.length) return null
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const { data: created, error } = await adminDb
|
|
136
|
+
.from('items')
|
|
137
|
+
.insert({
|
|
138
|
+
type_id: typeRow.id,
|
|
139
|
+
account_id: ctx.accountId,
|
|
140
|
+
title: `${action} — ${new Date().toISOString().slice(0, 10)}`,
|
|
141
|
+
status: 'active',
|
|
142
|
+
// Stamp the type's schemas like admin-data does — unstamped items hit
|
|
143
|
+
// the 'No permissions defined' gate when surfaced through admin-data.
|
|
144
|
+
design_schema: typeRow.design_schema || {},
|
|
145
|
+
validation_schema: typeRow.validation_schema || {},
|
|
146
|
+
created_by: ctx.principal?.type === 'human' ? ctx.principal.id : null,
|
|
147
|
+
data: {
|
|
148
|
+
action_type: action,
|
|
149
|
+
action_value: 1,
|
|
150
|
+
occurred_at: new Date().toISOString(),
|
|
151
|
+
source: data.source || 'app',
|
|
152
|
+
...data,
|
|
153
|
+
// Stamped after ...data so the stored scope always matches the
|
|
154
|
+
// dedupe lookup key — a caller-supplied data.dedupe_scope can never
|
|
155
|
+
// disagree with it.
|
|
156
|
+
...(dedupe_scope !== undefined ? { dedupe_scope } : {}),
|
|
157
|
+
},
|
|
158
|
+
})
|
|
159
|
+
.select('*')
|
|
160
|
+
.single()
|
|
161
|
+
if (error) {
|
|
162
|
+
console.warn(`emitSignal(${type_slug}/${action}) insert failed:`, error.message)
|
|
163
|
+
return null
|
|
164
|
+
}
|
|
165
|
+
// Signals are inserted via adminDb which bypasses admin-data's trigger
|
|
166
|
+
// dispatch — fire item_created explicitly so signal consumers (e.g. app
|
|
167
|
+
// pipelines wired to the signal type) see the event.
|
|
168
|
+
fireCreateTriggers('items', created.id, created, ctx).catch(err =>
|
|
169
|
+
console.warn(`emitSignal(${type_slug}/${action}) trigger dispatch failed:`, err?.message)
|
|
170
|
+
)
|
|
171
|
+
return created
|
|
172
|
+
} catch (err: any) {
|
|
173
|
+
console.warn(`emitSignal(${type_slug}/${action}) failed:`, err?.message)
|
|
174
|
+
return null
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// CIL[END stable_id="~/.framework/functions/_shared/funnel.ts:emitSignal:function"]
|
|
178
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Records a caller-defined action as a signal item under the declared type_slug, deduped once per account by default or once per dedupe_scope when provided.'; Called by demo triage/case-analysis/journey functions and ai-interactions submitFeedback" status="ready"]
|
|
179
|
+
// CIL[DOC-VECTOR type="blast_radius" content="Callers: custom_demo-triage (escalation signals), custom_demo-case-analysis (completion signals), custom_demo-journey (milestone signals), ai-interactions submitFeedback (funnel_signal)" status="ready"]
|
|
180
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified" status="ready"]
|
|
181
|
+
// CIL[DOC-VECTOR type="constraint" content="Never throws — a signal write must never break the product path; dedupe_scope must match data->>dedupe_scope on the stored row" status="ready"]
|
|
182
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
183
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
184
|
+
|
|
185
|
+
// CIL[DOC-MACRO purpose="Reads a caller-named boolean flag off the account's data bag."]
|
|
186
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Reads accounts.data[flag] for the context account and reports whether it is strictly true. Generic replacement for single-purpose flag helpers — callers supply the data key."]
|
|
187
|
+
// CIL[DOC-CONTEXT module="funnel" layer="app-specific" related_files=".framework/functions/_shared/db,.framework/functions/_shared/middleware,.framework/functions/_shared/trigger-engine"]
|
|
188
|
+
// CIL[DOC-CONTRACT inputs="ctx: CoreContext (required) — the context containing the account ID to check; flag: string (required) — the data key to read." outputs="Promise<boolean> — resolves to true if the flag is set, otherwise false." errors="none" side_effects="Makes a call to the database to retrieve account data." dependencies="adminDb.from('accounts')"]
|
|
189
|
+
// CIL[DOC-MICRO logic_summary="Select accounts.data for ctx.accountId and return data[flag] === true; any read failure resolves to false." notable_decisions="strict === true check keeps truthy-but-non-boolean values from masquerading as flags"]
|
|
190
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
191
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
192
|
+
/**
|
|
193
|
+
* Reads a caller-named boolean flag off the account's data bag.
|
|
194
|
+
*
|
|
195
|
+
* @inputSpec
|
|
196
|
+
* ctx: CoreContext (required) — the context containing the account ID to check.
|
|
197
|
+
* flag: string (required) — the data key to read.
|
|
198
|
+
* @outputSpec Promise<boolean> — resolves to true if the flag is set, otherwise false.
|
|
199
|
+
* @sideEffects Makes a call to the database to retrieve account data.
|
|
200
|
+
* @stability stable
|
|
201
|
+
* @calls adminDb.from('accounts')
|
|
202
|
+
* @cil-generated
|
|
203
|
+
*/
|
|
204
|
+
/** True when accounts.data[flag] === true for the context account. */
|
|
205
|
+
// CIL[BEGIN stable_id="~/.framework/functions/_shared/funnel.ts:accountDataFlag:function" type="function" identity="accountDataFlag" parent="~/.framework/functions/_shared/funnel.ts"]
|
|
206
|
+
// CIL[DOC-MACRO purpose="accountDataFlag function"]
|
|
207
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Implements the accountDataFlag function."]
|
|
208
|
+
// CIL[DOC-CONTEXT module=".framework/functions/_shared/funnel.ts" layer="app-specific" related_files=""]
|
|
209
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
210
|
+
// CIL[DOC-MICRO logic_summary="Implements the accountDataFlag function." notable_decisions="[]"]
|
|
211
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.8" flags="none"]
|
|
212
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
213
|
+
export async function accountDataFlag(ctx: CoreContext, flag: string): Promise<boolean> {
|
|
214
|
+
try {
|
|
215
|
+
const { data } = await adminDb
|
|
216
|
+
.from('accounts').select('data').eq('id', ctx.accountId).maybeSingle()
|
|
217
|
+
return data?.data?.[flag] === true
|
|
218
|
+
} catch {
|
|
219
|
+
return false
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// CIL[END stable_id="~/.framework/functions/_shared/funnel.ts:accountDataFlag:function"]
|
|
223
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Reads accounts.data[flag] for the context account and reports whether it is strictly true.'; No verified external callers" status="ready"]
|
|
224
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only" status="ready"]
|
|
225
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified" status="ready"]
|
|
226
|
+
// CIL[DOC-VECTOR type="constraint" content="Returns false on any read failure — never throws" status="ready"]
|
|
227
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
228
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module functions/_shared
|
|
3
|
+
* @audience internal
|
|
4
|
+
* @layer geo
|
|
5
|
+
* @stability stable
|
|
6
|
+
*
|
|
7
|
+
* Best-effort client geo/IP extraction from request headers
|
|
8
|
+
*
|
|
9
|
+
* @seeAlso .framework/functions/_shared/middleware.ts (provides RequestContext type)
|
|
10
|
+
* @seeAlso .framework/functions/_shared/funnel.ts (related functionality for handling requests)
|
|
11
|
+
*
|
|
12
|
+
* @cil-generated
|
|
13
|
+
*/
|
|
14
|
+
// CIL[BEGIN stable_id="~/.framework/functions/_shared/geo.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/functions/_shared/geo.ts"]
|
|
15
|
+
// CIL[DOC-MACRO purpose="Best-effort client geo/IP extraction from request headers"]
|
|
16
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Parse Netlify/CDN geo headers and client IP from ctx.headers for logging and policy decisions"]
|
|
17
|
+
// CIL[DOC-CONTEXT module="functions/_shared" layer="geo" related_files=".framework/functions/_shared/middleware.ts,.framework/functions/_shared/funnel.ts"]
|
|
18
|
+
// CIL[DOC-CONTRACT inputs="RequestContext with headers" outputs="{ country, ip } — both optional, 'unknown' never thrown" errors="never throws" side_effects="none"]
|
|
19
|
+
// CIL[DOC-MICRO logic_summary="Check x-nf-geo JSON (Netlify), then CDN fallbacks (cf-ipcountry, x-country, x-vercel-ip-country); IP from x-nf-client-connection-ip then first x-forwarded-for hop" notable_decisions="Log-and-allow: geo is a signal, never a gate — missing headers yield undefined, callers decide policy"]
|
|
20
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
21
|
+
// CIL[DOC-AUDIENCE audience="internal"]
|
|
22
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Best-effort client geo/IP extraction from request headers'; No verified external callers; imports: .framework/functions/_shared/middleware.ts"]
|
|
23
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
24
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
25
|
+
// CIL[DOC-VECTOR type="constraint" content="Never blocks; headers absent in local dev yield undefined"]
|
|
26
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
27
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
28
|
+
|
|
29
|
+
import type { RequestContext } from './middleware'
|
|
30
|
+
// CIL[END stable_id="~/.framework/functions/_shared/geo.ts:module_header:module_header"]
|
|
31
|
+
|
|
32
|
+
// CIL[DOC-MACRO purpose="ClientGeo represents geographical information about a client, including their country code and IP address."]
|
|
33
|
+
// CIL[DOC-RESPONSIBILITY responsibility="ClientGeo represents geographical information about a client, including their country code and IP address. It is used to provide location-based services and analytics."]
|
|
34
|
+
// CIL[DOC-CONTEXT module="geo" layer="app-specific" related_files=".framework/functions/_shared/middleware"]
|
|
35
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
36
|
+
// CIL[DOC-MICRO logic_summary="ClientGeo represents geographical information about a client, including their country code and IP address. It is used to provide location-based services and analytics." notable_decisions="none"]
|
|
37
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
38
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
39
|
+
/**
|
|
40
|
+
* ClientGeo represents geographical information about a client, including their country code and IP address. It is used to provide location-based services and analytics.
|
|
41
|
+
*
|
|
42
|
+
* @sideEffects none
|
|
43
|
+
* @stability stable
|
|
44
|
+
* @cil-generated
|
|
45
|
+
*/
|
|
46
|
+
// CIL[BEGIN stable_id="~/.framework/functions/_shared/geo.ts:clientgeo:type" type="type" identity="ClientGeo" parent="~/.framework/functions/_shared/geo.ts"]
|
|
47
|
+
// CIL[DOC-MACRO purpose="ClientGeo type"]
|
|
48
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Implements the ClientGeo type."]
|
|
49
|
+
// CIL[DOC-CONTEXT module=".framework/functions/_shared/geo.ts" layer="app-specific" related_files=""]
|
|
50
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
51
|
+
// CIL[DOC-MICRO logic_summary="Implements the ClientGeo type." notable_decisions="[]"]
|
|
52
|
+
// CIL[DOC-CONFIDENCE pass="first" score="0.8" flags="none"]
|
|
53
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
54
|
+
export type ClientGeo = {
|
|
55
|
+
/** ISO 3166-1 alpha-2 country code, lowercase (e.g. 'us', 'ca'). Undefined when unavailable. */
|
|
56
|
+
country?: string
|
|
57
|
+
/** Client IP address. Undefined when unavailable. */
|
|
58
|
+
ip?: string
|
|
59
|
+
}
|
|
60
|
+
// CIL[END stable_id="~/.framework/functions/_shared/geo.ts:clientgeo:type"]
|
|
61
|
+
// CIL[DOC-VECTOR type="context" content="purpose='ClientGeo represents geographical information about a client, including their country code and IP address.'; No verified external callers" status="ready"]
|
|
62
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only" status="ready"]
|
|
63
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified" status="ready"]
|
|
64
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented" status="ready"]
|
|
65
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
66
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
67
|
+
|
|
68
|
+
// CIL[BEGIN stable_id="~/.framework/functions/_shared/geo.ts:getclientgeo:function" type="function" identity="getClientGeo" parent="~/.framework/functions/_shared/geo.ts"]
|
|
69
|
+
// CIL[DOC-MACRO purpose="Extract client country and IP from request headers"]
|
|
70
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Implements getClientGeo"]
|
|
71
|
+
// CIL[DOC-CONTRACT inputs="RequestContext" outputs="ClientGeo" errors="none" side_effects="none"]
|
|
72
|
+
// CIL[DOC-MICRO logic_summary="Parses x-nf-geo JSON for country.code, falls back to flat country headers, then resolves IP from Netlify or forwarded-for headers" notable_decisions="[]"]
|
|
73
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
74
|
+
export function getClientGeo(ctx: RequestContext): ClientGeo {
|
|
75
|
+
const headers = ctx.headers || {}
|
|
76
|
+
let country: string | undefined
|
|
77
|
+
|
|
78
|
+
const nfGeo = headers['x-nf-geo']
|
|
79
|
+
if (nfGeo) {
|
|
80
|
+
try {
|
|
81
|
+
const parsed = JSON.parse(nfGeo)
|
|
82
|
+
const code = parsed?.country?.code
|
|
83
|
+
if (typeof code === 'string' && code) country = code.toLowerCase()
|
|
84
|
+
} catch { /* non-JSON header, ignore */ }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!country) {
|
|
88
|
+
const flat =
|
|
89
|
+
headers['cf-ipcountry'] ||
|
|
90
|
+
headers['x-country'] ||
|
|
91
|
+
headers['x-country-code'] ||
|
|
92
|
+
headers['x-vercel-ip-country']
|
|
93
|
+
if (typeof flat === 'string' && /^[a-zA-Z]{2}$/.test(flat)) {
|
|
94
|
+
country = flat.toLowerCase()
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const ip =
|
|
99
|
+
headers['x-nf-client-connection-ip'] ||
|
|
100
|
+
(headers['x-forwarded-for'] || '').split(',')[0].trim() ||
|
|
101
|
+
undefined
|
|
102
|
+
|
|
103
|
+
return { country, ip }
|
|
104
|
+
}
|
|
105
|
+
// CIL[END stable_id="~/.framework/functions/_shared/geo.ts:getclientgeo:function"]
|
|
@@ -1,25 +1,81 @@
|
|
|
1
|
-
// CIL[BEGIN
|
|
2
|
-
// CIL[DOC-MACRO purpose="
|
|
3
|
-
// CIL[DOC-RESPONSIBILITY responsibility="
|
|
4
|
-
// CIL[DOC-CONTEXT module="
|
|
5
|
-
// CIL[DOC-CONTRACT inputs="
|
|
6
|
-
// CIL[DOC-MICRO logic_summary="
|
|
7
|
-
// CIL[DOC-CONFIDENCE pass="
|
|
8
|
-
// CIL[DOC-STABILITY stability="
|
|
9
|
-
// CIL[DOC-AUDIENCE audience="
|
|
10
|
-
|
|
11
|
-
// CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
|
|
12
|
-
// CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
|
|
13
|
-
// CIL[DOC-VECTOR type="constraint" content="Uses Node crypto only."]
|
|
14
|
-
// CIL[DOC-VECTOR type="implementation_pattern" content="Hash on key creation; verify on inbound webhook."]
|
|
15
|
-
// CIL[DOC-VECTOR type="user_journey" content="Machine-principal credential lifecycle."]
|
|
1
|
+
// CIL[BEGIN stable_id="~/.framework/functions/_shared/hash-api-key.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/functions/_shared/hash-api-key.ts"]
|
|
2
|
+
// CIL[DOC-MACRO purpose="Provides PBKDF2-based API key hashing and verification utilities"]
|
|
3
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Imports crypto primitives and exports hashApiKey and verifyApiKey"]
|
|
4
|
+
// CIL[DOC-CONTEXT module="functions/_shared" layer="shared" related_files=".framework/functions/_shared/hash-api-key.ts"]
|
|
5
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
|
|
6
|
+
// CIL[DOC-MICRO logic_summary="Imports crypto functions used by hashing and verification" notable_decisions="[]"]
|
|
7
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
8
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
9
|
+
// CIL[DOC-AUDIENCE audience="developers"]
|
|
10
|
+
|
|
16
11
|
|
|
17
12
|
import { randomBytes, pbkdf2Sync, timingSafeEqual } from 'crypto'
|
|
13
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Provides PBKDF2-based API key hashing and verification utilities'; No verified external callers; imports: none"]
|
|
14
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
15
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
16
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
17
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
18
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
19
|
+
// CIL[END stable_id="~/.framework/functions/_shared/hash-api-key.ts:module_header:module_header"]
|
|
18
20
|
|
|
21
|
+
// CIL[BEGIN stable_id="~/.framework/functions/_shared/hash-api-key.ts:ITERATIONS:constant" type="constant" identity="ITERATIONS" parent="~/.framework/functions/_shared/hash-api-key.ts"]
|
|
22
|
+
// CIL[DOC-MACRO purpose="Defines the default PBKDF2 iteration count"]
|
|
23
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Stores the default number of PBKDF2 iterations for hashing"]
|
|
24
|
+
// CIL[DOC-CONTEXT module="functions/_shared" layer="shared" related_files=".framework/functions/_shared/hash-api-key.ts"]
|
|
25
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="number" errors="none" side_effects="none"]
|
|
26
|
+
// CIL[DOC-MICRO logic_summary="Constant default iteration count used by hashApiKey" notable_decisions="[]"]
|
|
27
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
28
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
19
29
|
const ITERATIONS = 100000
|
|
30
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Defines the default PBKDF2 iteration count'; No verified external callers; imports: none"]
|
|
31
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
32
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
33
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
34
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
35
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
36
|
+
// CIL[END stable_id="~/.framework/functions/_shared/hash-api-key.ts:ITERATIONS:constant"]
|
|
37
|
+
// CIL[BEGIN stable_id="~/.framework/functions/_shared/hash-api-key.ts:KEY_LENGTH:constant" type="constant" identity="KEY_LENGTH" parent="~/.framework/functions/_shared/hash-api-key.ts"]
|
|
38
|
+
// CIL[DOC-MACRO purpose="Defines the derived key length in bytes for PBKDF2"]
|
|
39
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Stores the PBKDF2 derived key length"]
|
|
40
|
+
// CIL[DOC-CONTEXT module="functions/_shared" layer="shared" related_files=".framework/functions/_shared/hash-api-key.ts"]
|
|
41
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="number" errors="none" side_effects="none"]
|
|
42
|
+
// CIL[DOC-MICRO logic_summary="Constant derived key length used by hashApiKey" notable_decisions="[]"]
|
|
43
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
44
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
20
45
|
const KEY_LENGTH = 32
|
|
46
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Defines the derived key length in bytes for PBKDF2'; No verified external callers; imports: none"]
|
|
47
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
48
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
49
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
50
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
51
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
52
|
+
// CIL[END stable_id="~/.framework/functions/_shared/hash-api-key.ts:KEY_LENGTH:constant"]
|
|
53
|
+
// CIL[BEGIN stable_id="~/.framework/functions/_shared/hash-api-key.ts:DIGEST:constant" type="constant" identity="DIGEST" parent="~/.framework/functions/_shared/hash-api-key.ts"]
|
|
54
|
+
// CIL[DOC-MACRO purpose="Defines the digest algorithm for PBKDF2"]
|
|
55
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Stores the PBKDF2 digest algorithm"]
|
|
56
|
+
// CIL[DOC-CONTEXT module="functions/_shared" layer="shared" related_files=".framework/functions/_shared/hash-api-key.ts"]
|
|
57
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="string" errors="none" side_effects="none"]
|
|
58
|
+
// CIL[DOC-MICRO logic_summary="Constant digest algorithm used by hashApiKey and verifyApiKey" notable_decisions="[]"]
|
|
59
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
60
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
21
61
|
const DIGEST = 'sha256'
|
|
62
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Defines the digest algorithm for PBKDF2'; No verified external callers; imports: none"]
|
|
63
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
64
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
65
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
66
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
67
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
68
|
+
// CIL[END stable_id="~/.framework/functions/_shared/hash-api-key.ts:DIGEST:constant"]
|
|
69
|
+
|
|
22
70
|
|
|
71
|
+
// CIL[BEGIN stable_id="~/.framework/functions/_shared/hash-api-key.ts:hashApiKey:function" type="function" identity="hashApiKey" parent="~/.framework/functions/_shared/hash-api-key.ts"]
|
|
72
|
+
// CIL[DOC-MACRO purpose="Hash a raw API key using PBKDF2 and return a storable key record"]
|
|
73
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Derive a salted PBKDF2 hash, prefix the raw key, and return a hash string"]
|
|
74
|
+
// CIL[DOC-CONTEXT module="functions/_shared" layer="shared" related_files=".framework/functions/_shared/hash-api-key.ts"]
|
|
75
|
+
// CIL[DOC-CONTRACT inputs="rawKey: string, iterations = ITERATIONS" outputs="{ key_prefix: string, key_hash: string, rawKey: string }" errors="none" side_effects="Uses crypto.randomBytes and pbkdf2Sync to generate salt and hash"]
|
|
76
|
+
// CIL[DOC-MICRO logic_summary="Extracts an 8-character prefix, generates a random salt, derives a 32-byte PBKDF2 hash, and encodes the result as pbkdf2$iterations$salt$hash" notable_decisions="[]"]
|
|
77
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
78
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
23
79
|
export function hashApiKey(rawKey: string, iterations = ITERATIONS): { key_prefix: string; key_hash: string; rawKey: string } {
|
|
24
80
|
const key_prefix = rawKey.slice(0, 8)
|
|
25
81
|
const salt = randomBytes(16)
|
|
@@ -28,7 +84,23 @@ export function hashApiKey(rawKey: string, iterations = ITERATIONS): { key_prefi
|
|
|
28
84
|
|
|
29
85
|
return { key_prefix, key_hash, rawKey }
|
|
30
86
|
}
|
|
87
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Hash a raw API key using PBKDF2 and return a storable key record'; No verified external callers; imports: none"]
|
|
88
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
89
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
90
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
91
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
92
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
93
|
+
// CIL[END stable_id="~/.framework/functions/_shared/hash-api-key.ts:hashApiKey:function"]
|
|
31
94
|
|
|
95
|
+
|
|
96
|
+
// CIL[BEGIN stable_id="~/.framework/functions/_shared/hash-api-key.ts:verifyApiKey:function" type="function" identity="verifyApiKey" parent="~/.framework/functions/_shared/hash-api-key.ts"]
|
|
97
|
+
// CIL[DOC-MACRO purpose="Verify a raw API key against a stored PBKDF2 hash"]
|
|
98
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Parse the stored hash, re-derive the key, and compare hashes timing-safely"]
|
|
99
|
+
// CIL[DOC-CONTEXT module="functions/_shared" layer="shared" related_files=".framework/functions/_shared/hash-api-key.ts"]
|
|
100
|
+
// CIL[DOC-CONTRACT inputs="rawKey: string, keyHash: string" outputs="boolean" errors="Returns false for invalid format, missing parts, non-positive iterations, base64 decode errors, or mismatched lengths" side_effects="Uses crypto.pbkdf2Sync and timingSafeEqual"]
|
|
101
|
+
// CIL[DOC-MICRO logic_summary="Validates the keyHash format, extracts iterations and salt, derives the hash with the stored length, and compares with timingSafeEqual" notable_decisions="[]"]
|
|
102
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
103
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
32
104
|
export function verifyApiKey(rawKey: string, keyHash: string): boolean {
|
|
33
105
|
if (!keyHash || !keyHash.startsWith('pbkdf2$')) {
|
|
34
106
|
return false
|
|
@@ -59,5 +131,10 @@ export function verifyApiKey(rawKey: string, keyHash: string): boolean {
|
|
|
59
131
|
return false
|
|
60
132
|
}
|
|
61
133
|
}
|
|
62
|
-
|
|
63
|
-
// CIL[
|
|
134
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Verify a raw API key against a stored PBKDF2 hash'; No verified external callers; imports: none"]
|
|
135
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
136
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
137
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
138
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
139
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
140
|
+
// CIL[END stable_id="~/.framework/functions/_shared/hash-api-key.ts:verifyApiKey:function"]
|