spine-framework 0.3.145 → 0.4.1
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 +5067 -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 +15 -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,513 @@
|
|
|
1
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/trigger-engine.test.ts:module_header:module_header" type="module_header" identity="module_header" parent="none"]
|
|
2
|
+
// CIL[DOC-MACRO purpose="Test setup and imports for trigger engine tests"]
|
|
3
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Configure test environment and mock dependencies"]
|
|
4
|
+
// CIL[DOC-CONTEXT module="tests/unit/trigger-engine" layer="test-unit" related_files=".framework/functions/_shared/audit.ts,.framework/functions/_shared/db.ts,.framework/functions/_shared/middleware.ts,.framework/functions/_shared/pipeline-runner.ts,.framework/functions/_shared/trigger-engine.ts"]
|
|
5
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="configured test mocks" errors="none" side_effects="Mocks database, audit, and pipeline runner"]
|
|
6
|
+
// CIL[DOC-MICRO logic_summary="Imports vitest, mocks db/audit/pipeline, imports trigger engine" notable_decisions="[]"]
|
|
7
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
8
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
9
|
+
// CIL[DOC-AUDIENCE audience="test"]
|
|
10
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
11
|
+
import type { CoreContext } from '../../functions/_shared/middleware.ts'
|
|
12
|
+
|
|
13
|
+
vi.mock('../../functions/_shared/db.ts', () => ({
|
|
14
|
+
adminDb: {
|
|
15
|
+
from: vi.fn(),
|
|
16
|
+
rpc: vi.fn()
|
|
17
|
+
}
|
|
18
|
+
}))
|
|
19
|
+
|
|
20
|
+
vi.mock('../../functions/_shared/audit.ts', () => ({
|
|
21
|
+
emitAudit: vi.fn().mockResolvedValue(undefined)
|
|
22
|
+
}))
|
|
23
|
+
|
|
24
|
+
vi.mock('../../functions/_shared/pipeline-runner.ts', () => ({
|
|
25
|
+
runPipeline: vi.fn().mockResolvedValue({
|
|
26
|
+
executionId: 'mock-exec-id',
|
|
27
|
+
pipelineId: 'mock-pipeline-id',
|
|
28
|
+
status: 'completed',
|
|
29
|
+
stages: [],
|
|
30
|
+
durationMs: 10
|
|
31
|
+
})
|
|
32
|
+
}))
|
|
33
|
+
|
|
34
|
+
import { checkAndFireTriggers, fireCreateTriggers, fireUpdateTriggers } from '../../functions/_shared/trigger-engine.ts'
|
|
35
|
+
import { runPipeline } from '../../functions/_shared/pipeline-runner.ts'
|
|
36
|
+
import { emitAudit } from '../../functions/_shared/audit.ts'
|
|
37
|
+
import { adminDb } from '../../functions/_shared/db.ts'
|
|
38
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Test setup and imports for trigger engine tests'; No verified external callers; imports: .framework/functions/_shared/middleware.ts,.framework/functions/_shared/trigger-engine.ts,.framework/functions/_shared/pipeline-runner.ts"]
|
|
39
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
40
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
41
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
42
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
43
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
44
|
+
// CIL[END stable_id="~/.framework/tests/unit/trigger-engine.test.ts:module_header:module_header"]
|
|
45
|
+
|
|
46
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/trigger-engine.test.ts:makeCtx:function" type="function" identity="makeCtx" parent="none"]
|
|
47
|
+
// CIL[DOC-MACRO purpose="Create test context with system principal"]
|
|
48
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Generate test context objects"]
|
|
49
|
+
// CIL[DOC-CONTEXT module="tests/unit/trigger-engine" layer="test-unit" related_files=".framework/functions/_shared/audit.ts,.framework/functions/_shared/db.ts,.framework/functions/_shared/middleware.ts,.framework/functions/_shared/pipeline-runner.ts,.framework/functions/_shared/trigger-engine.ts,.framework/tests/unit/trigger-engine.test.ts"]
|
|
50
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="CoreContext object" errors="none" side_effects="none"]
|
|
51
|
+
// CIL[DOC-MICRO logic_summary="Creates context with system machine principal and test data" notable_decisions="[]"]
|
|
52
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
53
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
54
|
+
function makeCtx(): CoreContext {
|
|
55
|
+
return {
|
|
56
|
+
principal: {
|
|
57
|
+
id: 'system', type: 'machine', accountId: null, scopes: ['*:*'],
|
|
58
|
+
provenance: { sourceType: 'manual', createdBy: null, invokedAt: new Date().toISOString() }
|
|
59
|
+
},
|
|
60
|
+
accountId: 'acct-1',
|
|
61
|
+
db: {},
|
|
62
|
+
requestId: 'req-test'
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Create test context with system principal'; No verified external callers; imports: .framework/functions/_shared/middleware.ts,.framework/functions/_shared/trigger-engine.ts,.framework/functions/_shared/pipeline-runner.ts"]
|
|
66
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
67
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
68
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
69
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
70
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
71
|
+
// CIL[END stable_id="~/.framework/tests/unit/trigger-engine.test.ts:makeCtx:function"]
|
|
72
|
+
|
|
73
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/trigger-engine.test.ts:makeTrigger:function" type="function" identity="makeTrigger" parent="none"]
|
|
74
|
+
// CIL[DOC-MACRO purpose="Create test trigger with default values"]
|
|
75
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Generate test trigger objects"]
|
|
76
|
+
// CIL[DOC-CONTEXT module="tests/unit/trigger-engine" layer="test-unit" related_files=".framework/functions/_shared/audit.ts,.framework/functions/_shared/db.ts,.framework/functions/_shared/middleware.ts,.framework/functions/_shared/pipeline-runner.ts,.framework/functions/_shared/trigger-engine.ts,.framework/tests/unit/trigger-engine.test.ts"]
|
|
77
|
+
// CIL[DOC-CONTRACT inputs="overrides: Record<string, any>" outputs="trigger object" errors="none" side_effects="none"]
|
|
78
|
+
// CIL[DOC-MICRO logic_summary="Returns default trigger with overrides applied" notable_decisions="[]"]
|
|
79
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
80
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
81
|
+
function makeTrigger(overrides: Record<string, any> = {}) {
|
|
82
|
+
return {
|
|
83
|
+
id: 'trigger-1',
|
|
84
|
+
name: 'Test Trigger',
|
|
85
|
+
event_type: 'item_created',
|
|
86
|
+
pipeline_id: 'pipeline-1',
|
|
87
|
+
is_active: true,
|
|
88
|
+
config: {},
|
|
89
|
+
...overrides
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Create test trigger with default values'; No verified external callers; imports: .framework/functions/_shared/middleware.ts,.framework/functions/_shared/trigger-engine.ts,.framework/functions/_shared/pipeline-runner.ts"]
|
|
93
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
94
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
95
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
96
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
97
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
98
|
+
// CIL[END stable_id="~/.framework/tests/unit/trigger-engine.test.ts:makeTrigger:function"]
|
|
99
|
+
|
|
100
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/trigger-engine.test.ts:mockDbTriggers:function" type="function" identity="mockDbTriggers" parent="none"]
|
|
101
|
+
// CIL[DOC-MACRO purpose="Mock database to return trigger list"]
|
|
102
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Configure database mock for trigger queries"]
|
|
103
|
+
// CIL[DOC-CONTEXT module="tests/unit/trigger-engine" layer="test-unit" related_files=".framework/functions/_shared/audit.ts,.framework/functions/_shared/db.ts,.framework/functions/_shared/middleware.ts,.framework/functions/_shared/pipeline-runner.ts,.framework/functions/_shared/trigger-engine.ts,.framework/tests/unit/trigger-engine.test.ts"]
|
|
104
|
+
// CIL[DOC-CONTRACT inputs="triggers: any[]" outputs="mocked adminDb.from" errors="none" side_effects="Mocks adminDb.from"]
|
|
105
|
+
// CIL[DOC-MICRO logic_summary="Sets up adminDb.from mock to return provided triggers" notable_decisions="[]"]
|
|
106
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
107
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
108
|
+
function mockDbTriggers(triggers: any[]) {
|
|
109
|
+
const chain = {
|
|
110
|
+
select: vi.fn().mockReturnThis(),
|
|
111
|
+
eq: vi.fn().mockReturnThis(),
|
|
112
|
+
order: vi.fn().mockResolvedValue({ data: triggers, error: null })
|
|
113
|
+
}
|
|
114
|
+
vi.mocked(adminDb.from).mockReturnValue(chain as any)
|
|
115
|
+
}
|
|
116
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Mock database to return trigger list'; No verified external callers; imports: .framework/functions/_shared/middleware.ts,.framework/functions/_shared/trigger-engine.ts,.framework/functions/_shared/pipeline-runner.ts"]
|
|
117
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
118
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
119
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
120
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
121
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
122
|
+
// CIL[END stable_id="~/.framework/tests/unit/trigger-engine.test.ts:mockDbTriggers:function"]
|
|
123
|
+
|
|
124
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/trigger-engine.test.ts:mockDbTriggersWithUpdate:function" type="function" identity="mockDbTriggersWithUpdate" parent="none"]
|
|
125
|
+
// CIL[DOC-MACRO purpose="Mock database with trigger query and stat updates"]
|
|
126
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Configure database mock for trigger operations"]
|
|
127
|
+
// CIL[DOC-CONTEXT module="tests/unit/trigger-engine" layer="test-unit" related_files=".framework/functions/_shared/audit.ts,.framework/functions/_shared/db.ts,.framework/functions/_shared/middleware.ts,.framework/functions/_shared/pipeline-runner.ts,.framework/functions/_shared/trigger-engine.ts,.framework/tests/unit/trigger-engine.test.ts"]
|
|
128
|
+
// CIL[DOC-CONTRACT inputs="triggers: any[]" outputs="mocked adminDb.from and adminDb.rpc" errors="none" side_effects="Mocks adminDb.from and adminDb.rpc"]
|
|
129
|
+
// CIL[DOC-MICRO logic_summary="Sets up mocks for trigger query and stat update operations" notable_decisions="[]"]
|
|
130
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
131
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
132
|
+
function mockDbTriggersWithUpdate(triggers: any[]) {
|
|
133
|
+
const updateChain = {
|
|
134
|
+
update: vi.fn().mockReturnThis(),
|
|
135
|
+
eq: vi.fn().mockResolvedValue({ data: null, error: null })
|
|
136
|
+
}
|
|
137
|
+
let callCount = 0
|
|
138
|
+
vi.mocked(adminDb.from).mockImplementation(() => {
|
|
139
|
+
callCount++
|
|
140
|
+
if (callCount === 1) {
|
|
141
|
+
return {
|
|
142
|
+
select: vi.fn().mockReturnThis(),
|
|
143
|
+
eq: vi.fn().mockReturnThis(),
|
|
144
|
+
order: vi.fn().mockResolvedValue({ data: triggers, error: null })
|
|
145
|
+
} as any
|
|
146
|
+
}
|
|
147
|
+
return updateChain as any
|
|
148
|
+
})
|
|
149
|
+
vi.mocked(adminDb.rpc).mockResolvedValue({ data: 1, error: null } as any)
|
|
150
|
+
}
|
|
151
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Mock database with trigger query and stat updates'; No verified external callers; imports: .framework/functions/_shared/middleware.ts,.framework/functions/_shared/trigger-engine.ts,.framework/functions/_shared/pipeline-runner.ts"]
|
|
152
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
153
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
154
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
155
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
156
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
157
|
+
// CIL[END stable_id="~/.framework/tests/unit/trigger-engine.test.ts:mockDbTriggersWithUpdate:function"]
|
|
158
|
+
|
|
159
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/trigger-engine.test.ts:describe_checkAndFireTriggers:block" type="block" identity="describe_checkAndFireTriggers" parent="none"]
|
|
160
|
+
// CIL[DOC-MACRO purpose="Test checkAndFireTriggers core functionality"]
|
|
161
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Verify trigger execution and error handling"]
|
|
162
|
+
// CIL[DOC-CONTEXT module="tests/unit/trigger-engine" layer="test-unit" related_files=".framework/functions/_shared/audit.ts,.framework/functions/_shared/db.ts,.framework/functions/_shared/middleware.ts,.framework/functions/_shared/pipeline-runner.ts,.framework/functions/_shared/trigger-engine.ts,.framework/tests/unit/trigger-engine.test.ts"]
|
|
163
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="test assertions" errors="none" side_effects="Tests trigger-engine.ts module"]
|
|
164
|
+
// CIL[DOC-MICRO logic_summary="Tests trigger matching, pipeline execution, audit emission, and error recovery" notable_decisions="[]"]
|
|
165
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
166
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
167
|
+
describe('checkAndFireTriggers', () => {
|
|
168
|
+
beforeEach(() => {
|
|
169
|
+
vi.clearAllMocks()
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('does nothing when no triggers match the event type', async () => {
|
|
173
|
+
mockDbTriggers([])
|
|
174
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', {}, makeCtx())
|
|
175
|
+
expect(runPipeline).not.toHaveBeenCalled()
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('does nothing when DB query errors', async () => {
|
|
179
|
+
const chain = {
|
|
180
|
+
select: vi.fn().mockReturnThis(),
|
|
181
|
+
eq: vi.fn().mockReturnThis(),
|
|
182
|
+
order: vi.fn().mockResolvedValue({ data: null, error: { message: 'DB error' } })
|
|
183
|
+
}
|
|
184
|
+
vi.mocked(adminDb.from).mockReturnValue(chain as any)
|
|
185
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', {}, makeCtx())
|
|
186
|
+
expect(runPipeline).not.toHaveBeenCalled()
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('fires pipeline for a matching trigger with no conditions', async () => {
|
|
190
|
+
mockDbTriggersWithUpdate([makeTrigger()])
|
|
191
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', {}, makeCtx())
|
|
192
|
+
expect(runPipeline).toHaveBeenCalledOnce()
|
|
193
|
+
expect(runPipeline).toHaveBeenCalledWith(
|
|
194
|
+
'pipeline-1',
|
|
195
|
+
expect.objectContaining({
|
|
196
|
+
event: 'item_created',
|
|
197
|
+
entity: expect.objectContaining({ type: 'items', id: 'item-1' })
|
|
198
|
+
}),
|
|
199
|
+
expect.any(Object)
|
|
200
|
+
)
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it('passes trigger metadata in the trigger data', async () => {
|
|
204
|
+
mockDbTriggersWithUpdate([makeTrigger({ id: 'trig-xyz', name: 'My Trigger' })])
|
|
205
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', {}, makeCtx())
|
|
206
|
+
expect(runPipeline).toHaveBeenCalledWith(
|
|
207
|
+
'pipeline-1',
|
|
208
|
+
expect.objectContaining({
|
|
209
|
+
trigger: { id: 'trig-xyz', name: 'My Trigger' }
|
|
210
|
+
}),
|
|
211
|
+
expect.any(Object)
|
|
212
|
+
)
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('emits audit on successful trigger fire', async () => {
|
|
216
|
+
mockDbTriggersWithUpdate([makeTrigger()])
|
|
217
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', {}, makeCtx())
|
|
218
|
+
expect(emitAudit).toHaveBeenCalledWith(
|
|
219
|
+
expect.any(Object),
|
|
220
|
+
'trigger.fired',
|
|
221
|
+
expect.objectContaining({ type: 'trigger', id: 'trigger-1' }),
|
|
222
|
+
expect.objectContaining({ execution_status: 'completed' })
|
|
223
|
+
)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
it('emits audit.failed and continues when runPipeline throws', async () => {
|
|
227
|
+
mockDbTriggers([makeTrigger({ id: 'bad-trigger' })])
|
|
228
|
+
vi.mocked(runPipeline).mockRejectedValueOnce(new Error('Pipeline exploded'))
|
|
229
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', {}, makeCtx())
|
|
230
|
+
expect(emitAudit).toHaveBeenCalledWith(
|
|
231
|
+
expect.any(Object),
|
|
232
|
+
'trigger.failed',
|
|
233
|
+
expect.objectContaining({ id: 'bad-trigger' }),
|
|
234
|
+
expect.objectContaining({ error: 'Pipeline exploded' })
|
|
235
|
+
)
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
it('fires all matching triggers, not just the first', async () => {
|
|
239
|
+
const t1 = makeTrigger({ id: 'trig-1', pipeline_id: 'pipe-1' })
|
|
240
|
+
const t2 = makeTrigger({ id: 'trig-2', pipeline_id: 'pipe-2' })
|
|
241
|
+
mockDbTriggersWithUpdate([t1, t2])
|
|
242
|
+
vi.mocked(runPipeline)
|
|
243
|
+
.mockResolvedValueOnce({ executionId: 'exec-1', pipelineId: 'pipe-1', status: 'completed', stages: [], durationMs: 5 })
|
|
244
|
+
.mockResolvedValueOnce({ executionId: 'exec-2', pipelineId: 'pipe-2', status: 'completed', stages: [], durationMs: 5 })
|
|
245
|
+
|
|
246
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', {}, makeCtx())
|
|
247
|
+
expect(runPipeline).toHaveBeenCalledTimes(2)
|
|
248
|
+
expect(runPipeline).toHaveBeenCalledWith('pipe-1', expect.any(Object), expect.any(Object))
|
|
249
|
+
expect(runPipeline).toHaveBeenCalledWith('pipe-2', expect.any(Object), expect.any(Object))
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it('continues to fire remaining triggers after one fails', async () => {
|
|
253
|
+
const t1 = makeTrigger({ id: 'trig-fail', pipeline_id: 'pipe-1' })
|
|
254
|
+
const t2 = makeTrigger({ id: 'trig-ok', pipeline_id: 'pipe-2' })
|
|
255
|
+
mockDbTriggers([t1, t2])
|
|
256
|
+
vi.mocked(runPipeline)
|
|
257
|
+
.mockRejectedValueOnce(new Error('pipe-1 failed'))
|
|
258
|
+
.mockResolvedValueOnce({ executionId: 'exec-2', pipelineId: 'pipe-2', status: 'completed', stages: [], durationMs: 5 })
|
|
259
|
+
|
|
260
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', {}, makeCtx())
|
|
261
|
+
expect(runPipeline).toHaveBeenCalledTimes(2)
|
|
262
|
+
})
|
|
263
|
+
})
|
|
264
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Test checkAndFireTriggers core functionality'; No verified external callers; imports: .framework/functions/_shared/middleware.ts,.framework/functions/_shared/trigger-engine.ts,.framework/functions/_shared/pipeline-runner.ts"]
|
|
265
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
266
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
267
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
268
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
269
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
270
|
+
// CIL[END stable_id="~/.framework/tests/unit/trigger-engine.test.ts:describe_checkAndFireTriggers:block"]
|
|
271
|
+
|
|
272
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/trigger-engine.test.ts:describe_triggerConditionFiltering:block" type="block" identity="describe_triggerConditionFiltering" parent="none"]
|
|
273
|
+
// CIL[DOC-MACRO purpose="Test trigger condition filtering logic"]
|
|
274
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Verify trigger condition evaluation"]
|
|
275
|
+
// CIL[DOC-CONTEXT module="tests/unit/trigger-engine" layer="test-unit" related_files=".framework/functions/_shared/audit.ts,.framework/functions/_shared/db.ts,.framework/functions/_shared/middleware.ts,.framework/functions/_shared/pipeline-runner.ts,.framework/functions/_shared/trigger-engine.ts,.framework/tests/unit/trigger-engine.test.ts"]
|
|
276
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="test assertions" errors="none" side_effects="Tests trigger-engine.ts module"]
|
|
277
|
+
// CIL[DOC-MICRO logic_summary="Tests entity_type, type_slug, and filter condition matching" notable_decisions="[]"]
|
|
278
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
279
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
280
|
+
describe('trigger condition filtering', () => {
|
|
281
|
+
beforeEach(() => {
|
|
282
|
+
vi.clearAllMocks()
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
it('skips trigger when entity_type filter does not match', async () => {
|
|
286
|
+
const trigger = makeTrigger({ config: { entity_type: 'people' } })
|
|
287
|
+
mockDbTriggers([trigger])
|
|
288
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', {}, makeCtx())
|
|
289
|
+
expect(runPipeline).not.toHaveBeenCalled()
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('fires trigger when entity_type filter matches', async () => {
|
|
293
|
+
const trigger = makeTrigger({ config: { entity_type: 'items' } })
|
|
294
|
+
mockDbTriggersWithUpdate([trigger])
|
|
295
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', {}, makeCtx())
|
|
296
|
+
expect(runPipeline).toHaveBeenCalledOnce()
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
it('skips trigger when type_slug filter does not match', async () => {
|
|
300
|
+
const trigger = makeTrigger({ config: { type_slug: 'invoice' } })
|
|
301
|
+
mockDbTriggers([trigger])
|
|
302
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', { type_slug: 'support_ticket' }, makeCtx())
|
|
303
|
+
expect(runPipeline).not.toHaveBeenCalled()
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
it('fires trigger when type_slug filter matches', async () => {
|
|
307
|
+
const trigger = makeTrigger({ config: { type_slug: 'support_ticket' } })
|
|
308
|
+
mockDbTriggersWithUpdate([trigger])
|
|
309
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', { type_slug: 'support_ticket' }, makeCtx())
|
|
310
|
+
expect(runPipeline).toHaveBeenCalledOnce()
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
it('skips trigger when simple filter value does not match', async () => {
|
|
314
|
+
const trigger = makeTrigger({ config: { filters: { status: 'open' } } })
|
|
315
|
+
mockDbTriggers([trigger])
|
|
316
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', { status: 'closed' }, makeCtx())
|
|
317
|
+
expect(runPipeline).not.toHaveBeenCalled()
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
it('fires trigger when simple filter value matches', async () => {
|
|
321
|
+
const trigger = makeTrigger({ config: { filters: { status: 'open' } } })
|
|
322
|
+
mockDbTriggersWithUpdate([trigger])
|
|
323
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', { status: 'open' }, makeCtx())
|
|
324
|
+
expect(runPipeline).toHaveBeenCalledOnce()
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
it('fires trigger when array filter includes the value', async () => {
|
|
328
|
+
const trigger = makeTrigger({ config: { filters: { priority: ['high', 'critical'] } } })
|
|
329
|
+
mockDbTriggersWithUpdate([trigger])
|
|
330
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', { priority: 'high' }, makeCtx())
|
|
331
|
+
expect(runPipeline).toHaveBeenCalledOnce()
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
it('skips trigger when value not in array filter', async () => {
|
|
335
|
+
const trigger = makeTrigger({ config: { filters: { priority: ['high', 'critical'] } } })
|
|
336
|
+
mockDbTriggers([trigger])
|
|
337
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', { priority: 'low' }, makeCtx())
|
|
338
|
+
expect(runPipeline).not.toHaveBeenCalled()
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
it('resolves nested dot-notation filter paths', async () => {
|
|
342
|
+
const trigger = makeTrigger({ config: { filters: { 'data.region': 'us-west' } } })
|
|
343
|
+
mockDbTriggersWithUpdate([trigger])
|
|
344
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', { data: { region: 'us-west' } }, makeCtx())
|
|
345
|
+
expect(runPipeline).toHaveBeenCalledOnce()
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
it('resolves Postgres-style ->> filter paths against the data column', async () => {
|
|
349
|
+
const trigger = makeTrigger({ config: { filters: { "data->>'sender_type'": { $eq: 'agent' } } } })
|
|
350
|
+
mockDbTriggersWithUpdate([trigger])
|
|
351
|
+
await checkAndFireTriggers('message_created', 'messages', 'msg-1', { data: { sender_type: 'agent' } }, makeCtx())
|
|
352
|
+
expect(runPipeline).toHaveBeenCalledOnce()
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
it('maps plural table names to singular event types (fireCreateTriggers)', async () => {
|
|
356
|
+
const trigger = makeTrigger({ config: { entity_type: 'items' } })
|
|
357
|
+
mockDbTriggersWithUpdate([trigger])
|
|
358
|
+
// admin-data passes the table name 'items'; the seeded event is 'item_created'
|
|
359
|
+
await fireCreateTriggers('items', 'item-1', {}, makeCtx())
|
|
360
|
+
expect(runPipeline).toHaveBeenCalledOnce()
|
|
361
|
+
expect(runPipeline).toHaveBeenCalledWith(
|
|
362
|
+
'pipeline-1',
|
|
363
|
+
expect.objectContaining({ event: 'item_created' }),
|
|
364
|
+
expect.any(Object)
|
|
365
|
+
)
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
it('maps plural table names to singular event types (fireUpdateTriggers)', async () => {
|
|
369
|
+
const trigger = makeTrigger({ config: { entity_type: 'items' } })
|
|
370
|
+
mockDbTriggersWithUpdate([trigger])
|
|
371
|
+
await fireUpdateTriggers('items', 'item-1', {}, makeCtx())
|
|
372
|
+
expect(runPipeline).toHaveBeenCalledWith(
|
|
373
|
+
'pipeline-1',
|
|
374
|
+
expect.objectContaining({ event: 'item_updated' }),
|
|
375
|
+
expect.any(Object)
|
|
376
|
+
)
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
it('maps people → person for event types', async () => {
|
|
380
|
+
const trigger = makeTrigger({ config: { entity_type: 'people' } })
|
|
381
|
+
mockDbTriggersWithUpdate([trigger])
|
|
382
|
+
await fireCreateTriggers('people', 'person-1', {}, makeCtx())
|
|
383
|
+
expect(runPipeline).toHaveBeenCalledWith(
|
|
384
|
+
'pipeline-1',
|
|
385
|
+
expect.objectContaining({ event: 'person_created' }),
|
|
386
|
+
expect.any(Object)
|
|
387
|
+
)
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
it('resolves type_slug from entityData.type_id when the slug is not inline', async () => {
|
|
391
|
+
const trigger = makeTrigger({ config: { entity_type: 'items', type_slug: 'kb_article' } })
|
|
392
|
+
const chain = {
|
|
393
|
+
select: vi.fn().mockReturnThis(),
|
|
394
|
+
eq: vi.fn().mockReturnThis(),
|
|
395
|
+
order: vi.fn().mockResolvedValue({ data: [trigger], error: null }),
|
|
396
|
+
maybeSingle: vi.fn().mockResolvedValue({ data: { slug: 'kb_article' }, error: null }),
|
|
397
|
+
update: vi.fn().mockReturnThis()
|
|
398
|
+
}
|
|
399
|
+
vi.mocked(adminDb.from).mockImplementation((table: string) => {
|
|
400
|
+
if (table === 'types') return chain as any
|
|
401
|
+
return chain as any
|
|
402
|
+
})
|
|
403
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', { type_id: 'type-uuid-1', status: 'published' }, makeCtx())
|
|
404
|
+
expect(runPipeline).toHaveBeenCalledOnce()
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
// CIL[DOC-NOTE type="behavior" content="The demo seed filter { data->>'ca_kb_candidate': { $eq: true } } relies on strict boolean equality — the jsonb value is a real boolean, so a string 'true' operand would never fire."]
|
|
408
|
+
it('matches a boolean $eq against a Postgres-style data->> filter path', async () => {
|
|
409
|
+
const trigger = makeTrigger({
|
|
410
|
+
config: {
|
|
411
|
+
type_slug: 'demo_support_ticket',
|
|
412
|
+
filters: { "data->>'ca_kb_candidate'": { $eq: true } }
|
|
413
|
+
}
|
|
414
|
+
})
|
|
415
|
+
mockDbTriggersWithUpdate([trigger])
|
|
416
|
+
await checkAndFireTriggers('item_updated', 'items', 'item-1', { type_slug: 'demo_support_ticket', data: { ca_kb_candidate: true } }, makeCtx())
|
|
417
|
+
expect(runPipeline).toHaveBeenCalledOnce()
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
it('does not fire a boolean data->> field when $eq uses the string "true"', async () => {
|
|
421
|
+
const trigger = makeTrigger({
|
|
422
|
+
config: { filters: { "data->>'ca_kb_candidate'": { $eq: 'true' } } }
|
|
423
|
+
})
|
|
424
|
+
mockDbTriggers([trigger])
|
|
425
|
+
await checkAndFireTriggers('item_updated', 'items', 'item-1', { data: { ca_kb_candidate: true } }, makeCtx())
|
|
426
|
+
expect(runPipeline).not.toHaveBeenCalled()
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
it('skips a boolean $eq filter when the data flag is false', async () => {
|
|
430
|
+
const trigger = makeTrigger({
|
|
431
|
+
config: { filters: { "data->>'ca_kb_candidate'": { $eq: true } } }
|
|
432
|
+
})
|
|
433
|
+
mockDbTriggers([trigger])
|
|
434
|
+
await checkAndFireTriggers('item_updated', 'items', 'item-1', { data: { ca_kb_candidate: false } }, makeCtx())
|
|
435
|
+
expect(runPipeline).not.toHaveBeenCalled()
|
|
436
|
+
})
|
|
437
|
+
})
|
|
438
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Test trigger condition filtering logic'; No verified external callers; imports: .framework/functions/_shared/middleware.ts,.framework/functions/_shared/trigger-engine.ts,.framework/functions/_shared/pipeline-runner.ts"]
|
|
439
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
440
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
441
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
442
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
443
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
444
|
+
// CIL[END stable_id="~/.framework/tests/unit/trigger-engine.test.ts:describe_triggerConditionFiltering:block"]
|
|
445
|
+
|
|
446
|
+
// CIL[BEGIN stable_id="~/.framework/tests/unit/trigger-engine.test.ts:describe_filterOperators:block" type="block" identity="describe_filterOperators" parent="none"]
|
|
447
|
+
// CIL[DOC-MACRO purpose="Test filter operator evaluation logic"]
|
|
448
|
+
// CIL[DOC-RESPONSIBILITY responsibility="Verify filter operator implementations"]
|
|
449
|
+
// CIL[DOC-CONTEXT module="tests/unit/trigger-engine" layer="test-unit" related_files=".framework/functions/_shared/audit.ts,.framework/functions/_shared/db.ts,.framework/functions/_shared/middleware.ts,.framework/functions/_shared/pipeline-runner.ts,.framework/functions/_shared/trigger-engine.ts,.framework/tests/unit/trigger-engine.test.ts"]
|
|
450
|
+
// CIL[DOC-CONTRACT inputs="none" outputs="test assertions" errors="none" side_effects="Tests trigger-engine.ts module"]
|
|
451
|
+
// CIL[DOC-MICRO logic_summary="Tests all filter operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists" notable_decisions="[]"]
|
|
452
|
+
// CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
|
|
453
|
+
// CIL[DOC-STABILITY stability="stable"]
|
|
454
|
+
describe('filter operators', () => {
|
|
455
|
+
beforeEach(() => {
|
|
456
|
+
vi.clearAllMocks()
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
async function testOp(operator: Record<string, any>, entityData: any, shouldFire: boolean) {
|
|
460
|
+
const trigger = makeTrigger({ config: { filters: { score: operator } } })
|
|
461
|
+
if (shouldFire) {
|
|
462
|
+
mockDbTriggersWithUpdate([trigger])
|
|
463
|
+
} else {
|
|
464
|
+
mockDbTriggers([trigger])
|
|
465
|
+
}
|
|
466
|
+
await checkAndFireTriggers('item_created', 'items', 'item-1', entityData, makeCtx())
|
|
467
|
+
if (shouldFire) {
|
|
468
|
+
expect(runPipeline).toHaveBeenCalledOnce()
|
|
469
|
+
} else {
|
|
470
|
+
expect(runPipeline).not.toHaveBeenCalled()
|
|
471
|
+
}
|
|
472
|
+
vi.clearAllMocks()
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
it('$eq — fires when equal', () => testOp({ $eq: 10 }, { score: 10 }, true))
|
|
476
|
+
it('$eq — skips when not equal', () => testOp({ $eq: 10 }, { score: 5 }, false))
|
|
477
|
+
|
|
478
|
+
it('$ne — fires when not equal', () => testOp({ $ne: 10 }, { score: 5 }, true))
|
|
479
|
+
it('$ne — skips when equal', () => testOp({ $ne: 10 }, { score: 10 }, false))
|
|
480
|
+
|
|
481
|
+
it('$gt — fires when greater', () => testOp({ $gt: 5 }, { score: 10 }, true))
|
|
482
|
+
it('$gt — skips when equal', () => testOp({ $gt: 5 }, { score: 5 }, false))
|
|
483
|
+
it('$gt — skips when less', () => testOp({ $gt: 5 }, { score: 3 }, false))
|
|
484
|
+
|
|
485
|
+
it('$gte — fires when equal', () => testOp({ $gte: 5 }, { score: 5 }, true))
|
|
486
|
+
it('$gte — fires when greater', () => testOp({ $gte: 5 }, { score: 6 }, true))
|
|
487
|
+
it('$gte — skips when less', () => testOp({ $gte: 5 }, { score: 4 }, false))
|
|
488
|
+
|
|
489
|
+
it('$lt — fires when less', () => testOp({ $lt: 10 }, { score: 5 }, true))
|
|
490
|
+
it('$lt — skips when equal', () => testOp({ $lt: 10 }, { score: 10 }, false))
|
|
491
|
+
|
|
492
|
+
it('$lte — fires when equal', () => testOp({ $lte: 10 }, { score: 10 }, true))
|
|
493
|
+
it('$lte — fires when less', () => testOp({ $lte: 10 }, { score: 9 }, true))
|
|
494
|
+
it('$lte — skips when greater', () => testOp({ $lte: 10 }, { score: 11 }, false))
|
|
495
|
+
|
|
496
|
+
it('$in — fires when value in array', () => testOp({ $in: ['a', 'b', 'c'] }, { score: 'b' }, true))
|
|
497
|
+
it('$in — skips when value not in array', () => testOp({ $in: ['a', 'b'] }, { score: 'z' }, false))
|
|
498
|
+
|
|
499
|
+
it('$nin — fires when value not in array', () => testOp({ $nin: ['a', 'b'] }, { score: 'z' }, true))
|
|
500
|
+
it('$nin — skips when value in array', () => testOp({ $nin: ['a', 'b'] }, { score: 'a' }, false))
|
|
501
|
+
|
|
502
|
+
it('$exists:true — fires when field exists', () => testOp({ $exists: true }, { score: 0 }, true))
|
|
503
|
+
it('$exists:true — skips when field is null', () => testOp({ $exists: true }, { score: null }, false))
|
|
504
|
+
it('$exists:false — fires when field is null', () => testOp({ $exists: false }, { score: null }, true))
|
|
505
|
+
it('$exists:false — skips when field has value', () => testOp({ $exists: false }, { score: 1 }, false))
|
|
506
|
+
})
|
|
507
|
+
// CIL[DOC-VECTOR type="context" content="purpose='Test filter operator evaluation logic'; No verified external callers; imports: .framework/functions/_shared/middleware.ts,.framework/functions/_shared/trigger-engine.ts,.framework/functions/_shared/pipeline-runner.ts"]
|
|
508
|
+
// CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
|
|
509
|
+
// CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
|
|
510
|
+
// CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
|
|
511
|
+
// CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
|
|
512
|
+
// CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
|
|
513
|
+
// CIL[END stable_id="~/.framework/tests/unit/trigger-engine.test.ts:describe_filterOperators:block"]
|