okengine 0.2.5 → 0.2.8
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/AGENTS.md +24 -23
- package/README.md +52 -346
- package/docs/spec/console.md +165 -142
- package/docs/spec/example.md +490 -312
- package/docs/spec/four-applications.md +495 -314
- package/docs/spec/unified-theory.md +184 -175
- package/package.json +46 -38
- package/spec/manifest.v1.schema.json +40 -9
- package/src/auth/api-keys.ts +2 -5
- package/src/auth/attenuation.ts +2 -6
- package/src/auth/auth.test.ts +53 -31
- package/src/auth/invoke-as.ts +1 -3
- package/src/auth/operator.ts +8 -26
- package/src/auth/planes.ts +2 -7
- package/src/auth/plugin.ts +4 -7
- package/src/auth/roles.ts +1 -5
- package/src/auth/sessions.ts +10 -39
- package/src/cli/args.ts +1 -5
- package/src/cli/ask-dev-mode.ts +40 -0
- package/src/cli/build.ts +1 -2
- package/src/cli/client-add.test.ts +8 -15
- package/src/cli/client-add.ts +5 -14
- package/src/cli/completion.test.ts +1 -5
- package/src/cli/completion.ts +4 -17
- package/src/cli/db-auto-push.test.ts +64 -0
- package/src/cli/db-auto-push.ts +72 -0
- package/src/cli/db.test.ts +189 -0
- package/src/cli/db.ts +386 -0
- package/src/cli/dev-app-runner.ts +2 -8
- package/src/cli/dev-db-push.test.ts +163 -0
- package/src/cli/dev-mode.test.ts +45 -0
- package/src/cli/dev-mode.ts +78 -0
- package/src/cli/dev.test.ts +148 -21
- package/src/cli/dev.ts +208 -134
- package/src/cli/doc-drift.test.ts +3 -2
- package/src/cli/doc-drift.ts +21 -50
- package/src/cli/doc-staleness.test.ts +69 -0
- package/src/cli/docker-cli.test.ts +7 -8
- package/src/cli/docker.ts +1 -3
- package/src/cli/doctor-diff-examples.ts +1 -3
- package/src/cli/doctor-diff.test.ts +1 -5
- package/src/cli/doctor-diff.ts +17 -46
- package/src/cli/doctor-pii.ts +2 -7
- package/src/cli/doctor.test.ts +26 -4
- package/src/cli/doctor.ts +35 -11
- package/src/cli/eval.ts +4 -14
- package/src/cli/gates-list.ts +1 -4
- package/src/cli/hero-meta.test.ts +17 -19
- package/src/cli/hero-meta.ts +20 -30
- package/src/cli/images.ts +3 -8
- package/src/cli/index.ts +11 -2
- package/src/cli/load-config.images.test.ts +14 -14
- package/src/cli/load-config.ts +41 -47
- package/src/cli/mcp-from-console.ts +1 -4
- package/src/cli/mode.ts +45 -0
- package/src/cli/ports.test.ts +4 -13
- package/src/cli/ports.ts +1 -3
- package/src/cli/privacy-erase.ts +1 -3
- package/src/cli/registry.ts +75 -25
- package/src/cli/schema.ts +3 -10
- package/src/cli/stack.ts +2 -7
- package/src/cli/start.ts +4 -15
- package/src/cli/upgrade.ts +1 -4
- package/src/client/budget-entry.ts +1 -6
- package/src/client/create.test.ts +6 -12
- package/src/client/create.ts +2 -8
- package/src/client/errors.ts +1 -4
- package/src/client/index.ts +1 -6
- package/src/client/notes-contract.test.ts +4 -13
- package/src/client/transport.ts +9 -30
- package/src/client/types.ts +8 -29
- package/src/compiler/aot.test.ts +2 -9
- package/src/compiler/aot.ts +1 -4
- package/src/compiler/differential.test.ts +3 -11
- package/src/compiler/dynamic.ts +1 -4
- package/src/compiler/effects-infer.ts +8 -34
- package/src/compiler/emit.ts +1 -3
- package/src/compiler/extract.test.ts +96 -30
- package/src/compiler/extract.ts +362 -156
- package/src/compiler/fixtures/skyport/oke.config.ts +1 -1
- package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +7 -3
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +1 -3
- package/src/compiler/fixtures/skyport/src/flows/support/index.ts +4 -1
- package/src/compiler/fixtures/skyport.expected.json +24 -83
- package/src/compiler/fixtures/triggers/five-triggers.ts +1 -2
- package/src/compiler/http-parse.ts +2 -9
- package/src/compiler/index.ts +1 -5
- package/src/compiler/response.ts +4 -7
- package/src/compiler/sucrose.ts +11 -31
- package/src/config/define-config.test.ts +57 -34
- package/src/config/index.ts +181 -37
- package/src/config/resolve-driver.test.ts +28 -17
- package/src/console/budget.test.ts +1 -4
- package/src/console/index.ts +1 -4
- package/src/console/server/access.test.ts +15 -43
- package/src/console/server/access.ts +19 -49
- package/src/console/server/ai.test.ts +3 -11
- package/src/console/server/ai.ts +23 -46
- package/src/console/server/app.ts +14 -45
- package/src/console/server/auth-rate.test.ts +4 -12
- package/src/console/server/bind.ts +1 -5
- package/src/console/server/channels.test.ts +2 -6
- package/src/console/server/channels.ts +10 -35
- package/src/console/server/claim.ts +2 -8
- package/src/console/server/clock.test.ts +2 -6
- package/src/console/server/clock.ts +8 -26
- package/src/console/server/console.test.ts +1 -3
- package/src/console/server/diff.test.ts +5 -17
- package/src/console/server/diff.ts +10 -31
- package/src/console/server/dry-run.audit.test.ts +2 -8
- package/src/console/server/flows.ts +64 -233
- package/src/console/server/gates.test.ts +14 -50
- package/src/console/server/gates.ts +13 -48
- package/src/console/server/index.ts +2 -9
- package/src/console/server/live.ts +3 -12
- package/src/console/server/operator-db.test.ts +4 -15
- package/src/console/server/operator-db.ts +13 -32
- package/src/console/server/plugin.ts +50 -53
- package/src/console/server/plugins.test.ts +2 -6
- package/src/console/server/plugins.ts +17 -56
- package/src/console/server/runs-pii.ts +2 -7
- package/src/console/server/security.gate.test.ts +18 -40
- package/src/console/server/serve.ts +10 -25
- package/src/console/server/signals.test.ts +1 -3
- package/src/console/server/signals.ts +17 -40
- package/src/console/server/state.ts +21 -59
- package/src/console/server/store.test.ts +20 -22
- package/src/console/server/store.ts +31 -76
- package/src/console/server/vault.test.ts +12 -29
- package/src/console/server/vault.ts +23 -60
- package/src/console/ui/access/AccessA11yView.tsx +6 -21
- package/src/console/ui/access/a11y.test.tsx +3 -12
- package/src/console/ui/access/acknowledgement.test.ts +1 -4
- package/src/console/ui/access/acknowledgement.ts +1 -3
- package/src/console/ui/access/blast-radius.ts +1 -3
- package/src/console/ui/access/confirmation.test.ts +1 -5
- package/src/console/ui/access/confirmation.ts +1 -4
- package/src/console/ui/access/fixture.ts +1 -5
- package/src/console/ui/access/grantable.test.ts +1 -3
- package/src/console/ui/access/grantable.ts +2 -7
- package/src/console/ui/access/index.ts +2 -9
- package/src/console/ui/access/provenance.test.ts +1 -3
- package/src/console/ui/access/provenance.ts +2 -6
- package/src/console/ui/access/search.test.ts +1 -5
- package/src/console/ui/access/search.ts +2 -6
- package/src/console/ui/ai/AiA11yView.tsx +6 -17
- package/src/console/ui/ai/a11y.test.tsx +2 -5
- package/src/console/ui/ai/fixture.ts +2 -7
- package/src/console/ui/ai/format.ts +1 -3
- package/src/console/ui/ai/group.test.ts +1 -3
- package/src/console/ui/ai/group.ts +4 -12
- package/src/console/ui/ai/index.ts +2 -9
- package/src/console/ui/ai/promotion.test.ts +2 -7
- package/src/console/ui/ai/promotion.ts +4 -13
- package/src/console/ui/ai/search.test.ts +2 -11
- package/src/console/ui/ai/search.ts +3 -13
- package/src/console/ui/ai/types.ts +1 -8
- package/src/console/ui/architecture/ArchitectureA11yView.tsx +8 -29
- package/src/console/ui/architecture/a11y.test.tsx +1 -2
- package/src/console/ui/architecture/declared.ts +1 -5
- package/src/console/ui/architecture/index.ts +7 -35
- package/src/console/ui/architecture/layout.ts +1 -3
- package/src/console/ui/architecture/pathologies.ts +10 -19
- package/src/console/ui/architecture/search.ts +6 -15
- package/src/console/ui/architecture/traffic.ts +4 -14
- package/src/console/ui/architecture/types.ts +2 -10
- package/src/console/ui/architecture/view.test.ts +7 -19
- package/src/console/ui/architecture/view.ts +9 -36
- package/src/console/ui/channels/ChannelsA11yView.tsx +4 -13
- package/src/console/ui/channels/a11y.test.tsx +1 -2
- package/src/console/ui/channels/confirmation.test.ts +1 -4
- package/src/console/ui/channels/confirmation.ts +1 -4
- package/src/console/ui/channels/findings.ts +1 -5
- package/src/console/ui/channels/index.ts +2 -8
- package/src/console/ui/channels/locale.test.ts +1 -3
- package/src/console/ui/channels/locale.ts +2 -8
- package/src/console/ui/channels/search.ts +4 -13
- package/src/console/ui/channels/taxonomy.ts +1 -3
- package/src/console/ui/clock/ClockA11yView.tsx +9 -26
- package/src/console/ui/clock/a11y.test.tsx +2 -5
- package/src/console/ui/clock/confirmation.test.ts +1 -3
- package/src/console/ui/clock/confirmation.ts +1 -4
- package/src/console/ui/clock/findings.ts +1 -4
- package/src/console/ui/clock/health.ts +2 -7
- package/src/console/ui/clock/index.ts +2 -10
- package/src/console/ui/clock/search.test.ts +1 -3
- package/src/console/ui/clock/search.ts +2 -6
- package/src/console/ui/clock/timeline.test.ts +1 -3
- package/src/console/ui/clock/waiting-on.test.ts +1 -5
- package/src/console/ui/clock/waiting-on.ts +2 -7
- package/src/console/ui/diff/DiffA11yView.tsx +8 -17
- package/src/console/ui/diff/a11y.test.tsx +2 -5
- package/src/console/ui/diff/group.test.ts +3 -13
- package/src/console/ui/diff/group.ts +2 -8
- package/src/console/ui/diff/index.ts +2 -8
- package/src/console/ui/diff/search.test.ts +1 -5
- package/src/console/ui/diff/search.ts +4 -16
- package/src/console/ui/diff/summary.ts +1 -2
- package/src/console/ui/dist/assets/{index-Dy4jht9P.js → index-BWo8R7NR.js} +2 -2
- package/src/console/ui/dist/assets/{panel-access-Dd37LU2c.js → panel-access-C0J2D-a2.js} +1 -1
- package/src/console/ui/dist/assets/{panel-ai-CC7LR6-J.js → panel-ai-D_m6WQI8.js} +1 -1
- package/src/console/ui/dist/assets/{panel-architecture-B5b3iKCz.js → panel-architecture-CKnXFyUx.js} +1 -1
- package/src/console/ui/dist/assets/{panel-channels-CeNjTKXp.js → panel-channels-BOmQ-onL.js} +1 -1
- package/src/console/ui/dist/assets/{panel-clock-DgFTLoHV.js → panel-clock-giAq0Ccv.js} +1 -1
- package/src/console/ui/dist/assets/{panel-diff-DxehccqB.js → panel-diff-cdonmH8c.js} +1 -1
- package/src/console/ui/dist/assets/{panel-flows-BtrVn-Eg.js → panel-flows-DlCU5zjA.js} +2 -2
- package/src/console/ui/dist/assets/{panel-gates-Z9MKRGdH.js → panel-gates-XclZxWD5.js} +1 -1
- package/src/console/ui/dist/assets/{panel-overview-Dt_AeXgd.js → panel-overview-BznEOTnb.js} +1 -1
- package/src/console/ui/dist/assets/{panel-plugins-DWd0TowH.js → panel-plugins-CcGM1g64.js} +1 -1
- package/src/console/ui/dist/assets/{panel-runs-DGstFHeq.js → panel-runs-CGWNHLR4.js} +1 -1
- package/src/console/ui/dist/assets/{panel-signals-DzEa2Fnt.js → panel-signals-CNywkdak.js} +1 -1
- package/src/console/ui/dist/assets/{panel-store-BJkbNgxx.js → panel-store-KmTbFHMH.js} +1 -1
- package/src/console/ui/dist/assets/{panel-traces-BaRVO3gM.js → panel-traces-DBLx2ilD.js} +1 -1
- package/src/console/ui/dist/assets/panel-vault-CEnFc0dk.js +1 -0
- package/src/console/ui/dist/index.html +1 -1
- package/src/console/ui/flows/FlowsA11yView.tsx +7 -22
- package/src/console/ui/flows/a11y.test.tsx +2 -8
- package/src/console/ui/flows/buffer.test.ts +3 -11
- package/src/console/ui/flows/buffer.ts +4 -11
- package/src/console/ui/flows/confirmation.ts +1 -3
- package/src/console/ui/flows/contract.test.ts +3 -7
- package/src/console/ui/flows/contract.ts +12 -36
- package/src/console/ui/flows/graph.test.ts +3 -11
- package/src/console/ui/flows/graph.ts +5 -15
- package/src/console/ui/flows/index.ts +1 -6
- package/src/console/ui/flows/save-as-test.ts +1 -3
- package/src/console/ui/flows/search.ts +8 -25
- package/src/console/ui/flows/tiers.ts +2 -9
- package/src/console/ui/gates/GatesA11yView.tsx +4 -13
- package/src/console/ui/gates/a11y.test.tsx +2 -5
- package/src/console/ui/gates/audit.ts +1 -3
- package/src/console/ui/gates/denial.test.ts +1 -3
- package/src/console/ui/gates/denial.ts +4 -12
- package/src/console/ui/gates/findings.ts +1 -3
- package/src/console/ui/gates/group.test.ts +3 -3
- package/src/console/ui/gates/group.ts +3 -11
- package/src/console/ui/gates/index.ts +2 -10
- package/src/console/ui/gates/search.ts +3 -10
- package/src/console/ui/overview/OverviewA11yView.tsx +9 -27
- package/src/console/ui/overview/a11y.test.tsx +1 -2
- package/src/console/ui/overview/busiest.ts +1 -3
- package/src/console/ui/overview/compose.ts +1 -13
- package/src/console/ui/overview/cost.ts +34 -38
- package/src/console/ui/overview/fixture.ts +21 -26
- package/src/console/ui/overview/golden.ts +4 -14
- package/src/console/ui/overview/index.ts +2 -9
- package/src/console/ui/overview/rank.test.ts +3 -10
- package/src/console/ui/overview/rank.ts +2 -7
- package/src/console/ui/overview/slo.test.ts +5 -9
- package/src/console/ui/overview/slo.ts +6 -23
- package/src/console/ui/overview/verdict.ts +3 -10
- package/src/console/ui/plugins/PluginsA11yView.tsx +8 -18
- package/src/console/ui/plugins/a11y.test.tsx +1 -2
- package/src/console/ui/plugins/fixture.ts +2 -4
- package/src/console/ui/plugins/group.test.ts +1 -3
- package/src/console/ui/plugins/group.ts +1 -6
- package/src/console/ui/plugins/index.ts +3 -13
- package/src/console/ui/plugins/search.ts +7 -19
- package/src/console/ui/runs/RunsA11yView.tsx +5 -20
- package/src/console/ui/runs/a11y.test.tsx +2 -5
- package/src/console/ui/runs/explain.ts +2 -6
- package/src/console/ui/runs/group.ts +3 -12
- package/src/console/ui/runs/histogram.test.ts +2 -11
- package/src/console/ui/runs/histogram.ts +1 -4
- package/src/console/ui/runs/index.ts +4 -18
- package/src/console/ui/runs/project.ts +2 -6
- package/src/console/ui/runs/query.test.ts +1 -3
- package/src/console/ui/runs/query.ts +12 -35
- package/src/console/ui/runs/search.ts +5 -19
- package/src/console/ui/runs/trace-link.test.ts +2 -6
- package/src/console/ui/runs/trace-link.ts +4 -16
- package/src/console/ui/runs/types.ts +1 -3
- package/src/console/ui/shell/App.tsx +2 -10
- package/src/console/ui/shell/client.ts +22 -115
- package/src/console/ui/shell/components/ui.tsx +1 -4
- package/src/console/ui/shell/layout/Shell.tsx +2 -4
- package/src/console/ui/shell/main.tsx +31 -96
- package/src/console/ui/shell/panels/access/AccessPanel.tsx +34 -98
- package/src/console/ui/shell/panels/ai/AiPanel.tsx +35 -106
- package/src/console/ui/shell/panels/architecture/ArchitecturePanel.tsx +18 -66
- package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +20 -57
- package/src/console/ui/shell/panels/clock/ClockPanel.tsx +22 -54
- package/src/console/ui/shell/panels/diff/DiffPanel.tsx +20 -61
- package/src/console/ui/shell/panels/flows/ContractEditor.tsx +6 -23
- package/src/console/ui/shell/panels/flows/FlowDrawer.tsx +13 -28
- package/src/console/ui/shell/panels/flows/FlowsPanel.tsx +9 -29
- package/src/console/ui/shell/panels/gates/GatesPanel.tsx +24 -80
- package/src/console/ui/shell/panels/overview/OverviewPanel.tsx +20 -58
- package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +16 -41
- package/src/console/ui/shell/panels/runs/RunsPanel.tsx +31 -99
- package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +38 -101
- package/src/console/ui/shell/panels/store/StorePanel.tsx +27 -66
- package/src/console/ui/shell/panels/traces/TracesPanel.tsx +28 -89
- package/src/console/ui/shell/panels/vault/VaultPanel.tsx +22 -53
- package/src/console/ui/shell/setup/Wizard.tsx +3 -5
- package/src/console/ui/shell/styles.css +1 -2
- package/src/console/ui/signals/SignalsA11yView.tsx +9 -33
- package/src/console/ui/signals/a11y.test.tsx +2 -5
- package/src/console/ui/signals/confirmation.test.ts +1 -5
- package/src/console/ui/signals/confirmation.ts +2 -6
- package/src/console/ui/signals/dry-run.ts +1 -3
- package/src/console/ui/signals/durable.ts +1 -3
- package/src/console/ui/signals/findings.ts +1 -3
- package/src/console/ui/signals/group.test.ts +2 -8
- package/src/console/ui/signals/group.ts +2 -10
- package/src/console/ui/signals/index.ts +1 -4
- package/src/console/ui/signals/monitor.ts +2 -7
- package/src/console/ui/signals/schema-form.test.ts +3 -14
- package/src/console/ui/signals/schema-form.ts +1 -4
- package/src/console/ui/signals/search.ts +4 -13
- package/src/console/ui/store/StoreA11yView.tsx +3 -9
- package/src/console/ui/store/a11y.test.tsx +2 -5
- package/src/console/ui/store/confirmation.test.ts +1 -3
- package/src/console/ui/store/confirmation.ts +1 -4
- package/src/console/ui/store/dry-run.ts +2 -5
- package/src/console/ui/store/fixture.ts +1 -2
- package/src/console/ui/store/group.test.ts +1 -6
- package/src/console/ui/store/group.ts +1 -4
- package/src/console/ui/store/index.ts +1 -5
- package/src/console/ui/store/search.ts +2 -6
- package/src/console/ui/store/will-not-fire.ts +1 -2
- package/src/console/ui/traces/TracesA11yView.tsx +10 -29
- package/src/console/ui/traces/a11y.test.tsx +1 -2
- package/src/console/ui/traces/chain.test.ts +5 -19
- package/src/console/ui/traces/chain.ts +1 -3
- package/src/console/ui/traces/critical-path.test.ts +1 -3
- package/src/console/ui/traces/critical-path.ts +2 -6
- package/src/console/ui/traces/filter.test.ts +2 -6
- package/src/console/ui/traces/filter.ts +5 -13
- package/src/console/ui/traces/fold.test.ts +4 -12
- package/src/console/ui/traces/fold.ts +1 -3
- package/src/console/ui/traces/mini.ts +1 -3
- package/src/console/ui/traces/replay.test.ts +1 -3
- package/src/console/ui/traces/replay.ts +1 -3
- package/src/console/ui/traces/sampling.ts +1 -4
- package/src/console/ui/traces/search.ts +4 -17
- package/src/console/ui/traces/tier.ts +1 -3
- package/src/console/ui/vault/VaultA11yView.tsx +5 -17
- package/src/console/ui/vault/a11y.test.tsx +1 -2
- package/src/console/ui/vault/blast-radius.ts +1 -3
- package/src/console/ui/vault/confirmation.test.ts +1 -5
- package/src/console/ui/vault/confirmation.ts +1 -4
- package/src/console/ui/vault/dormant.test.ts +1 -4
- package/src/console/ui/vault/export-safe.test.ts +1 -5
- package/src/console/ui/vault/export-safe.ts +1 -4
- package/src/console/ui/vault/fixture.ts +3 -3
- package/src/console/ui/vault/group.ts +1 -2
- package/src/console/ui/vault/index.ts +2 -10
- package/src/console/ui/vault/search.ts +2 -6
- package/src/console/ui/vault/types.ts +1 -1
- package/src/console/xss.gate.test.ts +1 -3
- package/src/docker/compose.ts +112 -20
- package/src/docker/derive.ts +5 -19
- package/src/docker/docker.test.ts +15 -15
- package/src/docker/dockerfile.integration.test.ts +12 -21
- package/src/docker/helpers.ts +5 -4
- package/src/docker/index.ts +4 -11
- package/src/docker/pin.ts +1 -9
- package/src/docker/recipes/index.ts +5 -6
- package/src/docker/recipes/mailpit.ts +22 -0
- package/src/docker/recipes/postgres.ts +17 -6
- package/src/docker/recipes/redis.ts +6 -1
- package/src/docker/recipes/rustfs.ts +33 -0
- package/src/docker/stack-id.test.ts +33 -0
- package/src/docker/stack-id.ts +25 -13
- package/src/docker/stack.integration.test.ts +6 -21
- package/src/docker/types.ts +13 -4
- package/src/drivers/ai-anthropic.ts +5 -14
- package/src/drivers/ai-mock.ts +2 -5
- package/src/drivers/ai-openai-compatible.ts +4 -10
- package/src/drivers/ai-providers.test.ts +6 -13
- package/src/drivers/bun-native-completeness.test.ts +6 -15
- package/src/drivers/channel-console.ts +6 -7
- package/src/drivers/channel-fcm.ts +20 -30
- package/src/drivers/channel-resend.ts +1 -3
- package/src/drivers/channel-smtp.ts +2 -6
- package/src/drivers/channel-types.ts +2 -12
- package/src/drivers/channel-unifonic.ts +2 -6
- package/src/drivers/channel-wa-cloud.ts +2 -6
- package/src/drivers/channel-webpush.ts +21 -60
- package/src/drivers/conformance.test.ts +2 -10
- package/src/drivers/conformance.ts +8 -21
- package/src/drivers/fs.ts +2 -8
- package/src/drivers/index.ts +12 -56
- package/src/drivers/memory.ts +14 -41
- package/src/drivers/pgvector.ts +7 -11
- package/src/drivers/postgres.ts +7 -13
- package/src/drivers/redis.ts +13 -37
- package/src/drivers/s3.ts +5 -17
- package/src/drivers/signal-engine.ts +12 -49
- package/src/drivers/signal-memory.ts +2 -8
- package/src/drivers/signal-nats.ts +4 -9
- package/src/drivers/signal-postgres.ts +62 -129
- package/src/drivers/signal-redis.ts +7 -21
- package/src/drivers/signal-types.ts +1 -4
- package/src/drivers/sqlite.ts +3 -12
- package/src/drivers/types.ts +4 -23
- package/src/drivers/vault-env.ts +2 -6
- package/src/drivers/vault-infisical.ts +1 -5
- package/src/drivers/vault-managed.ts +1 -5
- package/src/drivers/vault-memory.ts +1 -5
- package/src/drivers/vault-openbao.ts +1 -5
- package/src/drivers/vault-sops.ts +10 -35
- package/src/drivers/vault-types.ts +1 -7
- package/src/elements/ai/declare.ts +4 -14
- package/src/elements/ai/eval.ts +2 -6
- package/src/elements/ai/pii.ts +1 -3
- package/src/elements/ai/runtime.ts +12 -50
- package/src/elements/ai/schema.ts +4 -15
- package/src/elements/ai.test.ts +13 -24
- package/src/elements/ai.ts +1 -5
- package/src/elements/channel/consent.ts +3 -12
- package/src/elements/channel/costs.ts +4 -15
- package/src/elements/channel/declare.ts +1 -4
- package/src/elements/channel/email-auth.ts +1 -4
- package/src/elements/channel/locale.ts +2 -10
- package/src/elements/channel/mask.ts +1 -3
- package/src/elements/channel/mime.ts +2 -12
- package/src/elements/channel/outcomes.test.ts +1 -5
- package/src/elements/channel/outcomes.ts +5 -15
- package/src/elements/channel/receipts.ts +2 -7
- package/src/elements/channel/runtime.ts +28 -74
- package/src/elements/channel/suppression.ts +8 -14
- package/src/elements/channel.test.ts +8 -31
- package/src/elements/channel.ts +3 -14
- package/src/elements/clock/actions.ts +6 -18
- package/src/elements/clock/declare.ts +1 -3
- package/src/elements/clock/dst.ts +1 -5
- package/src/elements/clock/durable.ts +4 -16
- package/src/elements/clock/health.ts +2 -7
- package/src/elements/clock/leader.ts +2 -9
- package/src/elements/clock/reconcile.ts +4 -15
- package/src/elements/clock/runtime.ts +2 -5
- package/src/elements/clock/schedule.ts +4 -20
- package/src/elements/clock.test.ts +2 -7
- package/src/elements/clock.ts +5 -23
- package/src/elements/gate/permissions.ts +1 -4
- package/src/elements/gate/runtime.ts +7 -28
- package/src/elements/gate.ts +1 -5
- package/src/elements/index.ts +9 -7
- package/src/elements/signal/declare.ts +2 -7
- package/src/elements/signal/reconcile.test.ts +1 -4
- package/src/elements/signal/runtime.ts +1 -3
- package/src/elements/signal.test.ts +3 -15
- package/src/elements/signal.ts +2 -8
- package/src/elements/store/cache.test.ts +46 -0
- package/src/elements/store/cache.ts +3 -11
- package/src/elements/store/classify.ts +5 -22
- package/src/elements/store/declare.ts +8 -17
- package/src/elements/store/domain-ddl.test.ts +86 -0
- package/src/elements/store/emit-drizzle.ts +363 -0
- package/src/elements/store/files-policy.test.ts +1 -5
- package/src/elements/store/files-policy.ts +4 -12
- package/src/elements/store/load-plugin-tables.ts +103 -0
- package/src/elements/store/missing-relation.test.ts +83 -0
- package/src/elements/store/missing-relation.ts +38 -0
- package/src/elements/store/replica.ts +1 -4
- package/src/elements/store/runtime.ts +25 -28
- package/src/elements/store/schema-decl.test.ts +227 -0
- package/src/elements/store/schema-decl.ts +516 -0
- package/src/elements/store/sql-condition.ts +11 -21
- package/src/elements/store/sql-session.test.ts +50 -0
- package/src/elements/store/sql-session.ts +60 -63
- package/src/elements/store/table.ts +35 -19
- package/src/elements/store.test.ts +17 -39
- package/src/elements/store.ts +37 -31
- package/src/elements/vault/chain.ts +34 -0
- package/src/elements/vault/declare.ts +22 -32
- package/src/elements/vault/fingerprint.ts +2 -7
- package/src/elements/vault/redact.ts +2 -6
- package/src/elements/vault/runtime.ts +18 -32
- package/src/elements/vault.test.ts +12 -28
- package/src/elements/vault.ts +8 -12
- package/src/index.ts +5 -2
- package/src/kernel/adopt-routes.ts +15 -29
- package/src/kernel/app.ts +34 -94
- package/src/kernel/auth-resolve.ts +2 -9
- package/src/kernel/boot-bind/ai.ts +1 -4
- package/src/kernel/boot-bind/channel.ts +2 -8
- package/src/kernel/boot-bind/clock.ts +2 -6
- package/src/kernel/boot-bind/gate.ts +2 -8
- package/src/kernel/boot-bind/runs.ts +1 -3
- package/src/kernel/boot-bind/signal.ts +3 -10
- package/src/kernel/boot-bind/store.test.ts +17 -17
- package/src/kernel/boot-bind/store.ts +44 -50
- package/src/kernel/boot-bind/vault.ts +2 -8
- package/src/kernel/boot.test.ts +4 -9
- package/src/kernel/boot.ts +29 -76
- package/src/kernel/budget.test.ts +1 -3
- package/src/kernel/capability.ts +3 -10
- package/src/kernel/dry-run.test.ts +1 -4
- package/src/kernel/dry-run.ts +3 -11
- package/src/kernel/effects.test.ts +1 -5
- package/src/kernel/effects.ts +4 -11
- package/src/kernel/errors.registry.test.ts +2 -8
- package/src/kernel/errors.ts +23 -32
- package/src/kernel/flow.test.ts +12 -25
- package/src/kernel/flow.ts +5 -25
- package/src/kernel/fx.test.ts +15 -44
- package/src/kernel/fx.ts +27 -79
- package/src/kernel/hook-timing.test.ts +3 -6
- package/src/kernel/hook-timing.ts +5 -14
- package/src/kernel/hooks.test.ts +10 -10
- package/src/kernel/hooks.ts +3 -12
- package/src/kernel/index.ts +4 -17
- package/src/kernel/journal.ts +7 -31
- package/src/kernel/on.ts +1 -6
- package/src/kernel/pipeline.test.ts +10 -19
- package/src/kernel/pipeline.ts +5 -17
- package/src/kernel/plug.ts +4 -11
- package/src/kernel/plugin/conflicts.test.ts +13 -25
- package/src/kernel/plugin/decorate.test.ts +8 -13
- package/src/kernel/plugin/scoping.test.ts +4 -11
- package/src/kernel/plugin.ts +48 -24
- package/src/kernel/registry.ts +80 -29
- package/src/kernel/router.test.ts +15 -13
- package/src/kernel/router.ts +9 -29
- package/src/kernel/routing-budget.test.ts +1 -3
- package/src/kernel/run-telemetry.ts +1 -3
- package/src/kernel/triggers.ts +4 -21
- package/src/manifest/diff.test.ts +26 -91
- package/src/manifest/diff.ts +95 -225
- package/src/manifest/index.ts +2 -9
- package/src/manifest/types.ts +29 -5
- package/src/manifest/undeclared.test.ts +4 -12
- package/src/manifest/undeclared.ts +1 -2
- package/src/manifest/validate.test.ts +2 -6
- package/src/manifest/validate.ts +4 -14
- package/src/mcp/authorization.ts +8 -27
- package/src/mcp/confirmation.ts +3 -10
- package/src/mcp/data.ts +2 -6
- package/src/mcp/docs-index.ts +4 -7
- package/src/mcp/docs-mcp.test.ts +3 -12
- package/src/mcp/docs-server.ts +7 -27
- package/src/mcp/docs-tools.ts +2 -6
- package/src/mcp/index.ts +1 -6
- package/src/mcp/injection.gate.test.ts +4 -18
- package/src/mcp/mcp.test.ts +18 -49
- package/src/mcp/protocol.ts +3 -10
- package/src/mcp/server.ts +11 -52
- package/src/mcp/session.ts +4 -14
- package/src/mcp/tools.ts +14 -50
- package/src/plugins/catalogue.test.ts +3 -7
- package/src/plugins/catalogue.ts +1 -3
- package/src/plugins/node-import-scan.ts +2 -5
- package/src/plugins/supply-chain.ts +5 -17
- package/src/release/exports.ts +3 -8
- package/src/release/measure.exports.test.ts +1 -3
- package/src/release/measure.ts +15 -43
- package/src/runs/bench.test.ts +1 -3
- package/src/runs/collect.ts +8 -25
- package/src/runs/drivers/clickhouse.ts +2 -9
- package/src/runs/drivers/files.ts +9 -31
- package/src/runs/drivers/memory.ts +5 -22
- package/src/runs/drivers/postgres.ts +8 -20
- package/src/runs/duckdb.ts +2 -8
- package/src/runs/index.ts +3 -14
- package/src/runs/outlier.ts +3 -10
- package/src/runs/parquet.ts +8 -26
- package/src/runs/privacy.ts +2 -8
- package/src/runs/runs.test.ts +3 -9
- package/src/runs/runtime.ts +7 -32
- package/src/runs/shred.ts +10 -29
- package/src/runs/types.ts +1 -4
- package/src/runtime/bun.ts +4 -15
- package/src/runtime/cold-start.bench.ts +1 -3
- package/src/runtime/dev-request-log.test.ts +1 -4
- package/src/runtime/dev-request-log.ts +1 -4
- package/src/runtime/index.ts +1 -6
- package/src/runtime/primitives.ts +5 -4
- package/src/runtime/security.ts +3 -13
- package/src/runtime/serve.test.ts +4 -7
- package/src/runtime/types.ts +1 -4
- package/src/runtime/web-standard.ts +3 -15
- package/src/term.test.ts +4 -6
- package/src/term.ts +49 -98
- package/src/test/create-test-app.test.ts +4 -14
- package/src/test/create-test-app.ts +13 -31
- package/src/test/provisions.integration.test.ts +3 -11
- package/src/upgrade/codemods.test.ts +25 -6
- package/src/upgrade/codemods.ts +116 -3
- package/src/validation/standard-schema.test.ts +2 -5
- package/src/validation/standard-schema.ts +13 -33
- package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +0 -1
|
@@ -18,9 +18,7 @@ export type DeliveryOutcomeState =
|
|
|
18
18
|
export type DeliveryVerdict = "correct" | "retry" | "suppress" | "review";
|
|
19
19
|
|
|
20
20
|
/** Verdict for each of the seven states. */
|
|
21
|
-
export const VERDICT_BY_STATE: Readonly<
|
|
22
|
-
Record<DeliveryOutcomeState, DeliveryVerdict>
|
|
23
|
-
> = {
|
|
21
|
+
export const VERDICT_BY_STATE: Readonly<Record<DeliveryOutcomeState, DeliveryVerdict>> = {
|
|
24
22
|
"suppressed/opted-out": "correct",
|
|
25
23
|
"suppressed/prior-bounce": "correct",
|
|
26
24
|
"blocked/invalid-address": "review",
|
|
@@ -34,9 +32,7 @@ export const VERDICT_BY_STATE: Readonly<
|
|
|
34
32
|
* Consequence weight — higher is worse for sender reputation.
|
|
35
33
|
* Complaints outrank many hard bounces.
|
|
36
34
|
*/
|
|
37
|
-
export const CONSEQUENCE_WEIGHT: Readonly<
|
|
38
|
-
Record<DeliveryOutcomeState, number>
|
|
39
|
-
> = {
|
|
35
|
+
export const CONSEQUENCE_WEIGHT: Readonly<Record<DeliveryOutcomeState, number>> = {
|
|
40
36
|
"suppressed/opted-out": 0,
|
|
41
37
|
"suppressed/prior-bounce": 1,
|
|
42
38
|
"blocked/invalid-address": 3,
|
|
@@ -70,9 +66,7 @@ export interface OutcomeRow {
|
|
|
70
66
|
*
|
|
71
67
|
* @param rows - Taxonomy rows with counts
|
|
72
68
|
*/
|
|
73
|
-
export function rankByConsequence(
|
|
74
|
-
rows: readonly OutcomeRow[],
|
|
75
|
-
): readonly OutcomeRow[] {
|
|
69
|
+
export function rankByConsequence(rows: readonly OutcomeRow[]): readonly OutcomeRow[] {
|
|
76
70
|
return [...rows].sort((a, b) => {
|
|
77
71
|
if (b.weight !== a.weight) return b.weight - a.weight;
|
|
78
72
|
return b.count - a.count;
|
|
@@ -101,9 +95,7 @@ export function buildOutcomeRows(
|
|
|
101
95
|
*
|
|
102
96
|
* @param status - Receipt status
|
|
103
97
|
*/
|
|
104
|
-
export function isDeliveryOutcomeState(
|
|
105
|
-
status: string,
|
|
106
|
-
): status is DeliveryOutcomeState {
|
|
98
|
+
export function isDeliveryOutcomeState(status: string): status is DeliveryOutcomeState {
|
|
107
99
|
return (DELIVERY_OUTCOME_STATES as readonly string[]).includes(status);
|
|
108
100
|
}
|
|
109
101
|
|
|
@@ -120,7 +112,5 @@ export function formatAttemptChain(
|
|
|
120
112
|
}[],
|
|
121
113
|
): string {
|
|
122
114
|
if (attempts.length === 0) return "";
|
|
123
|
-
return attempts
|
|
124
|
-
.map((a) => `${a.driverId} ${a.ok ? "succeeded" : "failed"}`)
|
|
125
|
-
.join(" → ");
|
|
115
|
+
return attempts.map((a) => `${a.driverId} ${a.ok ? "succeeded" : "failed"}`).join(" → ");
|
|
126
116
|
}
|
|
@@ -13,10 +13,7 @@ export type DeliverySuccessStatus = "sent" | "fallback";
|
|
|
13
13
|
export type DeliveryLegacyStatus = "failed" | "opted-out";
|
|
14
14
|
|
|
15
15
|
/** Full receipt status — success paths + seven-state taxonomy. */
|
|
16
|
-
export type DeliveryStatus =
|
|
17
|
-
| DeliverySuccessStatus
|
|
18
|
-
| DeliveryLegacyStatus
|
|
19
|
-
| DeliveryOutcomeState;
|
|
16
|
+
export type DeliveryStatus = DeliverySuccessStatus | DeliveryLegacyStatus | DeliveryOutcomeState;
|
|
20
17
|
|
|
21
18
|
/** Delivery receipt. */
|
|
22
19
|
export interface DeliveryReceipt {
|
|
@@ -103,9 +100,7 @@ export function createReceiptLedger(): ReceiptLedger {
|
|
|
103
100
|
return undefined;
|
|
104
101
|
},
|
|
105
102
|
updateStatus(messageId, patch) {
|
|
106
|
-
const idx = rows.findIndex(
|
|
107
|
-
(r) => r.messageId === messageId || r.id === messageId,
|
|
108
|
-
);
|
|
103
|
+
const idx = rows.findIndex((r) => r.messageId === messageId || r.id === messageId);
|
|
109
104
|
if (idx < 0) return undefined;
|
|
110
105
|
const prev = rows[idx]!;
|
|
111
106
|
const next: DeliveryReceipt = {
|
|
@@ -5,11 +5,7 @@
|
|
|
5
5
|
* error hierarchy via sently). Fallback chains record every attempt.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
FallbackTransport,
|
|
10
|
-
type FallbackAttempt,
|
|
11
|
-
type Transport,
|
|
12
|
-
} from "sently";
|
|
8
|
+
import { FallbackTransport, type FallbackAttempt, type Transport } from "sently";
|
|
13
9
|
import { RetryTransport } from "sently/transports/retry";
|
|
14
10
|
import type {
|
|
15
11
|
ChannelAttempt,
|
|
@@ -20,14 +16,8 @@ import type {
|
|
|
20
16
|
import type { ChannelMedium } from "../../manifest/types.ts";
|
|
21
17
|
import type { ConsentStore } from "./consent.ts";
|
|
22
18
|
import type { ChannelTemplateDecl } from "./declare.ts";
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
type MediumCosts,
|
|
26
|
-
} from "./costs.ts";
|
|
27
|
-
import {
|
|
28
|
-
resolveLocale,
|
|
29
|
-
type LocaleChainStep,
|
|
30
|
-
} from "./locale.ts";
|
|
19
|
+
import { DEFAULT_MEDIUM_COSTS, type MediumCosts } from "./costs.ts";
|
|
20
|
+
import { resolveLocale, type LocaleChainStep } from "./locale.ts";
|
|
31
21
|
import type { DeliveryOutcomeState } from "./outcomes.ts";
|
|
32
22
|
import {
|
|
33
23
|
createReceiptLedger,
|
|
@@ -36,20 +26,14 @@ import {
|
|
|
36
26
|
type IngestOutcomeInput,
|
|
37
27
|
type ReceiptLedger,
|
|
38
28
|
} from "./receipts.ts";
|
|
39
|
-
import {
|
|
40
|
-
createSuppressionStore,
|
|
41
|
-
type SuppressionStore,
|
|
42
|
-
} from "./suppression.ts";
|
|
29
|
+
import { createSuppressionStore, type SuppressionStore } from "./suppression.ts";
|
|
43
30
|
|
|
44
31
|
/** Locale catalog: template → locale → rendered body. */
|
|
45
32
|
export type TemplateCatalog = Readonly<
|
|
46
33
|
Record<
|
|
47
34
|
string,
|
|
48
35
|
Readonly<
|
|
49
|
-
Record<
|
|
50
|
-
string,
|
|
51
|
-
{ readonly subject?: string; readonly text?: string; readonly html?: string }
|
|
52
|
-
>
|
|
36
|
+
Record<string, { readonly subject?: string; readonly text?: string; readonly html?: string }>
|
|
53
37
|
>
|
|
54
38
|
>
|
|
55
39
|
>;
|
|
@@ -107,10 +91,7 @@ export interface ChannelRuntime {
|
|
|
107
91
|
* @param template - Template name
|
|
108
92
|
* @param options - Recipient / data / locale / via
|
|
109
93
|
*/
|
|
110
|
-
send(
|
|
111
|
-
template: string,
|
|
112
|
-
options: ChannelSendOptions,
|
|
113
|
-
): Promise<ChannelSendResult>;
|
|
94
|
+
send(template: string, options: ChannelSendOptions): Promise<ChannelSendResult>;
|
|
114
95
|
/**
|
|
115
96
|
* Ingest a post-send provider outcome (bounce / complaint / …).
|
|
116
97
|
* Hard bounce auto-adds suppression. Console projects the ledger — never
|
|
@@ -126,18 +107,14 @@ export interface ChannelRuntime {
|
|
|
126
107
|
*
|
|
127
108
|
* @param options - Templates + drivers + catalog
|
|
128
109
|
*/
|
|
129
|
-
export function createChannelRuntime(
|
|
130
|
-
options: CreateChannelRuntimeOptions = {},
|
|
131
|
-
): ChannelRuntime {
|
|
110
|
+
export function createChannelRuntime(options: CreateChannelRuntimeOptions = {}): ChannelRuntime {
|
|
132
111
|
const templates = new Map<string, ChannelTemplateDecl>();
|
|
133
112
|
for (const t of options.templates ?? []) {
|
|
134
113
|
templates.set(t.name, t);
|
|
135
114
|
}
|
|
136
115
|
const suppression =
|
|
137
116
|
options.suppression ??
|
|
138
|
-
createSuppressionStore(
|
|
139
|
-
options.consent ? { consent: options.consent } : {},
|
|
140
|
-
);
|
|
117
|
+
createSuppressionStore(options.consent ? { consent: options.consent } : {});
|
|
141
118
|
const receipts = options.receipts ?? createReceiptLedger();
|
|
142
119
|
const costs = options.costs ?? { ...DEFAULT_MEDIUM_COSTS };
|
|
143
120
|
const catalog = options.catalog ?? {};
|
|
@@ -151,8 +128,7 @@ export function createChannelRuntime(
|
|
|
151
128
|
data: Readonly<Record<string, unknown>>,
|
|
152
129
|
): { subject?: string; text?: string; html?: string } {
|
|
153
130
|
const byLocale = catalog[template];
|
|
154
|
-
const entry =
|
|
155
|
-
byLocale?.[locale] ?? byLocale?.[defaultLocale] ?? byLocale?.en;
|
|
131
|
+
const entry = byLocale?.[locale] ?? byLocale?.[defaultLocale] ?? byLocale?.en;
|
|
156
132
|
if (!entry) {
|
|
157
133
|
return {
|
|
158
134
|
subject: template,
|
|
@@ -161,9 +137,7 @@ export function createChannelRuntime(
|
|
|
161
137
|
}
|
|
162
138
|
const interpolate = (s: string | undefined): string | undefined => {
|
|
163
139
|
if (s === undefined) return undefined;
|
|
164
|
-
return s.replace(/\{\{(\w+)\}\}/g, (_, key: string) =>
|
|
165
|
-
String(data[key] ?? ""),
|
|
166
|
-
);
|
|
140
|
+
return s.replace(/\{\{(\w+)\}\}/g, (_, key: string) => String(data[key] ?? ""));
|
|
167
141
|
};
|
|
168
142
|
return {
|
|
169
143
|
subject: interpolate(entry.subject),
|
|
@@ -172,10 +146,7 @@ export function createChannelRuntime(
|
|
|
172
146
|
};
|
|
173
147
|
}
|
|
174
148
|
|
|
175
|
-
function driversFor(
|
|
176
|
-
medium: ChannelMedium,
|
|
177
|
-
via?: readonly string[],
|
|
178
|
-
): ChannelDriver[] {
|
|
149
|
+
function driversFor(medium: ChannelMedium, via?: readonly string[]): ChannelDriver[] {
|
|
179
150
|
let list = drivers;
|
|
180
151
|
if (via && via.length > 0) {
|
|
181
152
|
list = via
|
|
@@ -184,13 +155,11 @@ export function createChannelRuntime(
|
|
|
184
155
|
}
|
|
185
156
|
return list.filter((d) => {
|
|
186
157
|
if (medium === "email" || medium === "any") {
|
|
187
|
-
return
|
|
188
|
-
d.channel?.mediums.includes("any")
|
|
158
|
+
return (
|
|
159
|
+
!!d.transport || d.channel?.mediums.includes(medium) || d.channel?.mediums.includes("any")
|
|
160
|
+
);
|
|
189
161
|
}
|
|
190
|
-
return (
|
|
191
|
-
d.channel?.mediums.includes(medium) ||
|
|
192
|
-
d.channel?.mediums.includes("any")
|
|
193
|
-
);
|
|
162
|
+
return d.channel?.mediums.includes(medium) || d.channel?.mediums.includes("any");
|
|
194
163
|
});
|
|
195
164
|
}
|
|
196
165
|
|
|
@@ -202,9 +171,7 @@ export function createChannelRuntime(
|
|
|
202
171
|
const transports: Transport[] = [];
|
|
203
172
|
for (const d of chain) {
|
|
204
173
|
if (!d.transport) continue;
|
|
205
|
-
const t = options.retry
|
|
206
|
-
? new RetryTransport(d.transport)
|
|
207
|
-
: d.transport;
|
|
174
|
+
const t = options.retry ? new RetryTransport(d.transport) : d.transport;
|
|
208
175
|
transports.push(t);
|
|
209
176
|
}
|
|
210
177
|
if (transports.length === 0) {
|
|
@@ -215,9 +182,7 @@ export function createChannelRuntime(
|
|
|
215
182
|
const fallback = new FallbackTransport(transports, {
|
|
216
183
|
onFallback(failedIndex, error) {
|
|
217
184
|
const provider =
|
|
218
|
-
transports[failedIndex]?.provider ??
|
|
219
|
-
chain[failedIndex]?.id ??
|
|
220
|
-
`driver-${failedIndex}`;
|
|
185
|
+
transports[failedIndex]?.provider ?? chain[failedIndex]?.id ?? `driver-${failedIndex}`;
|
|
221
186
|
recorded.push({ provider, error });
|
|
222
187
|
attempts.push({
|
|
223
188
|
driverId: provider,
|
|
@@ -317,9 +282,7 @@ export function createChannelRuntime(
|
|
|
317
282
|
};
|
|
318
283
|
}
|
|
319
284
|
|
|
320
|
-
function classifySendFailure(
|
|
321
|
-
attempts: readonly ChannelAttempt[],
|
|
322
|
-
): DeliveryOutcomeState {
|
|
285
|
+
function classifySendFailure(attempts: readonly ChannelAttempt[]): DeliveryOutcomeState {
|
|
323
286
|
const err = attempts
|
|
324
287
|
.map((a) => a.error ?? "")
|
|
325
288
|
.join(" ")
|
|
@@ -344,14 +307,9 @@ export function createChannelRuntime(
|
|
|
344
307
|
const existing = receipts.byMessageId(input.messageId);
|
|
345
308
|
if (input.state === "hard-bounce") {
|
|
346
309
|
const subject = input.to ?? existing?.to;
|
|
347
|
-
const medium = (input.medium ??
|
|
348
|
-
existing?.medium ??
|
|
349
|
-
"email") as ChannelMedium;
|
|
310
|
+
const medium = (input.medium ?? existing?.medium ?? "email") as ChannelMedium;
|
|
350
311
|
if (subject) {
|
|
351
|
-
suppression.addPriorBounce(
|
|
352
|
-
subject,
|
|
353
|
-
medium === "any" ? "email" : medium,
|
|
354
|
-
);
|
|
312
|
+
suppression.addPriorBounce(subject, medium === "any" ? "email" : medium);
|
|
355
313
|
}
|
|
356
314
|
}
|
|
357
315
|
|
|
@@ -384,15 +342,12 @@ export function createChannelRuntime(
|
|
|
384
342
|
throw new Error(`channel: unknown template "${template}"`);
|
|
385
343
|
}
|
|
386
344
|
const medium = decl.medium;
|
|
387
|
-
const checkMedium: ChannelMedium =
|
|
388
|
-
medium === "any" ? "email" : medium;
|
|
345
|
+
const checkMedium: ChannelMedium = medium === "any" ? "email" : medium;
|
|
389
346
|
|
|
390
347
|
const suppressed = suppression.isSuppressed(opts.to, checkMedium);
|
|
391
348
|
if (suppressed.suppressed) {
|
|
392
349
|
const status: DeliveryStatus =
|
|
393
|
-
suppressed.reason === "opted-out"
|
|
394
|
-
? "suppressed/opted-out"
|
|
395
|
-
: "suppressed/prior-bounce";
|
|
350
|
+
suppressed.reason === "opted-out" ? "suppressed/opted-out" : "suppressed/prior-bounce";
|
|
396
351
|
const resolved = resolveLocale({
|
|
397
352
|
locale: opts.locale,
|
|
398
353
|
profileLocale: opts.profileLocale,
|
|
@@ -409,10 +364,7 @@ export function createChannelRuntime(
|
|
|
409
364
|
status,
|
|
410
365
|
attempts: [],
|
|
411
366
|
at: now(),
|
|
412
|
-
error:
|
|
413
|
-
suppressed.reason === "opted-out"
|
|
414
|
-
? "opted out"
|
|
415
|
-
: "prior hard bounce",
|
|
367
|
+
error: suppressed.reason === "opted-out" ? "opted out" : "prior hard bounce",
|
|
416
368
|
};
|
|
417
369
|
receipts.record(receipt);
|
|
418
370
|
return {
|
|
@@ -469,8 +421,7 @@ export function createChannelRuntime(
|
|
|
469
421
|
|
|
470
422
|
let status: DeliveryStatus;
|
|
471
423
|
if (result.ok) {
|
|
472
|
-
status =
|
|
473
|
-
attempts.filter((a) => !a.ok).length > 0 ? "fallback" : "sent";
|
|
424
|
+
status = attempts.filter((a) => !a.ok).length > 0 ? "fallback" : "sent";
|
|
474
425
|
} else {
|
|
475
426
|
status = classifySendFailure(attempts);
|
|
476
427
|
}
|
|
@@ -490,7 +441,10 @@ export function createChannelRuntime(
|
|
|
490
441
|
...(result.ok
|
|
491
442
|
? {}
|
|
492
443
|
: {
|
|
493
|
-
error: attempts
|
|
444
|
+
error: attempts
|
|
445
|
+
.map((a) => a.error)
|
|
446
|
+
.filter(Boolean)
|
|
447
|
+
.join("; "),
|
|
494
448
|
}),
|
|
495
449
|
});
|
|
496
450
|
|
|
@@ -5,11 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { ChannelMedium } from "../../manifest/types.ts";
|
|
8
|
-
import {
|
|
9
|
-
createConsentStore,
|
|
10
|
-
type ConsentStore,
|
|
11
|
-
type OptOut,
|
|
12
|
-
} from "./consent.ts";
|
|
8
|
+
import { createConsentStore, type ConsentStore, type OptOut } from "./consent.ts";
|
|
13
9
|
|
|
14
10
|
/** Why an address is suppressed. */
|
|
15
11
|
export type SuppressionReason = "opted-out" | "prior-bounce";
|
|
@@ -35,9 +31,11 @@ export interface SuppressionStore {
|
|
|
35
31
|
isSuppressed(
|
|
36
32
|
subject: string,
|
|
37
33
|
medium: ChannelMedium,
|
|
38
|
-
):
|
|
39
|
-
readonly suppressed:
|
|
40
|
-
|
|
34
|
+
):
|
|
35
|
+
| { readonly suppressed: true; readonly reason: SuppressionReason }
|
|
36
|
+
| {
|
|
37
|
+
readonly suppressed: false;
|
|
38
|
+
};
|
|
41
39
|
/**
|
|
42
40
|
* Record an opt-out.
|
|
43
41
|
*
|
|
@@ -76,9 +74,7 @@ export function createSuppressionStore(
|
|
|
76
74
|
|
|
77
75
|
function bounceMatches(subject: string, medium: ChannelMedium): boolean {
|
|
78
76
|
return bounces.some(
|
|
79
|
-
(r) =>
|
|
80
|
-
r.subject === subject &&
|
|
81
|
-
(r.medium === "all" || r.medium === medium),
|
|
77
|
+
(r) => r.subject === subject && (r.medium === "all" || r.medium === medium),
|
|
82
78
|
);
|
|
83
79
|
}
|
|
84
80
|
|
|
@@ -97,9 +93,7 @@ export function createSuppressionStore(
|
|
|
97
93
|
consent.optOut(subject, medium);
|
|
98
94
|
},
|
|
99
95
|
addPriorBounce(subject, medium) {
|
|
100
|
-
if (
|
|
101
|
-
!bounces.some((r) => r.subject === subject && r.medium === medium)
|
|
102
|
-
) {
|
|
96
|
+
if (!bounces.some((r) => r.subject === subject && r.medium === medium)) {
|
|
103
97
|
bounces.push({ subject, medium, at: Date.now() });
|
|
104
98
|
}
|
|
105
99
|
},
|
|
@@ -9,17 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
import { describe, expect, test } from "bun:test";
|
|
11
11
|
import type { MailOptions, SendResult, Transport } from "sently";
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
openConsoleChannel,
|
|
15
|
-
type ChannelDriver,
|
|
16
|
-
} from "../drivers/index.ts";
|
|
17
|
-
import {
|
|
18
|
-
channel,
|
|
19
|
-
createChannelRuntime,
|
|
20
|
-
createConsentStore,
|
|
21
|
-
FallbackTransport,
|
|
22
|
-
} from "./channel.ts";
|
|
12
|
+
import { createChannelInbox, openConsoleChannel, type ChannelDriver } from "../drivers/index.ts";
|
|
13
|
+
import { channel, createChannelRuntime, createConsentStore, FallbackTransport } from "./channel.ts";
|
|
23
14
|
|
|
24
15
|
/** Create a sently-compatible transport that always fails. */
|
|
25
16
|
function failingTransport(provider: string): Transport {
|
|
@@ -56,10 +47,7 @@ function addressToString(input: MailOptions["to"]): string {
|
|
|
56
47
|
return input.address;
|
|
57
48
|
}
|
|
58
49
|
|
|
59
|
-
function driverFromTransport(
|
|
60
|
-
id: ChannelDriver["id"],
|
|
61
|
-
transport: Transport,
|
|
62
|
-
): ChannelDriver {
|
|
50
|
+
function driverFromTransport(id: ChannelDriver["id"], transport: Transport): ChannelDriver {
|
|
63
51
|
return { id, transport };
|
|
64
52
|
}
|
|
65
53
|
|
|
@@ -113,9 +101,7 @@ describe("fallback chain records both attempts", () => {
|
|
|
113
101
|
|
|
114
102
|
expect(result.ok).toBe(true);
|
|
115
103
|
expect(result.attempts.length).toBeGreaterThanOrEqual(2);
|
|
116
|
-
expect(result.attempts.some((a) => a.driverId === "smtp" && !a.ok)).toBe(
|
|
117
|
-
true,
|
|
118
|
-
);
|
|
104
|
+
expect(result.attempts.some((a) => a.driverId === "smtp" && !a.ok)).toBe(true);
|
|
119
105
|
expect(result.attempts.some((a) => a.ok)).toBe(true);
|
|
120
106
|
|
|
121
107
|
const receipts = runtime.receipts.forTemplate("booking-confirmed");
|
|
@@ -125,10 +111,7 @@ describe("fallback chain records both attempts", () => {
|
|
|
125
111
|
});
|
|
126
112
|
|
|
127
113
|
test("sently FallbackTransport is the same interface", async () => {
|
|
128
|
-
const fb = new FallbackTransport([
|
|
129
|
-
failingTransport("a"),
|
|
130
|
-
okTransport("b"),
|
|
131
|
-
]);
|
|
114
|
+
const fb = new FallbackTransport([failingTransport("a"), okTransport("b")]);
|
|
132
115
|
const r = await fb.send({
|
|
133
116
|
from: "a@b.c",
|
|
134
117
|
to: "d@e.f",
|
|
@@ -168,9 +151,7 @@ describe("consent and i18n", () => {
|
|
|
168
151
|
});
|
|
169
152
|
const blocked = await runtime.send("news", { to: "bounce@example.com" });
|
|
170
153
|
expect(blocked.ok).toBe(false);
|
|
171
|
-
expect(runtime.receipts.all().at(-1)!.status).toBe(
|
|
172
|
-
"suppressed/prior-bounce",
|
|
173
|
-
);
|
|
154
|
+
expect(runtime.receipts.all().at(-1)!.status).toBe("suppressed/prior-bounce");
|
|
174
155
|
});
|
|
175
156
|
|
|
176
157
|
test("locale chain is recorded on the receipt", async () => {
|
|
@@ -192,9 +173,7 @@ describe("consent and i18n", () => {
|
|
|
192
173
|
test("locale selects catalog body", async () => {
|
|
193
174
|
const inbox = createChannelInbox();
|
|
194
175
|
const runtime = createChannelRuntime({
|
|
195
|
-
templates: [
|
|
196
|
-
channel.template("hello", { medium: "email", locales: ["en", "ar"] }),
|
|
197
|
-
],
|
|
176
|
+
templates: [channel.template("hello", { medium: "email", locales: ["en", "ar"] })],
|
|
198
177
|
drivers: [openConsoleChannel({ inbox })],
|
|
199
178
|
catalog: {
|
|
200
179
|
hello: {
|
|
@@ -213,9 +192,7 @@ describe("console driver", () => {
|
|
|
213
192
|
test("dev inbox captures every medium", async () => {
|
|
214
193
|
const inbox = createChannelInbox();
|
|
215
194
|
const runtime = createChannelRuntime({
|
|
216
|
-
templates: [
|
|
217
|
-
channel.template("sms-otp", { medium: "sms" }),
|
|
218
|
-
],
|
|
195
|
+
templates: [channel.template("sms-otp", { medium: "sms" })],
|
|
219
196
|
drivers: [openConsoleChannel({ inbox })],
|
|
220
197
|
catalog: {
|
|
221
198
|
"sms-otp": { en: { text: "code {{code}}" } },
|
package/src/elements/channel.ts
CHANGED
|
@@ -19,9 +19,7 @@ export type {
|
|
|
19
19
|
ChannelTemplateOptions,
|
|
20
20
|
} from "./channel/declare.ts";
|
|
21
21
|
|
|
22
|
-
export {
|
|
23
|
-
createChannelRuntime,
|
|
24
|
-
} from "./channel/runtime.ts";
|
|
22
|
+
export { createChannelRuntime } from "./channel/runtime.ts";
|
|
25
23
|
export type {
|
|
26
24
|
ChannelRuntime,
|
|
27
25
|
ChannelSendOptions,
|
|
@@ -29,11 +27,7 @@ export type {
|
|
|
29
27
|
TemplateCatalog,
|
|
30
28
|
} from "./channel/runtime.ts";
|
|
31
29
|
|
|
32
|
-
export {
|
|
33
|
-
createConsentStore,
|
|
34
|
-
type ConsentStore,
|
|
35
|
-
type OptOut,
|
|
36
|
-
} from "./channel/consent.ts";
|
|
30
|
+
export { createConsentStore, type ConsentStore, type OptOut } from "./channel/consent.ts";
|
|
37
31
|
|
|
38
32
|
export {
|
|
39
33
|
createSuppressionStore,
|
|
@@ -81,12 +75,7 @@ export {
|
|
|
81
75
|
type MediumCosts,
|
|
82
76
|
} from "./channel/costs.ts";
|
|
83
77
|
|
|
84
|
-
export {
|
|
85
|
-
CHANNEL_PII_MASK,
|
|
86
|
-
maskEmail,
|
|
87
|
-
maskPhone,
|
|
88
|
-
maskRecipient,
|
|
89
|
-
} from "./channel/mask.ts";
|
|
78
|
+
export { CHANNEL_PII_MASK, maskEmail, maskPhone, maskRecipient } from "./channel/mask.ts";
|
|
90
79
|
|
|
91
80
|
export {
|
|
92
81
|
domainFromFrom,
|
|
@@ -50,10 +50,7 @@ export class ClockResourceNotFoundError extends Error {
|
|
|
50
50
|
* @param store - Cron store
|
|
51
51
|
* @param name - Cron name
|
|
52
52
|
*/
|
|
53
|
-
export async function pauseCron(
|
|
54
|
-
store: CronStore,
|
|
55
|
-
name: string,
|
|
56
|
-
): Promise<CronRow> {
|
|
53
|
+
export async function pauseCron(store: CronStore, name: string): Promise<CronRow> {
|
|
57
54
|
const row = await store.get(name);
|
|
58
55
|
if (!row) throw new ClockResourceNotFoundError("cron", name);
|
|
59
56
|
const next: CronRow = { ...row, status: "paused" };
|
|
@@ -74,19 +71,14 @@ export interface EditScheduleInput {
|
|
|
74
71
|
* @param store - Cron store
|
|
75
72
|
* @param input - Name + override cron and/or every
|
|
76
73
|
*/
|
|
77
|
-
export async function editSchedule(
|
|
78
|
-
store: CronStore,
|
|
79
|
-
input: EditScheduleInput,
|
|
80
|
-
): Promise<CronRow> {
|
|
74
|
+
export async function editSchedule(store: CronStore, input: EditScheduleInput): Promise<CronRow> {
|
|
81
75
|
const row = await store.get(input.name);
|
|
82
76
|
if (!row) throw new ClockResourceNotFoundError("cron", input.name);
|
|
83
77
|
if (!row.overridable) {
|
|
84
78
|
throw new ScheduleNotOverridableError(input.name);
|
|
85
79
|
}
|
|
86
|
-
const overrideCron =
|
|
87
|
-
|
|
88
|
-
const overrideEvery =
|
|
89
|
-
input.every !== undefined ? input.every : row.overrideEvery;
|
|
80
|
+
const overrideCron = input.cron !== undefined ? input.cron : row.overrideCron;
|
|
81
|
+
const overrideEvery = input.every !== undefined ? input.every : row.overrideEvery;
|
|
90
82
|
const next: CronRow = {
|
|
91
83
|
...row,
|
|
92
84
|
overrideCron,
|
|
@@ -125,9 +117,7 @@ export interface WakeEarlyResult {
|
|
|
125
117
|
*
|
|
126
118
|
* @param options - Journal, run id, optional flow resolver
|
|
127
119
|
*/
|
|
128
|
-
export async function wakeEarly(
|
|
129
|
-
options: WakeEarlyOptions,
|
|
130
|
-
): Promise<WakeEarlyResult> {
|
|
120
|
+
export async function wakeEarly(options: WakeEarlyOptions): Promise<WakeEarlyResult> {
|
|
131
121
|
const now = options.now ?? (() => Date.now());
|
|
132
122
|
const t = now();
|
|
133
123
|
const run = await options.journal.get(options.runId);
|
|
@@ -136,9 +126,7 @@ export async function wakeEarly(
|
|
|
136
126
|
}
|
|
137
127
|
|
|
138
128
|
const entries = run.entries.map((e) =>
|
|
139
|
-
e.kind === "sleep" && e.wakeAt === run.wakeAt
|
|
140
|
-
? { ...e, wakeAt: t }
|
|
141
|
-
: e,
|
|
129
|
+
e.kind === "sleep" && e.wakeAt === run.wakeAt ? { ...e, wakeAt: t } : e,
|
|
142
130
|
);
|
|
143
131
|
await options.journal.put({
|
|
144
132
|
...run,
|
|
@@ -44,9 +44,7 @@ export interface ClockDecl {
|
|
|
44
44
|
*/
|
|
45
45
|
export function clock(name: string, options: ClockOptions = {}): ClockDecl {
|
|
46
46
|
if (!options.cron && !options.every) {
|
|
47
|
-
throw new TypeError(
|
|
48
|
-
`clock("${name}"): require cron or every`,
|
|
49
|
-
);
|
|
47
|
+
throw new TypeError(`clock("${name}"): require cron or every`);
|
|
50
48
|
}
|
|
51
49
|
return {
|
|
52
50
|
name,
|
|
@@ -134,11 +134,7 @@ function classifyLocalInstant(
|
|
|
134
134
|
for (let offsetMin = -14 * 60; offsetMin <= 14 * 60; offsetMin += 15) {
|
|
135
135
|
const utc = base + hour * 3_600_000 + minute * 60_000 - offsetMin * 60_000;
|
|
136
136
|
const local = formatInZone(utc, timezone);
|
|
137
|
-
if (
|
|
138
|
-
local.date === dateStr &&
|
|
139
|
-
local.hour === hour &&
|
|
140
|
-
local.minute === minute
|
|
141
|
-
) {
|
|
137
|
+
if (local.date === dateStr && local.hour === hour && local.minute === minute) {
|
|
142
138
|
if (!matches.includes(utc)) matches.push(utc);
|
|
143
139
|
}
|
|
144
140
|
}
|
|
@@ -7,11 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { AnyFlowDef } from "../../kernel/flow.ts";
|
|
10
|
-
import {
|
|
11
|
-
createFxContext,
|
|
12
|
-
type CreateFxOptions,
|
|
13
|
-
type Fx,
|
|
14
|
-
} from "../../kernel/fx.ts";
|
|
10
|
+
import { createFxContext, type CreateFxOptions, type Fx } from "../../kernel/fx.ts";
|
|
15
11
|
import {
|
|
16
12
|
createJournal,
|
|
17
13
|
isJournalSuspend,
|
|
@@ -51,10 +47,7 @@ export interface RunDurableOptions {
|
|
|
51
47
|
/** Injectable clock. */
|
|
52
48
|
readonly now?: () => number;
|
|
53
49
|
/** Extra fx options (secrets, store runtime, …). */
|
|
54
|
-
readonly fx?: Omit<
|
|
55
|
-
CreateFxOptions,
|
|
56
|
-
"flow" | "effects" | "capability" | "now" | "journal"
|
|
57
|
-
>;
|
|
50
|
+
readonly fx?: Omit<CreateFxOptions, "flow" | "effects" | "capability" | "now" | "journal">;
|
|
58
51
|
}
|
|
59
52
|
|
|
60
53
|
/**
|
|
@@ -73,13 +66,8 @@ export async function runDurable<O = unknown>(
|
|
|
73
66
|
|
|
74
67
|
if (options.runId) {
|
|
75
68
|
const existing = await options.journalStore.get(options.runId);
|
|
76
|
-
if (
|
|
77
|
-
existing
|
|
78
|
-
now() < existing.wakeAt
|
|
79
|
-
) {
|
|
80
|
-
const sleepEntry = [...existing.entries]
|
|
81
|
-
.reverse()
|
|
82
|
-
.find((e) => e.kind === "sleep");
|
|
69
|
+
if (existing?.wakeAt !== undefined && now() < existing.wakeAt) {
|
|
70
|
+
const sleepEntry = [...existing.entries].reverse().find((e) => e.kind === "sleep");
|
|
83
71
|
return {
|
|
84
72
|
status: "sleeping",
|
|
85
73
|
runId: existing.id,
|
|
@@ -7,10 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { parseDurationMs } from "./duration.ts";
|
|
9
9
|
import { effectiveSchedule, type CronRow } from "./reconcile.ts";
|
|
10
|
-
import {
|
|
11
|
-
countMissedOccurrences,
|
|
12
|
-
previousOccurrence,
|
|
13
|
-
} from "./schedule.ts";
|
|
10
|
+
import { countMissedOccurrences, previousOccurrence } from "./schedule.ts";
|
|
14
11
|
|
|
15
12
|
/** Catch-up policy matching ClockRuntime physics. */
|
|
16
13
|
export type CatchUpPolicy = "one";
|
|
@@ -39,9 +36,7 @@ export interface CronHealth {
|
|
|
39
36
|
*/
|
|
40
37
|
export function cronHealth(row: CronRow, now: number): CronHealth {
|
|
41
38
|
const overdue = isOverdue(row, now);
|
|
42
|
-
const missedRuns = overdue
|
|
43
|
-
? countMissedFromRow(row, now)
|
|
44
|
-
: 0;
|
|
39
|
+
const missedRuns = overdue ? countMissedFromRow(row, now) : 0;
|
|
45
40
|
const driftMs = computeDrift(row, now);
|
|
46
41
|
|
|
47
42
|
return {
|
|
@@ -33,12 +33,7 @@ export interface LeaseStore {
|
|
|
33
33
|
* @param now - Current epoch-ms
|
|
34
34
|
* @param leaseMs - Lease TTL
|
|
35
35
|
*/
|
|
36
|
-
acquireLease?(
|
|
37
|
-
name: string,
|
|
38
|
-
instanceId: string,
|
|
39
|
-
now: number,
|
|
40
|
-
leaseMs: number,
|
|
41
|
-
): Promise<boolean>;
|
|
36
|
+
acquireLease?(name: string, instanceId: string, now: number, leaseMs: number): Promise<boolean>;
|
|
42
37
|
}
|
|
43
38
|
|
|
44
39
|
/** Options for {@link tryAcquireLease}. */
|
|
@@ -63,9 +58,7 @@ export interface AcquireLeaseOptions {
|
|
|
63
58
|
*
|
|
64
59
|
* @param options - Name, instance, clock, store
|
|
65
60
|
*/
|
|
66
|
-
export async function tryAcquireLease(
|
|
67
|
-
options: AcquireLeaseOptions,
|
|
68
|
-
): Promise<boolean> {
|
|
61
|
+
export async function tryAcquireLease(options: AcquireLeaseOptions): Promise<boolean> {
|
|
69
62
|
if (options.store.acquireLease) {
|
|
70
63
|
return options.store.acquireLease(
|
|
71
64
|
options.name,
|