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
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract store schema — ORM-agnostic declare site (`field.*` + `store.schema.table`).
|
|
3
|
+
*
|
|
4
|
+
* Survey-backed v1 primitives: `text` · `integer` + modifiers + PII tags + FKs.
|
|
5
|
+
* Dialect-specific Drizzle is emitted separately ({@link emitDrizzleSource}).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ColumnClassification } from "../../manifest/types.ts";
|
|
9
|
+
import type { ColumnDef, TableHandle } from "./table.ts";
|
|
10
|
+
import { id as idHelper, now as nowHelper } from "./table.ts";
|
|
11
|
+
|
|
12
|
+
/** SQL type primitives supported in v1. */
|
|
13
|
+
export type FieldSqlType = "text" | "integer";
|
|
14
|
+
|
|
15
|
+
/** Known `$defaultFn` helpers recognized by the Drizzle emitter. */
|
|
16
|
+
export type DefaultFnKind = "id" | "now" | "custom";
|
|
17
|
+
|
|
18
|
+
/** FK action — mirrors Drizzle `UpdateDeleteAction`. */
|
|
19
|
+
export type ReferenceAction = "cascade" | "restrict" | "no action" | "set null" | "set default";
|
|
20
|
+
|
|
21
|
+
/** Optional ON DELETE / ON UPDATE for {@link FieldBuilder.references}. */
|
|
22
|
+
export interface ReferenceActions {
|
|
23
|
+
readonly onDelete?: ReferenceAction;
|
|
24
|
+
readonly onUpdate?: ReferenceAction;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Resolved foreign-key target (lazy ref evaluated at finalize/emit). */
|
|
28
|
+
export interface ColumnReference {
|
|
29
|
+
/** Lazy target column (evaluated when emitting / finalizing). */
|
|
30
|
+
readonly ref: () => SchemaColumnDecl;
|
|
31
|
+
readonly actions?: ReferenceActions;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Finalized column declaration — also satisfies {@link ColumnDef} so
|
|
36
|
+
* {@link classificationsFromTable} / runtime masking work without Drizzle.
|
|
37
|
+
*/
|
|
38
|
+
export interface SchemaColumnDecl extends ColumnDef {
|
|
39
|
+
/** JS object key (e.g. `createdAt`). */
|
|
40
|
+
readonly key: string;
|
|
41
|
+
/** Database column name (e.g. `created_at`). */
|
|
42
|
+
readonly sqlName: string;
|
|
43
|
+
/** Declared SQL type. */
|
|
44
|
+
readonly sqlType: FieldSqlType;
|
|
45
|
+
readonly primaryKey: boolean;
|
|
46
|
+
readonly notNull: boolean;
|
|
47
|
+
readonly unique: boolean;
|
|
48
|
+
/** Literal `.default(v)` when set. */
|
|
49
|
+
readonly defaultValue?: string | number | boolean | null;
|
|
50
|
+
/** Runtime default applied on insert when the key is missing. */
|
|
51
|
+
readonly defaultFn?: () => unknown;
|
|
52
|
+
/** Emitter hint for `$defaultFn(id|now)`. */
|
|
53
|
+
readonly defaultFnKind?: DefaultFnKind;
|
|
54
|
+
/** Owning table SQL name (stamped by {@link schemaTable}). */
|
|
55
|
+
readonly tableName?: string;
|
|
56
|
+
/** Foreign key when `.references()` was used. */
|
|
57
|
+
readonly references?: ColumnReference;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Table from {@link store.schema.table} — extends {@link TableHandle}. */
|
|
61
|
+
export interface SchemaTableDecl extends TableHandle {
|
|
62
|
+
readonly kind: "schema-table";
|
|
63
|
+
readonly columns: Readonly<Record<string, SchemaColumnDecl>>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Fluent field builder before key finalization. */
|
|
67
|
+
export interface FieldBuilder {
|
|
68
|
+
primaryKey(): FieldBuilder;
|
|
69
|
+
notNull(): FieldBuilder;
|
|
70
|
+
unique(): FieldBuilder;
|
|
71
|
+
default(value: string | number | boolean | null): FieldBuilder;
|
|
72
|
+
defaultFn(fn: () => unknown): FieldBuilder;
|
|
73
|
+
pii(): FieldBuilder;
|
|
74
|
+
sensitive(): FieldBuilder;
|
|
75
|
+
retain(duration: string): FieldBuilder;
|
|
76
|
+
/** Override snake_case SQL name. */
|
|
77
|
+
as(sqlName: string): FieldBuilder;
|
|
78
|
+
/**
|
|
79
|
+
* Declare a foreign key to another column (dialect-agnostic).
|
|
80
|
+
*
|
|
81
|
+
* @param ref - Lazy target column (`() => links.code`)
|
|
82
|
+
* @param actions - Optional ON DELETE / ON UPDATE
|
|
83
|
+
*/
|
|
84
|
+
references(ref: () => SchemaColumnDecl, actions?: ReferenceActions): FieldBuilder;
|
|
85
|
+
/**
|
|
86
|
+
* Bind the JS key and produce a {@link SchemaColumnDecl}.
|
|
87
|
+
*
|
|
88
|
+
* @param key - Object key in the table column map
|
|
89
|
+
*/
|
|
90
|
+
finalize(key: string): SchemaColumnDecl;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
interface FieldState {
|
|
94
|
+
readonly sqlType: FieldSqlType;
|
|
95
|
+
readonly primaryKey: boolean;
|
|
96
|
+
readonly notNull: boolean;
|
|
97
|
+
readonly unique: boolean;
|
|
98
|
+
readonly defaultValue?: string | number | boolean | null;
|
|
99
|
+
readonly defaultFn?: () => unknown;
|
|
100
|
+
readonly defaultFnKind?: DefaultFnKind;
|
|
101
|
+
readonly classification?: ColumnClassification;
|
|
102
|
+
readonly sqlName?: string;
|
|
103
|
+
readonly references?: ColumnReference;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function camelToSnake(key: string): string {
|
|
107
|
+
return key
|
|
108
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1_$2")
|
|
109
|
+
.replace(/([A-Z])([A-Z][a-z])/g, "$1_$2")
|
|
110
|
+
.toLowerCase();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function defaultFnKindOf(fn: () => unknown): DefaultFnKind {
|
|
114
|
+
if (fn === idHelper) return "id";
|
|
115
|
+
if (fn === nowHelper) return "now";
|
|
116
|
+
const name = typeof fn.name === "string" ? fn.name : "";
|
|
117
|
+
if (name === "id") return "id";
|
|
118
|
+
if (name === "now") return "now";
|
|
119
|
+
return "custom";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function mergeClassification(
|
|
123
|
+
current: ColumnClassification | undefined,
|
|
124
|
+
patch: ColumnClassification,
|
|
125
|
+
): ColumnClassification {
|
|
126
|
+
return { ...(current ?? {}), ...patch };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function createBuilder(state: FieldState): FieldBuilder {
|
|
130
|
+
const next = (patch: Partial<FieldState>): FieldBuilder => createBuilder({ ...state, ...patch });
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
primaryKey: () => next({ primaryKey: true, notNull: true }),
|
|
134
|
+
notNull: () => next({ notNull: true }),
|
|
135
|
+
unique: () => next({ unique: true }),
|
|
136
|
+
default: (value) => next({ defaultValue: value }),
|
|
137
|
+
defaultFn: (fn) =>
|
|
138
|
+
next({
|
|
139
|
+
defaultFn: fn,
|
|
140
|
+
defaultFnKind: defaultFnKindOf(fn),
|
|
141
|
+
}),
|
|
142
|
+
pii: () => next({ classification: mergeClassification(state.classification, { pii: true }) }),
|
|
143
|
+
sensitive: () =>
|
|
144
|
+
next({ classification: mergeClassification(state.classification, { sensitive: true }) }),
|
|
145
|
+
retain: (duration) =>
|
|
146
|
+
next({ classification: mergeClassification(state.classification, { retain: duration }) }),
|
|
147
|
+
as: (sqlName) => next({ sqlName }),
|
|
148
|
+
references: (ref, actions) =>
|
|
149
|
+
next({
|
|
150
|
+
references: actions ? { ref, actions } : { ref },
|
|
151
|
+
}),
|
|
152
|
+
finalize(key: string): SchemaColumnDecl {
|
|
153
|
+
const sqlName = state.sqlName ?? camelToSnake(key);
|
|
154
|
+
return {
|
|
155
|
+
key,
|
|
156
|
+
name: sqlName,
|
|
157
|
+
sqlName,
|
|
158
|
+
sqlType: state.sqlType,
|
|
159
|
+
primaryKey: state.primaryKey,
|
|
160
|
+
notNull: state.notNull || state.primaryKey,
|
|
161
|
+
unique: state.unique,
|
|
162
|
+
...(state.defaultValue !== undefined ? { defaultValue: state.defaultValue } : {}),
|
|
163
|
+
...(state.defaultFn ? { defaultFn: state.defaultFn } : {}),
|
|
164
|
+
...(state.defaultFnKind ? { defaultFnKind: state.defaultFnKind } : {}),
|
|
165
|
+
...(state.classification ? { classification: state.classification } : {}),
|
|
166
|
+
...(state.references ? { references: state.references } : {}),
|
|
167
|
+
};
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Field builders — `field.text()` / `field.integer()`.
|
|
174
|
+
*/
|
|
175
|
+
export const field = {
|
|
176
|
+
text: (): FieldBuilder =>
|
|
177
|
+
createBuilder({
|
|
178
|
+
sqlType: "text",
|
|
179
|
+
primaryKey: false,
|
|
180
|
+
notNull: false,
|
|
181
|
+
unique: false,
|
|
182
|
+
}),
|
|
183
|
+
integer: (): FieldBuilder =>
|
|
184
|
+
createBuilder({
|
|
185
|
+
sqlType: "integer",
|
|
186
|
+
primaryKey: false,
|
|
187
|
+
notNull: false,
|
|
188
|
+
unique: false,
|
|
189
|
+
}),
|
|
190
|
+
} as const;
|
|
191
|
+
|
|
192
|
+
/** Column map input for {@link schemaTable}. */
|
|
193
|
+
export type SchemaColumnInput = FieldBuilder | SchemaColumnDecl;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Whether a value is a finalized {@link SchemaColumnDecl}.
|
|
197
|
+
*
|
|
198
|
+
* @param value - Unknown
|
|
199
|
+
*/
|
|
200
|
+
export function isSchemaColumnDecl(value: unknown): value is SchemaColumnDecl {
|
|
201
|
+
return (
|
|
202
|
+
!!value &&
|
|
203
|
+
typeof value === "object" &&
|
|
204
|
+
"sqlType" in value &&
|
|
205
|
+
"sqlName" in value &&
|
|
206
|
+
"primaryKey" in value &&
|
|
207
|
+
typeof (value as SchemaColumnDecl).sqlType === "string" &&
|
|
208
|
+
typeof (value as SchemaColumnDecl).sqlName === "string"
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Whether a value is a fluent {@link FieldBuilder}.
|
|
214
|
+
*
|
|
215
|
+
* @param value - Unknown
|
|
216
|
+
*/
|
|
217
|
+
export function isFieldBuilder(value: unknown): value is FieldBuilder {
|
|
218
|
+
return (
|
|
219
|
+
!!value &&
|
|
220
|
+
typeof value === "object" &&
|
|
221
|
+
typeof (value as FieldBuilder).finalize === "function" &&
|
|
222
|
+
typeof (value as FieldBuilder).primaryKey === "function" &&
|
|
223
|
+
!isSchemaColumnDecl(value)
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Finalize a column map (builders → decls).
|
|
229
|
+
*
|
|
230
|
+
* @param columns - Key → builder or decl
|
|
231
|
+
*/
|
|
232
|
+
export function finalizeColumnMap(
|
|
233
|
+
columns: Readonly<Record<string, SchemaColumnInput>>,
|
|
234
|
+
): Record<string, SchemaColumnDecl> {
|
|
235
|
+
const out: Record<string, SchemaColumnDecl> = {};
|
|
236
|
+
for (const [key, value] of Object.entries(columns)) {
|
|
237
|
+
out[key] = isFieldBuilder(value) ? value.finalize(key) : value;
|
|
238
|
+
}
|
|
239
|
+
return out;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Whether a value is a {@link SchemaTableDecl}.
|
|
244
|
+
*
|
|
245
|
+
* @param value - Unknown
|
|
246
|
+
*/
|
|
247
|
+
export function isSchemaTableDecl(value: unknown): value is SchemaTableDecl {
|
|
248
|
+
return (
|
|
249
|
+
!!value &&
|
|
250
|
+
typeof value === "object" &&
|
|
251
|
+
(value as SchemaTableDecl).kind === "schema-table" &&
|
|
252
|
+
typeof (value as SchemaTableDecl).name === "string" &&
|
|
253
|
+
"columns" in value
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Table decl with columns as own properties (`links.code`) for FK / relations.
|
|
259
|
+
*/
|
|
260
|
+
export type SchemaTableWithColumns<
|
|
261
|
+
C extends Record<string, SchemaColumnDecl> = Record<string, SchemaColumnDecl>,
|
|
262
|
+
> = Omit<SchemaTableDecl, "columns"> & {
|
|
263
|
+
readonly columns: Readonly<C>;
|
|
264
|
+
} & C;
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Declare an abstract schema table (ORM-agnostic).
|
|
268
|
+
*
|
|
269
|
+
* Columns are also exposed as own properties (`links.code`) for
|
|
270
|
+
* `.references(() => links.code)` ergonomics matching Drizzle.
|
|
271
|
+
*
|
|
272
|
+
* @param name - SQL table name
|
|
273
|
+
* @param columns - Column map using {@link field} builders
|
|
274
|
+
*/
|
|
275
|
+
export function schemaTable<C extends Record<string, SchemaColumnInput>>(
|
|
276
|
+
name: string,
|
|
277
|
+
columns: C,
|
|
278
|
+
): SchemaTableWithColumns<{ [K in keyof C]: SchemaColumnDecl }> {
|
|
279
|
+
const finalized = finalizeColumnMap(columns);
|
|
280
|
+
const stamped: Record<string, SchemaColumnDecl> = {};
|
|
281
|
+
for (const [key, col] of Object.entries(finalized)) {
|
|
282
|
+
stamped[key] = { ...col, tableName: name };
|
|
283
|
+
}
|
|
284
|
+
const table = {
|
|
285
|
+
kind: "schema-table" as const,
|
|
286
|
+
name,
|
|
287
|
+
columns: stamped,
|
|
288
|
+
...stamped,
|
|
289
|
+
};
|
|
290
|
+
return table as SchemaTableWithColumns<{ [K in keyof C]: SchemaColumnDecl }>;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ─── Relations (mirrors drizzle-orm `defineRelations`) ───────────────────────
|
|
294
|
+
|
|
295
|
+
/** Column path recorded by relation helpers (`r.links.code`). */
|
|
296
|
+
export interface RelationColumnRef {
|
|
297
|
+
readonly table: string;
|
|
298
|
+
readonly column: string;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** One relation config (serializable for emit). */
|
|
302
|
+
export interface SchemaRelationOne {
|
|
303
|
+
readonly kind: "one";
|
|
304
|
+
readonly target: string;
|
|
305
|
+
readonly from?: RelationColumnRef | readonly RelationColumnRef[];
|
|
306
|
+
readonly to?: RelationColumnRef | readonly RelationColumnRef[];
|
|
307
|
+
readonly optional?: boolean;
|
|
308
|
+
readonly alias?: string;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** Many relation config (serializable for emit). */
|
|
312
|
+
export interface SchemaRelationMany {
|
|
313
|
+
readonly kind: "many";
|
|
314
|
+
readonly target: string;
|
|
315
|
+
readonly from?: RelationColumnRef | readonly RelationColumnRef[];
|
|
316
|
+
readonly to?: RelationColumnRef | readonly RelationColumnRef[];
|
|
317
|
+
readonly alias?: string;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** One named relation on a source table. */
|
|
321
|
+
export type SchemaRelationEntry = SchemaRelationOne | SchemaRelationMany;
|
|
322
|
+
|
|
323
|
+
/** Per-table relation map. */
|
|
324
|
+
export type SchemaTableRelations = Readonly<Record<string, SchemaRelationEntry>>;
|
|
325
|
+
|
|
326
|
+
/** Full relations declaration from {@link schemaRelations}. */
|
|
327
|
+
export interface SchemaRelationsDecl {
|
|
328
|
+
readonly kind: "schema-relations";
|
|
329
|
+
/** Export names of tables passed to {@link schemaRelations}. */
|
|
330
|
+
readonly tableKeys: readonly string[];
|
|
331
|
+
/** Table export name → relation name → config. */
|
|
332
|
+
readonly config: Readonly<Record<string, SchemaTableRelations>>;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** Config input for one/many helpers. */
|
|
336
|
+
export interface RelationHelperConfig {
|
|
337
|
+
readonly from?: RelationColumnRef | readonly RelationColumnRef[];
|
|
338
|
+
readonly to?: RelationColumnRef | readonly RelationColumnRef[];
|
|
339
|
+
readonly optional?: boolean;
|
|
340
|
+
readonly alias?: string;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
type RelationTableCols = Record<string, RelationColumnRef> & {
|
|
344
|
+
readonly __table: string;
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
type RelationsBuilderFor<Keys extends string> = {
|
|
348
|
+
readonly [K in Keys]: RelationTableCols;
|
|
349
|
+
} & {
|
|
350
|
+
readonly one: {
|
|
351
|
+
readonly [K in Keys]: (config?: RelationHelperConfig) => SchemaRelationOne;
|
|
352
|
+
};
|
|
353
|
+
readonly many: {
|
|
354
|
+
readonly [K in Keys]: (config?: RelationHelperConfig) => SchemaRelationMany;
|
|
355
|
+
};
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
/** Untyped relations builder (emit / dynamic paths). */
|
|
359
|
+
type RelationsBuilder = RelationsBuilderFor<string>;
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Whether a value is a {@link SchemaRelationsDecl}.
|
|
363
|
+
*
|
|
364
|
+
* @param value - Unknown
|
|
365
|
+
*/
|
|
366
|
+
export function isSchemaRelationsDecl(value: unknown): value is SchemaRelationsDecl {
|
|
367
|
+
return (
|
|
368
|
+
!!value &&
|
|
369
|
+
typeof value === "object" &&
|
|
370
|
+
(value as SchemaRelationsDecl).kind === "schema-relations" &&
|
|
371
|
+
Array.isArray((value as SchemaRelationsDecl).tableKeys) &&
|
|
372
|
+
typeof (value as SchemaRelationsDecl).config === "object"
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function columnRefFrom(value: unknown): RelationColumnRef | undefined {
|
|
377
|
+
if (!value || typeof value !== "object") return undefined;
|
|
378
|
+
const col = value as SchemaColumnDecl & RelationColumnRef;
|
|
379
|
+
if (typeof col.table === "string" && typeof col.column === "string") {
|
|
380
|
+
return { table: col.table, column: col.column };
|
|
381
|
+
}
|
|
382
|
+
if (isSchemaColumnDecl(col) && typeof col.tableName === "string" && typeof col.key === "string") {
|
|
383
|
+
return { table: col.tableName, column: col.key };
|
|
384
|
+
}
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function normalizeColRefs(
|
|
389
|
+
value: RelationColumnRef | readonly RelationColumnRef[] | undefined,
|
|
390
|
+
): RelationColumnRef | readonly RelationColumnRef[] | undefined {
|
|
391
|
+
if (value === undefined) return undefined;
|
|
392
|
+
if (Array.isArray(value)) {
|
|
393
|
+
const out: RelationColumnRef[] = [];
|
|
394
|
+
for (const item of value) {
|
|
395
|
+
const ref = columnRefFrom(item) ?? (item as RelationColumnRef);
|
|
396
|
+
if (ref?.table && ref?.column) out.push({ table: ref.table, column: ref.column });
|
|
397
|
+
}
|
|
398
|
+
return out.length === 1 ? out[0] : out;
|
|
399
|
+
}
|
|
400
|
+
return columnRefFrom(value) ?? value;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Build recording helpers isomorphic to drizzle-orm `defineRelations` helpers.
|
|
405
|
+
*
|
|
406
|
+
* @param tableKeys - Export names of tables in the schema object
|
|
407
|
+
* @param tables - Table decls keyed by export name
|
|
408
|
+
*/
|
|
409
|
+
function createRelationsBuilder(
|
|
410
|
+
tableKeys: readonly string[],
|
|
411
|
+
tables: Readonly<Record<string, SchemaTableDecl>>,
|
|
412
|
+
): RelationsBuilder {
|
|
413
|
+
const builder: Record<string, unknown> = {};
|
|
414
|
+
|
|
415
|
+
for (const key of tableKeys) {
|
|
416
|
+
const table = tables[key];
|
|
417
|
+
if (!table) continue;
|
|
418
|
+
const cols = { __table: table.name } as RelationTableCols;
|
|
419
|
+
for (const [colKey] of Object.entries(table.columns)) {
|
|
420
|
+
cols[colKey] = { table: key, column: colKey };
|
|
421
|
+
}
|
|
422
|
+
builder[key] = cols;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const one: Record<string, (config?: RelationHelperConfig) => SchemaRelationOne> = {};
|
|
426
|
+
const many: Record<string, (config?: RelationHelperConfig) => SchemaRelationMany> = {};
|
|
427
|
+
|
|
428
|
+
for (const key of tableKeys) {
|
|
429
|
+
one[key] = (config) => ({
|
|
430
|
+
kind: "one",
|
|
431
|
+
target: key,
|
|
432
|
+
...(config?.from !== undefined ? { from: normalizeColRefs(config.from) } : {}),
|
|
433
|
+
...(config?.to !== undefined ? { to: normalizeColRefs(config.to) } : {}),
|
|
434
|
+
...(config?.optional !== undefined ? { optional: config.optional } : {}),
|
|
435
|
+
...(config?.alias !== undefined ? { alias: config.alias } : {}),
|
|
436
|
+
});
|
|
437
|
+
many[key] = (config) => ({
|
|
438
|
+
kind: "many",
|
|
439
|
+
target: key,
|
|
440
|
+
...(config?.from !== undefined ? { from: normalizeColRefs(config.from) } : {}),
|
|
441
|
+
...(config?.to !== undefined ? { to: normalizeColRefs(config.to) } : {}),
|
|
442
|
+
...(config?.alias !== undefined ? { alias: config.alias } : {}),
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
builder.one = one;
|
|
447
|
+
builder.many = many;
|
|
448
|
+
return builder as RelationsBuilder;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Declare abstract relations — mirrors drizzle-orm `defineRelations` shape.
|
|
453
|
+
*
|
|
454
|
+
* @param tables - Schema object of {@link SchemaTableDecl}s (export-name keys)
|
|
455
|
+
* @param configure - `(r) => ({ links: { daily: r.many.daily({ from, to }) } })`
|
|
456
|
+
*/
|
|
457
|
+
export function schemaRelations<T extends Readonly<Record<string, SchemaTableDecl>>>(
|
|
458
|
+
tables: T,
|
|
459
|
+
configure: (
|
|
460
|
+
r: RelationsBuilderFor<Extract<keyof T, string>>,
|
|
461
|
+
) => Readonly<Record<string, SchemaTableRelations | undefined>>,
|
|
462
|
+
): SchemaRelationsDecl {
|
|
463
|
+
const tableKeys = Object.keys(tables).filter((k) => isSchemaTableDecl(tables[k]));
|
|
464
|
+
const tableMap: Record<string, SchemaTableDecl> = {};
|
|
465
|
+
for (const k of tableKeys) {
|
|
466
|
+
tableMap[k] = tables[k]!;
|
|
467
|
+
}
|
|
468
|
+
const builder = createRelationsBuilder(tableKeys, tableMap) as RelationsBuilderFor<
|
|
469
|
+
Extract<keyof T, string>
|
|
470
|
+
>;
|
|
471
|
+
const raw = configure(builder);
|
|
472
|
+
const config: Record<string, SchemaTableRelations> = {};
|
|
473
|
+
for (const [tableKey, rels] of Object.entries(raw)) {
|
|
474
|
+
if (!rels) continue;
|
|
475
|
+
config[tableKey] = rels;
|
|
476
|
+
}
|
|
477
|
+
return {
|
|
478
|
+
kind: "schema-relations",
|
|
479
|
+
tableKeys,
|
|
480
|
+
config,
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** `store.schema` namespace — avoids colliding with {@link SqlStoreDecl.table} CDC. */
|
|
485
|
+
export const schema = {
|
|
486
|
+
table: schemaTable,
|
|
487
|
+
relations: schemaRelations,
|
|
488
|
+
} as const;
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Collect {@link SchemaTableDecl} values from a module's exports.
|
|
492
|
+
*
|
|
493
|
+
* @param mod - Module namespace object
|
|
494
|
+
*/
|
|
495
|
+
export function tablesFromExports(mod: Readonly<Record<string, unknown>>): SchemaTableDecl[] {
|
|
496
|
+
const out: SchemaTableDecl[] = [];
|
|
497
|
+
for (const value of Object.values(mod)) {
|
|
498
|
+
if (isSchemaTableDecl(value)) out.push(value);
|
|
499
|
+
}
|
|
500
|
+
return out;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Collect {@link SchemaRelationsDecl} values from a module's exports.
|
|
505
|
+
*
|
|
506
|
+
* @param mod - Module namespace object
|
|
507
|
+
*/
|
|
508
|
+
export function relationsFromExports(
|
|
509
|
+
mod: Readonly<Record<string, unknown>>,
|
|
510
|
+
): SchemaRelationsDecl[] {
|
|
511
|
+
const out: SchemaRelationsDecl[] = [];
|
|
512
|
+
for (const value of Object.values(mod)) {
|
|
513
|
+
if (isSchemaRelationsDecl(value)) out.push(value);
|
|
514
|
+
}
|
|
515
|
+
return out;
|
|
516
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drizzle condition compiler — parenthesized `or`/`and` groups, `like` /
|
|
3
|
+
* `ilike`, loud failures on unsupported operators, and `orderBy` terms.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, expect, test } from "bun:test";
|
|
7
|
+
import {
|
|
8
|
+
and,
|
|
9
|
+
asc,
|
|
10
|
+
between,
|
|
11
|
+
desc,
|
|
12
|
+
eq,
|
|
13
|
+
ilike,
|
|
14
|
+
inArray,
|
|
15
|
+
isNotNull,
|
|
16
|
+
isNull,
|
|
17
|
+
like,
|
|
18
|
+
lt,
|
|
19
|
+
not,
|
|
20
|
+
or,
|
|
21
|
+
sql,
|
|
22
|
+
} from "drizzle-orm";
|
|
23
|
+
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
|
24
|
+
import { compileOrderBy, compileWhere } from "./sql-condition.ts";
|
|
25
|
+
|
|
26
|
+
const notes = sqliteTable("notes", {
|
|
27
|
+
id: text("id").primaryKey(),
|
|
28
|
+
title: text("title").notNull(),
|
|
29
|
+
createdAt: integer("created_at").notNull(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("compileWhere", () => {
|
|
33
|
+
test("bare comparison stays unparenthesized", () => {
|
|
34
|
+
const c = compileWhere(eq(notes.id, "n1"));
|
|
35
|
+
expect(c.clause).toBe(`"id" = ?`);
|
|
36
|
+
expect(c.params).toEqual(["n1"]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("like compiles to LIKE + bound pattern", () => {
|
|
40
|
+
const c = compileWhere(like(notes.title, "%hello%"));
|
|
41
|
+
expect(c.clause).toBe(`"title" like ?`);
|
|
42
|
+
expect(c.params).toEqual(["%hello%"]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("ilike compiles to ILIKE + bound pattern (postgres parity)", () => {
|
|
46
|
+
const c = compileWhere(ilike(notes.title, "%Hello%"));
|
|
47
|
+
expect(c.clause).toBe(`"title" ilike ?`);
|
|
48
|
+
expect(c.params).toEqual(["%Hello%"]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("and joins with AND", () => {
|
|
52
|
+
const c = compileWhere(and(eq(notes.id, "n1"), eq(notes.title, "t")));
|
|
53
|
+
expect(c.clause).toBe(`("id" = ?) AND ("title" = ?)`);
|
|
54
|
+
expect(c.params).toEqual(["n1", "t"]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("or produces a parenthesized OR group — never flattened into AND", () => {
|
|
58
|
+
const c = compileWhere(or(eq(notes.id, "n1"), lt(notes.createdAt, 10)));
|
|
59
|
+
expect(c.clause).toBe(`("id" = ?) OR ("created_at" < ?)`);
|
|
60
|
+
expect(c.params).toEqual(["n1", 10]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("composite cursor predicate keeps OR/AND precedence", () => {
|
|
64
|
+
const c = compileWhere(
|
|
65
|
+
and(
|
|
66
|
+
like(notes.title, "%x%"),
|
|
67
|
+
or(lt(notes.createdAt, 100), and(eq(notes.createdAt, 100), lt(notes.id, "n1"))),
|
|
68
|
+
),
|
|
69
|
+
);
|
|
70
|
+
expect(c.clause).toBe(
|
|
71
|
+
`("title" like ?) AND (("created_at" < ?) OR (("created_at" = ?) AND ("id" < ?)))`,
|
|
72
|
+
);
|
|
73
|
+
expect(c.params).toEqual(["%x%", 100, 100, "n1"]);
|
|
74
|
+
expect(c.predicates.map((p) => p.op)).toEqual(["like", "<", "=", "<"]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("single-condition or() collapses to the leaf", () => {
|
|
78
|
+
const c = compileWhere(or(eq(notes.id, "n1")));
|
|
79
|
+
expect(c.clause).toBe(`"id" = ?`);
|
|
80
|
+
expect(c.params).toEqual(["n1"]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("plain equality maps are unchanged", () => {
|
|
84
|
+
const c = compileWhere({ id: "n1", title: "t" });
|
|
85
|
+
expect(c.clause).toBe(`"id" = ? AND "title" = ?`);
|
|
86
|
+
expect(c.params).toEqual(["n1", "t"]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("inArray expands one placeholder per value", () => {
|
|
90
|
+
const c = compileWhere(inArray(notes.id, ["a", "b", "c"]));
|
|
91
|
+
expect(c.clause).toBe(`"id" in (?, ?, ?)`);
|
|
92
|
+
expect(c.params).toEqual(["a", "b", "c"]);
|
|
93
|
+
expect(c.predicates.map((p) => p.op)).toEqual(["in"]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("isNull / isNotNull bind no value", () => {
|
|
97
|
+
const nil = compileWhere(isNull(notes.title));
|
|
98
|
+
expect(nil.clause).toBe(`"title" is null`);
|
|
99
|
+
expect(nil.params).toEqual([]);
|
|
100
|
+
const notNil = compileWhere(and(eq(notes.id, "n1"), isNotNull(notes.title)));
|
|
101
|
+
expect(notNil.clause).toBe(`("id" = ?) AND ("title" is not null)`);
|
|
102
|
+
expect(notNil.params).toEqual(["n1"]);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("boolean equality binds the boolean", () => {
|
|
106
|
+
const c = compileWhere(eq(notes.id, true as never));
|
|
107
|
+
expect(c.clause).toBe(`"id" = ?`);
|
|
108
|
+
expect(c.params).toEqual([true]);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("unsupported operators fail loudly instead of dropping predicates", () => {
|
|
112
|
+
expect(() => compileWhere(between(notes.createdAt, 1, 2))).toThrow(/unsupported operator/);
|
|
113
|
+
expect(() => compileWhere(not(eq(notes.id, "n1")))).toThrow(/unsupported fragment/);
|
|
114
|
+
expect(() => compileWhere(sql`title = 1`)).toThrow(/unsupported fragment/);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe("compileOrderBy", () => {
|
|
119
|
+
test("asc/desc terms with bare-column default", () => {
|
|
120
|
+
const terms = compileOrderBy([desc(notes.createdAt), asc(notes.id), notes.title]);
|
|
121
|
+
expect(terms).toEqual([
|
|
122
|
+
{ column: "created_at", direction: "DESC" },
|
|
123
|
+
{ column: "id", direction: "ASC" },
|
|
124
|
+
{ column: "title", direction: "ASC" },
|
|
125
|
+
]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("garbage terms fail loudly", () => {
|
|
129
|
+
expect(() => compileOrderBy([sql`random()`])).toThrow(/orderBy/);
|
|
130
|
+
expect(() => compileOrderBy([42])).toThrow(/orderBy/);
|
|
131
|
+
});
|
|
132
|
+
});
|