okengine 0.1.4
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 +91 -0
- package/README.md +327 -0
- package/docs/spec/complete-example.md +1188 -0
- package/docs/spec/console.md +739 -0
- package/docs/spec/four-applications.md +1188 -0
- package/docs/spec/unified-theory.md +489 -0
- package/package.json +152 -0
- package/spec/manifest.v1.schema.json +624 -0
- package/src/auth/api-keys.ts +168 -0
- package/src/auth/attenuation.ts +116 -0
- package/src/auth/auth.test.ts +273 -0
- package/src/auth/cross-plane.ts +98 -0
- package/src/auth/index.ts +112 -0
- package/src/auth/invites.ts +66 -0
- package/src/auth/invoke-as.ts +49 -0
- package/src/auth/operator.ts +178 -0
- package/src/auth/planes.ts +102 -0
- package/src/auth/plugin.ts +44 -0
- package/src/auth/roles.ts +84 -0
- package/src/auth/sessions.ts +418 -0
- package/src/auth/tables.ts +131 -0
- package/src/cli/args.ts +50 -0
- package/src/cli/branch.ts +75 -0
- package/src/cli/build.ts +86 -0
- package/src/cli/client-add.test.ts +140 -0
- package/src/cli/client-add.ts +164 -0
- package/src/cli/completion.test.ts +58 -0
- package/src/cli/completion.ts +296 -0
- package/src/cli/dev-app-runner.ts +77 -0
- package/src/cli/dev.test.ts +435 -0
- package/src/cli/dev.ts +594 -0
- package/src/cli/doc-drift.test.ts +146 -0
- package/src/cli/doc-drift.ts +397 -0
- package/src/cli/docker-cli.test.ts +221 -0
- package/src/cli/docker.ts +127 -0
- package/src/cli/doctor-diff-examples.ts +92 -0
- package/src/cli/doctor-diff.test.ts +91 -0
- package/src/cli/doctor-diff.ts +274 -0
- package/src/cli/doctor-pii.test.ts +98 -0
- package/src/cli/doctor-pii.ts +151 -0
- package/src/cli/doctor.test.ts +150 -0
- package/src/cli/doctor.ts +223 -0
- package/src/cli/eval.ts +122 -0
- package/src/cli/exit.ts +19 -0
- package/src/cli/gates-list.test.ts +47 -0
- package/src/cli/gates-list.ts +84 -0
- package/src/cli/images.ts +277 -0
- package/src/cli/index.ts +99 -0
- package/src/cli/json-out.test.ts +129 -0
- package/src/cli/load-config.ts +71 -0
- package/src/cli/mcp-from-console.ts +50 -0
- package/src/cli/privacy-erase.ts +73 -0
- package/src/cli/registry.ts +455 -0
- package/src/cli/safe-defaults.test.ts +92 -0
- package/src/cli/schema.ts +196 -0
- package/src/cli/stack.ts +82 -0
- package/src/cli/start.ts +106 -0
- package/src/cli/upgrade.ts +105 -0
- package/src/cli/vault-cmd.ts +140 -0
- package/src/client/budget-entry.ts +34 -0
- package/src/client/budget.test.ts +17 -0
- package/src/client/create.test.ts +159 -0
- package/src/client/create.ts +152 -0
- package/src/client/errors.ts +60 -0
- package/src/client/index.ts +35 -0
- package/src/client/notes-contract.test.ts +289 -0
- package/src/client/transport.test.ts +123 -0
- package/src/client/transport.ts +254 -0
- package/src/client/types.ts +282 -0
- package/src/compiler/aot.test.ts +248 -0
- package/src/compiler/aot.ts +182 -0
- package/src/compiler/differential.test.ts +241 -0
- package/src/compiler/dynamic.ts +54 -0
- package/src/compiler/effects-infer.ts +541 -0
- package/src/compiler/emit.ts +40 -0
- package/src/compiler/extract.test.ts +177 -0
- package/src/compiler/extract.ts +1368 -0
- package/src/compiler/fixtures/raw/raw-unannotated.ts +28 -0
- package/src/compiler/fixtures/skyport/oke.config.ts +24 -0
- package/src/compiler/fixtures/skyport/src/ai.ts +15 -0
- package/src/compiler/fixtures/skyport/src/app.ts +10 -0
- package/src/compiler/fixtures/skyport/src/channels.ts +6 -0
- package/src/compiler/fixtures/skyport/src/core.ts +3 -0
- package/src/compiler/fixtures/skyport/src/flows/bookings/index.ts +57 -0
- package/src/compiler/fixtures/skyport/src/flows/bookings/signals.ts +11 -0
- package/src/compiler/fixtures/skyport/src/flows/payments/index.ts +14 -0
- package/src/compiler/fixtures/skyport/src/flows/support/index.ts +20 -0
- package/src/compiler/fixtures/skyport/src/gates.ts +14 -0
- package/src/compiler/fixtures/skyport/src/journeys.ts +9 -0
- package/src/compiler/fixtures/skyport/src/schema.ts +3 -0
- package/src/compiler/fixtures/skyport/src/vault.ts +6 -0
- package/src/compiler/fixtures/skyport.expected.json +241 -0
- package/src/compiler/fixtures/triggers/five-triggers.ts +69 -0
- package/src/compiler/http-parse.ts +214 -0
- package/src/compiler/index.ts +67 -0
- package/src/compiler/response.ts +89 -0
- package/src/compiler/sucrose.ts +238 -0
- package/src/config/index.ts +167 -0
- package/src/console/budget.test.ts +73 -0
- package/src/console/index.ts +30 -0
- package/src/console/server/access.test.ts +370 -0
- package/src/console/server/access.ts +777 -0
- package/src/console/server/ai.test.ts +172 -0
- package/src/console/server/ai.ts +632 -0
- package/src/console/server/app.ts +294 -0
- package/src/console/server/auth-rate.test.ts +81 -0
- package/src/console/server/auth-rate.ts +72 -0
- package/src/console/server/bind.ts +25 -0
- package/src/console/server/channels.test.ts +179 -0
- package/src/console/server/channels.ts +469 -0
- package/src/console/server/claim.test.ts +66 -0
- package/src/console/server/claim.ts +134 -0
- package/src/console/server/clock.test.ts +239 -0
- package/src/console/server/clock.ts +390 -0
- package/src/console/server/console.test.ts +338 -0
- package/src/console/server/diff.test.ts +299 -0
- package/src/console/server/diff.ts +352 -0
- package/src/console/server/dry-run.audit.test.ts +118 -0
- package/src/console/server/flows-invoke.test.ts +148 -0
- package/src/console/server/flows.ts +3633 -0
- package/src/console/server/gates.test.ts +360 -0
- package/src/console/server/gates.ts +869 -0
- package/src/console/server/index.ts +141 -0
- package/src/console/server/live.test.ts +30 -0
- package/src/console/server/live.ts +109 -0
- package/src/console/server/plugin.ts +50 -0
- package/src/console/server/plugins.test.ts +131 -0
- package/src/console/server/plugins.ts +429 -0
- package/src/console/server/runs-pii.ts +98 -0
- package/src/console/server/security-headers.ts +97 -0
- package/src/console/server/security.gate.test.ts +286 -0
- package/src/console/server/serve.ts +319 -0
- package/src/console/server/signals.test.ts +88 -0
- package/src/console/server/signals.ts +282 -0
- package/src/console/server/state.ts +982 -0
- package/src/console/server/store.test.ts +233 -0
- package/src/console/server/store.ts +861 -0
- package/src/console/server/structural.ts +95 -0
- package/src/console/server/vault.test.ts +251 -0
- package/src/console/server/vault.ts +430 -0
- package/src/console/ui/access/AccessA11yView.tsx +237 -0
- package/src/console/ui/access/a11y.test.tsx +122 -0
- package/src/console/ui/access/acknowledgement.test.ts +13 -0
- package/src/console/ui/access/acknowledgement.ts +35 -0
- package/src/console/ui/access/blast-radius.test.ts +26 -0
- package/src/console/ui/access/blast-radius.ts +40 -0
- package/src/console/ui/access/confirmation.test.ts +38 -0
- package/src/console/ui/access/confirmation.ts +36 -0
- package/src/console/ui/access/fixture.ts +203 -0
- package/src/console/ui/access/grantable.test.ts +17 -0
- package/src/console/ui/access/grantable.ts +36 -0
- package/src/console/ui/access/hygiene.test.ts +12 -0
- package/src/console/ui/access/hygiene.ts +39 -0
- package/src/console/ui/access/index.ts +54 -0
- package/src/console/ui/access/provenance.test.ts +12 -0
- package/src/console/ui/access/provenance.ts +29 -0
- package/src/console/ui/access/search.test.ts +17 -0
- package/src/console/ui/access/search.ts +72 -0
- package/src/console/ui/access/types.ts +111 -0
- package/src/console/ui/ai/AiA11yView.tsx +265 -0
- package/src/console/ui/ai/a11y.test.tsx +99 -0
- package/src/console/ui/ai/findings.ts +41 -0
- package/src/console/ui/ai/fixture.ts +182 -0
- package/src/console/ui/ai/format.ts +90 -0
- package/src/console/ui/ai/group.test.ts +37 -0
- package/src/console/ui/ai/group.ts +88 -0
- package/src/console/ui/ai/index.ts +66 -0
- package/src/console/ui/ai/promotion.test.ts +77 -0
- package/src/console/ui/ai/promotion.ts +150 -0
- package/src/console/ui/ai/search.test.ts +40 -0
- package/src/console/ui/ai/search.ts +89 -0
- package/src/console/ui/ai/types.ts +139 -0
- package/src/console/ui/architecture/ArchitectureA11yView.tsx +163 -0
- package/src/console/ui/architecture/a11y.test.tsx +97 -0
- package/src/console/ui/architecture/boundary.ts +42 -0
- package/src/console/ui/architecture/declared.ts +165 -0
- package/src/console/ui/architecture/fixture.ts +74 -0
- package/src/console/ui/architecture/index.ts +84 -0
- package/src/console/ui/architecture/layers.ts +64 -0
- package/src/console/ui/architecture/layout.ts +68 -0
- package/src/console/ui/architecture/pathologies.ts +260 -0
- package/src/console/ui/architecture/search.ts +145 -0
- package/src/console/ui/architecture/traffic.ts +102 -0
- package/src/console/ui/architecture/types.ts +144 -0
- package/src/console/ui/architecture/view.test.ts +165 -0
- package/src/console/ui/architecture/view.ts +516 -0
- package/src/console/ui/channels/ChannelsA11yView.tsx +180 -0
- package/src/console/ui/channels/a11y.test.tsx +96 -0
- package/src/console/ui/channels/confirmation.test.ts +37 -0
- package/src/console/ui/channels/confirmation.ts +27 -0
- package/src/console/ui/channels/fallback.test.ts +15 -0
- package/src/console/ui/channels/fallback.ts +20 -0
- package/src/console/ui/channels/findings.ts +38 -0
- package/src/console/ui/channels/fixture.ts +134 -0
- package/src/console/ui/channels/group.ts +25 -0
- package/src/console/ui/channels/index.ts +57 -0
- package/src/console/ui/channels/locale.test.ts +17 -0
- package/src/console/ui/channels/locale.ts +31 -0
- package/src/console/ui/channels/mask.test.ts +14 -0
- package/src/console/ui/channels/mask.ts +12 -0
- package/src/console/ui/channels/search.ts +58 -0
- package/src/console/ui/channels/taxonomy.test.ts +17 -0
- package/src/console/ui/channels/taxonomy.ts +49 -0
- package/src/console/ui/channels/types.ts +117 -0
- package/src/console/ui/clock/ClockA11yView.tsx +182 -0
- package/src/console/ui/clock/a11y.test.tsx +99 -0
- package/src/console/ui/clock/confirmation.test.ts +32 -0
- package/src/console/ui/clock/confirmation.ts +34 -0
- package/src/console/ui/clock/findings.ts +40 -0
- package/src/console/ui/clock/fixture.ts +111 -0
- package/src/console/ui/clock/health.test.ts +25 -0
- package/src/console/ui/clock/health.ts +61 -0
- package/src/console/ui/clock/index.ts +49 -0
- package/src/console/ui/clock/search.test.ts +39 -0
- package/src/console/ui/clock/search.ts +76 -0
- package/src/console/ui/clock/timeline.test.ts +44 -0
- package/src/console/ui/clock/timeline.ts +41 -0
- package/src/console/ui/clock/types.ts +78 -0
- package/src/console/ui/clock/waiting-on.test.ts +34 -0
- package/src/console/ui/clock/waiting-on.ts +83 -0
- package/src/console/ui/confirmation.unify.test.ts +92 -0
- package/src/console/ui/diff/DiffA11yView.tsx +85 -0
- package/src/console/ui/diff/a11y.test.tsx +99 -0
- package/src/console/ui/diff/fixture.ts +85 -0
- package/src/console/ui/diff/group.test.ts +58 -0
- package/src/console/ui/diff/group.ts +97 -0
- package/src/console/ui/diff/index.ts +38 -0
- package/src/console/ui/diff/search.test.ts +32 -0
- package/src/console/ui/diff/search.ts +75 -0
- package/src/console/ui/diff/summary.ts +74 -0
- package/src/console/ui/diff/types.ts +48 -0
- package/src/console/ui/dist/assets/index-B71Yl_SS.js +10 -0
- package/src/console/ui/dist/assets/panel-access-Dd37LU2c.js +64 -0
- package/src/console/ui/dist/assets/panel-ai-CC7LR6-J.js +1 -0
- package/src/console/ui/dist/assets/panel-architecture-B5b3iKCz.js +1 -0
- package/src/console/ui/dist/assets/panel-channels-CeNjTKXp.js +1 -0
- package/src/console/ui/dist/assets/panel-clock-DgFTLoHV.js +1 -0
- package/src/console/ui/dist/assets/panel-diff-DxehccqB.js +1 -0
- package/src/console/ui/dist/assets/panel-flows-BtrVn-Eg.js +45 -0
- package/src/console/ui/dist/assets/panel-gates-Z9MKRGdH.js +1 -0
- package/src/console/ui/dist/assets/panel-overview-Bd48d9km.js +1 -0
- package/src/console/ui/dist/assets/panel-plugins-DWd0TowH.js +1 -0
- package/src/console/ui/dist/assets/panel-runs-BwsWqKeB.js +1 -0
- package/src/console/ui/dist/assets/panel-signals-9najbZY2.js +1 -0
- package/src/console/ui/dist/assets/panel-store-OHkP2pDp.js +1 -0
- package/src/console/ui/dist/assets/panel-traces-tn2JoY8U.js +1 -0
- package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +1 -0
- package/src/console/ui/dist/assets/rolldown-runtime-CNC7AqOf.js +1 -0
- package/src/console/ui/dist/assets/style-Cnl7WLya.css +3 -0
- package/src/console/ui/dist/index.html +14 -0
- package/src/console/ui/flows/FlowsA11yView.tsx +155 -0
- package/src/console/ui/flows/a11y.test.tsx +105 -0
- package/src/console/ui/flows/buffer.test.ts +91 -0
- package/src/console/ui/flows/buffer.ts +118 -0
- package/src/console/ui/flows/confirmation.ts +131 -0
- package/src/console/ui/flows/contract.test.ts +89 -0
- package/src/console/ui/flows/contract.ts +353 -0
- package/src/console/ui/flows/fixture.ts +81 -0
- package/src/console/ui/flows/graph.test.ts +101 -0
- package/src/console/ui/flows/graph.ts +549 -0
- package/src/console/ui/flows/index.ts +87 -0
- package/src/console/ui/flows/save-as-test.test.ts +47 -0
- package/src/console/ui/flows/save-as-test.ts +99 -0
- package/src/console/ui/flows/search.ts +255 -0
- package/src/console/ui/flows/tiers.ts +140 -0
- package/src/console/ui/gates/GatesA11yView.tsx +192 -0
- package/src/console/ui/gates/a11y.test.tsx +116 -0
- package/src/console/ui/gates/audit.test.ts +40 -0
- package/src/console/ui/gates/audit.ts +63 -0
- package/src/console/ui/gates/denial.test.ts +36 -0
- package/src/console/ui/gates/denial.ts +51 -0
- package/src/console/ui/gates/findings.ts +19 -0
- package/src/console/ui/gates/fixture.ts +189 -0
- package/src/console/ui/gates/group.test.ts +30 -0
- package/src/console/ui/gates/group.ts +102 -0
- package/src/console/ui/gates/index.ts +47 -0
- package/src/console/ui/gates/search.test.ts +49 -0
- package/src/console/ui/gates/search.ts +115 -0
- package/src/console/ui/gates/types.ts +121 -0
- package/src/console/ui/overview/OverviewA11yView.tsx +152 -0
- package/src/console/ui/overview/a11y.test.tsx +113 -0
- package/src/console/ui/overview/busiest.ts +26 -0
- package/src/console/ui/overview/compose.ts +133 -0
- package/src/console/ui/overview/cost.test.ts +39 -0
- package/src/console/ui/overview/cost.ts +138 -0
- package/src/console/ui/overview/fixture.ts +161 -0
- package/src/console/ui/overview/golden.ts +57 -0
- package/src/console/ui/overview/index.ts +59 -0
- package/src/console/ui/overview/rank.test.ts +136 -0
- package/src/console/ui/overview/rank.ts +210 -0
- package/src/console/ui/overview/slo.test.ts +73 -0
- package/src/console/ui/overview/slo.ts +229 -0
- package/src/console/ui/overview/types.ts +121 -0
- package/src/console/ui/overview/verdict.ts +131 -0
- package/src/console/ui/plugins/PluginsA11yView.tsx +227 -0
- package/src/console/ui/plugins/a11y.test.tsx +97 -0
- package/src/console/ui/plugins/command.test.ts +22 -0
- package/src/console/ui/plugins/command.ts +27 -0
- package/src/console/ui/plugins/findings.ts +39 -0
- package/src/console/ui/plugins/fixture.ts +233 -0
- package/src/console/ui/plugins/group.test.ts +38 -0
- package/src/console/ui/plugins/group.ts +67 -0
- package/src/console/ui/plugins/index.ts +40 -0
- package/src/console/ui/plugins/search.ts +67 -0
- package/src/console/ui/plugins/types.ts +115 -0
- package/src/console/ui/runs/RunsA11yView.tsx +267 -0
- package/src/console/ui/runs/a11y.test.tsx +99 -0
- package/src/console/ui/runs/explain.test.ts +23 -0
- package/src/console/ui/runs/explain.ts +32 -0
- package/src/console/ui/runs/fixture.ts +189 -0
- package/src/console/ui/runs/group.test.ts +24 -0
- package/src/console/ui/runs/group.ts +85 -0
- package/src/console/ui/runs/histogram.test.ts +31 -0
- package/src/console/ui/runs/histogram.ts +85 -0
- package/src/console/ui/runs/index.ts +75 -0
- package/src/console/ui/runs/project.ts +122 -0
- package/src/console/ui/runs/query.test.ts +50 -0
- package/src/console/ui/runs/query.ts +283 -0
- package/src/console/ui/runs/search.test.ts +45 -0
- package/src/console/ui/runs/search.ts +141 -0
- package/src/console/ui/runs/trace-link.test.ts +30 -0
- package/src/console/ui/runs/trace-link.ts +99 -0
- package/src/console/ui/runs/types.ts +137 -0
- package/src/console/ui/shell/App.tsx +56 -0
- package/src/console/ui/shell/client.ts +1715 -0
- package/src/console/ui/shell/components/ui.tsx +110 -0
- package/src/console/ui/shell/index.html +12 -0
- package/src/console/ui/shell/layout/Shell.tsx +95 -0
- package/src/console/ui/shell/main.tsx +281 -0
- package/src/console/ui/shell/panels/Access.tsx +5 -0
- package/src/console/ui/shell/panels/Ai.tsx +5 -0
- package/src/console/ui/shell/panels/Architecture.tsx +5 -0
- package/src/console/ui/shell/panels/Channels.tsx +5 -0
- package/src/console/ui/shell/panels/Clock.tsx +5 -0
- package/src/console/ui/shell/panels/Diff.tsx +5 -0
- package/src/console/ui/shell/panels/Flows.tsx +5 -0
- package/src/console/ui/shell/panels/Gates.tsx +5 -0
- package/src/console/ui/shell/panels/Overview.tsx +6 -0
- package/src/console/ui/shell/panels/Plugins.tsx +5 -0
- package/src/console/ui/shell/panels/Runs.tsx +5 -0
- package/src/console/ui/shell/panels/Signals.tsx +5 -0
- package/src/console/ui/shell/panels/Store.tsx +5 -0
- package/src/console/ui/shell/panels/Traces.tsx +5 -0
- package/src/console/ui/shell/panels/Vault.tsx +5 -0
- package/src/console/ui/shell/panels/access/AccessPanel.tsx +813 -0
- package/src/console/ui/shell/panels/ai/AiPanel.tsx +563 -0
- package/src/console/ui/shell/panels/architecture/ArchitecturePanel.tsx +377 -0
- package/src/console/ui/shell/panels/channels/ChannelsPanel.tsx +454 -0
- package/src/console/ui/shell/panels/clock/ClockPanel.tsx +479 -0
- package/src/console/ui/shell/panels/diff/DiffPanel.tsx +275 -0
- package/src/console/ui/shell/panels/flows/ContractEditor.tsx +301 -0
- package/src/console/ui/shell/panels/flows/FlowDrawer.tsx +350 -0
- package/src/console/ui/shell/panels/flows/FlowsPanel.tsx +502 -0
- package/src/console/ui/shell/panels/gates/GatesPanel.tsx +482 -0
- package/src/console/ui/shell/panels/overview/OverviewPanel.tsx +428 -0
- package/src/console/ui/shell/panels/plugins/PluginsPanel.tsx +413 -0
- package/src/console/ui/shell/panels/runs/RunsPanel.tsx +649 -0
- package/src/console/ui/shell/panels/signals/SignalsPanel.tsx +840 -0
- package/src/console/ui/shell/panels/store/StorePanel.tsx +787 -0
- package/src/console/ui/shell/panels/traces/TracesPanel.tsx +539 -0
- package/src/console/ui/shell/panels/vault/VaultPanel.tsx +449 -0
- package/src/console/ui/shell/plugin/Sandbox.tsx +29 -0
- package/src/console/ui/shell/setup/Wizard.tsx +189 -0
- package/src/console/ui/shell/styles.css +75 -0
- package/src/console/ui/signals/SignalsA11yView.tsx +276 -0
- package/src/console/ui/signals/a11y.test.tsx +99 -0
- package/src/console/ui/signals/confirmation.test.ts +39 -0
- package/src/console/ui/signals/confirmation.ts +63 -0
- package/src/console/ui/signals/dry-run.test.ts +18 -0
- package/src/console/ui/signals/dry-run.ts +36 -0
- package/src/console/ui/signals/durable.test.ts +22 -0
- package/src/console/ui/signals/durable.ts +41 -0
- package/src/console/ui/signals/findings.ts +33 -0
- package/src/console/ui/signals/fixture.ts +242 -0
- package/src/console/ui/signals/group.test.ts +32 -0
- package/src/console/ui/signals/group.ts +49 -0
- package/src/console/ui/signals/index.ts +52 -0
- package/src/console/ui/signals/monitor.test.ts +30 -0
- package/src/console/ui/signals/monitor.ts +79 -0
- package/src/console/ui/signals/schema-form.test.ts +34 -0
- package/src/console/ui/signals/schema-form.ts +112 -0
- package/src/console/ui/signals/search.ts +102 -0
- package/src/console/ui/signals/types.ts +71 -0
- package/src/console/ui/store/StoreA11yView.tsx +199 -0
- package/src/console/ui/store/a11y.test.tsx +99 -0
- package/src/console/ui/store/cache-view.test.ts +13 -0
- package/src/console/ui/store/cache-view.ts +36 -0
- package/src/console/ui/store/confirmation.test.ts +38 -0
- package/src/console/ui/store/confirmation.ts +55 -0
- package/src/console/ui/store/dry-run.test.ts +15 -0
- package/src/console/ui/store/dry-run.ts +29 -0
- package/src/console/ui/store/fixture.ts +163 -0
- package/src/console/ui/store/group.test.ts +21 -0
- package/src/console/ui/store/group.ts +41 -0
- package/src/console/ui/store/index.ts +40 -0
- package/src/console/ui/store/search.ts +78 -0
- package/src/console/ui/store/types.ts +65 -0
- package/src/console/ui/store/will-not-fire.test.ts +19 -0
- package/src/console/ui/store/will-not-fire.ts +40 -0
- package/src/console/ui/traces/TracesA11yView.tsx +200 -0
- package/src/console/ui/traces/a11y.test.tsx +97 -0
- package/src/console/ui/traces/chain.test.ts +61 -0
- package/src/console/ui/traces/chain.ts +144 -0
- package/src/console/ui/traces/critical-path.test.ts +24 -0
- package/src/console/ui/traces/critical-path.ts +59 -0
- package/src/console/ui/traces/filter.test.ts +78 -0
- package/src/console/ui/traces/filter.ts +112 -0
- package/src/console/ui/traces/fixture.ts +126 -0
- package/src/console/ui/traces/fold.test.ts +67 -0
- package/src/console/ui/traces/fold.ts +170 -0
- package/src/console/ui/traces/index.ts +64 -0
- package/src/console/ui/traces/mini.ts +65 -0
- package/src/console/ui/traces/replay.test.ts +24 -0
- package/src/console/ui/traces/replay.ts +35 -0
- package/src/console/ui/traces/sampling.test.ts +26 -0
- package/src/console/ui/traces/sampling.ts +66 -0
- package/src/console/ui/traces/search.ts +148 -0
- package/src/console/ui/traces/tier.ts +72 -0
- package/src/console/ui/traces/types.ts +125 -0
- package/src/console/ui/tsconfig.json +20 -0
- package/src/console/ui/vault/VaultA11yView.tsx +177 -0
- package/src/console/ui/vault/a11y.test.tsx +97 -0
- package/src/console/ui/vault/blast-radius.test.ts +35 -0
- package/src/console/ui/vault/blast-radius.ts +55 -0
- package/src/console/ui/vault/confirmation.test.ts +41 -0
- package/src/console/ui/vault/confirmation.ts +39 -0
- package/src/console/ui/vault/dormant.test.ts +37 -0
- package/src/console/ui/vault/dormant.ts +28 -0
- package/src/console/ui/vault/export-safe.test.ts +30 -0
- package/src/console/ui/vault/export-safe.ts +65 -0
- package/src/console/ui/vault/fixture.ts +75 -0
- package/src/console/ui/vault/group.test.ts +25 -0
- package/src/console/ui/vault/group.ts +50 -0
- package/src/console/ui/vault/index.ts +45 -0
- package/src/console/ui/vault/search.ts +62 -0
- package/src/console/ui/vault/types.ts +57 -0
- package/src/console/ui/vite.config.ts +84 -0
- package/src/console/xss.gate.test.ts +118 -0
- package/src/docker/compose.ts +226 -0
- package/src/docker/credentials.ts +25 -0
- package/src/docker/derive.ts +83 -0
- package/src/docker/docker.test.ts +185 -0
- package/src/docker/dockerfile.integration.test.ts +170 -0
- package/src/docker/dockerfile.ts +39 -0
- package/src/docker/helpers.ts +103 -0
- package/src/docker/index.ts +52 -0
- package/src/docker/pin.ts +90 -0
- package/src/docker/recipes/index.ts +33 -0
- package/src/docker/recipes/postgres.ts +18 -0
- package/src/docker/recipes/redis.ts +18 -0
- package/src/docker/stack.integration.test.ts +140 -0
- package/src/docker/stack.ts +62 -0
- package/src/docker/types.ts +134 -0
- package/src/drivers/ai-anthropic.ts +128 -0
- package/src/drivers/ai-mock.ts +82 -0
- package/src/drivers/ai-openai-compatible.ts +139 -0
- package/src/drivers/ai-providers.test.ts +163 -0
- package/src/drivers/ai-types.ts +83 -0
- package/src/drivers/bun-native-completeness.test.ts +155 -0
- package/src/drivers/channel-console.ts +118 -0
- package/src/drivers/channel-fcm.ts +96 -0
- package/src/drivers/channel-resend.ts +27 -0
- package/src/drivers/channel-smtp.ts +33 -0
- package/src/drivers/channel-types.ts +160 -0
- package/src/drivers/channel-unifonic.ts +79 -0
- package/src/drivers/channel-wa-cloud.ts +84 -0
- package/src/drivers/channel-webpush.ts +317 -0
- package/src/drivers/conformance.test.ts +89 -0
- package/src/drivers/conformance.ts +162 -0
- package/src/drivers/fs.ts +73 -0
- package/src/drivers/index.ts +194 -0
- package/src/drivers/kv-lua.ts +222 -0
- package/src/drivers/memory.ts +472 -0
- package/src/drivers/pgvector.ts +130 -0
- package/src/drivers/postgres.ts +221 -0
- package/src/drivers/redis.ts +254 -0
- package/src/drivers/s3.ts +129 -0
- package/src/drivers/signal-engine.ts +690 -0
- package/src/drivers/signal-memory.ts +27 -0
- package/src/drivers/signal-nats.ts +131 -0
- package/src/drivers/signal-postgres.ts +1089 -0
- package/src/drivers/signal-redis.ts +275 -0
- package/src/drivers/signal-types.ts +328 -0
- package/src/drivers/sqlite.ts +53 -0
- package/src/drivers/types.ts +334 -0
- package/src/drivers/vault-dotenv-parse.ts +56 -0
- package/src/drivers/vault-env.ts +101 -0
- package/src/drivers/vault-infisical.ts +61 -0
- package/src/drivers/vault-managed.ts +42 -0
- package/src/drivers/vault-memory.ts +38 -0
- package/src/drivers/vault-openbao.ts +80 -0
- package/src/drivers/vault-sops.ts +252 -0
- package/src/drivers/vault-types.ts +84 -0
- package/src/elements/ai/declare.ts +206 -0
- package/src/elements/ai/eval.ts +113 -0
- package/src/elements/ai/pii.ts +108 -0
- package/src/elements/ai/runtime.ts +540 -0
- package/src/elements/ai/schema.ts +194 -0
- package/src/elements/ai.test.ts +393 -0
- package/src/elements/ai.ts +56 -0
- package/src/elements/channel/consent.ts +81 -0
- package/src/elements/channel/costs.test.ts +62 -0
- package/src/elements/channel/costs.ts +130 -0
- package/src/elements/channel/declare.ts +141 -0
- package/src/elements/channel/email-auth.test.ts +47 -0
- package/src/elements/channel/email-auth.ts +109 -0
- package/src/elements/channel/locale.ts +97 -0
- package/src/elements/channel/mask.test.ts +19 -0
- package/src/elements/channel/mask.ts +60 -0
- package/src/elements/channel/mime.ts +23 -0
- package/src/elements/channel/outcomes.test.ts +40 -0
- package/src/elements/channel/outcomes.ts +126 -0
- package/src/elements/channel/receipts.ts +121 -0
- package/src/elements/channel/runtime.ts +500 -0
- package/src/elements/channel/suppression.ts +136 -0
- package/src/elements/channel.test.ts +232 -0
- package/src/elements/channel.ts +109 -0
- package/src/elements/clock/actions.ts +170 -0
- package/src/elements/clock/declare.ts +58 -0
- package/src/elements/clock/dst.ts +197 -0
- package/src/elements/clock/durable.ts +140 -0
- package/src/elements/clock/duration.ts +29 -0
- package/src/elements/clock/health.test.ts +103 -0
- package/src/elements/clock/health.ts +112 -0
- package/src/elements/clock/leader.ts +127 -0
- package/src/elements/clock/reconcile.ts +241 -0
- package/src/elements/clock/runtime.ts +243 -0
- package/src/elements/clock/schedule.ts +262 -0
- package/src/elements/clock/time-travel.ts +53 -0
- package/src/elements/clock.test.ts +365 -0
- package/src/elements/clock.ts +80 -0
- package/src/elements/gate/declare.ts +134 -0
- package/src/elements/gate/permissions.ts +104 -0
- package/src/elements/gate/runtime.ts +177 -0
- package/src/elements/gate/strategies.ts +388 -0
- package/src/elements/gate.test.ts +145 -0
- package/src/elements/gate.ts +41 -0
- package/src/elements/index.ts +169 -0
- package/src/elements/signal/chaos-child.ts +47 -0
- package/src/elements/signal/declare.ts +77 -0
- package/src/elements/signal/dry-run-replay.test.ts +95 -0
- package/src/elements/signal/dry-run-write-isolation.test.ts +94 -0
- package/src/elements/signal/reconcile.test.ts +23 -0
- package/src/elements/signal/reconcile.ts +122 -0
- package/src/elements/signal/runtime.ts +94 -0
- package/src/elements/signal.test.ts +457 -0
- package/src/elements/signal.ts +26 -0
- package/src/elements/store/cache.ts +197 -0
- package/src/elements/store/classify.ts +125 -0
- package/src/elements/store/declare.ts +200 -0
- package/src/elements/store/files-policy.test.ts +31 -0
- package/src/elements/store/files-policy.ts +70 -0
- package/src/elements/store/replica.ts +68 -0
- package/src/elements/store/runtime.ts +393 -0
- package/src/elements/store/sql-condition.ts +205 -0
- package/src/elements/store/sql-session.ts +579 -0
- package/src/elements/store/table.ts +282 -0
- package/src/elements/store.test.ts +370 -0
- package/src/elements/store.ts +99 -0
- package/src/elements/vault/declare.ts +143 -0
- package/src/elements/vault/fingerprint.ts +30 -0
- package/src/elements/vault/redact.ts +52 -0
- package/src/elements/vault/runtime.ts +461 -0
- package/src/elements/vault.test.ts +296 -0
- package/src/elements/vault.ts +44 -0
- package/src/index.ts +183 -0
- package/src/kernel/adopt-routes.ts +194 -0
- package/src/kernel/app.ts +1068 -0
- package/src/kernel/auth-resolve.ts +93 -0
- package/src/kernel/boot.test.ts +173 -0
- package/src/kernel/boot.ts +439 -0
- package/src/kernel/budget-entry.ts +55 -0
- package/src/kernel/budget.test.ts +19 -0
- package/src/kernel/call.test.ts +83 -0
- package/src/kernel/capability.ts +113 -0
- package/src/kernel/dry-run.test.ts +64 -0
- package/src/kernel/dry-run.ts +171 -0
- package/src/kernel/effects.test.ts +89 -0
- package/src/kernel/effects.ts +140 -0
- package/src/kernel/errors.registry.test.ts +39 -0
- package/src/kernel/errors.ts +225 -0
- package/src/kernel/flow.test.ts +154 -0
- package/src/kernel/flow.ts +290 -0
- package/src/kernel/fx.test.ts +245 -0
- package/src/kernel/fx.ts +996 -0
- package/src/kernel/hook-timing.test.ts +72 -0
- package/src/kernel/hook-timing.ts +190 -0
- package/src/kernel/hooks.test.ts +181 -0
- package/src/kernel/hooks.ts +275 -0
- package/src/kernel/index.ts +278 -0
- package/src/kernel/journal.ts +405 -0
- package/src/kernel/journey.ts +75 -0
- package/src/kernel/on.ts +74 -0
- package/src/kernel/pipeline.test.ts +349 -0
- package/src/kernel/pipeline.ts +245 -0
- package/src/kernel/plug.ts +82 -0
- package/src/kernel/plugin/capabilities.test.ts +84 -0
- package/src/kernel/plugin/conflicts.test.ts +106 -0
- package/src/kernel/plugin/decorate.test.ts +111 -0
- package/src/kernel/plugin/scoping.test.ts +138 -0
- package/src/kernel/plugin.ts +485 -0
- package/src/kernel/rate-limit.ts +25 -0
- package/src/kernel/registry.ts +398 -0
- package/src/kernel/router.test.ts +114 -0
- package/src/kernel/router.ts +556 -0
- package/src/kernel/routing-budget.test.ts +19 -0
- package/src/kernel/run-telemetry.ts +70 -0
- package/src/kernel/triggers.ts +324 -0
- package/src/kernel/unit.ts +88 -0
- package/src/manifest/diff.test.ts +866 -0
- package/src/manifest/diff.ts +1670 -0
- package/src/manifest/fixtures/base.manifest.json +112 -0
- package/src/manifest/fixtures/skyport.excerpt.json +51 -0
- package/src/manifest/fixtures/skyport.manifest.json +153 -0
- package/src/manifest/index.ts +27 -0
- package/src/manifest/types.ts +358 -0
- package/src/manifest/undeclared.test.ts +82 -0
- package/src/manifest/undeclared.ts +65 -0
- package/src/manifest/validate.test.ts +105 -0
- package/src/manifest/validate.ts +174 -0
- package/src/mcp/authorization.ts +340 -0
- package/src/mcp/confirmation.ts +174 -0
- package/src/mcp/data.ts +90 -0
- package/src/mcp/index.ts +80 -0
- package/src/mcp/injection.gate.test.ts +176 -0
- package/src/mcp/mcp.test.ts +348 -0
- package/src/mcp/protocol.ts +172 -0
- package/src/mcp/server.ts +283 -0
- package/src/mcp/session.ts +139 -0
- package/src/mcp/tools.ts +391 -0
- package/src/plugins/catalogue.test.ts +48 -0
- package/src/plugins/catalogue.ts +113 -0
- package/src/plugins/node-import-scan.test.ts +28 -0
- package/src/plugins/node-import-scan.ts +98 -0
- package/src/plugins/supply-chain.test.ts +48 -0
- package/src/plugins/supply-chain.ts +460 -0
- package/src/release/index.ts +30 -0
- package/src/release/limits.ts +20 -0
- package/src/release/measure.ts +443 -0
- package/src/release/publish.ts +56 -0
- package/src/runs/bench.test.ts +93 -0
- package/src/runs/collect.ts +144 -0
- package/src/runs/drivers/clickhouse.ts +108 -0
- package/src/runs/drivers/files.ts +201 -0
- package/src/runs/drivers/memory.ts +94 -0
- package/src/runs/drivers/postgres.ts +110 -0
- package/src/runs/duckdb.ts +99 -0
- package/src/runs/index.ts +82 -0
- package/src/runs/outlier.ts +180 -0
- package/src/runs/parquet.ts +210 -0
- package/src/runs/privacy.ts +49 -0
- package/src/runs/runs.test.ts +324 -0
- package/src/runs/runtime.ts +229 -0
- package/src/runs/shred.ts +243 -0
- package/src/runs/telemetry.ts +10 -0
- package/src/runs/types.ts +179 -0
- package/src/runtime/bun.ts +140 -0
- package/src/runtime/cold-start.bench.ts +20 -0
- package/src/runtime/index.ts +46 -0
- package/src/runtime/primitives.ts +242 -0
- package/src/runtime/security.test.ts +170 -0
- package/src/runtime/security.ts +168 -0
- package/src/runtime/serve.test.ts +223 -0
- package/src/runtime/types.ts +217 -0
- package/src/runtime/web-standard.ts +77 -0
- package/src/test/create-test-app.test.ts +161 -0
- package/src/test/create-test-app.ts +533 -0
- package/src/test/index.ts +21 -0
- package/src/test/provisions.integration.test.ts +174 -0
- package/src/upgrade/codemods.test.ts +58 -0
- package/src/upgrade/codemods.ts +83 -0
- package/src/validation/index.ts +23 -0
- package/src/validation/standard-schema.test.ts +96 -0
- package/src/validation/standard-schema.ts +316 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Three cache tiers.
|
|
3
|
+
*
|
|
4
|
+
* - Tier 1 — automatic for live / read flows; invalidation computed from effects
|
|
5
|
+
* - Tier 2 — a flag on any flow (`cache: "5m"`)
|
|
6
|
+
* - Tier 3 — manual via `fx.cache.getOrSet`
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Effects, ResourceRef } from "../../manifest/types.ts";
|
|
10
|
+
|
|
11
|
+
/** Cache tier identifiers. */
|
|
12
|
+
export type CacheTier = 1 | 2 | 3;
|
|
13
|
+
|
|
14
|
+
/** One cached entry with its provenance. */
|
|
15
|
+
export interface CacheEntry<T = unknown> {
|
|
16
|
+
readonly tier: CacheTier;
|
|
17
|
+
readonly key: string;
|
|
18
|
+
readonly value: T;
|
|
19
|
+
/** Resources this entry was derived from (tier 1). */
|
|
20
|
+
readonly resources: readonly ResourceRef[];
|
|
21
|
+
/** Absolute expiry epoch-ms, or null for no TTL. */
|
|
22
|
+
readonly expiresAt: number | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Invalidation event produced by a write. */
|
|
26
|
+
export interface InvalidationEvent {
|
|
27
|
+
/** Resources written. */
|
|
28
|
+
readonly resources: readonly ResourceRef[];
|
|
29
|
+
/** Keys removed. */
|
|
30
|
+
readonly keys: readonly string[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Compute the tier-1 cache key for a read effect set.
|
|
35
|
+
*
|
|
36
|
+
* Format: `computed:{resource}` or `computed:{resource}/{dim}` when dims given.
|
|
37
|
+
*
|
|
38
|
+
* @param resource - Store resource ref
|
|
39
|
+
* @param dims - Optional dimension suffixes (e.g. `userId`)
|
|
40
|
+
*/
|
|
41
|
+
export function computedCacheKey(
|
|
42
|
+
resource: ResourceRef,
|
|
43
|
+
dims?: readonly string[],
|
|
44
|
+
): string {
|
|
45
|
+
if (!dims || dims.length === 0) return `computed:${resource}`;
|
|
46
|
+
return `computed:${resource}/${dims.join("/")}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Tier-1 keys implied by a read effect set.
|
|
51
|
+
*
|
|
52
|
+
* @param effects - Flow effects (must include reads)
|
|
53
|
+
* @param dimsByResource - Optional per-resource dimension suffixes
|
|
54
|
+
*/
|
|
55
|
+
export function tier1KeysForReads(
|
|
56
|
+
effects: Effects,
|
|
57
|
+
dimsByResource?: Readonly<Record<string, readonly string[]>>,
|
|
58
|
+
): string[] {
|
|
59
|
+
const reads = effects.reads ?? [];
|
|
60
|
+
return reads.map((resource) =>
|
|
61
|
+
computedCacheKey(resource, dimsByResource?.[resource]),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Resources whose tier-1 entries a write must invalidate.
|
|
67
|
+
*
|
|
68
|
+
* @param effects - Write flow effects
|
|
69
|
+
*/
|
|
70
|
+
export function resourcesTouchedByWrites(effects: Effects): ResourceRef[] {
|
|
71
|
+
return [...(effects.writes ?? [])];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Whether a tier-1 key is invalidated by a write effect set.
|
|
76
|
+
*
|
|
77
|
+
* Auto-invalidation fires on exactly the writes touching the read's keys.
|
|
78
|
+
*
|
|
79
|
+
* @param key - Tier-1 cache key (`computed:…`)
|
|
80
|
+
* @param writeEffects - Effects of the writing flow
|
|
81
|
+
*/
|
|
82
|
+
export function isInvalidatedByWrite(
|
|
83
|
+
key: string,
|
|
84
|
+
writeEffects: Effects,
|
|
85
|
+
): boolean {
|
|
86
|
+
if (!key.startsWith("computed:")) return false;
|
|
87
|
+
const body = key.slice("computed:".length);
|
|
88
|
+
const resource = body.split("/")[0] ?? "";
|
|
89
|
+
return (writeEffects.writes ?? []).some((w) => w === resource);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** In-memory multi-tier cache used by the store runtime and tests. */
|
|
93
|
+
export interface StoreCache {
|
|
94
|
+
/**
|
|
95
|
+
* Get a value by key.
|
|
96
|
+
*
|
|
97
|
+
* @param key - Cache key
|
|
98
|
+
*/
|
|
99
|
+
get<T = unknown>(key: string): T | undefined;
|
|
100
|
+
/**
|
|
101
|
+
* Set a value.
|
|
102
|
+
*
|
|
103
|
+
* @param entry - Entry to store
|
|
104
|
+
*/
|
|
105
|
+
set<T>(entry: CacheEntry<T>): void;
|
|
106
|
+
/**
|
|
107
|
+
* Invalidate every tier-1 entry whose resources intersect `resources`.
|
|
108
|
+
*
|
|
109
|
+
* @param resources - Written resources
|
|
110
|
+
*/
|
|
111
|
+
invalidate(resources: readonly ResourceRef[]): InvalidationEvent;
|
|
112
|
+
/**
|
|
113
|
+
* Apply write effects: invalidate exactly the keys those writes touch.
|
|
114
|
+
*
|
|
115
|
+
* @param writeEffects - Writing flow's effects
|
|
116
|
+
*/
|
|
117
|
+
invalidateFromEffects(writeEffects: Effects): InvalidationEvent;
|
|
118
|
+
/** Snapshot of keys currently held. */
|
|
119
|
+
keys(): string[];
|
|
120
|
+
/** Clear all tiers. */
|
|
121
|
+
clear(): void;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Create an in-memory store cache (all three tiers share one map; tier is metadata).
|
|
126
|
+
*
|
|
127
|
+
* @param now - Clock for TTL expiry
|
|
128
|
+
*/
|
|
129
|
+
export function createStoreCache(now: () => number = () => Date.now()): StoreCache {
|
|
130
|
+
const entries = new Map<string, CacheEntry>();
|
|
131
|
+
|
|
132
|
+
function alive(entry: CacheEntry): boolean {
|
|
133
|
+
return entry.expiresAt === null || entry.expiresAt > now();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
get<T = unknown>(key: string): T | undefined {
|
|
138
|
+
const entry = entries.get(key);
|
|
139
|
+
if (!entry) return undefined;
|
|
140
|
+
if (!alive(entry)) {
|
|
141
|
+
entries.delete(key);
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
return entry.value as T;
|
|
145
|
+
},
|
|
146
|
+
set<T>(entry: CacheEntry<T>): void {
|
|
147
|
+
entries.set(entry.key, entry as CacheEntry);
|
|
148
|
+
},
|
|
149
|
+
invalidate(resources: readonly ResourceRef[]): InvalidationEvent {
|
|
150
|
+
const set = new Set(resources);
|
|
151
|
+
const keys: string[] = [];
|
|
152
|
+
for (const [key, entry] of entries) {
|
|
153
|
+
if (entry.tier !== 1) continue;
|
|
154
|
+
if (entry.resources.some((r) => set.has(r))) {
|
|
155
|
+
keys.push(key);
|
|
156
|
+
entries.delete(key);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return { resources: [...resources], keys };
|
|
160
|
+
},
|
|
161
|
+
invalidateFromEffects(writeEffects: Effects): InvalidationEvent {
|
|
162
|
+
return this.invalidate(resourcesTouchedByWrites(writeEffects));
|
|
163
|
+
},
|
|
164
|
+
keys(): string[] {
|
|
165
|
+
return [...entries.keys()];
|
|
166
|
+
},
|
|
167
|
+
clear(): void {
|
|
168
|
+
entries.clear();
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Parse a short TTL string (`"5m"`, `"1h"`, `"30s"`) to milliseconds.
|
|
175
|
+
*
|
|
176
|
+
* @param ttl - Duration string
|
|
177
|
+
*/
|
|
178
|
+
export function parseTtlMs(ttl: string): number {
|
|
179
|
+
const match = /^(\d+)(ms|s|m|h|d)$/.exec(ttl.trim());
|
|
180
|
+
if (!match) return 0;
|
|
181
|
+
const n = Number(match[1]);
|
|
182
|
+
const unit = match[2];
|
|
183
|
+
switch (unit) {
|
|
184
|
+
case "ms":
|
|
185
|
+
return n;
|
|
186
|
+
case "s":
|
|
187
|
+
return n * 1000;
|
|
188
|
+
case "m":
|
|
189
|
+
return n * 60_000;
|
|
190
|
+
case "h":
|
|
191
|
+
return n * 3_600_000;
|
|
192
|
+
case "d":
|
|
193
|
+
return n * 86_400_000;
|
|
194
|
+
default:
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PII / sensitivity classification — lives in the schema, enforced at the
|
|
3
|
+
* driver boundary so it survives raw SQL (`SELECT *` masks classified columns).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ColumnClassification } from "../../manifest/types.ts";
|
|
7
|
+
import {
|
|
8
|
+
classificationKey,
|
|
9
|
+
type ClassificationMap,
|
|
10
|
+
type SqlRow,
|
|
11
|
+
} from "../../drivers/types.ts";
|
|
12
|
+
|
|
13
|
+
/** Default mask token for PII columns when reveal is denied. */
|
|
14
|
+
export const PII_MASK = "[redacted]";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Tag a column (or field) with privacy / retention classification.
|
|
18
|
+
*
|
|
19
|
+
* Use in schema metadata or `store.sql` classify maps:
|
|
20
|
+
* `email: classify({ pii: true })`.
|
|
21
|
+
*
|
|
22
|
+
* @param classification - Tags to attach
|
|
23
|
+
*/
|
|
24
|
+
export function classify(
|
|
25
|
+
classification: ColumnClassification,
|
|
26
|
+
): ColumnClassification {
|
|
27
|
+
return { ...classification };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Build a classification map from a nested table → column → tags structure.
|
|
32
|
+
*
|
|
33
|
+
* @param tables - Per-table column classifications
|
|
34
|
+
*/
|
|
35
|
+
export function buildClassificationMap(
|
|
36
|
+
tables: Readonly<
|
|
37
|
+
Record<string, Readonly<Record<string, ColumnClassification>>>
|
|
38
|
+
>,
|
|
39
|
+
): ClassificationMap {
|
|
40
|
+
const map = new Map<string, ColumnClassification>();
|
|
41
|
+
for (const [table, cols] of Object.entries(tables)) {
|
|
42
|
+
for (const [column, tags] of Object.entries(cols)) {
|
|
43
|
+
map.set(classificationKey(table, column), tags);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return map;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Whether a column is classified as PII.
|
|
51
|
+
*
|
|
52
|
+
* @param map - Classification map
|
|
53
|
+
* @param table - Table name (may be unknown for opaque queries)
|
|
54
|
+
* @param column - Column name
|
|
55
|
+
*/
|
|
56
|
+
export function isPiiColumn(
|
|
57
|
+
map: ClassificationMap,
|
|
58
|
+
table: string | undefined,
|
|
59
|
+
column: string,
|
|
60
|
+
): boolean {
|
|
61
|
+
if (table) {
|
|
62
|
+
const direct = map.get(classificationKey(table, column));
|
|
63
|
+
if (direct?.pii) return true;
|
|
64
|
+
}
|
|
65
|
+
// Survive raw SQL when only the column name is known: match any table.
|
|
66
|
+
for (const [key, tags] of map) {
|
|
67
|
+
if (tags.pii && key.endsWith(`.${column}`)) return true;
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Options for {@link maskRows}. */
|
|
73
|
+
export interface MaskRowsOptions {
|
|
74
|
+
/** Classification map from the schema. */
|
|
75
|
+
readonly classifications: ClassificationMap;
|
|
76
|
+
/** Table hint when known (from query builder). */
|
|
77
|
+
readonly table?: string;
|
|
78
|
+
/**
|
|
79
|
+
* When true, PII columns are returned in cleartext (requires `pii:reveal`).
|
|
80
|
+
* Default false.
|
|
81
|
+
*/
|
|
82
|
+
readonly revealPii?: boolean;
|
|
83
|
+
/** Mask token (defaults to {@link PII_MASK}). */
|
|
84
|
+
readonly mask?: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Mask classified columns on result rows at the driver boundary.
|
|
89
|
+
*
|
|
90
|
+
* @param rows - Raw driver rows
|
|
91
|
+
* @param options - Classification + reveal policy
|
|
92
|
+
*/
|
|
93
|
+
export function maskRows(
|
|
94
|
+
rows: readonly SqlRow[],
|
|
95
|
+
options: MaskRowsOptions,
|
|
96
|
+
): SqlRow[] {
|
|
97
|
+
if (options.revealPii || options.classifications.size === 0) {
|
|
98
|
+
return rows.map((r) => ({ ...r }));
|
|
99
|
+
}
|
|
100
|
+
const mask = options.mask ?? PII_MASK;
|
|
101
|
+
return rows.map((row) => {
|
|
102
|
+
const out: SqlRow = {};
|
|
103
|
+
for (const [column, value] of Object.entries(row)) {
|
|
104
|
+
out[column] = isPiiColumn(
|
|
105
|
+
options.classifications,
|
|
106
|
+
options.table,
|
|
107
|
+
column,
|
|
108
|
+
)
|
|
109
|
+
? mask
|
|
110
|
+
: value;
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Extract a FROM table name from a SQL string when possible.
|
|
118
|
+
* Best-effort — used so `SELECT *` still finds classifications.
|
|
119
|
+
*
|
|
120
|
+
* @param sql - SQL text
|
|
121
|
+
*/
|
|
122
|
+
export function tableFromSql(sql: string): string | undefined {
|
|
123
|
+
const match = /\bfrom\s+["`]?([a-zA-Z_][a-zA-Z0-9_]*)["`]?/i.exec(sql);
|
|
124
|
+
return match?.[1];
|
|
125
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store facet declarations — `store.sql` · `store.kv` · `store.files` · `store.index`.
|
|
3
|
+
*
|
|
4
|
+
* Declarations are data; I/O happens only through drivers via the runtime / `fx`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ColumnClassification, ResourceRef, StoreFacet } from "../../manifest/types.ts";
|
|
8
|
+
import { resolveTableName, type TableHandle } from "./table.ts";
|
|
9
|
+
|
|
10
|
+
/** CDC trigger shape (mirrors kernel `CdcTrigger` without importing it). */
|
|
11
|
+
export interface StoreCdcTrigger {
|
|
12
|
+
readonly kind: "cdc";
|
|
13
|
+
readonly table: string;
|
|
14
|
+
readonly column?: string;
|
|
15
|
+
readonly store?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Handle from {@link SqlStoreDecl.table}. */
|
|
19
|
+
export interface StoreTableCdc {
|
|
20
|
+
readonly name: string;
|
|
21
|
+
/**
|
|
22
|
+
* CDC trigger when `column` (or any column) changes.
|
|
23
|
+
*
|
|
24
|
+
* @param column - Optional column name
|
|
25
|
+
*/
|
|
26
|
+
changed(column?: string): StoreCdcTrigger;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Base fields shared by every facet declaration. */
|
|
30
|
+
export interface StoreDeclBase {
|
|
31
|
+
/** Facet kind. */
|
|
32
|
+
readonly facet: StoreFacet;
|
|
33
|
+
/** Logical name (becomes the resource suffix). */
|
|
34
|
+
readonly name: string;
|
|
35
|
+
/** Resource ref `facet:name`. */
|
|
36
|
+
readonly ref: ResourceRef;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Options for {@link store.sql}. */
|
|
40
|
+
export interface SqlStoreOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Schema tables — okengine {@link TableHandle}s and/or Drizzle tables.
|
|
43
|
+
* Classification is read from TableHandle columns when present.
|
|
44
|
+
*/
|
|
45
|
+
readonly schema?: Readonly<Record<string, TableHandle | unknown>>;
|
|
46
|
+
/**
|
|
47
|
+
* Explicit classifications: `table → column → tags`.
|
|
48
|
+
* Merged with schema-derived tags; wins on conflict for the same column.
|
|
49
|
+
*/
|
|
50
|
+
readonly classify?: Readonly<
|
|
51
|
+
Record<string, Readonly<Record<string, ColumnClassification>>>
|
|
52
|
+
>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** SQL store declaration. */
|
|
56
|
+
export interface SqlStoreDecl extends StoreDeclBase {
|
|
57
|
+
readonly facet: "sql";
|
|
58
|
+
readonly ref: `sql:${string}`;
|
|
59
|
+
readonly schema?: SqlStoreOptions["schema"];
|
|
60
|
+
readonly classify?: SqlStoreOptions["classify"];
|
|
61
|
+
/**
|
|
62
|
+
* CDC handle — `db.table(orders).changed("status")`.
|
|
63
|
+
*
|
|
64
|
+
* @param table - Table handle or Drizzle table
|
|
65
|
+
*/
|
|
66
|
+
table(table: TableHandle | unknown): StoreTableCdc;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Options for {@link store.kv}. */
|
|
70
|
+
export interface KvStoreOptions {
|
|
71
|
+
/** Optional description. */
|
|
72
|
+
readonly description?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** KV store declaration. */
|
|
76
|
+
export interface KvStoreDecl extends StoreDeclBase {
|
|
77
|
+
readonly facet: "kv";
|
|
78
|
+
readonly ref: `kv:${string}`;
|
|
79
|
+
readonly description?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Options for {@link store.files}. */
|
|
83
|
+
export interface FilesStoreOptions {
|
|
84
|
+
/** Optional description. */
|
|
85
|
+
readonly description?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Files store declaration. */
|
|
89
|
+
export interface FilesStoreDecl extends StoreDeclBase {
|
|
90
|
+
readonly facet: "files";
|
|
91
|
+
readonly ref: `files:${string}`;
|
|
92
|
+
readonly description?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Options for {@link store.index}. */
|
|
96
|
+
export interface IndexStoreOptions {
|
|
97
|
+
/** Vector dimensions (default 3 for tests; set explicitly in apps). */
|
|
98
|
+
readonly dims?: number;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Index store declaration. */
|
|
102
|
+
export interface IndexStoreDecl extends StoreDeclBase {
|
|
103
|
+
readonly facet: "index";
|
|
104
|
+
readonly ref: `index:${string}`;
|
|
105
|
+
readonly dims?: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Any store declaration. */
|
|
109
|
+
export type StoreDecl =
|
|
110
|
+
| SqlStoreDecl
|
|
111
|
+
| KvStoreDecl
|
|
112
|
+
| FilesStoreDecl
|
|
113
|
+
| IndexStoreDecl;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Declare a SQL store.
|
|
117
|
+
*
|
|
118
|
+
* @param name - Store name
|
|
119
|
+
* @param options - Schema / classification
|
|
120
|
+
*/
|
|
121
|
+
export function sql(name: string, options: SqlStoreOptions = {}): SqlStoreDecl {
|
|
122
|
+
const decl: SqlStoreDecl = {
|
|
123
|
+
facet: "sql",
|
|
124
|
+
name,
|
|
125
|
+
ref: `sql:${name}`,
|
|
126
|
+
schema: options.schema,
|
|
127
|
+
classify: options.classify,
|
|
128
|
+
table(table) {
|
|
129
|
+
const tableName = resolveTableName(table);
|
|
130
|
+
return {
|
|
131
|
+
name: tableName,
|
|
132
|
+
changed(column?: string): StoreCdcTrigger {
|
|
133
|
+
return column === undefined
|
|
134
|
+
? { kind: "cdc", table: tableName, store: name }
|
|
135
|
+
: { kind: "cdc", table: tableName, column, store: name };
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
return decl;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Declare a KV store.
|
|
145
|
+
*
|
|
146
|
+
* @param name - Namespace name
|
|
147
|
+
* @param options - Options
|
|
148
|
+
*/
|
|
149
|
+
export function kv(name: string, options: KvStoreOptions = {}): KvStoreDecl {
|
|
150
|
+
return {
|
|
151
|
+
facet: "kv",
|
|
152
|
+
name,
|
|
153
|
+
ref: `kv:${name}`,
|
|
154
|
+
description: options.description,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Declare a files store.
|
|
160
|
+
*
|
|
161
|
+
* @param name - Bucket name
|
|
162
|
+
* @param options - Options
|
|
163
|
+
*/
|
|
164
|
+
export function files(
|
|
165
|
+
name: string,
|
|
166
|
+
options: FilesStoreOptions = {},
|
|
167
|
+
): FilesStoreDecl {
|
|
168
|
+
return {
|
|
169
|
+
facet: "files",
|
|
170
|
+
name,
|
|
171
|
+
ref: `files:${name}`,
|
|
172
|
+
description: options.description,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Declare a vector index store.
|
|
178
|
+
*
|
|
179
|
+
* @param name - Index name
|
|
180
|
+
* @param options - Dimensions
|
|
181
|
+
*/
|
|
182
|
+
export function index(
|
|
183
|
+
name: string,
|
|
184
|
+
options: IndexStoreOptions = {},
|
|
185
|
+
): IndexStoreDecl {
|
|
186
|
+
return {
|
|
187
|
+
facet: "index",
|
|
188
|
+
name,
|
|
189
|
+
ref: `index:${name}`,
|
|
190
|
+
dims: options.dims,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Public `store` element — four facets. */
|
|
195
|
+
export const store = {
|
|
196
|
+
sql,
|
|
197
|
+
kv,
|
|
198
|
+
files,
|
|
199
|
+
index,
|
|
200
|
+
} as const;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
contentAddressedKey,
|
|
4
|
+
fileKeyWarnings,
|
|
5
|
+
projectFileKeys,
|
|
6
|
+
} from "./files-policy.ts";
|
|
7
|
+
|
|
8
|
+
describe("files-policy", () => {
|
|
9
|
+
test("flags non-ASCII keys for signed-URL risk", () => {
|
|
10
|
+
const warnings = fileKeyWarnings("фото/id.jpg");
|
|
11
|
+
expect(warnings).toHaveLength(1);
|
|
12
|
+
expect(warnings[0]?.code).toBe("non_ascii_key");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("ascii keys produce no warnings", () => {
|
|
16
|
+
expect(fileKeyWarnings("a/b/c.png")).toEqual([]);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("contentAddressedKey is stable sha256 hex", async () => {
|
|
20
|
+
const a = await contentAddressedKey("hello");
|
|
21
|
+
const b = await contentAddressedKey("hello");
|
|
22
|
+
expect(a).toBe(b);
|
|
23
|
+
expect(a).toMatch(/^[0-9a-f]{64}$/);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("projectFileKeys attaches warnings per key", () => {
|
|
27
|
+
const rows = projectFileKeys(["ok.txt", "café.bin"]);
|
|
28
|
+
expect(rows[0]?.warnings).toEqual([]);
|
|
29
|
+
expect(rows[1]?.warnings[0]?.code).toBe("non_ascii_key");
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Files facet operational policy — content-addressed keys + driver warnings.
|
|
3
|
+
*
|
|
4
|
+
* Warnings are generated here (element physics) so the Console surfaces them
|
|
5
|
+
* automatically rather than inventing hand-written copy (console §9.5 · §11).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** One operational warning attached to a files key. */
|
|
9
|
+
export interface FileKeyWarning {
|
|
10
|
+
/** Stable machine code. */
|
|
11
|
+
readonly code: "non_ascii_key";
|
|
12
|
+
/** Human-readable operator message. */
|
|
13
|
+
readonly message: string;
|
|
14
|
+
/** Object key that triggered the warning. */
|
|
15
|
+
readonly key: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Operational warnings for a files object key.
|
|
20
|
+
*
|
|
21
|
+
* Non-ASCII keys break signed-URL percent-encoding on S3-compatible stores.
|
|
22
|
+
*
|
|
23
|
+
* @param key - Object key
|
|
24
|
+
*/
|
|
25
|
+
export function fileKeyWarnings(key: string): readonly FileKeyWarning[] {
|
|
26
|
+
const warnings: FileKeyWarning[] = [];
|
|
27
|
+
// eslint-disable-next-line no-control-regex -- intentional: detect non-ASCII
|
|
28
|
+
if (/[^\x00-\x7F]/.test(key)) {
|
|
29
|
+
warnings.push({
|
|
30
|
+
code: "non_ascii_key",
|
|
31
|
+
message:
|
|
32
|
+
"Non-ASCII object key — signed URL encoding may break on S3-compatible stores.",
|
|
33
|
+
key,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return warnings;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Content-addressed object key (sha256 hex of bytes).
|
|
41
|
+
*
|
|
42
|
+
* @param data - Object bytes or UTF-8 string
|
|
43
|
+
*/
|
|
44
|
+
export async function contentAddressedKey(
|
|
45
|
+
data: Uint8Array | string,
|
|
46
|
+
): Promise<string> {
|
|
47
|
+
const bytes =
|
|
48
|
+
typeof data === "string"
|
|
49
|
+
? new TextEncoder().encode(data)
|
|
50
|
+
: new Uint8Array(data);
|
|
51
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
52
|
+
return [...new Uint8Array(digest)]
|
|
53
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
54
|
+
.join("");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Attach warnings to a list of object keys.
|
|
59
|
+
*
|
|
60
|
+
* @param keys - Object keys from the driver
|
|
61
|
+
*/
|
|
62
|
+
export function projectFileKeys(keys: readonly string[]): ReadonlyArray<{
|
|
63
|
+
readonly key: string;
|
|
64
|
+
readonly warnings: readonly FileKeyWarning[];
|
|
65
|
+
}> {
|
|
66
|
+
return keys.map((key) => ({
|
|
67
|
+
key,
|
|
68
|
+
warnings: fileKeyWarnings(key),
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Automatic replica routing — derived from the effect set.
|
|
3
|
+
*
|
|
4
|
+
* Read-only flows (reads declared, no store writes) route to replicas.
|
|
5
|
+
* No annotations required on the flow.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Effects } from "../../manifest/types.ts";
|
|
9
|
+
import type { SqlConnectOptions, SqlRole } from "../../drivers/types.ts";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Whether a flow's effect set is store-read-only (eligible for replica routing).
|
|
13
|
+
*
|
|
14
|
+
* Emits / sends / asks do not force the primary — only `writes` do.
|
|
15
|
+
*
|
|
16
|
+
* @param effects - Declared or inferred effects
|
|
17
|
+
*/
|
|
18
|
+
export function isReadOnlyStoreFlow(effects: Effects): boolean {
|
|
19
|
+
const writes = effects.writes?.length ?? 0;
|
|
20
|
+
return writes === 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Choose the SQL connection role for a flow from its effects.
|
|
25
|
+
*
|
|
26
|
+
* @param effects - Flow effects
|
|
27
|
+
* @param hasReplicas - Whether replicas are configured
|
|
28
|
+
*/
|
|
29
|
+
export function sqlRoleForEffects(
|
|
30
|
+
effects: Effects,
|
|
31
|
+
hasReplicas: boolean,
|
|
32
|
+
): SqlRole {
|
|
33
|
+
if (hasReplicas && isReadOnlyStoreFlow(effects)) return "replica";
|
|
34
|
+
return "primary";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** SQL binding with optional read replicas. */
|
|
38
|
+
export interface SqlBindingConfig {
|
|
39
|
+
/** Primary connection options. */
|
|
40
|
+
readonly primary: SqlConnectOptions;
|
|
41
|
+
/** Read replicas (round-robin). */
|
|
42
|
+
readonly replicas?: readonly SqlConnectOptions[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolve which connection options to use for a flow.
|
|
47
|
+
*
|
|
48
|
+
* @param config - Primary + replicas
|
|
49
|
+
* @param effects - Flow effects
|
|
50
|
+
* @param pickReplica - Index picker (defaults to 0)
|
|
51
|
+
*/
|
|
52
|
+
export function resolveSqlTarget(
|
|
53
|
+
config: SqlBindingConfig,
|
|
54
|
+
effects: Effects,
|
|
55
|
+
pickReplica: (count: number) => number = () => 0,
|
|
56
|
+
): { readonly role: SqlRole; readonly options: SqlConnectOptions } {
|
|
57
|
+
const replicas = config.replicas ?? [];
|
|
58
|
+
const role = sqlRoleForEffects(effects, replicas.length > 0);
|
|
59
|
+
if (role === "replica") {
|
|
60
|
+
const idx = Math.abs(pickReplica(replicas.length)) % replicas.length;
|
|
61
|
+
const options = replicas[idx]!;
|
|
62
|
+
return { role: "replica", options: { ...options, role: "replica" } };
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
role: "primary",
|
|
66
|
+
options: { ...config.primary, role: "primary" },
|
|
67
|
+
};
|
|
68
|
+
}
|