okengine 0.2.6 → 0.3.2
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 +33 -30
- package/README.md +74 -340
- package/{spec/manifest.v1.schema.json → manifest.v1.schema.json} +40 -9
- package/package.json +47 -44
- package/site/content/docs/ai/llms-txt.mdx +54 -0
- package/site/content/docs/ai/mcp.mdx +123 -0
- package/site/content/docs/ai/meta.json +5 -0
- package/site/content/docs/ai/skills.mdx +53 -0
- package/site/content/docs/console/access.mdx +29 -0
- package/site/content/docs/console/ai.mdx +35 -0
- package/site/content/docs/console/architecture.mdx +35 -0
- package/site/content/docs/console/channels.mdx +37 -0
- package/site/content/docs/console/clock.mdx +31 -0
- package/site/content/docs/console/flows.mdx +31 -0
- package/site/content/docs/console/gates.mdx +35 -0
- package/site/content/docs/console/manifest-diff.mdx +34 -0
- package/site/content/docs/console/meta.json +23 -0
- package/site/content/docs/console/overview.mdx +40 -0
- package/site/content/docs/console/plugins.mdx +41 -0
- package/site/content/docs/console/privacy.mdx +32 -0
- package/site/content/docs/console/runs.mdx +40 -0
- package/site/content/docs/console/signals.mdx +31 -0
- package/site/content/docs/console/store.mdx +32 -0
- package/site/content/docs/console/tenancy.mdx +32 -0
- package/site/content/docs/console/traces.mdx +34 -0
- package/site/content/docs/console/vault.mdx +37 -0
- package/site/content/docs/elements/ai.mdx +180 -0
- package/site/content/docs/elements/channel.mdx +167 -0
- package/site/content/docs/elements/clock.mdx +182 -0
- package/site/content/docs/elements/flow.mdx +288 -0
- package/site/content/docs/elements/gate.mdx +171 -0
- package/site/content/docs/elements/meta.json +5 -0
- package/site/content/docs/elements/signal.mdx +171 -0
- package/site/content/docs/elements/store.mdx +320 -0
- package/site/content/docs/elements/vault.mdx +263 -0
- package/site/content/docs/get-started/basic-usage.mdx +124 -0
- package/site/content/docs/get-started/comparison.mdx +65 -0
- package/site/content/docs/get-started/installation.mdx +113 -0
- package/site/content/docs/get-started/introduction.mdx +123 -0
- package/site/content/docs/get-started/meta.json +5 -0
- package/site/content/docs/index.mdx +63 -0
- package/site/content/docs/meta.json +5 -0
- package/site/content/docs/plugins/compression.mdx +60 -0
- package/site/content/docs/plugins/cors.mdx +92 -0
- package/site/content/docs/plugins/csrf.mdx +96 -0
- package/site/content/docs/plugins/ip-allowlist.mdx +92 -0
- package/site/content/docs/plugins/maintenance-mode.mdx +101 -0
- package/site/content/docs/plugins/meta.json +15 -0
- package/site/content/docs/plugins/security-headers.mdx +136 -0
- package/site/content/docs/reference/cli.md +101 -0
- package/site/content/docs/reference/configuration.mdx +159 -0
- package/site/content/docs/reference/environment-variables.mdx +87 -0
- package/site/content/docs/reference/errors.mdx +80 -0
- package/site/content/docs/reference/fx.mdx +117 -0
- package/site/content/docs/reference/meta.json +5 -0
- package/site/content/docs/reference/plugins.mdx +249 -0
- package/site/content/docs/reference/security.md +63 -0
- package/src/auth/api-keys.ts +2 -5
- package/src/auth/attenuation.ts +2 -6
- package/src/auth/auth.test.ts +56 -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 +456 -0
- package/src/cli/dev-app-runner.ts +2 -8
- package/src/cli/dev-db-push.test.ts +193 -0
- package/src/cli/dev-mode.test.ts +45 -0
- package/src/cli/dev-mode.ts +78 -0
- package/src/cli/dev-schema-sync.test.ts +66 -0
- package/src/cli/dev-schema-sync.ts +139 -0
- package/src/cli/dev.test.ts +264 -15
- package/src/cli/dev.ts +355 -139
- package/src/cli/doc-staleness.test.ts +69 -0
- package/src/cli/docker-cli.test.ts +27 -8
- package/src/cli/docker.ts +11 -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/drizzle-env.test.ts +67 -0
- package/src/cli/drizzle-env.ts +78 -0
- package/src/cli/ensure-drizzle-config.ts +50 -0
- 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 +47 -47
- package/src/cli/mcp-from-console.ts +1 -4
- package/src/cli/mode.ts +65 -0
- package/src/cli/openbao-bootstrap.test.ts +147 -0
- package/src/cli/openbao-bootstrap.ts +280 -0
- package/src/cli/openbao-restart.integration.test.ts +136 -0
- package/src/cli/ports.test.ts +7 -14
- package/src/cli/ports.ts +7 -5
- package/src/cli/privacy-erase.ts +1 -3
- package/src/cli/registry.ts +75 -25
- package/src/cli/resolve-dev-sql-env.test.ts +48 -0
- package/src/cli/resolve-dev-sql-env.ts +42 -0
- package/src/cli/schema.ts +3 -10
- package/src/cli/stack.ts +8 -10
- package/src/cli/start.ts +4 -15
- package/src/cli/upgrade.ts +1 -4
- package/src/cli/vault-cmd.ts +63 -0
- 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 +15 -30
- 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 +136 -30
- package/src/compiler/extract.ts +485 -157
- package/src/compiler/fixtures/skyport/oke.config.ts +3 -3
- 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 +16 -7
- package/src/compiler/sucrose.ts +11 -31
- package/src/config/define-config.test.ts +55 -34
- package/src/config/index.ts +177 -46
- 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 +16 -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 +44 -66
- 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 +215 -27
- package/src/docker/derive.ts +6 -14
- package/src/docker/docker.test.ts +118 -15
- package/src/docker/dockerfile.integration.test.ts +12 -21
- package/src/docker/helpers.ts +5 -4
- package/src/docker/index.ts +9 -6
- package/src/docker/pin.ts +1 -9
- package/src/docker/recipes/index.ts +6 -6
- package/src/docker/recipes/mailpit.ts +22 -0
- package/src/docker/recipes/openbao.ts +47 -0
- package/src/docker/recipes/postgres.ts +17 -6
- package/src/docker/recipes/redis.ts +11 -2
- package/src/docker/recipes/rustfs.ts +32 -0
- package/src/docker/stack-id.test.ts +73 -5
- package/src/docker/stack-id.ts +124 -33
- package/src/docker/stack.integration.test.ts +6 -21
- package/src/docker/stack.ts +36 -4
- package/src/docker/types.ts +16 -4
- package/src/docs-origin.ts +4 -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/drizzle-dialect.test.ts +20 -0
- package/src/drivers/drizzle-dialect.ts +37 -0
- package/src/drivers/fs.ts +2 -8
- package/src/drivers/index.ts +12 -57
- package/src/drivers/memory.ts +287 -75
- 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 +15 -18
- 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-driver-removal.test.ts +55 -0
- 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.test.ts +97 -0
- package/src/drivers/vault-openbao.ts +103 -40
- package/src/drivers/vault-types.ts +3 -16
- 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 +11 -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/resource-list-docs.fixture.ts +56 -0
- package/src/elements/store/resource-list-docs.test.ts +79 -0
- package/src/elements/store/resource.test.ts +253 -0
- package/src/elements/store/resource.ts +786 -0
- 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.test.ts +132 -0
- package/src/elements/store/sql-condition.ts +293 -65
- package/src/elements/store/sql-session.test.ts +135 -0
- package/src/elements/store/sql-session.ts +241 -84
- package/src/elements/store/table.ts +69 -23
- package/src/elements/store.test.ts +17 -39
- package/src/elements/store.ts +53 -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 +19 -33
- package/src/elements/vault.test.ts +12 -55
- package/src/elements/vault.ts +9 -13
- package/src/index.ts +5 -2
- package/src/kernel/adopt-routes.ts +15 -29
- package/src/kernel/app.ts +92 -118
- package/src/kernel/auth-resolve.ts +2 -9
- package/src/kernel/boot-bind/ai.ts +1 -4
- package/src/kernel/boot-bind/channel.test.ts +60 -0
- package/src/kernel/boot-bind/channel.ts +61 -5
- 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 +27 -18
- package/src/kernel/boot-bind/store.ts +93 -52
- package/src/kernel/boot-bind/vault.ts +2 -8
- package/src/kernel/boot.test.ts +4 -10
- package/src/kernel/boot.ts +30 -77
- 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/edge.test.ts +68 -0
- 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 +13 -25
- package/src/kernel/fx.test.ts +31 -40
- package/src/kernel/fx.ts +135 -90
- package/src/kernel/hook-timing.test.ts +3 -6
- package/src/kernel/hook-timing.ts +5 -14
- package/src/kernel/hooks.test.ts +43 -10
- package/src/kernel/hooks.ts +25 -12
- package/src/kernel/index.ts +11 -17
- package/src/kernel/journal.ts +7 -31
- package/src/kernel/on.ts +45 -9
- 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 +81 -27
- package/src/kernel/registry-isolation.test.ts +74 -0
- package/src/kernel/registry.ts +102 -30
- 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 +63 -21
- package/src/manifest/diff.test.ts +26 -91
- package/src/manifest/diff.ts +95 -225
- package/src/manifest/fixtures/skyport.excerpt.json +1 -1
- package/src/manifest/fixtures/skyport.manifest.json +1 -1
- package/src/manifest/index.ts +3 -10
- package/src/manifest/types.ts +30 -6
- 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 +5 -15
- 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/compression.test.ts +127 -0
- package/src/plugins/compression.ts +94 -0
- package/src/plugins/config-source.test.ts +204 -0
- package/src/plugins/config-source.ts +209 -0
- package/src/plugins/cors.test.ts +138 -0
- package/src/plugins/cors.ts +129 -0
- package/src/plugins/csrf.test.ts +102 -0
- package/src/plugins/csrf.ts +86 -0
- package/src/plugins/headers.ts +54 -0
- package/src/plugins/index.ts +26 -0
- package/src/plugins/ip-allowlist.test.ts +105 -0
- package/src/plugins/ip-allowlist.ts +76 -0
- package/src/plugins/maintenance-mode.test.ts +91 -0
- package/src/plugins/maintenance-mode.ts +85 -0
- package/src/plugins/node-import-scan.ts +2 -5
- package/src/plugins/security-headers.test.ts +243 -0
- package/src/plugins/security-headers.ts +255 -0
- 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 +16 -45
- 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 +27 -33
- 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/docs/spec/console.md +0 -739
- package/docs/spec/example.md +0 -1196
- package/docs/spec/four-applications.md +0 -1195
- package/docs/spec/unified-theory.md +0 -489
- package/src/cli/doc-drift.test.ts +0 -146
- package/src/cli/doc-drift.ts +0 -430
- package/src/cli/doctor-diff-examples.ts +0 -92
- package/src/console/ui/dist/assets/panel-vault-BbfWdox0.js +0 -1
- package/src/drivers/vault-sops.ts +0 -271
package/src/compiler/extract.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
AiPrompt,
|
|
16
16
|
Channel,
|
|
17
17
|
Clock,
|
|
18
|
+
DeclaredColumn,
|
|
18
19
|
Effects,
|
|
19
20
|
Flow,
|
|
20
21
|
Gate,
|
|
@@ -66,6 +67,11 @@ interface ProjectScope {
|
|
|
66
67
|
bindings: Map<string, InferBinding>;
|
|
67
68
|
signals: Record<string, Signal>;
|
|
68
69
|
stores: Record<string, Store>;
|
|
70
|
+
/** Binding or table name → declared columns from `store.schema.table`. */
|
|
71
|
+
schemaTables: Map<
|
|
72
|
+
string,
|
|
73
|
+
{ readonly name: string; readonly columns: Record<string, DeclaredColumn> }
|
|
74
|
+
>;
|
|
69
75
|
clocks: Record<string, Clock>;
|
|
70
76
|
gates: Record<string, Gate>;
|
|
71
77
|
/** Local binding name → gate manifest id (policy name or rate expression). */
|
|
@@ -82,6 +88,11 @@ interface ProjectScope {
|
|
|
82
88
|
flows: Record<string, Flow>;
|
|
83
89
|
/** Export name → flow id (for agent tools). */
|
|
84
90
|
flowExports: Map<string, string>;
|
|
91
|
+
/** Local binding name → store.resource declaration (for on(http.resource)). */
|
|
92
|
+
resources: Map<
|
|
93
|
+
string,
|
|
94
|
+
{ storeName: string; storeRef: string; unit?: string; breaking?: boolean }
|
|
95
|
+
>;
|
|
85
96
|
}
|
|
86
97
|
|
|
87
98
|
/**
|
|
@@ -89,9 +100,7 @@ interface ProjectScope {
|
|
|
89
100
|
*
|
|
90
101
|
* @param options - Root directory and/or explicit files
|
|
91
102
|
*/
|
|
92
|
-
export async function extractManifest(
|
|
93
|
-
options: ExtractManifestOptions = {},
|
|
94
|
-
): Promise<Manifest> {
|
|
103
|
+
export async function extractManifest(options: ExtractManifestOptions = {}): Promise<Manifest> {
|
|
95
104
|
const files = options.files
|
|
96
105
|
? [...options.files]
|
|
97
106
|
: await readSources(options.rootDir ?? ".", options.glob ?? "**/*.{ts,tsx}");
|
|
@@ -101,6 +110,7 @@ export async function extractManifest(
|
|
|
101
110
|
bindings: new Map(),
|
|
102
111
|
signals: {},
|
|
103
112
|
stores: {},
|
|
113
|
+
schemaTables: new Map(),
|
|
104
114
|
clocks: {},
|
|
105
115
|
gates: {},
|
|
106
116
|
gateIds: new Map(),
|
|
@@ -111,6 +121,7 @@ export async function extractManifest(
|
|
|
111
121
|
drivers: {},
|
|
112
122
|
flows: {},
|
|
113
123
|
flowExports: new Map(),
|
|
124
|
+
resources: new Map(),
|
|
114
125
|
};
|
|
115
126
|
|
|
116
127
|
const parsed = files.map((file) => {
|
|
@@ -191,10 +202,7 @@ export async function extractFromSources(
|
|
|
191
202
|
return extractManifest({ files, app });
|
|
192
203
|
}
|
|
193
204
|
|
|
194
|
-
async function readSources(
|
|
195
|
-
rootDir: string,
|
|
196
|
-
pattern: string,
|
|
197
|
-
): Promise<SourceFile[]> {
|
|
205
|
+
async function readSources(rootDir: string, pattern: string): Promise<SourceFile[]> {
|
|
198
206
|
// Bun.Glob (global) — bare `import … from "bun"` is rejected by JSR.
|
|
199
207
|
const glob = new Bun.Glob(pattern);
|
|
200
208
|
const files: SourceFile[] = [];
|
|
@@ -273,13 +281,304 @@ function finalizeRefs(scope: ProjectScope): void {
|
|
|
273
281
|
(id) => scope.flowExports.get(id) ?? scope.bindings.get(id)?.ref ?? id,
|
|
274
282
|
);
|
|
275
283
|
}
|
|
284
|
+
|
|
285
|
+
// Merge declared columns into store.tables (handles declare-after-sql order).
|
|
286
|
+
for (const store of Object.values(scope.stores)) {
|
|
287
|
+
if (!store.tables) continue;
|
|
288
|
+
for (const [name, table] of Object.entries(store.tables)) {
|
|
289
|
+
const declared = [...scope.schemaTables.values()].find((t) => t.name === name);
|
|
290
|
+
if (!declared) continue;
|
|
291
|
+
const existing = table.columns ?? {};
|
|
292
|
+
if (Object.keys(existing).length === 0) {
|
|
293
|
+
store.tables[name] = { ...table, columns: declared.columns };
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Orphan abstract tables (declared but not wired via store.sql schema):
|
|
299
|
+
// attach under the first sql store, or create a synthetic "app" sql store.
|
|
300
|
+
const attachedNames = new Set<string>();
|
|
301
|
+
for (const store of Object.values(scope.stores)) {
|
|
302
|
+
if (!store.tables) continue;
|
|
303
|
+
for (const name of Object.keys(store.tables)) attachedNames.add(name);
|
|
304
|
+
}
|
|
305
|
+
const orphanByName = new Map<string, { name: string; columns: Record<string, DeclaredColumn> }>();
|
|
306
|
+
for (const t of scope.schemaTables.values()) {
|
|
307
|
+
if (!attachedNames.has(t.name)) orphanByName.set(t.name, t);
|
|
308
|
+
}
|
|
309
|
+
if (orphanByName.size > 0) {
|
|
310
|
+
let target = Object.entries(scope.stores).find(([, s]) => s.facet === "sql")?.[1];
|
|
311
|
+
if (!target) {
|
|
312
|
+
scope.stores.app = scope.stores.app ?? { facet: "sql" };
|
|
313
|
+
target = scope.stores.app!;
|
|
314
|
+
}
|
|
315
|
+
target.tables = target.tables ?? {};
|
|
316
|
+
for (const t of orphanByName.values()) {
|
|
317
|
+
if (target.tables[t.name]) continue;
|
|
318
|
+
target.tables[t.name] = { columns: t.columns };
|
|
319
|
+
}
|
|
320
|
+
}
|
|
276
321
|
}
|
|
277
322
|
|
|
278
|
-
function
|
|
279
|
-
call
|
|
280
|
-
|
|
323
|
+
function collectSchemaTable(call: CallExpression, program: AstNode, scope: ProjectScope): void {
|
|
324
|
+
const tableName = stringArg(call.arguments[0]);
|
|
325
|
+
if (!tableName) return;
|
|
326
|
+
const colsNode = objectArg(call.arguments[1]);
|
|
327
|
+
const columns = colsNode ? parseDeclaredColumns(colsNode) : {};
|
|
328
|
+
const entry = { name: tableName, columns };
|
|
329
|
+
scope.schemaTables.set(tableName, entry);
|
|
330
|
+
const bindingName = enclosingConstName(call, program);
|
|
331
|
+
if (bindingName) scope.schemaTables.set(bindingName, entry);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function attachSchemaOption(
|
|
335
|
+
optsNode: AstNode | undefined,
|
|
336
|
+
store: Store,
|
|
281
337
|
scope: ProjectScope,
|
|
282
338
|
): void {
|
|
339
|
+
const opts = objectArg(optsNode);
|
|
340
|
+
if (!opts) return;
|
|
341
|
+
const schemaNode = objectProp(opts, "schema");
|
|
342
|
+
if (!schemaNode) return;
|
|
343
|
+
|
|
344
|
+
// schema: { notes } or schema: { notes: store.schema.table(...) }
|
|
345
|
+
if (schemaNode.type === "ObjectExpression") {
|
|
346
|
+
store.tables = store.tables ?? {};
|
|
347
|
+
for (const prop of objectProperties(schemaNode)) {
|
|
348
|
+
const key = propKey(prop);
|
|
349
|
+
const value = (prop as AstNode & { value?: AstNode }).value;
|
|
350
|
+
if (!key || !value) continue;
|
|
351
|
+
|
|
352
|
+
if (value.type === "Identifier") {
|
|
353
|
+
const id = (value as Identifier).name;
|
|
354
|
+
const declared = scope.schemaTables.get(id) ?? scope.schemaTables.get(key);
|
|
355
|
+
if (declared) {
|
|
356
|
+
store.tables[declared.name] = { columns: declared.columns };
|
|
357
|
+
} else {
|
|
358
|
+
store.tables[key] = store.tables[key] ?? {};
|
|
359
|
+
}
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (value.type === "CallExpression") {
|
|
364
|
+
const inline = parseInlineSchemaTable(value as CallExpression);
|
|
365
|
+
if (inline) {
|
|
366
|
+
store.tables[inline.name] = { columns: inline.columns };
|
|
367
|
+
scope.schemaTables.set(inline.name, inline);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// schema: notesModule (identifier — unknown shape; skip columns)
|
|
375
|
+
if (schemaNode.type === "Identifier") {
|
|
376
|
+
// Namespace import — table names unknown at extract time.
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function parseInlineSchemaTable(
|
|
381
|
+
call: CallExpression,
|
|
382
|
+
): { name: string; columns: Record<string, DeclaredColumn> } | undefined {
|
|
383
|
+
const callee = call.callee;
|
|
384
|
+
if (callee.type !== "MemberExpression") return undefined;
|
|
385
|
+
const member = callee as AstNode & { object: AstNode; property: AstNode };
|
|
386
|
+
if (identifierName(member.property) !== "table") return undefined;
|
|
387
|
+
if (member.object.type !== "MemberExpression") return undefined;
|
|
388
|
+
const inner = member.object as AstNode & { object: AstNode; property: AstNode };
|
|
389
|
+
if (identifierName(inner.object) !== "store" || identifierName(inner.property) !== "schema") {
|
|
390
|
+
return undefined;
|
|
391
|
+
}
|
|
392
|
+
const tableName = stringArg(call.arguments[0]);
|
|
393
|
+
if (!tableName) return undefined;
|
|
394
|
+
const colsNode = objectArg(call.arguments[1]);
|
|
395
|
+
return {
|
|
396
|
+
name: tableName,
|
|
397
|
+
columns: colsNode ? parseDeclaredColumns(colsNode) : {},
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function parseDeclaredColumns(obj: AstNode): Record<string, DeclaredColumn> {
|
|
402
|
+
const out: Record<string, DeclaredColumn> = {};
|
|
403
|
+
for (const prop of objectProperties(obj)) {
|
|
404
|
+
const key = propKey(prop);
|
|
405
|
+
const value = (prop as AstNode & { value?: AstNode }).value;
|
|
406
|
+
if (!key || !value) continue;
|
|
407
|
+
const col = parseFieldChain(value, key);
|
|
408
|
+
if (col) out[key] = col;
|
|
409
|
+
}
|
|
410
|
+
return out;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Walk `field.text().notNull().pii()` (and similar) into a DeclaredColumn.
|
|
415
|
+
*
|
|
416
|
+
* @param node - AST node for the column value
|
|
417
|
+
* @param key - JS key (for default sqlName)
|
|
418
|
+
*/
|
|
419
|
+
function parseFieldChain(node: AstNode, key: string): DeclaredColumn | undefined {
|
|
420
|
+
const chain: string[] = [];
|
|
421
|
+
let sqlType: "text" | "integer" | undefined;
|
|
422
|
+
let sqlName: string | undefined;
|
|
423
|
+
let defaultValue: string | number | boolean | null | undefined;
|
|
424
|
+
let hasDefault = false;
|
|
425
|
+
let cur: AstNode | undefined = node;
|
|
426
|
+
|
|
427
|
+
while (cur && cur.type === "CallExpression") {
|
|
428
|
+
const call = cur as CallExpression;
|
|
429
|
+
const callee = call.callee;
|
|
430
|
+
if (callee.type === "MemberExpression") {
|
|
431
|
+
const member = callee as AstNode & { object: AstNode; property: AstNode };
|
|
432
|
+
const method = identifierName(member.property);
|
|
433
|
+
if (method) {
|
|
434
|
+
chain.push(method);
|
|
435
|
+
if (method === "as") {
|
|
436
|
+
sqlName = stringArg(call.arguments[0]) ?? sqlName;
|
|
437
|
+
}
|
|
438
|
+
if (method === "default") {
|
|
439
|
+
const lit = call.arguments[0];
|
|
440
|
+
if (lit && lit.type === "Literal") {
|
|
441
|
+
const v = (lit as Literal).value;
|
|
442
|
+
if (
|
|
443
|
+
v === null ||
|
|
444
|
+
typeof v === "string" ||
|
|
445
|
+
typeof v === "number" ||
|
|
446
|
+
typeof v === "boolean"
|
|
447
|
+
) {
|
|
448
|
+
defaultValue = v as string | number | boolean | null;
|
|
449
|
+
hasDefault = true;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
cur = member.object;
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
if (callee.type === "Identifier" && (callee as Identifier).name === "field") {
|
|
458
|
+
// shouldn't happen — field.text() is MemberExpression
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Remaining should be field.text / field.integer
|
|
465
|
+
if (cur && cur.type === "MemberExpression") {
|
|
466
|
+
const member = cur as AstNode & { object: AstNode; property: AstNode };
|
|
467
|
+
if (identifierName(member.object) === "field") {
|
|
468
|
+
const t = identifierName(member.property);
|
|
469
|
+
if (t === "text" || t === "integer") sqlType = t;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Also: field.text() ends as CallExpression with callee MemberExpression field.text
|
|
474
|
+
if (!sqlType && node.type === "CallExpression") {
|
|
475
|
+
// Re-walk to find field.text() / field.integer() as a call
|
|
476
|
+
let probe: AstNode | undefined = node;
|
|
477
|
+
while (probe && probe.type === "CallExpression") {
|
|
478
|
+
const call = probe as CallExpression;
|
|
479
|
+
const callee = call.callee;
|
|
480
|
+
if (callee.type === "MemberExpression") {
|
|
481
|
+
const member = callee as AstNode & { object: AstNode; property: AstNode };
|
|
482
|
+
if (
|
|
483
|
+
identifierName(member.object) === "field" &&
|
|
484
|
+
(identifierName(member.property) === "text" ||
|
|
485
|
+
identifierName(member.property) === "integer")
|
|
486
|
+
) {
|
|
487
|
+
sqlType = identifierName(member.property) as "text" | "integer";
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
probe = member.object;
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (!sqlType) return undefined;
|
|
498
|
+
|
|
499
|
+
const methods = new Set(chain);
|
|
500
|
+
const col: DeclaredColumn = {
|
|
501
|
+
type: sqlType,
|
|
502
|
+
sqlName: sqlName ?? camelToSnakeKey(key),
|
|
503
|
+
nullable: !(methods.has("notNull") || methods.has("primaryKey")),
|
|
504
|
+
...(methods.has("primaryKey") ? { primaryKey: true } : {}),
|
|
505
|
+
...(methods.has("unique") ? { unique: true } : {}),
|
|
506
|
+
...(hasDefault ? { default: defaultValue ?? null } : {}),
|
|
507
|
+
...(methods.has("pii") ? { pii: true } : {}),
|
|
508
|
+
...(methods.has("sensitive") ? { sensitive: true } : {}),
|
|
509
|
+
};
|
|
510
|
+
if (methods.has("retain")) {
|
|
511
|
+
// retain("7y") — find the call in the original chain by re-walk
|
|
512
|
+
let probe: AstNode | undefined = node;
|
|
513
|
+
while (probe && probe.type === "CallExpression") {
|
|
514
|
+
const call = probe as CallExpression;
|
|
515
|
+
const callee = call.callee;
|
|
516
|
+
if (callee.type === "MemberExpression") {
|
|
517
|
+
const member = callee as AstNode & { object: AstNode; property: AstNode };
|
|
518
|
+
if (identifierName(member.property) === "retain") {
|
|
519
|
+
const dur = stringArg(call.arguments[0]);
|
|
520
|
+
if (dur) col.retain = dur;
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
probe = member.object;
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (methods.has("references")) {
|
|
530
|
+
// .references(() => links.code) — record table.column when statically readable
|
|
531
|
+
let probe: AstNode | undefined = node;
|
|
532
|
+
while (probe && probe.type === "CallExpression") {
|
|
533
|
+
const call = probe as CallExpression;
|
|
534
|
+
const callee = call.callee;
|
|
535
|
+
if (callee.type === "MemberExpression") {
|
|
536
|
+
const member = callee as AstNode & { object: AstNode; property: AstNode };
|
|
537
|
+
if (identifierName(member.property) === "references") {
|
|
538
|
+
const ref = parseReferencesArg(call.arguments[0]);
|
|
539
|
+
if (ref) col.references = ref;
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
probe = member.object;
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return col;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Parse `.references(() => table.column)` into a Manifest reference.
|
|
553
|
+
*
|
|
554
|
+
* @param arg - First argument to `.references(...)`
|
|
555
|
+
*/
|
|
556
|
+
function parseReferencesArg(
|
|
557
|
+
arg: AstNode | undefined,
|
|
558
|
+
): { table?: string; column?: string } | undefined {
|
|
559
|
+
if (!arg) return undefined;
|
|
560
|
+
// ArrowFunctionExpression: () => links.code
|
|
561
|
+
if (arg.type === "ArrowFunctionExpression" || arg.type === "FunctionExpression") {
|
|
562
|
+
const fn = arg as AstNode & { body: AstNode };
|
|
563
|
+
const body = fn.body;
|
|
564
|
+
if (body.type === "MemberExpression") {
|
|
565
|
+
const member = body as AstNode & { object: AstNode; property: AstNode };
|
|
566
|
+
const table = identifierName(member.object);
|
|
567
|
+
const column = identifierName(member.property);
|
|
568
|
+
if (table || column) return { ...(table ? { table } : {}), ...(column ? { column } : {}) };
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return {};
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function camelToSnakeKey(key: string): string {
|
|
575
|
+
return key
|
|
576
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1_$2")
|
|
577
|
+
.replace(/([A-Z])([A-Z][a-z])/g, "$1_$2")
|
|
578
|
+
.toLowerCase();
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function visitDeclarationCall(call: CallExpression, program: AstNode, scope: ProjectScope): void {
|
|
283
582
|
const callee = call.callee;
|
|
284
583
|
|
|
285
584
|
// store.sql("name") / store.kv(...) / …
|
|
@@ -299,6 +598,9 @@ function visitDeclarationCall(
|
|
|
299
598
|
const ref = `${facet}:${storeName}` as const;
|
|
300
599
|
const bindingName = enclosingConstName(call, program);
|
|
301
600
|
scope.stores[storeName] = scope.stores[storeName] ?? { facet };
|
|
601
|
+
if (facet === "sql") {
|
|
602
|
+
attachSchemaOption(call.arguments[1], scope.stores[storeName]!, scope);
|
|
603
|
+
}
|
|
302
604
|
if (bindingName) {
|
|
303
605
|
scope.bindings.set(bindingName, {
|
|
304
606
|
kind: "store",
|
|
@@ -307,6 +609,35 @@ function visitDeclarationCall(
|
|
|
307
609
|
});
|
|
308
610
|
}
|
|
309
611
|
}
|
|
612
|
+
|
|
613
|
+
// store.resource(db, table, { … }) — remember for on(http.resource(…))
|
|
614
|
+
if (prop === "resource") {
|
|
615
|
+
const bindingName = enclosingConstName(call, program);
|
|
616
|
+
const dbName = identifierName(call.arguments[0]);
|
|
617
|
+
const storeBinding = dbName ? scope.bindings.get(dbName) : undefined;
|
|
618
|
+
const ref =
|
|
619
|
+
storeBinding?.kind === "store" ? storeBinding.ref : (`sql:${dbName ?? "store"}` as const);
|
|
620
|
+
const storeName = ref.split(":")[1] ?? dbName ?? "store";
|
|
621
|
+
const unit = stringProp(objectArg(call.arguments[2]) ?? ({} as never), "unit");
|
|
622
|
+
const opts = objectArg(call.arguments[2]);
|
|
623
|
+
const breaking = opts ? boolProp(opts, "breaking") === true : false;
|
|
624
|
+
if (bindingName) {
|
|
625
|
+
scope.resources.set(bindingName, {
|
|
626
|
+
storeName,
|
|
627
|
+
storeRef: ref,
|
|
628
|
+
unit,
|
|
629
|
+
...(breaking ? { breaking: true } : {}),
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// store.schema.table("notes", { … })
|
|
636
|
+
if (prop === "table" && member.object.type === "MemberExpression") {
|
|
637
|
+
const inner = member.object as AstNode & { object: AstNode; property: AstNode };
|
|
638
|
+
if (identifierName(inner.object) === "store" && identifierName(inner.property) === "schema") {
|
|
639
|
+
collectSchemaTable(call, program, scope);
|
|
640
|
+
}
|
|
310
641
|
}
|
|
311
642
|
|
|
312
643
|
if (obj === "gate" && prop === "policy") {
|
|
@@ -326,8 +657,7 @@ function visitDeclarationCall(
|
|
|
326
657
|
|
|
327
658
|
if (obj === "gate" && prop === "rate") {
|
|
328
659
|
const opts = objectArg(call.arguments[0]);
|
|
329
|
-
const strategy = (stringProp(opts, "strategy") ??
|
|
330
|
-
"sliding-window-counter") as RateStrategy;
|
|
660
|
+
const strategy = (stringProp(opts, "strategy") ?? "sliding-window-counter") as RateStrategy;
|
|
331
661
|
const max = numberProp(opts, "max");
|
|
332
662
|
const per = stringProp(opts, "per");
|
|
333
663
|
const keyBy = stringProp(opts, "keyBy");
|
|
@@ -362,9 +692,7 @@ function visitDeclarationCall(
|
|
|
362
692
|
const medium = stringProp(opts, "medium");
|
|
363
693
|
const locales = stringArrayProp(opts, "locales");
|
|
364
694
|
scope.channels[templateName] = {
|
|
365
|
-
...(medium
|
|
366
|
-
? { medium: medium as Channel["medium"] }
|
|
367
|
-
: { medium: "email" }),
|
|
695
|
+
...(medium ? { medium: medium as Channel["medium"] } : { medium: "email" }),
|
|
368
696
|
...(locales ? { locales } : {}),
|
|
369
697
|
};
|
|
370
698
|
const bindingName = enclosingConstName(call, program);
|
|
@@ -382,12 +710,8 @@ function visitDeclarationCall(
|
|
|
382
710
|
const opts = objectArg(call.arguments[1]);
|
|
383
711
|
if (modelName) {
|
|
384
712
|
const model: AiModel = {
|
|
385
|
-
...(stringProp(opts, "provider")
|
|
386
|
-
|
|
387
|
-
: {}),
|
|
388
|
-
...(stringProp(opts, "tier")
|
|
389
|
-
? { tier: stringProp(opts, "tier") }
|
|
390
|
-
: {}),
|
|
713
|
+
...(stringProp(opts, "provider") ? { provider: stringProp(opts, "provider") } : {}),
|
|
714
|
+
...(stringProp(opts, "tier") ? { tier: stringProp(opts, "tier") } : {}),
|
|
391
715
|
};
|
|
392
716
|
scope.ai.models = scope.ai.models ?? {};
|
|
393
717
|
scope.ai.models[modelName] = model;
|
|
@@ -463,9 +787,7 @@ function visitDeclarationCall(
|
|
|
463
787
|
...(stringProp(opts, "description")
|
|
464
788
|
? { description: stringProp(opts, "description") }
|
|
465
789
|
: {}),
|
|
466
|
-
...(stringProp(opts, "rotate")
|
|
467
|
-
? { rotate: stringProp(opts, "rotate") }
|
|
468
|
-
: {}),
|
|
790
|
+
...(stringProp(opts, "rotate") ? { rotate: stringProp(opts, "rotate") } : {}),
|
|
469
791
|
};
|
|
470
792
|
const bindingName = enclosingConstName(call, program);
|
|
471
793
|
if (bindingName) {
|
|
@@ -512,9 +834,7 @@ function visitDeclarationCall(
|
|
|
512
834
|
const opts = objectArg(call.arguments[1]);
|
|
513
835
|
if (name && opts) {
|
|
514
836
|
scope.clocks[name] = {
|
|
515
|
-
...(stringProp(opts, "every")
|
|
516
|
-
? { every: stringProp(opts, "every") }
|
|
517
|
-
: {}),
|
|
837
|
+
...(stringProp(opts, "every") ? { every: stringProp(opts, "every") } : {}),
|
|
518
838
|
...(stringProp(opts, "cron") ? { cron: stringProp(opts, "cron") } : {}),
|
|
519
839
|
...(boolProp(opts, "overridable") !== undefined
|
|
520
840
|
? { overridable: boolProp(opts, "overridable") }
|
|
@@ -547,9 +867,7 @@ function visitDeclarationCall(
|
|
|
547
867
|
},
|
|
548
868
|
}
|
|
549
869
|
: {}),
|
|
550
|
-
...(stringProp(opts, "composes")
|
|
551
|
-
? { composes: stringProp(opts, "composes") }
|
|
552
|
-
: {}),
|
|
870
|
+
...(stringProp(opts, "composes") ? { composes: stringProp(opts, "composes") } : {}),
|
|
553
871
|
...(flows && flows.length > 0 ? { flows } : {}),
|
|
554
872
|
};
|
|
555
873
|
scope.journeys[name] = journey;
|
|
@@ -570,9 +888,7 @@ function visitDeclarationCall(
|
|
|
570
888
|
...(stringProp(opts, "description")
|
|
571
889
|
? { description: stringProp(opts, "description") }
|
|
572
890
|
: {}),
|
|
573
|
-
...(stringProp(opts, "rotate")
|
|
574
|
-
? { rotate: stringProp(opts, "rotate") }
|
|
575
|
-
: {}),
|
|
891
|
+
...(stringProp(opts, "rotate") ? { rotate: stringProp(opts, "rotate") } : {}),
|
|
576
892
|
};
|
|
577
893
|
const bindingName = enclosingConstName(call, program);
|
|
578
894
|
if (bindingName) {
|
|
@@ -601,20 +917,13 @@ function collectAgent(call: CallExpression, scope: ProjectScope): void {
|
|
|
601
917
|
scope.ai.agents[agentName] = agent;
|
|
602
918
|
}
|
|
603
919
|
|
|
604
|
-
function collectConfig(
|
|
605
|
-
opts: AstNode | undefined,
|
|
606
|
-
scope: ProjectScope,
|
|
607
|
-
): void {
|
|
920
|
+
function collectConfig(opts: AstNode | undefined, scope: ProjectScope): void {
|
|
608
921
|
if (!opts) return;
|
|
609
922
|
|
|
610
923
|
const tenancy = objectProp(opts, "tenancy");
|
|
611
924
|
if (tenancy) {
|
|
612
925
|
const isolation = stringProp(tenancy, "isolation");
|
|
613
|
-
if (
|
|
614
|
-
isolation === "row" ||
|
|
615
|
-
isolation === "schema" ||
|
|
616
|
-
isolation === "database"
|
|
617
|
-
) {
|
|
926
|
+
if (isolation === "row" || isolation === "schema" || isolation === "database") {
|
|
618
927
|
scope.tenancy = { isolation };
|
|
619
928
|
}
|
|
620
929
|
}
|
|
@@ -622,21 +931,15 @@ function collectConfig(
|
|
|
622
931
|
const i18n = objectProp(opts, "i18n");
|
|
623
932
|
if (i18n) {
|
|
624
933
|
scope.i18n = {
|
|
625
|
-
...(stringArrayProp(i18n, "locales")
|
|
626
|
-
|
|
627
|
-
: {}),
|
|
628
|
-
...(stringProp(i18n, "default")
|
|
629
|
-
? { default: stringProp(i18n, "default") }
|
|
630
|
-
: {}),
|
|
934
|
+
...(stringArrayProp(i18n, "locales") ? { locales: stringArrayProp(i18n, "locales") } : {}),
|
|
935
|
+
...(stringProp(i18n, "default") ? { default: stringProp(i18n, "default") } : {}),
|
|
631
936
|
};
|
|
632
937
|
const dir = objectProp(i18n, "dir");
|
|
633
938
|
if (dir && dir.type === "ObjectExpression") {
|
|
634
939
|
const dirMap: Record<string, "ltr" | "rtl"> = {};
|
|
635
940
|
for (const prop of objectProperties(dir)) {
|
|
636
941
|
const key = propKey(prop);
|
|
637
|
-
const val = stringArg(
|
|
638
|
-
(prop as AstNode & { value?: AstNode }).value,
|
|
639
|
-
);
|
|
942
|
+
const val = stringArg((prop as AstNode & { value?: AstNode }).value);
|
|
640
943
|
if (key && (val === "ltr" || val === "rtl")) dirMap[key] = val;
|
|
641
944
|
}
|
|
642
945
|
if (Object.keys(dirMap).length > 0) {
|
|
@@ -666,9 +969,7 @@ function collectConfig(
|
|
|
666
969
|
if (drivers) {
|
|
667
970
|
const prodArr = arrayProp(drivers, "prod");
|
|
668
971
|
if (prodArr && prodArr.length > 0 && prodArr.every((el) => stringArg(el))) {
|
|
669
|
-
scope.drivers.prod = prodArr
|
|
670
|
-
.map((el) => stringArg(el)!)
|
|
671
|
-
.filter((x) => x.length > 0);
|
|
972
|
+
scope.drivers.prod = prodArr.map((el) => stringArg(el)!).filter((x) => x.length > 0);
|
|
672
973
|
} else {
|
|
673
974
|
const prod = new Set<string>();
|
|
674
975
|
collectDriverProtocols(drivers, "prod", prod);
|
|
@@ -677,11 +978,7 @@ function collectConfig(
|
|
|
677
978
|
}
|
|
678
979
|
}
|
|
679
980
|
|
|
680
|
-
function collectDriverProtocols(
|
|
681
|
-
node: AstNode,
|
|
682
|
-
env: string,
|
|
683
|
-
into: Set<string>,
|
|
684
|
-
): void {
|
|
981
|
+
function collectDriverProtocols(node: AstNode, env: string, into: Set<string>): void {
|
|
685
982
|
if (node.type !== "ObjectExpression") return;
|
|
686
983
|
for (const prop of objectProperties(node)) {
|
|
687
984
|
const key = propKey(prop);
|
|
@@ -704,10 +1001,7 @@ function collectDriverProtocols(
|
|
|
704
1001
|
// role: { dev, test, prod } or nested store: { sql: { prod } }
|
|
705
1002
|
const envVal = objectProp(value, env);
|
|
706
1003
|
if (envVal) {
|
|
707
|
-
if (
|
|
708
|
-
envVal.type === "Literal" &&
|
|
709
|
-
typeof (envVal as Literal).value === "string"
|
|
710
|
-
) {
|
|
1004
|
+
if (envVal.type === "Literal" && typeof (envVal as Literal).value === "string") {
|
|
711
1005
|
into.add((envVal as Literal).value as string);
|
|
712
1006
|
} else if (envVal.type === "ObjectExpression") {
|
|
713
1007
|
const driver = stringProp(envVal, "driver");
|
|
@@ -720,11 +1014,7 @@ function collectDriverProtocols(
|
|
|
720
1014
|
}
|
|
721
1015
|
}
|
|
722
1016
|
|
|
723
|
-
function collectFlows(
|
|
724
|
-
file: SourceFile,
|
|
725
|
-
program: AstNode,
|
|
726
|
-
scope: ProjectScope,
|
|
727
|
-
): void {
|
|
1017
|
+
function collectFlows(file: SourceFile, program: AstNode, scope: ProjectScope): void {
|
|
728
1018
|
walk(program, (node) => {
|
|
729
1019
|
if (node.type !== "CallExpression") return;
|
|
730
1020
|
const call = node as CallExpression;
|
|
@@ -733,7 +1023,14 @@ function collectFlows(
|
|
|
733
1023
|
if (callee === "on") {
|
|
734
1024
|
const triggerNode = call.arguments[0];
|
|
735
1025
|
const flowNode = call.arguments[1];
|
|
736
|
-
if (!triggerNode
|
|
1026
|
+
if (!triggerNode) return;
|
|
1027
|
+
|
|
1028
|
+
// on(http.resource(path, bag)) — expand the mount into five bindings.
|
|
1029
|
+
if (!flowNode) {
|
|
1030
|
+
registerResourceMount(call, triggerNode, file, program, scope);
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
737
1034
|
const flowCall = unwrapFlowCall(flowNode);
|
|
738
1035
|
if (!flowCall) return;
|
|
739
1036
|
const exportName = enclosingConstName(call, program);
|
|
@@ -780,7 +1077,10 @@ function isOnFlowArgument(flowCall: CallExpression, program: AstNode): boolean {
|
|
|
780
1077
|
}
|
|
781
1078
|
|
|
782
1079
|
function unwrapFlowCall(node: AstNode): CallExpression | undefined {
|
|
783
|
-
if (
|
|
1080
|
+
if (
|
|
1081
|
+
node.type === "CallExpression" &&
|
|
1082
|
+
identifierName((node as CallExpression).callee) === "flow"
|
|
1083
|
+
) {
|
|
784
1084
|
return node as CallExpression;
|
|
785
1085
|
}
|
|
786
1086
|
return undefined;
|
|
@@ -845,15 +1145,11 @@ function registerFlow(args: {
|
|
|
845
1145
|
}
|
|
846
1146
|
}
|
|
847
1147
|
|
|
848
|
-
const trigger = args.triggerNode
|
|
849
|
-
? parseTrigger(args.triggerNode, args.scope)
|
|
850
|
-
: undefined;
|
|
1148
|
+
const trigger = args.triggerNode ? parseTrigger(args.triggerNode, args.scope) : undefined;
|
|
851
1149
|
|
|
852
1150
|
const gates = trigger?.gates;
|
|
853
1151
|
const liveFromTrigger = trigger?.live;
|
|
854
|
-
const manifestTrigger = trigger
|
|
855
|
-
? stripTriggerExtras(trigger.trigger)
|
|
856
|
-
: undefined;
|
|
1152
|
+
const manifestTrigger = trigger ? stripTriggerExtras(trigger.trigger) : undefined;
|
|
857
1153
|
|
|
858
1154
|
const flow: Flow = {};
|
|
859
1155
|
|
|
@@ -877,15 +1173,13 @@ function registerFlow(args: {
|
|
|
877
1173
|
|
|
878
1174
|
if (boolProp(opts, "durable")) flow.durable = true;
|
|
879
1175
|
if (boolProp(opts, "live") || liveFromTrigger) flow.live = true;
|
|
1176
|
+
if (boolProp(opts, "breaking")) flow.breaking = true;
|
|
880
1177
|
|
|
881
1178
|
const slo = parseSlo(objectProp(opts, "slo"));
|
|
882
1179
|
if (slo) flow.slo = slo;
|
|
883
1180
|
|
|
884
1181
|
const stepsFromOpts = stringArrayProp(opts, "steps");
|
|
885
|
-
const steps =
|
|
886
|
-
stepsFromOpts && stepsFromOpts.length > 0
|
|
887
|
-
? stepsFromOpts
|
|
888
|
-
: inferred.steps;
|
|
1182
|
+
const steps = stepsFromOpts && stepsFromOpts.length > 0 ? stepsFromOpts : inferred.steps;
|
|
889
1183
|
if (steps.length > 0) flow.steps = steps;
|
|
890
1184
|
|
|
891
1185
|
if (boolProp(opts, "nondeterministic") || inferred.nondeterministic) {
|
|
@@ -898,17 +1192,14 @@ function registerFlow(args: {
|
|
|
898
1192
|
...(numberProp(cost, "estimatePerCall") !== undefined
|
|
899
1193
|
? { estimatePerCall: numberProp(cost, "estimatePerCall") }
|
|
900
1194
|
: {}),
|
|
901
|
-
...(numberProp(cost, "budget") !== undefined
|
|
902
|
-
? { budget: numberProp(cost, "budget") }
|
|
903
|
-
: {}),
|
|
1195
|
+
...(numberProp(cost, "budget") !== undefined ? { budget: numberProp(cost, "budget") } : {}),
|
|
904
1196
|
};
|
|
905
1197
|
} else if (inferred.nondeterministic) {
|
|
906
1198
|
// Derive budget from the first ask prompt when present.
|
|
907
1199
|
const ask = effects?.asks?.[0];
|
|
908
1200
|
if (ask) {
|
|
909
1201
|
const promptName = ask.split("@")[0]!;
|
|
910
|
-
const promptBudget = args.scope.ai.prompts?.[promptName]?.budget
|
|
911
|
-
?.maxCostPerCall;
|
|
1202
|
+
const promptBudget = args.scope.ai.prompts?.[promptName]?.budget?.maxCostPerCall;
|
|
912
1203
|
if (promptBudget !== undefined) {
|
|
913
1204
|
flow.cost = {
|
|
914
1205
|
estimatePerCall: Number((promptBudget * 0.55).toFixed(3)),
|
|
@@ -935,8 +1226,7 @@ function registerFlow(args: {
|
|
|
935
1226
|
if (inferred.cacheIneligible) {
|
|
936
1227
|
flow.cache = false;
|
|
937
1228
|
} else {
|
|
938
|
-
const cache = (opts &&
|
|
939
|
-
objectProperties(opts).find((p) => propKey(p) === "cache")) as
|
|
1229
|
+
const cache = (opts && objectProperties(opts).find((p) => propKey(p) === "cache")) as
|
|
940
1230
|
| AstNode
|
|
941
1231
|
| undefined;
|
|
942
1232
|
if (cache) {
|
|
@@ -958,6 +1248,93 @@ function registerFlow(args: {
|
|
|
958
1248
|
args.scope.flows[name] = flow;
|
|
959
1249
|
}
|
|
960
1250
|
|
|
1251
|
+
/**
|
|
1252
|
+
* Expand `on(http.resource(path, bag))` into the five CRUD flows.
|
|
1253
|
+
*
|
|
1254
|
+
* The flows are synthesized at runtime by `store.resource(…)`; statically we
|
|
1255
|
+
* register list/create/get/update/remove with their HTTP triggers and the
|
|
1256
|
+
* resource's store effects so the Manifest stays complete.
|
|
1257
|
+
*
|
|
1258
|
+
* @param onCall - The outer `on(…)` call
|
|
1259
|
+
* @param triggerNode - `http.resource(…)` expression
|
|
1260
|
+
* @param file - Source file
|
|
1261
|
+
* @param program - Program root (for export-name lookup)
|
|
1262
|
+
* @param scope - Project scope
|
|
1263
|
+
*/
|
|
1264
|
+
function registerResourceMount(
|
|
1265
|
+
onCall: CallExpression,
|
|
1266
|
+
triggerNode: AstNode,
|
|
1267
|
+
file: SourceFile,
|
|
1268
|
+
program: AstNode,
|
|
1269
|
+
scope: ProjectScope,
|
|
1270
|
+
): void {
|
|
1271
|
+
if (triggerNode.type !== "CallExpression") return;
|
|
1272
|
+
const httpCall = triggerNode as CallExpression;
|
|
1273
|
+
const callee = httpCall.callee;
|
|
1274
|
+
if (callee.type !== "MemberExpression") return;
|
|
1275
|
+
const member = callee as AstNode & { object: AstNode; property: AstNode };
|
|
1276
|
+
if (identifierName(member.object) !== "http") return;
|
|
1277
|
+
if (identifierName(member.property) !== "resource") return;
|
|
1278
|
+
|
|
1279
|
+
const path = stringArg(httpCall.arguments[0]);
|
|
1280
|
+
if (!path) return;
|
|
1281
|
+
|
|
1282
|
+
// bag: `notesR.all()` → resolve `notesR` to its store.resource declaration.
|
|
1283
|
+
const bagNode = httpCall.arguments[1];
|
|
1284
|
+
let baseName: string | undefined;
|
|
1285
|
+
if (bagNode?.type === "CallExpression") {
|
|
1286
|
+
const bagCallee = (bagNode as CallExpression).callee;
|
|
1287
|
+
if (bagCallee.type === "MemberExpression") {
|
|
1288
|
+
baseName = identifierName((bagCallee as AstNode & { object: AstNode }).object);
|
|
1289
|
+
}
|
|
1290
|
+
} else if (bagNode?.type === "Identifier") {
|
|
1291
|
+
baseName = identifierName(bagNode);
|
|
1292
|
+
}
|
|
1293
|
+
const resource = baseName ? scope.resources.get(baseName) : undefined;
|
|
1294
|
+
|
|
1295
|
+
const unit = resource?.unit;
|
|
1296
|
+
const storeRef = resource?.storeRef as Effects["reads"] extends readonly (infer R)[] | undefined
|
|
1297
|
+
? R
|
|
1298
|
+
: never;
|
|
1299
|
+
const effects: Effects | undefined = storeRef
|
|
1300
|
+
? { reads: [storeRef], writes: [storeRef] }
|
|
1301
|
+
: undefined;
|
|
1302
|
+
const idPath = `${path}/:id`;
|
|
1303
|
+
const line = lineAt(file.source, onCall.start ?? 0);
|
|
1304
|
+
|
|
1305
|
+
const verbs = [
|
|
1306
|
+
{ op: "list", method: "GET", p: path, eff: storeRef ? { reads: [storeRef] } : undefined },
|
|
1307
|
+
{ op: "create", method: "POST", p: path, eff: storeRef ? { writes: [storeRef] } : undefined },
|
|
1308
|
+
{ op: "get", method: "GET", p: idPath, eff: storeRef ? { reads: [storeRef] } : undefined },
|
|
1309
|
+
{ op: "update", method: "PATCH", p: idPath, eff: effects },
|
|
1310
|
+
{
|
|
1311
|
+
op: "remove",
|
|
1312
|
+
method: "DELETE",
|
|
1313
|
+
p: idPath,
|
|
1314
|
+
eff: storeRef ? { writes: [storeRef] } : undefined,
|
|
1315
|
+
},
|
|
1316
|
+
] as const;
|
|
1317
|
+
|
|
1318
|
+
for (const v of verbs) {
|
|
1319
|
+
// Short op names match `export const list = mounted.list` so Manifest Diff
|
|
1320
|
+
// keeps flow identity across a handwritten → store.resource migration.
|
|
1321
|
+
const name = v.op;
|
|
1322
|
+
const flow: Flow = {
|
|
1323
|
+
trigger: { http: { method: v.method as never, path: v.p } },
|
|
1324
|
+
...(v.eff ? { effects: v.eff as Effects } : {}),
|
|
1325
|
+
...(resource?.breaking ? { breaking: true } : {}),
|
|
1326
|
+
source: `${file.path}:${line}`,
|
|
1327
|
+
};
|
|
1328
|
+
scope.flows[name] = flow;
|
|
1329
|
+
scope.bindings.set(name, { kind: "flow", ref: name });
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
const exportName = enclosingConstName(onCall, program);
|
|
1333
|
+
if (exportName) {
|
|
1334
|
+
scope.bindings.set(exportName, { kind: "flow", ref: unit ?? baseName ?? exportName });
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
|
|
961
1338
|
interface ParsedTrigger {
|
|
962
1339
|
trigger: Trigger;
|
|
963
1340
|
gates?: string[];
|
|
@@ -1002,10 +1379,7 @@ function parseTrigger(node: AstNode, scope: ProjectScope): ParsedTrigger | undef
|
|
|
1002
1379
|
return undefined;
|
|
1003
1380
|
}
|
|
1004
1381
|
|
|
1005
|
-
function parseHttpTrigger(
|
|
1006
|
-
call: CallExpression,
|
|
1007
|
-
scope: ProjectScope,
|
|
1008
|
-
): ParsedTrigger | undefined {
|
|
1382
|
+
function parseHttpTrigger(call: CallExpression, scope: ProjectScope): ParsedTrigger | undefined {
|
|
1009
1383
|
// Walk the chain: http.METHOD(path).gate(...).live()
|
|
1010
1384
|
let current: AstNode = call;
|
|
1011
1385
|
let method: string | undefined;
|
|
@@ -1059,15 +1433,7 @@ function parseHttpTrigger(
|
|
|
1059
1433
|
|
|
1060
1434
|
if (!method || !path) return undefined;
|
|
1061
1435
|
|
|
1062
|
-
const httpMethods = [
|
|
1063
|
-
"GET",
|
|
1064
|
-
"POST",
|
|
1065
|
-
"PUT",
|
|
1066
|
-
"PATCH",
|
|
1067
|
-
"DELETE",
|
|
1068
|
-
"HEAD",
|
|
1069
|
-
"OPTIONS",
|
|
1070
|
-
] as const;
|
|
1436
|
+
const httpMethods = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"] as const;
|
|
1071
1437
|
if (!(httpMethods as readonly string[]).includes(method)) return undefined;
|
|
1072
1438
|
|
|
1073
1439
|
return {
|
|
@@ -1082,9 +1448,7 @@ function parseHttpTrigger(
|
|
|
1082
1448
|
};
|
|
1083
1449
|
}
|
|
1084
1450
|
|
|
1085
|
-
function parseCdcTrigger(
|
|
1086
|
-
call: CallExpression,
|
|
1087
|
-
): Trigger["cdc"] | undefined {
|
|
1451
|
+
function parseCdcTrigger(call: CallExpression): Trigger["cdc"] | undefined {
|
|
1088
1452
|
// *.changed("col") or *.changed()
|
|
1089
1453
|
const callee = call.callee;
|
|
1090
1454
|
if (callee.type !== "MemberExpression") return undefined;
|
|
@@ -1098,25 +1462,17 @@ function parseCdcTrigger(
|
|
|
1098
1462
|
const inner = obj as CallExpression;
|
|
1099
1463
|
const innerCallee = inner.callee;
|
|
1100
1464
|
if (identifierName(innerCallee) === "table") {
|
|
1101
|
-
const tableName =
|
|
1102
|
-
stringArg(inner.arguments[0]) ?? identifierName(inner.arguments[0]);
|
|
1465
|
+
const tableName = stringArg(inner.arguments[0]) ?? identifierName(inner.arguments[0]);
|
|
1103
1466
|
if (!tableName) return undefined;
|
|
1104
|
-
return column
|
|
1105
|
-
? { table: tableName, column }
|
|
1106
|
-
: { table: tableName };
|
|
1467
|
+
return column ? { table: tableName, column } : { table: tableName };
|
|
1107
1468
|
}
|
|
1108
1469
|
if (
|
|
1109
1470
|
innerCallee.type === "MemberExpression" &&
|
|
1110
|
-
identifierName(
|
|
1111
|
-
(innerCallee as AstNode & { property: AstNode }).property,
|
|
1112
|
-
) === "table"
|
|
1471
|
+
identifierName((innerCallee as AstNode & { property: AstNode }).property) === "table"
|
|
1113
1472
|
) {
|
|
1114
|
-
const tableName =
|
|
1115
|
-
stringArg(inner.arguments[0]) ?? identifierName(inner.arguments[0]);
|
|
1473
|
+
const tableName = stringArg(inner.arguments[0]) ?? identifierName(inner.arguments[0]);
|
|
1116
1474
|
if (!tableName) return undefined;
|
|
1117
|
-
return column
|
|
1118
|
-
? { table: tableName, column }
|
|
1119
|
-
: { table: tableName };
|
|
1475
|
+
return column ? { table: tableName, column } : { table: tableName };
|
|
1120
1476
|
}
|
|
1121
1477
|
}
|
|
1122
1478
|
return undefined;
|
|
@@ -1146,9 +1502,7 @@ function parseEffectsObject(node: AstNode | undefined): Effects | undefined {
|
|
|
1146
1502
|
return effects;
|
|
1147
1503
|
}
|
|
1148
1504
|
|
|
1149
|
-
function parseErrors(
|
|
1150
|
-
node: AstNode | undefined,
|
|
1151
|
-
): Flow["errors"] | undefined {
|
|
1505
|
+
function parseErrors(node: AstNode | undefined): Flow["errors"] | undefined {
|
|
1152
1506
|
if (!node) return undefined;
|
|
1153
1507
|
if (node.type === "ObjectExpression") {
|
|
1154
1508
|
const names: string[] = [];
|
|
@@ -1187,10 +1541,7 @@ function parseSlo(node: AstNode | undefined): Slo | undefined {
|
|
|
1187
1541
|
};
|
|
1188
1542
|
}
|
|
1189
1543
|
|
|
1190
|
-
function schemaProp(
|
|
1191
|
-
obj: AstNode | undefined,
|
|
1192
|
-
key: string,
|
|
1193
|
-
): string | undefined {
|
|
1544
|
+
function schemaProp(obj: AstNode | undefined, key: string): string | undefined {
|
|
1194
1545
|
const node = objectProp(obj, key);
|
|
1195
1546
|
if (!node) return undefined;
|
|
1196
1547
|
// Identifier / member schemas → opaque placeholder matching the spec excerpt.
|
|
@@ -1205,10 +1556,7 @@ function schemaProp(
|
|
|
1205
1556
|
|
|
1206
1557
|
// ── AST helpers ────────────────────────────────────────────────────────────
|
|
1207
1558
|
|
|
1208
|
-
function enclosingConstName(
|
|
1209
|
-
call: CallExpression,
|
|
1210
|
-
program: AstNode,
|
|
1211
|
-
): string | undefined {
|
|
1559
|
+
function enclosingConstName(call: CallExpression, program: AstNode): string | undefined {
|
|
1212
1560
|
const targetStart = call.start;
|
|
1213
1561
|
if (targetStart === undefined) return undefined;
|
|
1214
1562
|
let found: string | undefined;
|
|
@@ -1239,10 +1587,7 @@ function objectArg(node: AstNode | undefined): AstNode | undefined {
|
|
|
1239
1587
|
return undefined;
|
|
1240
1588
|
}
|
|
1241
1589
|
|
|
1242
|
-
function objectProp(
|
|
1243
|
-
obj: AstNode | undefined,
|
|
1244
|
-
key: string,
|
|
1245
|
-
): AstNode | undefined {
|
|
1590
|
+
function objectProp(obj: AstNode | undefined, key: string): AstNode | undefined {
|
|
1246
1591
|
if (!obj || obj.type !== "ObjectExpression") return undefined;
|
|
1247
1592
|
for (const prop of objectProperties(obj)) {
|
|
1248
1593
|
if (propKey(prop) === key) {
|
|
@@ -1253,9 +1598,9 @@ function objectProp(
|
|
|
1253
1598
|
}
|
|
1254
1599
|
|
|
1255
1600
|
function objectProperties(obj: AstNode): AstNode[] {
|
|
1256
|
-
return (
|
|
1257
|
-
(
|
|
1258
|
-
)
|
|
1601
|
+
return ((obj as AstNode & { properties?: AstNode[] }).properties ?? []).filter(
|
|
1602
|
+
(p) => p.type === "Property" || p.type === "ObjectProperty",
|
|
1603
|
+
);
|
|
1259
1604
|
}
|
|
1260
1605
|
|
|
1261
1606
|
function propKey(prop: AstNode): string | undefined {
|
|
@@ -1268,50 +1613,33 @@ function propKey(prop: AstNode): string | undefined {
|
|
|
1268
1613
|
return undefined;
|
|
1269
1614
|
}
|
|
1270
1615
|
|
|
1271
|
-
function stringProp(
|
|
1272
|
-
obj: AstNode | undefined,
|
|
1273
|
-
key: string,
|
|
1274
|
-
): string | undefined {
|
|
1616
|
+
function stringProp(obj: AstNode | undefined, key: string): string | undefined {
|
|
1275
1617
|
return stringArg(objectProp(obj, key));
|
|
1276
1618
|
}
|
|
1277
1619
|
|
|
1278
|
-
function numberProp(
|
|
1279
|
-
obj: AstNode | undefined,
|
|
1280
|
-
key: string,
|
|
1281
|
-
): number | undefined {
|
|
1620
|
+
function numberProp(obj: AstNode | undefined, key: string): number | undefined {
|
|
1282
1621
|
const node = objectProp(obj, key);
|
|
1283
1622
|
if (!node || node.type !== "Literal") return undefined;
|
|
1284
1623
|
const v = (node as Literal).value;
|
|
1285
1624
|
return typeof v === "number" ? v : undefined;
|
|
1286
1625
|
}
|
|
1287
1626
|
|
|
1288
|
-
function boolProp(
|
|
1289
|
-
obj: AstNode | undefined,
|
|
1290
|
-
key: string,
|
|
1291
|
-
): boolean | undefined {
|
|
1627
|
+
function boolProp(obj: AstNode | undefined, key: string): boolean | undefined {
|
|
1292
1628
|
const node = objectProp(obj, key);
|
|
1293
1629
|
if (!node || node.type !== "Literal") return undefined;
|
|
1294
1630
|
const v = (node as Literal).value;
|
|
1295
1631
|
return typeof v === "boolean" ? v : undefined;
|
|
1296
1632
|
}
|
|
1297
1633
|
|
|
1298
|
-
function stringArrayProp(
|
|
1299
|
-
obj: AstNode | undefined,
|
|
1300
|
-
key: string,
|
|
1301
|
-
): string[] | undefined {
|
|
1634
|
+
function stringArrayProp(obj: AstNode | undefined, key: string): string[] | undefined {
|
|
1302
1635
|
const node = objectProp(obj, key);
|
|
1303
1636
|
if (!node || node.type !== "ArrayExpression") return undefined;
|
|
1304
1637
|
const els = (node as AstNode & { elements?: AstNode[] }).elements ?? [];
|
|
1305
|
-
const out = els
|
|
1306
|
-
.map((el) => stringArg(el))
|
|
1307
|
-
.filter((x): x is string => typeof x === "string");
|
|
1638
|
+
const out = els.map((el) => stringArg(el)).filter((x): x is string => typeof x === "string");
|
|
1308
1639
|
return out;
|
|
1309
1640
|
}
|
|
1310
1641
|
|
|
1311
|
-
function arrayProp(
|
|
1312
|
-
obj: AstNode | undefined,
|
|
1313
|
-
key: string,
|
|
1314
|
-
): AstNode[] | undefined {
|
|
1642
|
+
function arrayProp(obj: AstNode | undefined, key: string): AstNode[] | undefined {
|
|
1315
1643
|
const node = objectProp(obj, key);
|
|
1316
1644
|
if (!node || node.type !== "ArrayExpression") return undefined;
|
|
1317
1645
|
return ((node as AstNode & { elements?: AstNode[] }).elements ?? []).filter(
|