@cosmicdrift/kumiko-framework 0.387.0 → 0.389.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/AGENTS.md +9 -1
- package/agents/examples/anonymous-access.md +1 -1
- package/agents/examples/apex-landing.md +204 -0
- package/agents/examples/api-worker-split.md +64 -0
- package/agents/examples/app-config.md +14 -0
- package/agents/examples/auth-mfa.md +115 -0
- package/agents/examples/env-composition.md +28 -1
- package/agents/examples/golden-path.md +13 -1
- package/agents/examples/legal-pages.md +1 -1
- package/agents/examples/lifecycle-hooks.md +55 -2
- package/agents/examples/list-table-options.md +466 -0
- package/agents/examples/pdf-audit-export.md +4 -2
- package/agents/examples/prod-boot-options.md +16 -0
- package/agents/examples/rate-limiting.md +48 -0
- package/agents/examples/record-detail-layout.md +692 -0
- package/agents/examples/seed-migration.md +212 -0
- package/agents/examples/state-machine.md +3 -2
- package/agents/examples/tenant-isolation.md +73 -1
- package/agents/examples/wizard-form.md +941 -0
- package/agents/examples/writeform-section.md +548 -0
- package/agents/examples.md +1 -0
- package/agents/features.md +19 -2
- package/agents/widgets.md +2 -0
- package/dist/api/api-constants.d.ts +1 -0
- package/dist/api/api-constants.js +2 -0
- package/dist/api/auth-routes-shared.d.ts +2 -0
- package/dist/api/auth-routes-shared.js +5 -0
- package/dist/api/client-error-route.js +10 -5
- package/dist/api/entity-read-access.d.ts +2 -1
- package/dist/api/entity-read-access.js +14 -0
- package/dist/api/export/export-cells-reference.d.ts +9 -0
- package/dist/api/export/export-cells-reference.js +21 -0
- package/dist/api/export/export-cells.d.ts +11 -0
- package/dist/api/export/export-cells.js +136 -0
- package/dist/api/export/export-ciphertext-guard.d.ts +2 -0
- package/dist/api/export/export-ciphertext-guard.js +27 -0
- package/dist/api/export/export-columns.d.ts +29 -0
- package/dist/api/export/export-columns.js +85 -0
- package/dist/api/export/export-errors.d.ts +4 -0
- package/dist/api/export/export-errors.js +14 -0
- package/dist/api/export/export-route.d.ts +4 -0
- package/dist/api/export/export-route.js +74 -0
- package/dist/api/export/export-rows.d.ts +13 -0
- package/dist/api/export/export-rows.js +62 -0
- package/dist/api/export/export-screen.d.ts +12 -0
- package/dist/api/export/export-screen.js +47 -0
- package/dist/api/export/export-xlsx-writer.d.ts +4 -0
- package/dist/api/export/export-xlsx-writer.js +21 -0
- package/dist/api/extra-route.d.ts +4 -0
- package/dist/api/index.d.ts +3 -1
- package/dist/api/index.js +3 -1
- package/dist/api/pii-leak-guard.d.ts +3 -0
- package/dist/api/pii-leak-guard.js +3 -2
- package/dist/api/request-context.d.ts +1 -1
- package/dist/api/request-context.js +1 -1
- package/dist/api/routes.d.ts +16 -0
- package/dist/api/routes.js +2 -2
- package/dist/api/server-boot-checks.d.ts +10 -0
- package/dist/api/server-boot-checks.js +157 -0
- package/dist/api/server-event-dispatcher.d.ts +26 -0
- package/dist/api/server-event-dispatcher.js +208 -0
- package/dist/api/server-extra-routes.d.ts +11 -0
- package/dist/api/server-extra-routes.js +206 -0
- package/dist/api/server-middleware.d.ts +20 -0
- package/dist/api/server-middleware.js +211 -0
- package/dist/api/server-routes.d.ts +19 -0
- package/dist/api/server-routes.js +299 -0
- package/dist/api/server-websocket-routes.d.ts +4 -0
- package/dist/api/server-websocket-routes.js +108 -0
- package/dist/api/server-wiring.d.ts +32 -0
- package/dist/api/server-wiring.js +238 -0
- package/dist/api/server.d.ts +10 -15
- package/dist/api/server.js +48 -1251
- package/dist/api/session-cookies.js +4 -4
- package/dist/api/sse-route.d.ts +2 -1
- package/dist/api/sse-route.js +27 -13
- package/dist/config/index.d.ts +3 -1
- package/dist/crypto/owner-transition-rekey.d.ts +5 -1
- package/dist/crypto/owner-transition-rekey.js +1 -1
- package/dist/crypto/pg-kms-adapter.d.ts +1 -0
- package/dist/crypto/pg-kms-adapter.js +12 -18
- package/dist/crypto/pii-field-encryption.d.ts +5 -1
- package/dist/crypto/pii-field-encryption.js +3 -1
- package/dist/db/aggregate-sql.d.ts +23 -0
- package/dist/db/aggregate-sql.js +165 -0
- package/dist/db/closed-connection.d.ts +9 -0
- package/dist/db/closed-connection.js +88 -0
- package/dist/db/connection.d.ts +4 -5
- package/dist/db/connection.js +29 -20
- package/dist/db/dialect.js +1 -1
- package/dist/db/distinct-on.d.ts +1 -1
- package/dist/db/driver-row-coercion.d.ts +2 -0
- package/dist/db/driver-row-coercion.js +186 -0
- package/dist/db/driver-value-preparation.d.ts +16 -0
- package/dist/db/driver-value-preparation.js +101 -0
- package/dist/db/entity-field-encryption.d.ts +1 -0
- package/dist/db/entity-field-encryption.js +1 -1
- package/dist/db/entity-table-meta.js +103 -110
- package/dist/db/event-store-executor-context.d.ts +2 -1
- package/dist/db/event-store-executor-read-aggregate-currencies.d.ts +17 -0
- package/dist/db/event-store-executor-read-aggregate-currencies.js +40 -0
- package/dist/db/event-store-executor-read-aggregate.d.ts +8 -0
- package/dist/db/event-store-executor-read-aggregate.js +143 -0
- package/dist/db/event-store-executor-read-filter.d.ts +31 -0
- package/dist/db/event-store-executor-read-filter.js +206 -0
- package/dist/db/event-store-executor-read-paging.d.ts +14 -0
- package/dist/db/event-store-executor-read-paging.js +99 -0
- package/dist/db/event-store-executor-read-rows.d.ts +6 -0
- package/dist/db/event-store-executor-read-rows.js +32 -0
- package/dist/db/event-store-executor-read-search.d.ts +28 -0
- package/dist/db/event-store-executor-read-search.js +221 -0
- package/dist/db/event-store-executor-read-sort.d.ts +41 -0
- package/dist/db/event-store-executor-read-sort.js +130 -0
- package/dist/db/event-store-executor-read-where.d.ts +24 -0
- package/dist/db/event-store-executor-read-where.js +81 -0
- package/dist/db/event-store-executor-read.d.ts +2 -9
- package/dist/db/event-store-executor-read.js +48 -701
- package/dist/db/event-store-executor-write-atomic.d.ts +4 -0
- package/dist/db/event-store-executor-write-atomic.js +41 -0
- package/dist/db/event-store-executor-write-steps.d.ts +17 -0
- package/dist/db/event-store-executor-write-steps.js +95 -0
- package/dist/db/event-store-executor-write.js +405 -509
- package/dist/db/event-store-executor.js +2 -1
- package/dist/db/executor-managed-tables.js +1 -1
- package/dist/db/index.d.ts +7 -3
- package/dist/db/index.js +6 -2
- package/dist/db/list-aggregate-labels.d.ts +14 -0
- package/dist/db/list-aggregate-labels.js +26 -0
- package/dist/db/migrate-generator.js +71 -86
- package/dist/db/migrate-runner.js +7 -4
- package/dist/db/pg-error.js +26 -21
- package/dist/db/queries/backfill-pii.js +3 -6
- package/dist/db/queries/database-now.js +2 -1
- package/dist/db/queries/shadow-swap.js +2 -3
- package/dist/db/query.d.ts +16 -15
- package/dist/db/query.js +76 -804
- package/dist/db/reference-data.js +19 -2
- package/dist/db/statement-timeout.js +2 -1
- package/dist/db/table-builder.js +113 -166
- package/dist/db/table-info.d.ts +10 -0
- package/dist/db/table-info.js +45 -0
- package/dist/db/temporal-value-guards.d.ts +6 -0
- package/dist/db/temporal-value-guards.js +15 -0
- package/dist/db/tenant-db-runner.d.ts +1 -0
- package/dist/db/tenant-db-runner.js +3 -0
- package/dist/db/tenant-db-upsert.d.ts +5 -0
- package/dist/db/tenant-db-upsert.js +91 -0
- package/dist/db/tenant-db.js +50 -10
- package/dist/db/where-clause.d.ts +6 -0
- package/dist/db/where-clause.js +236 -0
- package/dist/derivatives/delete-stored-file.js +2 -1
- package/dist/engine/boot-validator/boot-check.js +1 -1
- package/dist/engine/boot-validator/bundled-lists-table.d.ts +2 -0
- package/dist/engine/boot-validator/bundled-lists-table.js +36 -0
- package/dist/engine/boot-validator/detail-screens.js +2 -2
- package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
- package/dist/engine/boot-validator/entity-handler.js +17 -0
- package/dist/engine/boot-validator/entity-list-aggregate-options.d.ts +3 -0
- package/dist/engine/boot-validator/entity-list-aggregate-options.js +23 -0
- package/dist/engine/boot-validator/form-derivations.js +35 -1
- package/dist/engine/boot-validator/i18n-keys.js +5 -3
- package/dist/engine/boot-validator/index.js +2 -0
- package/dist/engine/boot-validator/list-bulk-actions.d.ts +3 -0
- package/dist/engine/boot-validator/list-bulk-actions.js +49 -0
- package/dist/engine/boot-validator/list-column-layout.d.ts +8 -0
- package/dist/engine/boot-validator/list-column-layout.js +41 -0
- package/dist/engine/boot-validator/list-table-aggregate-options.d.ts +24 -0
- package/dist/engine/boot-validator/list-table-aggregate-options.js +145 -0
- package/dist/engine/boot-validator/list-table-aggregate-query.d.ts +10 -0
- package/dist/engine/boot-validator/list-table-aggregate-query.js +31 -0
- package/dist/engine/boot-validator/list-table-export-options.d.ts +1 -0
- package/dist/engine/boot-validator/list-table-export-options.js +27 -0
- package/dist/engine/boot-validator/list-table-options.d.ts +3 -0
- package/dist/engine/boot-validator/list-table-options.js +77 -0
- package/dist/engine/boot-validator/list-table-reject-unknown-keys.d.ts +1 -0
- package/dist/engine/boot-validator/list-table-reject-unknown-keys.js +7 -0
- package/dist/engine/boot-validator/pii-retention.js +233 -155
- package/dist/engine/boot-validator/projection-list-screens.d.ts +1 -1
- package/dist/engine/boot-validator/projection-list-screens.js +3 -13
- package/dist/engine/boot-validator/related-list-table-rules.d.ts +4 -0
- package/dist/engine/boot-validator/related-list-table-rules.js +42 -0
- package/dist/engine/boot-validator/screens-actions.d.ts +1 -0
- package/dist/engine/boot-validator/screens-actions.js +8 -1
- package/dist/engine/boot-validator/screens-dashboard.js +2 -5
- package/dist/engine/boot-validator/screens-forms.js +2 -6
- package/dist/engine/boot-validator/screens-kind-config-edit.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-config-edit.js +88 -0
- package/dist/engine/boot-validator/screens-kind-entity-edit.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-entity-edit.js +55 -0
- package/dist/engine/boot-validator/screens-kind-entity-list.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-entity-list.js +235 -0
- package/dist/engine/boot-validator/screens-kind-projection-detail.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-projection-detail.js +235 -0
- package/dist/engine/boot-validator/screens-kind-projection-list.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-projection-list.js +56 -0
- package/dist/engine/boot-validator/screens-kind-secret-mint.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-secret-mint.js +28 -0
- package/dist/engine/boot-validator/screens-kind-shared.d.ts +37 -0
- package/dist/engine/boot-validator/screens-kind-shared.js +142 -0
- package/dist/engine/boot-validator/screens-lists.d.ts +2 -1
- package/dist/engine/boot-validator/screens-lists.js +24 -11
- package/dist/engine/boot-validator/screens.d.ts +0 -2
- package/dist/engine/boot-validator/screens.js +99 -860
- package/dist/engine/build-app-schema.d.ts +2 -1
- package/dist/engine/build-app-schema.js +65 -97
- package/dist/engine/build-config-feature-schema.js +6 -1
- package/dist/engine/define-feature.js +13 -2
- package/dist/engine/define-handler.js +1 -0
- package/dist/engine/entity-handlers.d.ts +13 -0
- package/dist/engine/entity-handlers.js +10 -21
- package/dist/engine/entity-list-runtime-options.d.ts +8 -0
- package/dist/engine/entity-list-runtime-options.js +18 -0
- package/dist/engine/feature-ast/extractors/handlers.js +1 -0
- package/dist/engine/feature-ast/extractors/index.d.ts +1 -1
- package/dist/engine/feature-ast/extractors/index.js +1 -1
- package/dist/engine/feature-ast/extractors/round1.d.ts +2 -1
- package/dist/engine/feature-ast/extractors/round1.js +6 -0
- package/dist/engine/feature-ast/extractors/round2.js +25 -6
- package/dist/engine/feature-ast/index.d.ts +1 -1
- package/dist/engine/feature-ast/parse.js +3 -1
- package/dist/engine/feature-ast/patch.d.ts +2 -0
- package/dist/engine/feature-ast/patch.js +5 -182
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +10 -245
- package/dist/engine/feature-ast/pattern-change-schema.js +10 -163
- package/dist/engine/feature-ast/pattern-id-matchers.d.ts +7 -0
- package/dist/engine/feature-ast/pattern-id-matchers.js +211 -0
- package/dist/engine/feature-ast/pattern-id-schema.d.ts +251 -0
- package/dist/engine/feature-ast/pattern-id-schema.js +166 -0
- package/dist/engine/feature-ast/patterns.d.ts +14 -1
- package/dist/engine/feature-ast/patterns.js +44 -51
- package/dist/engine/feature-ast/render.js +52 -87
- package/dist/engine/feature-builder-state.d.ts +1 -0
- package/dist/engine/feature-builder-state.js +1 -0
- package/dist/engine/feature-entity-handlers.js +3 -0
- package/dist/engine/feature-manifest.d.ts +1 -0
- package/dist/engine/feature-manifest.js +1 -0
- package/dist/engine/field-access.d.ts +1 -0
- package/dist/engine/field-access.js +12 -0
- package/dist/engine/form-derivations.js +18 -7
- package/dist/engine/handler-field-references.js +1 -1
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +1 -1
- package/dist/engine/pattern-library/library.js +2 -1
- package/dist/engine/pattern-library/static-schemas.d.ts +1 -0
- package/dist/engine/pattern-library/static-schemas.js +9 -0
- package/dist/engine/project-app-schema-for-roles.d.ts +2 -0
- package/dist/engine/project-app-schema-for-roles.js +6 -4
- package/dist/engine/registry-ingest.js +1 -1
- package/dist/engine/schema-builder.js +228 -212
- package/dist/engine/screen-filter-ops.js +2 -0
- package/dist/engine/screen-helpers.js +1 -1
- package/dist/engine/screen-kind.d.ts +7 -0
- package/dist/engine/screen-kind.js +1 -0
- package/dist/engine/strip-disabled-features-from-app-schema.js +58 -17
- package/dist/engine/types/feature.d.ts +1 -0
- package/dist/engine/types/index.d.ts +4 -3
- package/dist/engine/types/screen.d.ts +1 -1
- package/dist/engine/types/screen.js +1 -1
- package/dist/engine/validate-projection-allowlist.js +2 -1
- package/dist/entrypoint/index.d.ts +6 -1
- package/dist/entrypoint/index.js +10 -1
- package/dist/errors/docs-url.d.ts +1 -1
- package/dist/errors/docs-url.js +22 -2
- package/dist/es-ops/context.js +1 -1
- package/dist/event-store/admin-api.js +1 -1
- package/dist/event-store/event-store.d.ts +1 -0
- package/dist/event-store/event-store.js +17 -8
- package/dist/event-store/provenance-append.js +1 -1
- package/dist/files/bind-pending-file-refs.js +3 -3
- package/dist/files/create-file-ref-from-bytes.js +7 -1
- package/dist/files/create-file-ref.d.ts +3 -0
- package/dist/files/create-file-ref.js +11 -2
- package/dist/files/file-handle.js +2 -3
- package/dist/files/file-routes.d.ts +2 -0
- package/dist/files/file-routes.js +40 -10
- package/dist/files/provider-resolver.d.ts +1 -0
- package/dist/files/provider-resolver.js +1 -1
- package/dist/files/record-write-check.d.ts +6 -0
- package/dist/files/record-write-check.js +13 -0
- package/dist/files/resolve-content-type.js +43 -0
- package/dist/files/upload-ledger.d.ts +2 -2
- package/dist/files/upload-ledger.js +11 -4
- package/dist/i18n/required-surface-keys.js +189 -208
- package/dist/internal/stack-support.d.ts +2 -1
- package/dist/internal/stack-support.js +1 -1
- package/dist/internal/testing.js +1 -1
- package/dist/jobs/job-runner-context.d.ts +32 -0
- package/dist/jobs/job-runner-context.js +305 -0
- package/dist/jobs/job-runner-execution.d.ts +7 -0
- package/dist/jobs/job-runner-execution.js +160 -0
- package/dist/jobs/job-runner-gates.d.ts +19 -0
- package/dist/jobs/job-runner-gates.js +198 -0
- package/dist/jobs/job-runner-helpers.d.ts +17 -3
- package/dist/jobs/job-runner-helpers.js +61 -2
- package/dist/jobs/job-runner-scope.d.ts +44 -0
- package/dist/jobs/job-runner-scope.js +102 -0
- package/dist/jobs/job-runner-types.d.ts +12 -0
- package/dist/jobs/job-runner.d.ts +0 -1
- package/dist/jobs/job-runner.js +50 -657
- package/dist/observability/escape-hatch-report.d.ts +3 -0
- package/dist/observability/escape-hatch-report.js +21 -3
- package/dist/pipeline/after-rollback-sink.d.ts +6 -0
- package/dist/pipeline/after-rollback-sink.js +32 -0
- package/dist/pipeline/cascade-handler.js +165 -138
- package/dist/pipeline/dispatch-batch.js +8 -1
- package/dist/pipeline/dispatch-query.js +10 -3
- package/dist/pipeline/dispatch-shared.d.ts +13 -3
- package/dist/pipeline/dispatch-shared.js +62 -710
- package/dist/pipeline/dispatch-write.js +296 -189
- package/dist/pipeline/event-dispatcher-admin.js +2 -1
- package/dist/pipeline/event-dispatcher-delivery.js +6 -6
- package/dist/pipeline/event-dispatcher.d.ts +1 -0
- package/dist/pipeline/event-dispatcher.js +6 -9
- package/dist/pipeline/handler-context-auth.d.ts +9 -0
- package/dist/pipeline/handler-context-auth.js +54 -0
- package/dist/pipeline/handler-context-config.d.ts +14 -0
- package/dist/pipeline/handler-context-config.js +113 -0
- package/dist/pipeline/handler-context-db.d.ts +24 -0
- package/dist/pipeline/handler-context-db.js +149 -0
- package/dist/pipeline/handler-context-dispatch.d.ts +23 -0
- package/dist/pipeline/handler-context-dispatch.js +101 -0
- package/dist/pipeline/handler-context-events.d.ts +41 -0
- package/dist/pipeline/handler-context-events.js +239 -0
- package/dist/pipeline/handler-context-notify.d.ts +12 -0
- package/dist/pipeline/handler-context-notify.js +55 -0
- package/dist/pipeline/handler-context-observability.d.ts +7 -0
- package/dist/pipeline/handler-context-observability.js +21 -0
- package/dist/pipeline/handler-context-scope.d.ts +48 -0
- package/dist/pipeline/handler-context-scope.js +88 -0
- package/dist/pipeline/notify-scope.js +12 -1
- package/dist/pipeline/pre-handler-gates.js +20 -4
- package/dist/pipeline/pre-transaction-reservations.d.ts +1 -0
- package/dist/pipeline/pre-transaction-reservations.js +9 -3
- package/dist/pipeline/projection-rebuild.js +2 -1
- package/dist/pipeline/system-hooks.d.ts +3 -1
- package/dist/pipeline/system-hooks.js +55 -6
- package/dist/pipeline/write-result-masking.js +13 -10
- package/dist/schema-cli/apply-command.d.ts +2 -0
- package/dist/schema-cli/apply-command.js +97 -0
- package/dist/schema-cli/baseline-command.d.ts +2 -0
- package/dist/schema-cli/baseline-command.js +33 -0
- package/dist/schema-cli/generate-command.d.ts +2 -0
- package/dist/schema-cli/generate-command.js +90 -0
- package/dist/schema-cli/shared.d.ts +39 -0
- package/dist/schema-cli/shared.js +95 -0
- package/dist/schema-cli/status-command.d.ts +2 -0
- package/dist/schema-cli/status-command.js +73 -0
- package/dist/schema-cli/validate-boot-input.d.ts +5 -0
- package/dist/schema-cli/validate-boot-input.js +130 -0
- package/dist/schema-cli/validate-command.d.ts +2 -0
- package/dist/schema-cli/validate-command.js +130 -0
- package/dist/schema-cli.d.ts +2 -18
- package/dist/schema-cli.js +47 -604
- package/dist/search/meilisearch-adapter.js +2 -1
- package/dist/search/reindex-entity.js +15 -5
- package/dist/ui-types/index.d.ts +2 -2
- package/dist/ui-types/index.js +1 -1
- package/dist/upgrade-changelog.d.ts +2 -0
- package/dist/upgrade-changelog.js +14 -0
- package/dist/upgrade-cli.d.ts +3 -12
- package/dist/upgrade-cli.js +171 -197
- package/dist/upgrade-marker-parse.d.ts +32 -0
- package/dist/upgrade-marker-parse.js +73 -0
- package/package.json +9 -6
- package/src/changes.json +555 -0
- package/src/scripts/codemod/codemod-support.ts +1 -1
- package/src/scripts/codemod/db-factory.ts +1 -1
- package/src/scripts/codemod/feature-layout.ts +10 -2
- package/src/scripts/codemod/object-form.ts +41 -4
- package/src/scripts/codemod/package-merge.ts +58 -8
- package/src/scripts/codemod/pii-personal-migration.ts +175 -145
- package/src/scripts/codemod/testing-injection.ts +122 -42
- package/src/scripts/codemod/testing-move.ts +29 -7
- package/dist/engine/provider-plugin-context.d.ts +0 -3
- package/dist/engine/provider-plugin-context.js +0 -28
package/dist/db/connection.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import postgres from "postgres";
|
|
2
1
|
import { readPositiveIntEnv } from "../utils/env-parse.js";
|
|
3
2
|
// A DB outage can leave an in-flight query referenced on a dead connection,
|
|
4
|
-
// so a plain
|
|
3
|
+
// so a plain close() (no timeout = wait for queries to finish) hangs forever —
|
|
5
4
|
// close() must always bound how long shutdown waits.
|
|
6
5
|
export const DEFAULT_DB_CLOSE_TIMEOUT_SECONDS = 5;
|
|
7
6
|
// Keyed by the pool so withoutStatementTimeout can open a sibling with identical connect options.
|
|
@@ -11,20 +10,28 @@ export function openUnboundedSiblingPool(db) {
|
|
|
11
10
|
return undefined;
|
|
12
11
|
return unboundedSiblingFactories.get(db)?.();
|
|
13
12
|
}
|
|
13
|
+
// Bun.SQL treats `true` and a TLS object without rejectUnauthorized as sslmode=require, which
|
|
14
|
+
// skips certificate checks; postgres-js verified in both cases, so keep that unless turned off.
|
|
15
|
+
function verifiedTls(ssl) {
|
|
16
|
+
if (ssl === true)
|
|
17
|
+
return "verify-full";
|
|
18
|
+
if (typeof ssl === "object" && !(ssl instanceof Blob) && ssl.rejectUnauthorized === undefined) {
|
|
19
|
+
return { ...ssl, rejectUnauthorized: true };
|
|
20
|
+
}
|
|
21
|
+
return ssl;
|
|
22
|
+
}
|
|
14
23
|
export function createDbConnection({ url, ...options }) {
|
|
15
|
-
const
|
|
24
|
+
const sqlOptions = {};
|
|
16
25
|
if (options.maxConnections !== undefined)
|
|
17
|
-
|
|
26
|
+
sqlOptions.max = options.maxConnections;
|
|
18
27
|
if (options.idleTimeoutSeconds !== undefined) {
|
|
19
|
-
|
|
28
|
+
sqlOptions.idleTimeout = options.idleTimeoutSeconds;
|
|
20
29
|
}
|
|
21
30
|
if (options.connectTimeoutSeconds !== undefined) {
|
|
22
|
-
|
|
31
|
+
sqlOptions.connectionTimeout = options.connectTimeoutSeconds;
|
|
23
32
|
}
|
|
24
33
|
if (options.ssl !== undefined)
|
|
25
|
-
|
|
26
|
-
if (options.onNotice !== undefined)
|
|
27
|
-
pgOptions.onnotice = options.onNotice;
|
|
34
|
+
sqlOptions.tls = verifiedTls(options.ssl);
|
|
28
35
|
const serverParameters = {};
|
|
29
36
|
if (options.statementTimeoutMs !== undefined) {
|
|
30
37
|
serverParameters["statement_timeout"] = options.statementTimeoutMs;
|
|
@@ -36,48 +43,50 @@ export function createDbConnection({ url, ...options }) {
|
|
|
36
43
|
if (options.lockTimeoutMs !== undefined)
|
|
37
44
|
serverParameters["lock_timeout"] = options.lockTimeoutMs;
|
|
38
45
|
if (Object.keys(serverParameters).length > 0)
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
sqlOptions.connection = serverParameters;
|
|
47
|
+
// Bun.SQL opens all `max` connections on the first query, so a pool always costs `max` backends.
|
|
48
|
+
const client = new Bun.SQL(url, sqlOptions);
|
|
41
49
|
// A run that never settles (a caller gave up on it via its own timeout) never reaches the
|
|
42
50
|
// sibling's own close; close() ends it so shutdown and DROP DATABASE are not blocked by it.
|
|
43
51
|
const openSiblings = new Set();
|
|
44
52
|
unboundedSiblingFactories.set(client, () => {
|
|
45
53
|
// Two connections are enough for lock-in-tx plus DDL; the cap keeps parallel rebuilds from exhausting max_connections.
|
|
46
|
-
const sibling =
|
|
47
|
-
...
|
|
54
|
+
const sibling = new Bun.SQL(url, {
|
|
55
|
+
...sqlOptions,
|
|
48
56
|
max: 4,
|
|
49
|
-
connection: { ...
|
|
57
|
+
connection: { ...sqlOptions.connection, statement_timeout: 0 },
|
|
50
58
|
});
|
|
51
59
|
openSiblings.add(sibling);
|
|
52
60
|
return {
|
|
53
61
|
client: sibling,
|
|
54
62
|
close: async (timeoutSeconds) => {
|
|
55
63
|
openSiblings.delete(sibling);
|
|
56
|
-
await sibling.
|
|
64
|
+
await sibling.close({ timeout: timeoutSeconds });
|
|
57
65
|
},
|
|
58
66
|
};
|
|
59
67
|
});
|
|
60
68
|
return {
|
|
61
69
|
db: client,
|
|
62
70
|
client,
|
|
71
|
+
listenClient: client,
|
|
63
72
|
close: async () => {
|
|
64
73
|
const timeout = options.closeTimeoutSeconds ?? DEFAULT_DB_CLOSE_TIMEOUT_SECONDS;
|
|
65
74
|
unboundedSiblingFactories.delete(client);
|
|
66
75
|
const pools = [client, ...openSiblings];
|
|
67
76
|
openSiblings.clear();
|
|
68
|
-
const results = await Promise.allSettled(pools.map((pool) => pool.
|
|
77
|
+
const results = await Promise.allSettled(pools.map((pool) => pool.close({ timeout })));
|
|
69
78
|
const failure = results.find((result) => result.status === "rejected");
|
|
70
79
|
if (failure !== undefined)
|
|
71
80
|
throw failure.reason;
|
|
72
81
|
},
|
|
73
82
|
};
|
|
74
83
|
}
|
|
75
|
-
// DB_PROVIDER
|
|
76
|
-
//
|
|
84
|
+
// DB_PROVIDER once selected between drivers; a config that still sets it is stale,
|
|
85
|
+
// so fail loudly instead of silently ignoring it.
|
|
77
86
|
function assertNoRemovedDbProvider(env) {
|
|
78
87
|
const provider = env["DB_PROVIDER"];
|
|
79
|
-
if (provider !== undefined && provider !== ""
|
|
80
|
-
throw new Error(`DB_PROVIDER="${provider}" is no longer supported:
|
|
88
|
+
if (provider !== undefined && provider !== "") {
|
|
89
|
+
throw new Error(`DB_PROVIDER="${provider}" is no longer supported: Bun.SQL is the only driver. Remove DB_PROVIDER from the environment.`);
|
|
81
90
|
}
|
|
82
91
|
}
|
|
83
92
|
export function dbConnectionOptionsFromEnv(env = process.env) {
|
package/dist/db/dialect.js
CHANGED
|
@@ -251,7 +251,7 @@ export function primaryKey(opts) {
|
|
|
251
251
|
// Limits: no nested SqlExpression composition (drizzle's recursive
|
|
252
252
|
// `sql\`${other}\``) — schema-files use single-level expressions only.
|
|
253
253
|
// Unforgeable via JSON — a client-supplied jsonb value can fake `kind:
|
|
254
|
-
// "sql-expr"` but can never carry a Symbol, so isSqlExpression() (db/
|
|
254
|
+
// "sql-expr"` but can never carry a Symbol, so isSqlExpression() (db/driver-value-preparation.ts)
|
|
255
255
|
// can't be tricked into treating request data as a raw SQL literal. The same
|
|
256
256
|
// brand gate is enforced here in the schema DSL (sql`...` interpolation +
|
|
257
257
|
// literalDefault) and in entity-table-meta's sqlExpressionText — a duck-typed
|
package/dist/db/distinct-on.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { OrderByClause } from "@cosmicdrift/kumiko-types/where-clause-types";
|
|
2
|
-
import type { TableInfo } from "./
|
|
2
|
+
import type { TableInfo } from "./table-info.ts";
|
|
3
3
|
type DistinctOnClause = {
|
|
4
4
|
/** Physical column names, unquoted. */
|
|
5
5
|
readonly columns: readonly string[];
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
2
|
+
import { createFallbackLogger } from "../logging/index.js";
|
|
3
|
+
import { parseJsonSafe } from "../utils/safe-json.js";
|
|
4
|
+
import { isTemporalInstant, isTemporalPlainDate } from "./temporal-value-guards.js";
|
|
5
|
+
// --- Value coercion at the driver boundary ------------------------------
|
|
6
|
+
//
|
|
7
|
+
// The driver returns timestamptz as JS Date (or ISO string depending on
|
|
8
|
+
// driver config). Framework contract says timestamptz surfaces as
|
|
9
|
+
// Temporal.Instant — without coercion every read hands callers a Date and
|
|
10
|
+
// downstream code that does .epochMilliseconds / .add(...) crashes.
|
|
11
|
+
//
|
|
12
|
+
// Symmetric on write: Temporal.Instant doesn't bind directly as a driver
|
|
13
|
+
// param — convert to ISO string when the column pgType is timestamptz.
|
|
14
|
+
// Postgres bigint → JS: safe integers become `number` (matches createBigIntField /
|
|
15
|
+
// drizzle mode:"number"); values outside ±2^53 stay `bigint` (money cents, raw
|
|
16
|
+
// unmanaged columns). Uses BigInt equality check so 9007199254740993n never
|
|
17
|
+
// silently rounds to a safe integer.
|
|
18
|
+
function coerceBigIntFromDriver(value) {
|
|
19
|
+
let bi;
|
|
20
|
+
if (typeof value === "string") {
|
|
21
|
+
try {
|
|
22
|
+
bi = BigInt(value);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
bi = value;
|
|
30
|
+
}
|
|
31
|
+
const min = BigInt(Number.MIN_SAFE_INTEGER);
|
|
32
|
+
const max = BigInt(Number.MAX_SAFE_INTEGER);
|
|
33
|
+
if (bi >= min && bi <= max) {
|
|
34
|
+
const n = Number(bi);
|
|
35
|
+
if (BigInt(n) === bi)
|
|
36
|
+
return n;
|
|
37
|
+
}
|
|
38
|
+
return bi;
|
|
39
|
+
}
|
|
40
|
+
function instantFromDriver(value) {
|
|
41
|
+
if (value === null || value === undefined)
|
|
42
|
+
return null;
|
|
43
|
+
if (isTemporalInstant(value))
|
|
44
|
+
return value;
|
|
45
|
+
// Number coercion via +date — equivalent to .getTime() without a Date-API
|
|
46
|
+
// method (guard-no-date-api). Date → epoch milliseconds, then to Temporal.Instant.
|
|
47
|
+
if (value instanceof Date)
|
|
48
|
+
return Temporal.Instant.fromEpochMilliseconds(+value);
|
|
49
|
+
if (typeof value === "string") {
|
|
50
|
+
try {
|
|
51
|
+
return Temporal.Instant.from(value);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (typeof value === "number")
|
|
58
|
+
return Temporal.Instant.fromEpochMilliseconds(value);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
// Verified empirically: a `date`
|
|
62
|
+
// column comes back as a native JS Date, but ALWAYS anchored at UTC
|
|
63
|
+
// midnight regardless of process TZ — epochMilliseconds is stable across
|
|
64
|
+
// zones, only the local wall-clock read of that Date would drift the day
|
|
65
|
+
// (e.g. America/Los_Angeles reads back the 14th for a stored 15th). Routing
|
|
66
|
+
// through Instant→ZonedDateTimeISO("UTC")→PlainDate reads the calendar day
|
|
67
|
+
// back deterministically without ever touching a local Date getter — the
|
|
68
|
+
// same +value idiom instantFromDriver uses (guard-no-date-api: no
|
|
69
|
+
// `.getTime()`), just anchored to a fixed zone instead of process-local.
|
|
70
|
+
// A real `date` column always arrives as a UTC-midnight Date. Anything else means the column is
|
|
71
|
+
// still timestamptz (declared `date` before its ALTER ran); the UTC anchor then silently shifts
|
|
72
|
+
// the day, so say so once per column instead of returning the wrong day quietly.
|
|
73
|
+
const warnedNonMidnightDateColumns = new Set();
|
|
74
|
+
const MS_PER_DAY = 86_400_000;
|
|
75
|
+
const dateColumnDriftLog = createFallbackLogger("bun-sql");
|
|
76
|
+
function warnIfNotDateColumn(epochMs, column) {
|
|
77
|
+
// skip: midnight value is a correct date column, and an already-warned column is deduped
|
|
78
|
+
if (epochMs % MS_PER_DAY === 0 || warnedNonMidnightDateColumns.has(column))
|
|
79
|
+
return;
|
|
80
|
+
warnedNonMidnightDateColumns.add(column);
|
|
81
|
+
dateColumnDriftLog.warn(`column "${column}" is declared as date but returned a non-midnight timestamp; it is probably still timestamptz. Run the ALTER ... TYPE date migration, otherwise days can shift.`);
|
|
82
|
+
}
|
|
83
|
+
function plainDateFromDriver(value, column) {
|
|
84
|
+
if (value === null || value === undefined)
|
|
85
|
+
return null;
|
|
86
|
+
if (isTemporalPlainDate(value))
|
|
87
|
+
return value;
|
|
88
|
+
if (typeof value === "string") {
|
|
89
|
+
const isoDay = /^\d{4}-\d{2}-\d{2}/.exec(value)?.[0];
|
|
90
|
+
if (isoDay === undefined)
|
|
91
|
+
return null;
|
|
92
|
+
try {
|
|
93
|
+
return Temporal.PlainDate.from(isoDay);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (value instanceof Date) {
|
|
100
|
+
warnIfNotDateColumn(+value, column);
|
|
101
|
+
return Temporal.Instant.fromEpochMilliseconds(+value).toZonedDateTimeISO("UTC").toPlainDate();
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
function coerceTimestamptz(value) {
|
|
106
|
+
const instant = instantFromDriver(value);
|
|
107
|
+
return instant !== null ? instant : value;
|
|
108
|
+
}
|
|
109
|
+
function coerceDate(value, column) {
|
|
110
|
+
const day = plainDateFromDriver(value, column);
|
|
111
|
+
return day !== null ? day : value;
|
|
112
|
+
}
|
|
113
|
+
function coerceJsonb(value) {
|
|
114
|
+
return typeof value === "string" ? parseJsonSafe(value, value) : value;
|
|
115
|
+
}
|
|
116
|
+
function coerceBigIntColumn(value, column, info) {
|
|
117
|
+
if (typeof value !== "string" && typeof value !== "bigint")
|
|
118
|
+
return value;
|
|
119
|
+
if (info.bigintJsModeOf(column) === "number")
|
|
120
|
+
return coerceBigIntFromDriver(value);
|
|
121
|
+
if (typeof value === "string") {
|
|
122
|
+
try {
|
|
123
|
+
return BigInt(value);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// leave as string on parse error
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return value;
|
|
131
|
+
}
|
|
132
|
+
function numberUnlessNaN(value) {
|
|
133
|
+
if (typeof value !== "string" && typeof value !== "bigint")
|
|
134
|
+
return value;
|
|
135
|
+
const n = Number(value);
|
|
136
|
+
return Number.isNaN(n) ? value : n;
|
|
137
|
+
}
|
|
138
|
+
// Keyed by the driver's pgType string, not PgType: legacy tables report int4/smallint/int2.
|
|
139
|
+
const DRIVER_VALUE_COERCERS = new Map([
|
|
140
|
+
["timestamptz", coerceTimestamptz],
|
|
141
|
+
["timestamptz(3)", coerceTimestamptz],
|
|
142
|
+
["date", coerceDate],
|
|
143
|
+
["jsonb", coerceJsonb],
|
|
144
|
+
["bigint", coerceBigIntColumn],
|
|
145
|
+
["bigserial", coerceBigIntColumn],
|
|
146
|
+
// Drivers may return int4 as bigint or numeric string.
|
|
147
|
+
// Drizzle coerced to number for numberField columns — match that.
|
|
148
|
+
["integer", numberUnlessNaN],
|
|
149
|
+
["int4", numberUnlessNaN],
|
|
150
|
+
["smallint", numberUnlessNaN],
|
|
151
|
+
["int2", numberUnlessNaN],
|
|
152
|
+
]);
|
|
153
|
+
// pg returns numeric(p,s) as a string to preserve precision; the
|
|
154
|
+
// decimal-field contract surfaces it as JS number (safe ≤ 2^53). A
|
|
155
|
+
// non-numeric string would be DB corruption — leave it untouched so it
|
|
156
|
+
// surfaces loud downstream rather than becoming a silent NaN.
|
|
157
|
+
function driverValueCoercerFor(pgType) {
|
|
158
|
+
if (pgType === undefined)
|
|
159
|
+
return undefined;
|
|
160
|
+
const coercer = DRIVER_VALUE_COERCERS.get(pgType);
|
|
161
|
+
if (coercer)
|
|
162
|
+
return coercer;
|
|
163
|
+
return pgType.startsWith("numeric") ? numberUnlessNaN : undefined;
|
|
164
|
+
}
|
|
165
|
+
export function coerceDriverRow(row, info) {
|
|
166
|
+
const out = {};
|
|
167
|
+
let changed = false;
|
|
168
|
+
for (const key of Object.keys(row)) {
|
|
169
|
+
// Blind-index columns are pure lookup artifacts — the
|
|
170
|
+
// deterministic HMAC must never leak into caller/API responses.
|
|
171
|
+
if ((key.endsWith("_bidx") || key.endsWith("Bidx")) && info.hasColumn(key)) {
|
|
172
|
+
changed = true;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const value = row[key];
|
|
176
|
+
const coercer = driverValueCoercerFor(info.pgTypeOf(key));
|
|
177
|
+
const coerced = coercer ? coercer(value, key, info) : value;
|
|
178
|
+
const fieldName = info.fieldOf(key);
|
|
179
|
+
if (fieldName !== key)
|
|
180
|
+
changed = true;
|
|
181
|
+
if (coerced !== value)
|
|
182
|
+
changed = true;
|
|
183
|
+
out[fieldName] = coerced;
|
|
184
|
+
}
|
|
185
|
+
return changed ? out : row;
|
|
186
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
2
|
+
export type PreparedValue = {
|
|
3
|
+
readonly kind: "param";
|
|
4
|
+
readonly sql: string;
|
|
5
|
+
readonly bound: unknown;
|
|
6
|
+
} | {
|
|
7
|
+
readonly kind: "literal";
|
|
8
|
+
readonly literal: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function isSqlExpression(v: unknown): v is {
|
|
11
|
+
kind: "sql-expr";
|
|
12
|
+
text: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function prepareJsonbValue(value: unknown): PreparedValue | undefined;
|
|
15
|
+
export declare function instantToTimestamptzParam(instant: Temporal.Instant): string;
|
|
16
|
+
export declare function prepareValue(value: unknown, pgType: string | undefined): PreparedValue;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
2
|
+
import { isPiiCiphertext } from "../crypto/pii-ciphertext-format.js";
|
|
3
|
+
import { SQL_EXPR_BRAND } from "./dialect.js";
|
|
4
|
+
import { isTemporalInstant, isTemporalPlainDate, isTemporalPlainDateTimeLike, } from "./temporal-value-guards.js";
|
|
5
|
+
// Checks the brand Symbol, not the `kind` string — a client-supplied jsonb
|
|
6
|
+
// value can fake `kind: "sql-expr"` over JSON but can never carry a Symbol,
|
|
7
|
+
// so request data can't be smuggled in as a raw SQL literal.
|
|
8
|
+
export function isSqlExpression(v) {
|
|
9
|
+
return typeof v === "object" && v !== null && SQL_EXPR_BRAND in v;
|
|
10
|
+
}
|
|
11
|
+
// A `date` column takes a plain "yyyy-mm-dd" string (or PlainDate.toString())
|
|
12
|
+
// as-is — no Instant detour. Postgres parses a DATE literal without ever
|
|
13
|
+
// consulting the session timezone, so this is TZ-independent by construction
|
|
14
|
+
// (verified empirically — see plainDateFromDriver above for the read side).
|
|
15
|
+
// Compat cushion: code (this field used to be an Instant) may still
|
|
16
|
+
// hand a Temporal.Instant into a where-filter or write on a `date` column —
|
|
17
|
+
// same UTC anchor as plainDateFromDriver's read side, so a filter built with
|
|
18
|
+
// `Temporal.Now.instant()` keeps working instead of binding a raw Instant the
|
|
19
|
+
// driver can't serialize.
|
|
20
|
+
function prepareDateValue(value) {
|
|
21
|
+
if (isTemporalPlainDate(value)) {
|
|
22
|
+
return { kind: "param", sql: "", bound: value.toString() };
|
|
23
|
+
}
|
|
24
|
+
if (isTemporalPlainDateTimeLike(value)) {
|
|
25
|
+
return { kind: "param", sql: "", bound: value.toPlainDate().toString() };
|
|
26
|
+
}
|
|
27
|
+
if (isTemporalInstant(value)) {
|
|
28
|
+
const day = value.toZonedDateTimeISO("UTC").toPlainDate();
|
|
29
|
+
return { kind: "param", sql: "", bound: day.toString() };
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
// structured values bind directly with an ::jsonb cast; JSON.stringify first
|
|
34
|
+
// would produce a JSON-string scalar instead of the structured value.
|
|
35
|
+
export function prepareJsonbValue(value) {
|
|
36
|
+
// Bun.SQL types JS numbers as int4/float8 params, which Postgres refuses for a jsonb column.
|
|
37
|
+
// Non-finite numbers fall through so Postgres rejects them instead of storing JSON null.
|
|
38
|
+
if (typeof value === "boolean" || (typeof value === "number" && Number.isFinite(value))) {
|
|
39
|
+
return { kind: "param", sql: "::text::jsonb", bound: JSON.stringify(value) };
|
|
40
|
+
}
|
|
41
|
+
if (typeof value !== "object" || isTemporalInstant(value)) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
if (!Array.isArray(value)) {
|
|
45
|
+
return { kind: "param", sql: "::jsonb", bound: value };
|
|
46
|
+
}
|
|
47
|
+
// All-boolean arrays are inferred as boolean[] by the driver; route via text::jsonb.
|
|
48
|
+
if (value.length > 0 && value.every((entry) => typeof entry === "boolean")) {
|
|
49
|
+
return { kind: "param", sql: "::text::jsonb", bound: JSON.stringify(value) };
|
|
50
|
+
}
|
|
51
|
+
return { kind: "param", sql: "::jsonb", bound: value };
|
|
52
|
+
}
|
|
53
|
+
const INTEGER_PG_TYPES = new Set([
|
|
54
|
+
"integer",
|
|
55
|
+
"int4",
|
|
56
|
+
"smallint",
|
|
57
|
+
"int2",
|
|
58
|
+
"bigint",
|
|
59
|
+
"int8",
|
|
60
|
+
"serial",
|
|
61
|
+
"bigserial",
|
|
62
|
+
]);
|
|
63
|
+
// Postgres rounds sub-millisecond params into timestamptz(3); truncating to ms here keeps the
|
|
64
|
+
// stored instant identical across drivers and equal to what a Date param would store.
|
|
65
|
+
export function instantToTimestamptzParam(instant) {
|
|
66
|
+
return Temporal.Instant.fromEpochMilliseconds(instant.epochMilliseconds).toString();
|
|
67
|
+
}
|
|
68
|
+
function prepareDriverNeutralScalar(value, pgType) {
|
|
69
|
+
if ((pgType === "timestamptz" || pgType === "timestamptz(3)") && isTemporalInstant(value)) {
|
|
70
|
+
return { kind: "param", sql: "", bound: instantToTimestamptzParam(value) };
|
|
71
|
+
}
|
|
72
|
+
// Sent as text, so Postgres rejects 3.5 for an integer column instead of rounding it.
|
|
73
|
+
if (typeof value === "number" && pgType !== undefined && INTEGER_PG_TYPES.has(pgType)) {
|
|
74
|
+
return { kind: "param", sql: "", bound: String(value) };
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
export function prepareValue(value, pgType) {
|
|
79
|
+
if (isSqlExpression(value)) {
|
|
80
|
+
return { kind: "literal", literal: value.text };
|
|
81
|
+
}
|
|
82
|
+
// A PII-encrypted jsonb field is a JSON string scalar; a raw string bound to
|
|
83
|
+
// ::jsonb would be parsed as JSON text (or double-encoded).
|
|
84
|
+
if (pgType === "jsonb" && isPiiCiphertext(value)) {
|
|
85
|
+
return { kind: "param", sql: "::text::jsonb", bound: JSON.stringify(value) };
|
|
86
|
+
}
|
|
87
|
+
if (pgType === "jsonb" && value !== null) {
|
|
88
|
+
const prepared = prepareJsonbValue(value);
|
|
89
|
+
if (prepared)
|
|
90
|
+
return prepared;
|
|
91
|
+
}
|
|
92
|
+
const scalar = prepareDriverNeutralScalar(value, pgType);
|
|
93
|
+
if (scalar)
|
|
94
|
+
return scalar;
|
|
95
|
+
if (pgType === "date") {
|
|
96
|
+
const prepared = prepareDateValue(value);
|
|
97
|
+
if (prepared)
|
|
98
|
+
return prepared;
|
|
99
|
+
}
|
|
100
|
+
return { kind: "param", sql: "", bound: value };
|
|
101
|
+
}
|
|
@@ -3,6 +3,7 @@ import { type Logger } from "../logging/index.ts";
|
|
|
3
3
|
import type { EnvelopeCipher } from "../secrets/envelope-cipher.ts";
|
|
4
4
|
export declare function collectEncryptedFieldNames(entity: EntityDefinition): ReadonlySet<string>;
|
|
5
5
|
export declare function isSensitiveLabelField(entity: EntityDefinition, field: string): boolean;
|
|
6
|
+
export declare function holdsCiphertextOrSecret(entity: EntityDefinition, field: string): boolean;
|
|
6
7
|
export declare function isListFilterAllowed(entity: EntityDefinition, field: string, filter: {
|
|
7
8
|
readonly op: string;
|
|
8
9
|
readonly value: unknown;
|
|
@@ -24,7 +24,7 @@ export function isSensitiveLabelField(entity, field) {
|
|
|
24
24
|
}
|
|
25
25
|
// A `sensitive` (find: "secret") field is never comparable either, even while its value is still
|
|
26
26
|
// legacy plaintext.
|
|
27
|
-
function holdsCiphertextOrSecret(entity, field) {
|
|
27
|
+
export function holdsCiphertextOrSecret(entity, field) {
|
|
28
28
|
const def = entity.fields[field];
|
|
29
29
|
if (def === undefined)
|
|
30
30
|
return false;
|