@ultimat3/cli 29.3.2 → 30.0.0
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/CLAUDE.md +64 -40
- package/README.md +61 -11
- package/package.json +42 -28
- package/src/affected-tests.ts +97 -0
- package/src/affected-unowned.ts +43 -0
- package/src/affected.ts +25 -6
- package/src/app-boundaries.ts +29 -1
- package/src/app-evals.ts +12 -4
- package/src/app-load-unresolved.ts +62 -0
- package/src/app-load.ts +30 -37
- package/src/app-mcp.ts +14 -2
- package/src/app-module-graph.ts +232 -0
- package/src/app-reload-graph.ts +2 -2
- package/src/app-root.ts +37 -1
- package/src/bin.ts +1 -0
- package/src/boundary-findings.ts +6 -0
- package/src/budgets-freshness.ts +32 -0
- package/src/budgets.ts +20 -1
- package/src/build-capture-env.ts +9 -0
- package/src/cdp-shot-a11y.ts +2 -39
- package/src/ci-tested.ts +243 -0
- package/src/client-emit-customer-runtime.ts +139 -0
- package/src/client-emit-customer.ts +108 -0
- package/src/client-emit-drift.ts +72 -0
- package/src/client-emit-header.ts +67 -0
- package/src/client-emit-schemas.ts +175 -0
- package/src/client-emit-types.ts +121 -0
- package/src/client-emit-vendor.ts +146 -0
- package/src/client-errors-routes.ts +229 -0
- package/src/client-errors-script.ts +74 -0
- package/src/client-vitals-route.ts +197 -0
- package/src/client-vitals-script.ts +56 -0
- package/src/cmd-affected.ts +23 -0
- package/src/cmd-build-spec.ts +7 -1
- package/src/cmd-build.ts +35 -9
- package/src/cmd-ci-spec.ts +42 -6
- package/src/cmd-ci.ts +99 -2
- package/src/cmd-client-customer.ts +134 -0
- package/src/cmd-client-source.ts +81 -0
- package/src/cmd-client-spec.ts +48 -0
- package/src/cmd-client-write.ts +41 -0
- package/src/cmd-client.ts +111 -0
- package/src/cmd-db-recount.ts +120 -0
- package/src/cmd-db-spec.ts +36 -6
- package/src/cmd-db.ts +13 -3
- package/src/cmd-deploy-compose.ts +61 -3
- package/src/cmd-deploy-drain.ts +10 -2
- package/src/cmd-deploy-helm.ts +11 -0
- package/src/cmd-deploy-preflight.ts +118 -0
- package/src/cmd-deploy-spec.ts +58 -6
- package/src/cmd-deploy-verify.ts +380 -0
- package/src/cmd-deploy.ts +42 -9
- package/src/cmd-docs.ts +34 -4
- package/src/cmd-doctor.ts +66 -9
- package/src/cmd-drill-spec.ts +25 -0
- package/src/cmd-drill.ts +176 -0
- package/src/cmd-env-init.ts +102 -0
- package/src/cmd-env-spec.ts +10 -3
- package/src/cmd-env.ts +15 -10
- package/src/cmd-errors-recent.ts +60 -0
- package/src/cmd-errors-spec.ts +9 -1
- package/src/cmd-errors.ts +4 -0
- package/src/cmd-exercise-spec.ts +19 -0
- package/src/cmd-exercise.ts +129 -0
- package/src/cmd-generate-spec.ts +9 -1
- package/src/cmd-generate.ts +32 -27
- package/src/cmd-graph-load.ts +97 -0
- package/src/cmd-graph-spec.ts +16 -0
- package/src/cmd-graph.ts +73 -0
- package/src/cmd-i18n.ts +4 -1
- package/src/cmd-impact-spec.ts +13 -0
- package/src/cmd-impact.ts +64 -0
- package/src/cmd-jobs-resolve.ts +76 -0
- package/src/cmd-jobs-spec.ts +21 -2
- package/src/cmd-jobs.ts +10 -6
- package/src/cmd-mail-spec.ts +28 -0
- package/src/cmd-mail.ts +70 -0
- package/src/cmd-mcp-lock.ts +69 -0
- package/src/cmd-mcp-spec.ts +23 -4
- package/src/cmd-mcp.ts +17 -3
- package/src/cmd-new-spec.ts +8 -1
- package/src/cmd-new.ts +61 -9
- package/src/cmd-planned.ts +0 -6
- package/src/cmd-repro-spec.ts +31 -0
- package/src/cmd-repro.ts +174 -0
- package/src/cmd-stop-spec.ts +21 -0
- package/src/cmd-stop.ts +166 -0
- package/src/cmd-test-spec.ts +17 -4
- package/src/cmd-test.ts +70 -14
- package/src/cmd-upgrade-spec.ts +26 -0
- package/src/cmd-upgrade.ts +274 -0
- package/src/cmd-verify-spec.ts +3 -3
- package/src/db-backfill.ts +6 -0
- package/src/db-diagnostics.ts +106 -0
- package/src/db-generate.ts +28 -8
- package/src/db-reset-template.ts +1 -1
- package/src/db-rolling.ts +45 -0
- package/src/db-seed-generate.ts +188 -0
- package/src/deploy-verify-findings.ts +98 -0
- package/src/deploy-verify-flags.ts +109 -0
- package/src/deploy-verify-grade.ts +185 -0
- package/src/deploy-verify-queues.ts +53 -0
- package/src/dev-boot.ts +35 -15
- package/src/dev-child-watch.ts +16 -4
- package/src/dev-dashboard-guard.ts +8 -1
- package/src/dev-dashboard.ts +7 -0
- package/src/dev-lock-identity.ts +61 -0
- package/src/dev-lock-port-holder.ts +80 -0
- package/src/dev-lock-steal.ts +124 -0
- package/src/dev-lock.ts +92 -120
- package/src/dev-mail-templates.ts +51 -0
- package/src/dev-rebuild.ts +26 -2
- package/src/dev-route-table.ts +17 -0
- package/src/dev-supervisor.ts +5 -2
- package/src/dev-watch-tree.ts +11 -10
- package/src/dev-watch.ts +1 -1
- package/src/dispatch.ts +29 -6
- package/src/docs-section.ts +69 -0
- package/src/docs-sources.ts +100 -0
- package/src/doctor-auth.ts +11 -2
- package/src/doctor-chart-alerts.ts +55 -0
- package/src/doctor-pooler.ts +107 -0
- package/src/doctor-rolling.ts +35 -0
- package/src/doctor-row-security.ts +40 -0
- package/src/document-options.ts +13 -0
- package/src/drill-sync-lane.ts +25 -0
- package/src/drill-targets.ts +39 -0
- package/src/duplicate-packages.ts +1 -1
- package/src/env-production.ts +93 -0
- package/src/error-codes-shot.ts +59 -0
- package/src/error-codes-tooling.ts +29 -0
- package/src/error-codes.ts +100 -54
- package/src/error-contract.ts +2 -2
- package/src/errors.ts +22 -3
- package/src/exercise-database.ts +99 -0
- package/src/exercise-findings.ts +159 -0
- package/src/exercise-overrides.ts +42 -0
- package/src/exercise-registration.ts +58 -0
- package/src/exercise-static.ts +88 -0
- package/src/exercise-subject.ts +69 -0
- package/src/failure-capture-boot.ts +30 -0
- package/src/framework-env.ts +7 -1
- package/src/framework-schema.ts +72 -5
- package/src/generate-catalog-locales.ts +19 -0
- package/src/generate-chart-alerts.ts +156 -0
- package/src/generate-feature-required.ts +65 -0
- package/src/generate-feature.ts +83 -6
- package/src/generate-files.ts +24 -0
- package/src/generate-format-written.ts +32 -0
- package/src/generate-kinds.ts +6 -0
- package/src/generate-sign-in-config.ts +100 -0
- package/src/generate-sign-in-manifest.ts +28 -0
- package/src/generate-sign-in-mode.ts +101 -0
- package/src/generate-sign-in.ts +250 -0
- package/src/generate-write.ts +44 -7
- package/src/hand-rolled-socket.ts +72 -0
- package/src/i18n-app-key-shapes.ts +29 -0
- package/src/i18n-audit.ts +10 -3
- package/src/i18n-registration.ts +18 -3
- package/src/index.ts +2 -0
- package/src/island-bundle.ts +4 -3
- package/src/island-disposer.ts +116 -0
- package/src/island-realtime-state.ts +34 -6
- package/src/island-realtime.ts +6 -2
- package/src/island-routes.ts +3 -2
- package/src/isr-query-advice.ts +6 -11
- package/src/isr-query-guard.ts +4 -3
- package/src/job-budget.ts +95 -0
- package/src/jobs-json.ts +11 -0
- package/src/lint-findings.ts +104 -0
- package/src/local-cli.ts +34 -4
- package/src/loopback-host.ts +2 -2
- package/src/mail-preview-findings.ts +24 -0
- package/src/mcp-errors.ts +82 -2
- package/src/mcp-host.ts +11 -1
- package/src/mcp-lock-findings.ts +57 -0
- package/src/measure-paths.ts +12 -4
- package/src/measure-scope.ts +27 -13
- package/src/messages-client.ts +23 -0
- package/src/messages-db.ts +56 -0
- package/src/messages-gate.ts +107 -0
- package/src/messages-repro.ts +11 -0
- package/src/messages-shot.ts +51 -0
- package/src/messages-upgrade.ts +45 -0
- package/src/messages.ts +53 -178
- package/src/metrics-endpoint.ts +23 -5
- package/src/new-shapes-generate.ts +71 -0
- package/src/new-shapes.ts +63 -0
- package/src/openapi-compile-ir.ts +68 -0
- package/src/openapi-compile-schema.ts +284 -0
- package/src/openapi-compile.ts +273 -0
- package/src/openapi-refuse.ts +45 -0
- package/src/otlp-export.ts +2 -1
- package/src/output-findings.ts +123 -0
- package/src/output.ts +32 -8
- package/src/package-boundaries.ts +110 -0
- package/src/page-pins.ts +23 -0
- package/src/plain-routes.ts +13 -0
- package/src/prerender-entries.ts +59 -0
- package/src/prerender-heaviest.ts +27 -0
- package/src/prerender-preload.ts +6 -0
- package/src/prerender-report.ts +12 -0
- package/src/prerender-seo-reads.ts +38 -0
- package/src/prerender-served.ts +22 -7
- package/src/prerender-vitals.ts +42 -0
- package/src/prerender.ts +61 -32
- package/src/pwa-artifacts.ts +4 -3
- package/src/pwa-manifest-locales.ts +1 -1
- package/src/registry.ts +18 -0
- package/src/repro-bundle.ts +65 -0
- package/src/repro-errors.ts +47 -0
- package/src/repro-job.ts +72 -0
- package/src/repro-literal.ts +41 -0
- package/src/repro-request.ts +48 -0
- package/src/repro-write.ts +100 -0
- package/src/retain-sweep.ts +27 -0
- package/src/retained-assets-boot.ts +113 -0
- package/src/retained-assets-layout.ts +110 -0
- package/src/retained-assets-read.ts +142 -0
- package/src/retained-assets-sweep.ts +70 -0
- package/src/retained-assets.ts +171 -0
- package/src/revalidated-response.ts +8 -5
- package/src/role-replicator.ts +7 -1
- package/src/role-start-devices.ts +119 -0
- package/src/role-start-types.ts +6 -0
- package/src/role-start.ts +22 -5
- package/src/role-sync.ts +7 -1
- package/src/route-islands.ts +42 -0
- package/src/route-table.ts +2 -1
- package/src/row-security-boot.ts +58 -0
- package/src/row-security-wanted.ts +32 -0
- package/src/runtime-admin.ts +0 -1
- package/src/runtime-assets-site-cache.ts +55 -0
- package/src/runtime-assets-site.ts +136 -0
- package/src/runtime-assets.ts +99 -55
- package/src/runtime-bindings.ts +19 -3
- package/src/runtime-cache.ts +23 -14
- package/src/runtime-isr-outcome.ts +114 -60
- package/src/runtime-purge.ts +14 -2
- package/src/runtime-queue.ts +31 -4
- package/src/runtime-render-document.ts +204 -0
- package/src/runtime-render.ts +48 -198
- package/src/runtime-replica-position.ts +62 -0
- package/src/runtime-replica.ts +18 -3
- package/src/runtime-s3-path-style.ts +17 -0
- package/src/runtime-services.ts +19 -6
- package/src/runtime-statement-bound.ts +22 -0
- package/src/runtime-storage-upload.ts +9 -0
- package/src/runtime-storage.ts +42 -138
- package/src/schema-diff-views.ts +54 -0
- package/src/schema-diff.ts +21 -3
- package/src/schema-drift.ts +28 -9
- package/src/schema-dump-drift.ts +91 -7
- package/src/script-csp.ts +1 -1
- package/src/seo-cards.ts +26 -0
- package/src/seo-routes.ts +22 -8
- package/src/serve-boot.ts +54 -21
- package/src/serve-drain.ts +1 -0
- package/src/serve-env.ts +26 -8
- package/src/serve-retire.ts +12 -6
- package/src/serve-web.ts +42 -4
- package/src/serve.ts +9 -7
- package/src/site-asset-routes.ts +17 -27
- package/src/site-config.ts +4 -0
- package/src/site-llms-pages.ts +106 -0
- package/src/site-llms.ts +64 -0
- package/src/site-seo-write.ts +4 -2
- package/src/site-seo.ts +55 -6
- package/src/source-mtime.ts +50 -0
- package/src/static-capture-errors.ts +41 -0
- package/src/static-capture.ts +56 -0
- package/src/static-cards-serve.ts +39 -0
- package/src/static-cards.ts +93 -0
- package/src/static-document.ts +14 -13
- package/src/static-images.ts +115 -0
- package/src/stream-hole-scope.ts +55 -0
- package/src/style-bundle.ts +1 -1
- package/src/style-routes.ts +3 -2
- package/src/sw-precache-plan.ts +2 -2
- package/src/sw-routes.ts +2 -2
- package/src/templates/action-tests.ts +306 -0
- package/src/templates/action.ts +81 -269
- package/src/templates/backfill.ts +1 -1
- package/src/templates/entity.ts +21 -5
- package/src/templates/github/ci.yml.ts +8 -9
- package/src/templates/guard-bare-error.ts +2 -2
- package/src/templates/guard-error-declared.ts +263 -0
- package/src/templates/guard-native-confirm.ts +132 -0
- package/src/templates/guard-raw-fetch.ts +151 -0
- package/src/templates/guard-raw-interval.ts +121 -0
- package/src/templates/guard-repo-raw-sql.ts +71 -2
- package/src/templates/job.ts +1 -1
- package/src/templates/naming.ts +5 -0
- package/src/templates/pinned/docker-compose.yml +6 -0
- package/src/templates/pinned-images.ts +8 -0
- package/src/templates/policy.ts +85 -2
- package/src/templates/query.ts +70 -13
- package/src/templates/resource-create.ts +3 -1
- package/src/templates/resource-form-island.ts +15 -4
- package/src/templates/resource-page-db.ts +83 -0
- package/src/templates/resource-page.ts +161 -52
- package/src/templates/route.ts +4 -6
- package/src/templates/scaffold-app.ts +3 -0
- package/src/templates/scaffold-auth.ts +2 -1
- package/src/templates/scaffold-bin.ts +5 -52
- package/src/templates/scaffold-claude-agents.ts +19 -19
- package/src/templates/scaffold-claude-commands.ts +28 -31
- package/src/templates/scaffold-claude.ts +26 -23
- package/src/templates/scaffold-container-compose.ts +6 -1
- package/src/templates/scaffold-container.ts +24 -4
- package/src/templates/scaffold-dashboard-bare.ts +3 -3
- package/src/templates/scaffold-dashboard-example-tests.ts +49 -9
- package/src/templates/scaffold-dashboard-example-view.ts +156 -0
- package/src/templates/scaffold-dashboard-example.ts +43 -131
- package/src/templates/scaffold-dashboard-shared.ts +3 -7
- package/src/templates/scaffold-db-client.ts +20 -6
- package/src/templates/scaffold-db-package.ts +5 -14
- package/src/templates/scaffold-docs.ts +73 -57
- package/src/templates/scaffold-domain-package.ts +18 -14
- package/src/templates/scaffold-entries.ts +7 -2
- package/src/templates/scaffold-exercise.ts +37 -0
- package/src/templates/scaffold-guards.ts +13 -1
- package/src/templates/scaffold-helm-alerts.ts +234 -0
- package/src/templates/scaffold-helm-templates.ts +7 -5
- package/src/templates/scaffold-helm.ts +46 -12
- package/src/templates/scaffold-i18n.ts +6 -2
- package/src/templates/scaffold-mcp-endpoint.ts +182 -0
- package/src/templates/scaffold-mcp-json.ts +31 -0
- package/src/templates/scaffold-mcp-package.ts +59 -56
- package/src/templates/scaffold-repo.ts +7 -2
- package/src/templates/scaffold-shell.ts +45 -79
- package/src/templates/scaffold-test-services.ts +13 -5
- package/src/templates/shapes/api-keys.ts +170 -0
- package/src/templates/shapes/api.ts +101 -0
- package/src/templates/shapes/blog-site.ts +340 -0
- package/src/templates/shapes/blog-slice.ts +265 -0
- package/src/templates/shapes/blog.ts +114 -0
- package/src/templates/shapes/dashboard-detail.ts +242 -0
- package/src/templates/shapes/dashboard.ts +53 -0
- package/src/templates/shapes/index.ts +17 -0
- package/src/templates/shapes/overlay.ts +99 -0
- package/src/templates/shapes/shared-edits.ts +46 -0
- package/src/templates/sign-in-action-tests.ts +237 -0
- package/src/templates/sign-in-authenticator.ts +112 -0
- package/src/templates/sign-in-oidc.ts +189 -0
- package/src/templates/sign-in-pages.ts +148 -0
- package/src/templates/sign-in-password.ts +216 -0
- package/src/templates/sign-in-shared.ts +268 -0
- package/src/templates/slice-foundation.ts +70 -15
- package/src/test-batches.ts +1 -1
- package/src/test-database.ts +1 -1
- package/src/test-shards.ts +7 -1
- package/src/test-workers.ts +18 -12
- package/src/tool-gate-names.ts +34 -0
- package/src/upgrade-agents-link.ts +49 -0
- package/src/upgrade-bun-floor.ts +225 -0
- package/src/upgrade-gate.ts +228 -0
- package/src/upgrade-pins.ts +63 -0
- package/src/upgrade-plan.ts +214 -0
- package/src/upgrade-registry.ts +53 -0
- package/src/upgrade-report.ts +139 -0
- package/src/upgrade-root.ts +74 -0
- package/src/verify-budgets-build.ts +118 -0
- package/src/verify-checks.ts +67 -24
- package/src/verify-coverage-run.ts +1 -1
- package/src/verify-e2e-lane.ts +41 -0
- package/src/verify-exercise.ts +64 -0
- package/src/verify-floor.ts +41 -0
- package/src/verify-merge.ts +19 -4
- package/src/verify-role-load.ts +2 -0
- package/src/verify-run.ts +28 -8
- package/src/verify-shard.ts +12 -2
- package/src/verify-skip-ceiling.ts +87 -0
- package/src/verify-step.ts +5 -0
- package/src/verify-tests.ts +25 -5
- package/src/view-drift.ts +55 -0
- package/src/worker-bundle.ts +3 -2
- package/src/workspace-graph.ts +38 -19
- package/src/write-generated.ts +43 -0
- package/src/zone-boot.ts +20 -0
- package/wiki/Actions.md +342 -0
- package/wiki/Admin-Dashboard.md +432 -0
- package/wiki/Agents.md +958 -0
- package/wiki/Auth.md +195 -0
- package/wiki/Bare-VM.md +111 -0
- package/wiki/Batching-And-Preloading.md +136 -0
- package/wiki/Building-Your-Own-Base.md +203 -0
- package/wiki/CI-Parallel-Gate.md +220 -0
- package/wiki/CLI-Reference.md +1894 -0
- package/wiki/Caching-And-Invalidation.md +255 -0
- package/wiki/Client-Data.md +194 -0
- package/wiki/Client-Navigation.md +513 -0
- package/wiki/Compared.md +93 -0
- package/wiki/Configuration.md +868 -0
- package/wiki/Contributing.md +12 -0
- package/wiki/Deploy-Recipes.md +122 -0
- package/wiki/Deployment.md +508 -0
- package/wiki/Entities-And-Migrations.md +1116 -0
- package/wiki/FAQ.md +175 -0
- package/wiki/Feature-Flags.md +65 -0
- package/wiki/Getting-Started.md +221 -0
- package/wiki/Home.md +160 -0
- package/wiki/I18n.md +239 -0
- package/wiki/Installation.md +206 -0
- package/wiki/Interface-Rules.md +339 -0
- package/wiki/Jobs-And-Workflows.md +783 -0
- package/wiki/Known-Gaps.md +185 -0
- package/wiki/MCP-And-AI.md +753 -0
- package/wiki/Mail.md +138 -0
- package/wiki/Migrating-An-Existing-App.md +594 -0
- package/wiki/Migrating-From-Django.md +54 -0
- package/wiki/Migrating-From-Laravel.md +53 -0
- package/wiki/Migrating-From-Node-Drizzle.md +53 -0
- package/wiki/Migrating-From-Rails.md +53 -0
- package/wiki/Migrations-And-Backfills.md +552 -0
- package/wiki/Money.md +338 -0
- package/wiki/N-Plus-One-Detection.md +197 -0
- package/wiki/Notify.md +208 -0
- package/wiki/Observability.md +245 -0
- package/wiki/Outbound-HTTP.md +109 -0
- package/wiki/PWA-And-Offline.md +343 -0
- package/wiki/Policies-And-Authz.md +251 -0
- package/wiki/Project-Layout.md +150 -0
- package/wiki/Queries-And-Live-Queries.md +429 -0
- package/wiki/Realtime.md +734 -0
- package/wiki/Resource-Management.md +121 -0
- package/wiki/Routes-And-Render-Modes.md +521 -0
- package/wiki/SEO.md +288 -0
- package/wiki/Scheduled-Tasks.md +175 -0
- package/wiki/Scraping.md +493 -0
- package/wiki/Shapes.md +63 -0
- package/wiki/Stability.md +97 -0
- package/wiki/Static-Assets.md +166 -0
- package/wiki/Storage-And-Uploads.md +404 -0
- package/wiki/Testing.md +1313 -0
- package/wiki/The-Eight-Primitives.md +247 -0
- package/wiki/Theming.md +362 -0
- package/wiki/Timezones-And-Dates.md +194 -0
- package/wiki/Troubleshooting.md +163 -0
- package/wiki/Tutorial-01-First-App.md +289 -0
- package/wiki/Tutorial-02-First-Feature.md +339 -0
- package/wiki/Tutorial-03-Auth-And-Admin.md +379 -0
- package/wiki/Tutorial-04-Jobs-And-Realtime.md +263 -0
- package/wiki/Tutorial-05-Deploy-Free.md +231 -0
- package/wiki/Tutorial-06-Growing-Up.md +158 -0
- package/wiki/UI-Components.md +156 -0
- package/wiki/Upgrading.md +3667 -0
- package/src/stored-object-headers.ts +0 -43
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Resource management
|
|
2
|
+
|
|
3
|
+
Every database resource the framework hands out — a pooled connection, PGlite's single session
|
|
4
|
+
turn — is `Disposable`. Hold it with `using` or `await using` and it goes back on every exit path:
|
|
5
|
+
a normal return, a throw, an early `return` buried three branches deep. There is no second way to
|
|
6
|
+
release one.
|
|
7
|
+
|
|
8
|
+
## Why RAII here
|
|
9
|
+
|
|
10
|
+
A pinned connection that never comes back, or an advisory lock released on a different session
|
|
11
|
+
than the one that took it, was a real bug class — not a hypothetical one. `pg_advisory_lock` is
|
|
12
|
+
scoped to a Postgres **session**, not a statement: take it on a pooled handle and the pool can hand
|
|
13
|
+
that physical connection to someone else before the unlock runs, and the unlock lands on a session
|
|
14
|
+
that never held the lock. `withTransaction`'s `BEGIN` used to sit above its guarded block; a `BEGIN`
|
|
15
|
+
that itself rejected — a dead connection, a server in recovery — returned the pin to nobody, and on
|
|
16
|
+
PGlite that stranded the process's one session forever.
|
|
17
|
+
|
|
18
|
+
The fix in every one of these places is the same shape: put the resource behind a `using`
|
|
19
|
+
declaration that starts guarding *before* the risky call, not a hand-rolled `try`/`finally` that
|
|
20
|
+
only covers what someone remembered to put inside its `try`.
|
|
21
|
+
|
|
22
|
+
## The idiom
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
using connection = await client.reserve();
|
|
26
|
+
await connection.execute(sql`...`);
|
|
27
|
+
// released automatically here, however the block exits
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`await using` is the same shape for a resource whose teardown is itself async. `As of 2026-08`
|
|
31
|
+
nothing in `@ultimat3/db` needs it — `release()` on both drivers is synchronous — but the language
|
|
32
|
+
feature is there for a `Disposable` whose cleanup does need to await, and this line is what to
|
|
33
|
+
update when the first async call site lands.
|
|
34
|
+
|
|
35
|
+
Inside the framework, three call sites hold a pin this way, all in `@ultimat3/db`:
|
|
36
|
+
|
|
37
|
+
| Site | What it pins, and why |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `withTransaction` (`packages/db/src/transaction.ts`) | one connection for `BEGIN`…`COMMIT`/`ROLLBACK` — a pooled `BEGIN` landing on a different physical connection than the statements after it would not be a transaction at all |
|
|
40
|
+
| `readOnlyQuery` (`packages/db/src/readonly-query.ts`) | one connection for `BEGIN READ ONLY`…`ROLLBACK` — same reasoning, read-only path |
|
|
41
|
+
| the migration advisory lock (`packages/db/src/migrate.ts`) | one session for `pg_advisory_lock`…`pg_advisory_unlock` — the lock and the unlock must be the same session or the unlock is a silent no-op |
|
|
42
|
+
|
|
43
|
+
In each, the declaration reads `using reserved: DbConnection | undefined = isReservable(client) ? await client.reserve() : undefined;` — a connection that isn't reservable (a fake in a test, a plain
|
|
44
|
+
`DbClient`) disposes of nothing, which is correct: there was never a pin to give back.
|
|
45
|
+
|
|
46
|
+
## Idempotent release
|
|
47
|
+
|
|
48
|
+
`release()` — and `[Symbol.dispose]`, which is the exact same function — can be called more than
|
|
49
|
+
once. The second call is a no-op, never an error. This matters because a caller can hold a
|
|
50
|
+
`DbConnection` past a `using` scope's own disposal and call `release()` by hand; on both drivers
|
|
51
|
+
(`packages/db/src/client.ts` for pooled Postgres, `packages/db/src/pglite-turns.ts` for the
|
|
52
|
+
embedded single session) release is a `held` flag flip or a settled promise's `resolve`, neither of
|
|
53
|
+
which a second call can act on twice. A double release is a no-op precisely so two owners on one
|
|
54
|
+
exit path — `withTransaction`'s `finally` and the `using` declaration's own disposal, historically —
|
|
55
|
+
can never fight over who releases first.
|
|
56
|
+
|
|
57
|
+
One more consequence of the connection being `Disposable`: once `release()` has run, a *late*
|
|
58
|
+
statement issued on the stale handle does not silently corrupt whoever holds that physical
|
|
59
|
+
connection now — `client.ts` routes it back through the pool for a connection of its own instead of
|
|
60
|
+
running it directly. Direct execution only happens while the pin is actually held.
|
|
61
|
+
|
|
62
|
+
## What is `Disposable`
|
|
63
|
+
|
|
64
|
+
| Type | Package | Disposed by |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| `DbConnection` (`client.reserve()`'s return) | `@ultimat3/db` (`client.ts`) | `release()` / `[Symbol.dispose]` |
|
|
67
|
+
| `Turn` (PGlite's single-session turn queue, `queue.take()`'s return) | `@ultimat3/db` (`pglite-turns.ts`) | `release()` / `[Symbol.dispose]` |
|
|
68
|
+
|
|
69
|
+
`Turn` is the PGlite-specific version of the same problem: embedded Postgres is one session, not a
|
|
70
|
+
pool, so two units of work that both `BEGIN` would be sharing one transaction unless something
|
|
71
|
+
serializes them. `createTurnQueue()`'s `take()` hands out a `Turn`, and `using turn = await
|
|
72
|
+
queue.take()` gives the session back to the next waiter on every exit, exactly the shape
|
|
73
|
+
`DbConnection` uses.
|
|
74
|
+
|
|
75
|
+
The test double follows the same contract: `reservableOver()` (`packages/db/src/fake-reservable-fixture.ts`)
|
|
76
|
+
wraps any `DbClient` in a `ReservableClient` whose `reserve()` returns a connection with the same
|
|
77
|
+
idempotent `release()` / `[Symbol.dispose]` pairing, and counts reservations against releases — a
|
|
78
|
+
test proving no leak asserts `pins.reserves === pins.releases` rather than reasoning about it.
|
|
79
|
+
|
|
80
|
+
## Enforced, not documented
|
|
81
|
+
|
|
82
|
+
Per the framework's own rule that a convention which isn't a build error doesn't exist,
|
|
83
|
+
`packages/db/src/type-pins.ts` carries compile-time assertions — `_DbConnectionIsDisposable` and
|
|
84
|
+
`_TurnIsDisposable` — that fail `tsc` if `Disposable` is ever dropped from `DbConnection` or `Turn`.
|
|
85
|
+
It lives in `src/`, not as a `.test.ts`: `tsconfig.json` excludes test files from `tsc -b`, so a
|
|
86
|
+
type-level assertion written there could never fail. If a future edit quietly removes `Disposable`
|
|
87
|
+
from either interface, every `using` declaration built on it stops compiling as a scope-bound
|
|
88
|
+
resource and degrades back into a hand-rolled `try`/`finally` — silently, at every call site, unless
|
|
89
|
+
this pin catches it first. A regression here is a typecheck failure across `@ultimat3/db`, not a
|
|
90
|
+
leaked connection discovered under load.
|
|
91
|
+
|
|
92
|
+
## The same pattern outside the database
|
|
93
|
+
|
|
94
|
+
Realtime read hooks are `Disposable` too, the same aliasing trick. In 21.0.0 the
|
|
95
|
+
accessors `useQuery` and `useRecord` return set `[Symbol.dispose]` to the exact same function as
|
|
96
|
+
`release()`, so `using feed = useQuery(...)` (`packages/realtime/src/use-query.ts`) tears a
|
|
97
|
+
subscription down on scope exit the same way a `using
|
|
98
|
+
connection` returns a pin — different subsystem, same idiom. See [Realtime](Realtime).
|
|
99
|
+
|
|
100
|
+
## Bounded, not disposable
|
|
101
|
+
|
|
102
|
+
Two resources have no owner to give them back, so they are bounded instead. `As of 2026-08-23`,
|
|
103
|
+
both mechanisms live in `@ultimat3/core`.
|
|
104
|
+
|
|
105
|
+
| Resource | Bound | What happens at the bound |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| a slot in a concurrency gate (`flightGate`) | `maxConcurrent` running, `maxQueued` waiting | past the queue the answer is a **refusal** — `X_FLIGHT_GATE_OVERLOADED`, 503, `Retry-After: 1`. Never a longer queue: an unbounded queue turns a load spike into a memory fault and answers it minutes late. `@ultimat3/auth` throws its own `X_OVERLOADED` through the same gate |
|
|
108
|
+
| a key held by an in-flight load (`singleFlight`) | an optional `deadlineMs` | the **key** is freed and the next caller may start its own. The work is not cancelled and no promise is rejected — the framework holds no signal that could abort a caller's function, and pretending otherwise would be a second, false promise. `cacheStack` sets 30 s, `@ultimat3/auth`'s JWKS refresh twice its transport timeout |
|
|
109
|
+
| a joiner's stake in a load whose leader was aborted (`singleFlight`) | an optional `signal: () => AbortSignal \| undefined`, read at each `run` | a joiner with a live signal re-leads with its own `work`, under its own context; the first leads and the rest join it. Its own signal dead, or the leader's live: the rejection is shared, ordinary failures included. Off by default; `cacheStack` sets it. `As of 2026-11` |
|
|
110
|
+
| a retry loop (`retry`) | the ambient `ctx.signal`, and the smaller of `timeBudgetMs` and what is left of `ctx.deadlineAt` | no further attempt, and a wait ends at once. The last error reaches the caller unchanged, or the signal's reason when no attempt ran. There is no opt-out: work that must outlive its caller runs inside `runToCompletion(fn)`, where the signal is live and the deadline null. `As of 2026-11` |
|
|
111
|
+
|
|
112
|
+
A slot is handed **over** on release rather than released and re-acquired: decrementing first would
|
|
113
|
+
let a caller arriving in the same tick past the ceiling while a waiter's continuation is still a
|
|
114
|
+
queued microtask, which is how a bounded pool goes over its bound under exactly the load it exists
|
|
115
|
+
for.
|
|
116
|
+
|
|
117
|
+
## See also
|
|
118
|
+
|
|
119
|
+
- [Entities and migrations](Entities-And-Migrations) — the transaction and query paths these pins hold up
|
|
120
|
+
- [Caching and invalidation](Caching-And-Invalidation) — the load deadline on the read ladder
|
|
121
|
+
- [Error codes](Error-Codes)
|