@cosmicdrift/kumiko-framework 0.388.0 → 0.390.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 +12 -4
- package/README.md +1 -1
- package/agents/examples/apex-surface-auth.md +2 -3
- package/agents/examples/api-worker-split.md +9 -2
- package/agents/examples/auth-claims.md +1 -1
- package/agents/examples/cross-feature-events.md +12 -12
- package/agents/examples/crypto-shredding-hr.md +2 -2
- package/agents/examples/encrypted-tenant-config.md +1 -1
- package/agents/examples/error-contract.md +12 -7
- package/agents/examples/event-sourcing.md +29 -29
- package/agents/examples/files-post-processing.md +10 -8
- package/agents/examples/golden-path.md +13 -1
- package/agents/examples/inbound-mail-triage.md +1 -0
- package/agents/examples/lane-routing.md +2 -1
- package/agents/examples/legal-pages.md +2 -2
- package/agents/examples/lifecycle-hooks.md +55 -2
- package/agents/examples/list-table-options.md +466 -0
- package/agents/examples/managed-config.md +4 -4
- package/agents/examples/managed-pages.md +1 -1
- package/agents/examples/pdf-audit-export.md +4 -2
- package/agents/examples/relations.md +19 -4
- package/agents/examples/search.md +4 -4
- package/agents/examples/secrets-demo.md +9 -6
- package/agents/examples/state-machine.md +5 -4
- package/agents/examples/tenant-isolation.md +73 -1
- package/agents/examples/tenant-settings.md +1 -1
- package/agents/examples/user-data-rights.md +3 -6
- package/agents/examples/workflow-engine.md +53 -47
- package/agents/examples.md +5 -4
- package/agents/features.md +1 -1
- package/agents/widgets.md +1 -0
- package/dist/api/api-constants.d.ts +1 -0
- package/dist/api/api-constants.js +2 -0
- package/dist/api/auth-middleware.js +1 -1
- package/dist/api/auth-routes-invite.js +2 -1
- 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 +2 -3
- package/dist/api/entity-read-access.d.ts +4 -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 +31 -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 +75 -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 +13 -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 +7 -1
- package/dist/api/index.d.ts +23 -27
- package/dist/api/index.js +13 -15
- package/dist/api/jwt.js +1 -1
- package/dist/api/observability-middleware.d.ts +1 -1
- package/dist/api/observability-middleware.js +2 -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-internals.d.ts +31 -0
- package/dist/api/request-context-internals.js +11 -0
- package/dist/api/request-context-scopes.d.ts +14 -0
- package/dist/api/request-context-scopes.js +69 -0
- package/dist/api/request-context.d.ts +1 -40
- package/dist/api/request-context.js +22 -51
- package/dist/api/request-id-middleware.d.ts +1 -1
- package/dist/api/route-registrars.d.ts +2 -1
- package/dist/api/route-registrars.js +1 -1
- package/dist/api/routes.d.ts +16 -0
- package/dist/api/routes.js +9 -5
- package/dist/api/server-boot-checks.d.ts +1 -1
- package/dist/api/server-boot-checks.js +2 -2
- package/dist/api/server-event-dispatcher.d.ts +6 -1
- package/dist/api/server-event-dispatcher.js +3 -4
- package/dist/api/server-extra-routes.d.ts +1 -1
- package/dist/api/server-extra-routes.js +5 -1
- package/dist/api/server-middleware.js +3 -1
- package/dist/api/server-routes.d.ts +3 -2
- package/dist/api/server-routes.js +8 -2
- package/dist/api/server-wiring.js +8 -4
- package/dist/api/server.d.ts +2 -1
- package/dist/api/sse-route.d.ts +3 -1
- package/dist/api/sse-route.js +11 -1
- package/dist/api/tenant-scoped-secrets.d.ts +2 -0
- package/dist/api/tenant-scoped-secrets.js +13 -0
- package/dist/api/websocket-route.d.ts +1 -1
- package/dist/auth/index.d.ts +0 -1
- package/dist/auth/roles.d.ts +0 -5
- package/dist/compliance/duration-spec.d.ts +0 -1
- package/dist/compliance/duration-spec.js +0 -5
- package/dist/compliance/index.d.ts +4 -4
- package/dist/compliance/index.js +2 -2
- package/dist/compliance/profiles.d.ts +0 -15
- package/dist/compliance/profiles.js +0 -23
- package/dist/config/index.d.ts +11 -9
- package/dist/config/index.js +13 -1
- package/dist/config/internals.d.ts +9 -0
- package/dist/config/internals.js +1 -0
- package/dist/consumer-cli-internals.d.ts +4 -0
- package/dist/consumer-cli-internals.js +1 -0
- package/dist/consumer-cli.d.ts +1 -4
- package/dist/consumer-cli.js +5 -1
- package/dist/crypto/event-pii.js +1 -1
- package/dist/crypto/index.d.ts +8 -9
- package/dist/crypto/index.js +7 -8
- package/dist/crypto/pg-kms-adapter.d.ts +1 -0
- package/dist/crypto/pg-kms-adapter.js +17 -18
- package/dist/db/aggregate-sql.d.ts +2 -0
- package/dist/db/aggregate-sql.js +10 -0
- package/dist/db/apply-entity-event.d.ts +1 -1
- package/dist/db/apply-entity-event.js +15 -47
- package/dist/db/blind-index-cleanup.js +91 -77
- package/dist/db/closed-connection.js +12 -19
- package/dist/db/collect-table-metas.js +1 -1
- package/dist/db/compound-types.d.ts +1 -1
- package/dist/db/compound-types.js +0 -7
- package/dist/db/config-seed.d.ts +2 -1
- package/dist/db/config-seed.js +6 -1
- package/dist/db/connection.d.ts +7 -6
- package/dist/db/connection.js +43 -24
- package/dist/db/cross-tenant-reads.d.ts +1 -1
- package/dist/db/cross-tenant-reads.js +1 -1
- package/dist/db/dialect.d.ts +0 -5
- package/dist/db/dialect.js +0 -3
- package/dist/db/driver-row-coercion.js +4 -4
- package/dist/db/driver-value-preparation.js +6 -4
- package/dist/db/eagerload.d.ts +2 -1
- package/dist/db/entity-field-encryption.d.ts +1 -1
- package/dist/db/entity-table-meta.d.ts +1 -1
- package/dist/db/event-store-executor-context.d.ts +3 -1
- package/dist/db/event-store-executor-context.js +4 -3
- 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.js +36 -5
- package/dist/db/event-store-executor-read-filter.d.ts +1 -1
- package/dist/db/event-store-executor-read-filter.js +6 -3
- package/dist/db/event-store-executor-read-rows.d.ts +2 -1
- package/dist/db/event-store-executor-read-search.d.ts +2 -1
- package/dist/db/event-store-executor-read-sort.d.ts +2 -1
- package/dist/db/event-store-executor-read.js +2 -2
- package/dist/db/event-store-executor-write-atomic.d.ts +4 -0
- package/dist/db/event-store-executor-write-atomic.js +42 -0
- package/dist/db/event-store-executor-write-steps.d.ts +3 -5
- package/dist/db/event-store-executor-write-steps.js +0 -5
- package/dist/db/event-store-executor-write.js +22 -19
- package/dist/db/event-store-executor.d.ts +1 -1
- package/dist/db/event-store-executor.js +2 -1
- package/dist/db/executor-managed-tables.d.ts +7 -1
- package/dist/db/executor-managed-tables.js +12 -3
- package/dist/db/index.d.ts +29 -28
- package/dist/db/index.js +22 -20
- package/dist/db/known-table-names.d.ts +6 -0
- package/dist/db/known-table-names.js +21 -0
- package/dist/db/list-aggregate-labels.d.ts +1 -1
- package/dist/db/located-timestamp.d.ts +1 -1
- package/dist/db/located-timestamp.js +0 -5
- package/dist/db/migrate-generator.d.ts +17 -1
- package/dist/db/migrate-generator.js +50 -20
- package/dist/db/migrate-primary-key.d.ts +12 -0
- package/dist/db/migrate-primary-key.js +45 -0
- package/dist/db/migrate-rls-diff.d.ts +20 -0
- package/dist/db/migrate-rls-diff.js +61 -0
- package/dist/db/migrate-runner.js +7 -3
- package/dist/db/money.d.ts +1 -1
- package/dist/db/money.js +1 -1
- package/dist/db/parent-ref-clause.d.ts +2 -1
- package/dist/db/pg-error.d.ts +2 -0
- package/dist/db/pg-error.js +37 -21
- package/dist/db/queries/backfill-pii.js +3 -6
- package/dist/db/queries/database-now.js +2 -1
- package/dist/db/queries/ddl.d.ts +3 -0
- package/dist/db/queries/ddl.js +34 -1
- package/dist/db/queries/event-store.js +4 -2
- package/dist/db/queries/event-tenants.d.ts +2 -0
- package/dist/db/queries/event-tenants.js +17 -0
- package/dist/db/queries/rebuild-rls-guard.d.ts +2 -0
- package/dist/db/queries/rebuild-rls-guard.js +29 -0
- package/dist/db/queries/rls-boot-catalog.d.ts +16 -0
- package/dist/db/queries/rls-boot-catalog.js +39 -0
- package/dist/db/queries/rls-catalog.d.ts +6 -0
- package/dist/db/queries/rls-catalog.js +82 -0
- package/dist/db/queries/rls-scope.d.ts +3 -0
- package/dist/db/queries/rls-scope.js +17 -0
- package/dist/db/queries/rls-shape.d.ts +10 -0
- package/dist/db/queries/rls-shape.js +42 -0
- package/dist/db/queries/session-advisory-lock.d.ts +2 -0
- package/dist/db/queries/session-advisory-lock.js +23 -0
- package/dist/db/queries/shadow-swap.d.ts +1 -2
- package/dist/db/queries/shadow-swap.js +11 -28
- package/dist/db/queries/test-stack.d.ts +4 -1
- package/dist/db/queries/test-stack.js +83 -2
- package/dist/db/query.d.ts +14 -0
- package/dist/db/query.js +77 -48
- package/dist/db/reference-data.d.ts +1 -1
- package/dist/db/reference-data.js +20 -3
- package/dist/db/render-ddl.d.ts +6 -2
- package/dist/db/render-ddl.js +7 -7
- package/dist/db/render-rls-ddl.d.ts +35 -0
- package/dist/db/render-rls-ddl.js +127 -0
- package/dist/db/replay-migration-sql.d.ts +3 -1
- package/dist/db/replay-migration-sql.js +22 -5
- package/dist/db/replay-rls.d.ts +25 -0
- package/dist/db/replay-rls.js +156 -0
- package/dist/db/row-level-security-boot-check.d.ts +5 -0
- package/dist/db/row-level-security-boot-check.js +85 -0
- package/dist/db/row-level-security-connection.d.ts +3 -0
- package/dist/db/row-level-security-connection.js +213 -0
- package/dist/db/row-level-security-scope.d.ts +33 -0
- package/dist/db/row-level-security-scope.js +127 -0
- package/dist/db/scope-tenant-db-to-tenant.d.ts +3 -0
- package/dist/db/scope-tenant-db-to-tenant.js +14 -0
- package/dist/db/sql-identifier.d.ts +1 -0
- package/dist/db/sql-identifier.js +3 -0
- package/dist/db/statement-timeout.js +2 -1
- package/dist/db/table-builder.d.ts +2 -1
- package/dist/db/table-builder.js +1 -1
- package/dist/db/table-info.js +1 -1
- package/dist/db/table-push.d.ts +11 -2
- package/dist/db/table-push.js +15 -2
- package/dist/db/tenant-db-runner.d.ts +3 -1
- package/dist/db/tenant-db-runner.js +4 -3
- package/dist/db/tenant-db-scoped-rebind.d.ts +10 -0
- package/dist/db/tenant-db-scoped-rebind.js +33 -0
- package/dist/db/tenant-db-statement-runner.d.ts +13 -0
- package/dist/db/tenant-db-statement-runner.js +48 -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.d.ts +17 -12
- package/dist/db/tenant-db.js +60 -121
- package/dist/db/tenant-id-filter.d.ts +3 -0
- package/dist/db/tenant-id-filter.js +30 -0
- package/dist/db/transaction-serialization.d.ts +6 -0
- package/dist/db/transaction-serialization.js +60 -0
- package/dist/derivatives/delete-stored-file.js +1 -1
- package/dist/derivatives/index.d.ts +2 -5
- package/dist/derivatives/index.js +2 -3
- package/dist/engine/access.d.ts +2 -1
- package/dist/engine/access.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/entity-handler.d.ts +2 -1
- package/dist/engine/boot-validator/entity-handler.js +1 -1
- 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/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 +11 -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/nav.d.ts +3 -1
- package/dist/engine/boot-validator/nav.js +1 -1
- package/dist/engine/boot-validator/ownership.d.ts +2 -1
- package/dist/engine/boot-validator/parent-ref.d.ts +2 -1
- package/dist/engine/boot-validator/projection-list-screens.d.ts +2 -1
- package/dist/engine/boot-validator/projection-list-screens.js +3 -13
- package/dist/engine/boot-validator/query-refs.d.ts +2 -1
- 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-forms.d.ts +2 -1
- package/dist/engine/boot-validator/screens-kind-entity-list.js +7 -0
- package/dist/engine/boot-validator/screens-kind-projection-list.js +4 -0
- package/dist/engine/boot-validator/screens-kind-shared.d.ts +3 -1
- package/dist/engine/boot-validator/screens-lists.js +12 -2
- package/dist/engine/boot-validator/screens.d.ts +2 -1
- package/dist/engine/boot-validator/workspaces.d.ts +3 -1
- package/dist/engine/bridge-stub.d.ts +5 -2
- package/dist/engine/bridge-stub.js +39 -24
- package/dist/engine/build-app-schema.d.ts +2 -1
- package/dist/engine/build-app-schema.js +9 -1
- package/dist/engine/build-config-feature-schema.js +6 -1
- package/dist/engine/config-helpers.d.ts +2 -2
- package/dist/engine/config-helpers.js +0 -9
- package/dist/engine/constants.d.ts +0 -10
- package/dist/engine/constants.js +0 -8
- package/dist/engine/create-app.js +1 -1
- package/dist/engine/define-feature.d.ts +2 -1
- package/dist/engine/define-feature.js +5 -2
- package/dist/engine/define-handler.d.ts +1 -1
- package/dist/engine/define-step.d.ts +0 -1
- package/dist/engine/define-step.js +0 -3
- package/dist/engine/embedded-derived.d.ts +1 -1
- package/dist/engine/entity-handlers.d.ts +2 -1
- package/dist/engine/entity-handlers.js +7 -7
- package/dist/engine/entity-list-runtime-options.js +3 -1
- package/dist/engine/entity-list-searchability.d.ts +2 -1
- package/dist/engine/event-def.d.ts +2 -1
- package/dist/engine/event-helpers.d.ts +3 -1
- package/dist/engine/event-helpers.js +3 -3
- package/dist/engine/extension-names.d.ts +2 -8
- package/dist/engine/extension-names.js +2 -2
- package/dist/engine/extension-selector-plugins.d.ts +1 -1
- package/dist/engine/extensions/storage-provider.d.ts +1 -2
- package/dist/engine/extensions/user-data.d.ts +0 -8
- package/dist/engine/factories.d.ts +2 -1
- package/dist/engine/feature-builder-state.d.ts +9 -1
- package/dist/engine/feature-builder-state.js +1 -0
- package/dist/engine/feature-changelog.d.ts +1 -1
- package/dist/engine/feature-config-events-jobs.d.ts +4 -1
- package/dist/engine/feature-entity-handlers.d.ts +5 -1
- package/dist/engine/feature-manifest.d.ts +1 -0
- package/dist/engine/feature-manifest.js +2 -1
- package/dist/engine/feature-ui-extensions.d.ts +7 -1
- package/dist/engine/feature-ui-extensions.js +6 -4
- package/dist/engine/field-access.d.ts +3 -1
- package/dist/engine/field-access.js +12 -0
- package/dist/engine/handler-field-references.d.ts +0 -4
- package/dist/engine/index.d.ts +58 -55
- package/dist/engine/index.js +23 -28
- package/dist/engine/instruction-fields.d.ts +1 -1
- package/dist/engine/personal-data-fields.d.ts +1 -1
- package/dist/engine/qualified-name.d.ts +0 -1
- package/dist/engine/read-claim.d.ts +2 -1
- package/dist/engine/registry-facade.js +0 -3
- package/dist/engine/registry-ingest.d.ts +2 -1
- package/dist/engine/registry-ingest.js +1 -2
- package/dist/engine/registry-state.d.ts +12 -2
- package/dist/engine/registry-state.js +2 -3
- package/dist/engine/registry-validate.d.ts +3 -1
- package/dist/engine/registry-validate.js +7 -7
- package/dist/engine/run-in.d.ts +1 -1
- package/dist/engine/run-in.js +0 -1
- package/dist/engine/schema-builder.d.ts +1 -1
- package/dist/engine/schema-builder.js +1 -1
- package/dist/engine/screen-filter-ops.d.ts +2 -1
- package/dist/engine/screen-filter-ops.js +2 -5
- package/dist/engine/soft-delete-cleanup.d.ts +1 -1
- package/dist/engine/soft-delete-cleanup.js +4 -4
- package/dist/engine/state-machine.js +2 -1
- package/dist/engine/steps/_read-source.js +1 -1
- package/dist/engine/steps/aggregate-append-event.js +2 -2
- package/dist/engine/steps/call-feature.js +2 -2
- package/dist/engine/steps/mail-send.js +1 -1
- package/dist/engine/steps/retry.js +1 -1
- package/dist/engine/steps/wait-for-event.js +1 -1
- package/dist/engine/steps/wait.js +1 -1
- package/dist/engine/steps/webhook-send.js +1 -1
- package/dist/engine/strip-disabled-features-from-app-schema.js +7 -3
- package/dist/engine/template-variables.d.ts +2 -1
- package/dist/engine/transfer-adjacency.d.ts +1 -1
- package/dist/engine/types/feature.d.ts +1 -0
- package/dist/engine/types/index.d.ts +8 -30
- package/dist/engine/types/index.js +2 -10
- package/dist/engine/types/screen.d.ts +1 -1
- package/dist/engine/types/screen.js +1 -1
- package/dist/engine/validation.d.ts +2 -1
- package/dist/entrypoint/index.d.ts +9 -35
- package/dist/entrypoint/index.js +10 -1
- package/dist/entrypoint/internals.d.ts +40 -0
- package/dist/entrypoint/internals.js +1 -0
- package/dist/entrypoint/row-level-security-mode-check.d.ts +3 -0
- package/dist/entrypoint/row-level-security-mode-check.js +19 -0
- package/dist/env/dry-run-internals.d.ts +10 -0
- package/dist/env/dry-run-internals.js +1 -0
- package/dist/env/dry-run.d.ts +1 -10
- package/dist/env/dry-run.js +2 -1
- package/dist/env/index.d.ts +1 -85
- package/dist/env/index.js +6 -50
- package/dist/env/internals.d.ts +87 -0
- package/dist/env/internals.js +53 -0
- package/dist/errors/classes.js +2 -2
- package/dist/errors/index.d.ts +5 -14
- package/dist/errors/index.js +4 -9
- package/dist/errors/member-resolution.js +1 -1
- package/dist/errors/reasons.d.ts +0 -1
- package/dist/errors/reasons.js +7 -7
- package/dist/errors/serialize.d.ts +0 -18
- package/dist/errors/serialize.js +0 -27
- package/dist/es-ops/index.d.ts +4 -4
- package/dist/es-ops/index.js +2 -2
- package/dist/es-ops/operations-schema.d.ts +1 -1
- package/dist/es-ops/types.d.ts +4 -5
- package/dist/event-store/admin-api-internals.d.ts +14 -0
- package/dist/event-store/admin-api-internals.js +1 -0
- package/dist/event-store/admin-api.d.ts +1 -14
- package/dist/event-store/admin-api.js +11 -6
- package/dist/event-store/archive.js +4 -2
- package/dist/event-store/errors.js +2 -2
- package/dist/event-store/event-store.d.ts +1 -0
- package/dist/event-store/event-store.js +42 -32
- package/dist/event-store/events-schema.js +4 -1
- package/dist/event-store/index.d.ts +11 -13
- package/dist/event-store/index.js +7 -9
- package/dist/event-store/provenance-append.js +1 -1
- package/dist/event-store/rebuild-dead-letter.js +4 -2
- package/dist/event-store/snapshot.js +4 -3
- package/dist/event-store/stream-scope.d.ts +2 -0
- package/dist/event-store/stream-scope.js +6 -0
- package/dist/event-store/upcaster-dead-letter.js +4 -2
- package/dist/event-store/upcaster.d.ts +2 -1
- package/dist/files/action-form-upload.d.ts +2 -1
- package/dist/files/action-form-upload.js +1 -1
- package/dist/files/assert-file-refs-belong-to-tenant.d.ts +2 -1
- package/dist/files/assert-file-refs-belong-to-tenant.js +1 -1
- package/dist/files/bind-pending-file-refs.d.ts +2 -1
- package/dist/files/bind-pending-file-refs.js +2 -1
- package/dist/files/bind-referenced-files.write.js +1 -1
- package/dist/files/create-file-ref-from-bytes.d.ts +6 -3
- package/dist/files/create-file-ref-from-bytes.js +6 -3
- package/dist/files/create-file-ref.d.ts +6 -2
- package/dist/files/create-file-ref.js +13 -4
- package/dist/files/dispose-removed-file-refs.d.ts +2 -1
- package/dist/files/dispose-removed-file-refs.js +1 -1
- package/dist/files/feature.js +2 -2
- package/dist/files/file-handle.js +1 -1
- package/dist/files/file-routes.d.ts +3 -1
- package/dist/files/file-routes.js +41 -4
- package/dist/files/index.d.ts +11 -13
- package/dist/files/index.js +5 -7
- package/dist/files/load-save-file-refs.d.ts +2 -1
- package/dist/files/load-save-file-refs.js +1 -1
- package/dist/files/provider-resolver.d.ts +6 -4
- package/dist/files/provider-resolver.js +5 -5
- package/dist/files/record-write-check.d.ts +6 -0
- package/dist/files/record-write-check.js +13 -0
- package/dist/files/storage-tracking.js +4 -2
- package/dist/files/sweep-orphaned-uploads.d.ts +1 -1
- package/dist/files/sweep-orphaned-uploads.js +1 -1
- package/dist/files/upload-ledger.d.ts +2 -2
- package/dist/files/upload-ledger.js +4 -4
- package/dist/i18n/index.d.ts +2 -8
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/internals.d.ts +7 -0
- package/dist/i18n/internals.js +1 -0
- package/dist/i18n/required-surface-keys.d.ts +4 -1
- package/dist/i18n/required-surface-keys.js +7 -2
- package/dist/internal/row-level-security.d.ts +3 -0
- package/dist/internal/row-level-security.js +3 -0
- package/dist/internal/stack-support.d.ts +1 -1
- package/dist/internal/stack-support.js +1 -1
- package/dist/internal/testing.d.ts +1 -0
- package/dist/internal/testing.js +10 -6
- package/dist/jobs/index.d.ts +1 -1
- package/dist/jobs/job-append-event.d.ts +12 -0
- package/dist/jobs/job-append-event.js +30 -0
- package/dist/jobs/job-runner-context.d.ts +2 -2
- package/dist/jobs/job-runner-context.js +46 -16
- package/dist/jobs/job-runner-execution.d.ts +1 -1
- package/dist/jobs/job-runner-execution.js +6 -4
- package/dist/jobs/job-runner-gates.d.ts +1 -1
- package/dist/jobs/job-runner-gates.js +0 -1
- package/dist/jobs/job-runner-helpers.d.ts +11 -4
- package/dist/jobs/job-runner-helpers.js +10 -1
- package/dist/jobs/job-runner-scope.d.ts +5 -2
- package/dist/jobs/job-runner-scope.js +6 -6
- package/dist/jobs/job-runner-types.d.ts +2 -1
- package/dist/jobs/job-runner.js +22 -6
- package/dist/jobs/max-per-tenant.d.ts +1 -1
- package/dist/logging/index.d.ts +1 -2
- package/dist/logging/index.js +1 -1
- package/dist/logging/pino-logger.js +1 -1
- package/dist/logging/utils.d.ts +1 -0
- package/dist/logging/utils.js +6 -0
- package/dist/migrations/index.d.ts +3 -3
- package/dist/migrations/index.js +3 -3
- package/dist/migrations/pending-rebuilds.d.ts +1 -1
- package/dist/migrations/pending-rebuilds.js +1 -1
- package/dist/migrations/pii-event-backfill.js +4 -1
- package/dist/observability/escape-hatch-report.d.ts +2 -1
- package/dist/observability/escape-hatch-report.js +9 -2
- package/dist/observability/index.d.ts +7 -15
- package/dist/observability/index.js +4 -12
- package/dist/observability/metric-validator.js +1 -1
- package/dist/observability/metrics-handle.d.ts +0 -1
- package/dist/observability/metrics-handle.js +2 -15
- package/dist/observability/recording-tracer.d.ts +1 -2
- package/dist/observability/recording-tracer.js +0 -6
- package/dist/observability/sensitive-filter.d.ts +0 -2
- package/dist/observability/sensitive-filter.js +0 -29
- package/dist/pipeline/active-membership.d.ts +2 -1
- package/dist/pipeline/active-membership.js +6 -2
- package/dist/pipeline/after-rollback-sink.d.ts +6 -0
- package/dist/pipeline/after-rollback-sink.js +32 -0
- package/dist/pipeline/append-event-core.d.ts +2 -1
- package/dist/pipeline/append-event-core.js +2 -2
- package/dist/pipeline/attach-outside-transaction.d.ts +3 -0
- package/dist/pipeline/attach-outside-transaction.js +13 -0
- package/dist/pipeline/auth-claims-resolver.d.ts +2 -1
- package/dist/pipeline/cascade-handler.d.ts +2 -1
- package/dist/pipeline/cascade-handler.js +3 -2
- package/dist/pipeline/config-accessor-factory.d.ts +1 -1
- package/dist/pipeline/config-accessor-factory.js +1 -1
- package/dist/pipeline/context-services.d.ts +56 -0
- package/dist/pipeline/context-services.js +73 -0
- package/dist/pipeline/dispatch-batch.d.ts +2 -1
- package/dist/pipeline/dispatch-batch.js +39 -19
- package/dist/pipeline/dispatch-query.js +5 -4
- package/dist/pipeline/dispatch-scope.d.ts +6 -0
- package/dist/pipeline/dispatch-scope.js +27 -0
- package/dist/pipeline/dispatch-shared.d.ts +13 -9
- package/dist/pipeline/dispatch-shared.js +75 -194
- package/dist/pipeline/dispatch-stream.js +8 -4
- package/dist/pipeline/dispatch-write.d.ts +2 -1
- package/dist/pipeline/dispatch-write.js +10 -8
- package/dist/pipeline/dispatcher-utils.d.ts +3 -1
- package/dist/pipeline/dispatcher-utils.js +8 -2
- package/dist/pipeline/dispatcher.d.ts +3 -2
- package/dist/pipeline/dispatcher.js +4 -3
- package/dist/pipeline/event-consumer-state.d.ts +0 -1
- package/dist/pipeline/event-dispatcher-delivery.d.ts +1 -1
- package/dist/pipeline/event-dispatcher-delivery.js +7 -3
- package/dist/pipeline/event-dispatcher.d.ts +3 -2
- package/dist/pipeline/event-dispatcher.js +13 -12
- package/dist/pipeline/event-retention.js +59 -56
- package/dist/pipeline/handler-access-error.js +2 -1
- package/dist/pipeline/handler-context-auth.d.ts +5 -6
- package/dist/pipeline/handler-context-auth.js +7 -4
- package/dist/pipeline/handler-context-config.d.ts +2 -5
- package/dist/pipeline/handler-context-config.js +2 -2
- package/dist/pipeline/handler-context-db.d.ts +32 -7
- package/dist/pipeline/handler-context-db.js +26 -17
- package/dist/pipeline/handler-context-dispatch.d.ts +14 -6
- package/dist/pipeline/handler-context-dispatch.js +18 -5
- package/dist/pipeline/handler-context-events.d.ts +22 -38
- package/dist/pipeline/handler-context-events.js +42 -35
- package/dist/pipeline/handler-context-member-resolution.d.ts +2 -0
- package/dist/pipeline/handler-context-member-resolution.js +110 -0
- package/dist/pipeline/handler-context-notify.d.ts +3 -2
- package/dist/pipeline/handler-context-notify.js +1 -1
- package/dist/pipeline/handler-context-observability.d.ts +2 -4
- package/dist/pipeline/handler-context-observability.js +7 -5
- package/dist/pipeline/handler-context-phase-denials.d.ts +11 -0
- package/dist/pipeline/handler-context-phase-denials.js +98 -0
- package/dist/pipeline/handler-context-scope.d.ts +7 -10
- package/dist/pipeline/handler-context-scope.js +18 -20
- package/dist/pipeline/index.d.ts +9 -21
- package/dist/pipeline/index.js +5 -7
- package/dist/pipeline/irreversible-operation-gate.d.ts +1 -1
- package/dist/pipeline/irreversible-operation-gate.js +2 -2
- package/dist/pipeline/lifecycle-pipeline.d.ts +8 -7
- package/dist/pipeline/lifecycle-pipeline.js +9 -15
- package/dist/pipeline/member-read-only-transaction.js +11 -3
- package/dist/pipeline/member-reader.d.ts +1 -1
- package/dist/pipeline/member-reader.js +2 -2
- package/dist/pipeline/msp-rebuild.js +5 -3
- package/dist/pipeline/multi-stream-apply-context.js +3 -2
- package/dist/pipeline/notify-scope.d.ts +2 -1
- package/dist/pipeline/pre-handler-gates.d.ts +2 -1
- package/dist/pipeline/pre-transaction-reservations.d.ts +3 -3
- package/dist/pipeline/pre-transaction-reservations.js +9 -7
- package/dist/pipeline/projection-rebuild.js +12 -5
- package/dist/pipeline/projection-state.d.ts +0 -6
- package/dist/pipeline/projection-state.js +0 -5
- package/dist/pipeline/projections-runner.d.ts +2 -1
- package/dist/pipeline/projections-runner.js +10 -6
- package/dist/pipeline/system-hooks.d.ts +6 -2
- package/dist/pipeline/system-hooks.js +61 -10
- package/dist/pipeline/system-identity-switch.d.ts +4 -2
- package/dist/pipeline/system-identity-switch.js +51 -36
- package/dist/pipeline/typed-append-event.d.ts +2 -0
- package/dist/pipeline/typed-append-event.js +4 -0
- package/dist/pipeline/write-origin.js +2 -2
- package/dist/pipeline/write-result-masking.d.ts +1 -1
- package/dist/pipeline/write-result-masking.js +13 -10
- package/dist/random/index.d.ts +1 -2
- package/dist/random/index.js +1 -2
- package/dist/rate-limit/bucket.d.ts +2 -1
- package/dist/rate-limit/middleware.js +2 -1
- package/dist/redis/index.d.ts +3 -4
- package/dist/redis/index.js +2 -2
- package/dist/schema-cli/apply-command.js +5 -2
- package/dist/schema-cli/baseline-command.js +7 -2
- package/dist/schema-cli/generate-command.js +7 -9
- package/dist/schema-cli/shared.d.ts +4 -0
- package/dist/schema-cli/shared.js +21 -2
- package/dist/schema-cli/status-command.js +8 -2
- package/dist/schema-cli/validate-command.js +52 -4
- package/dist/schema-cli.d.ts +0 -1
- package/dist/search/configure-tenant-index.d.ts +1 -3
- package/dist/search/configure-tenant-index.js +0 -4
- package/dist/search/index.d.ts +1 -4
- package/dist/search/index.js +0 -2
- package/dist/search/meilisearch-adapter-internals.d.ts +8 -0
- package/dist/search/meilisearch-adapter-internals.js +9 -0
- package/dist/search/meilisearch-adapter.d.ts +1 -8
- package/dist/search/meilisearch-adapter.js +6 -7
- package/dist/search/meilisearch-peer.d.ts +2 -0
- package/dist/search/meilisearch-peer.js +5 -0
- package/dist/search/reindex-entity.js +107 -91
- package/dist/secrets/index.d.ts +3 -4
- package/dist/secrets/index.js +2 -3
- package/dist/seeding/index.d.ts +2 -2
- package/dist/seeding/index.js +1 -2
- package/dist/time/index.d.ts +2 -2
- package/dist/time/index.js +1 -1
- package/dist/time/tz-context.d.ts +0 -5
- package/dist/time/tz-context.js +0 -9
- package/dist/ui-types/index.d.ts +8 -11
- package/dist/ui-types/index.js +3 -3
- package/dist/upgrade-changelog.d.ts +3 -0
- package/dist/upgrade-changelog.js +18 -0
- package/dist/upgrade-cli-internals.d.ts +8 -0
- package/dist/upgrade-cli-internals.js +63 -0
- package/dist/upgrade-cli.d.ts +1 -18
- package/dist/upgrade-cli.js +29 -137
- package/dist/upgrade-marker-parse.d.ts +32 -0
- package/dist/upgrade-marker-parse.js +73 -0
- package/dist/utils/import-optional-peer.d.ts +6 -0
- package/dist/utils/import-optional-peer.js +19 -0
- package/dist/utils/index.d.ts +1 -4
- package/dist/utils/index.js +1 -4
- package/package.json +27 -20
- package/src/changes.json +451 -0
- package/dist/crypto/request-kms-cache.d.ts +0 -8
- package/dist/crypto/request-kms-cache.js +0 -21
- package/dist/engine/codemod/index.d.ts +0 -2
- package/dist/engine/codemod/index.js +0 -1
- package/dist/engine/codemod/pipeline-codemod.d.ts +0 -50
- package/dist/engine/codemod/pipeline-codemod.js +0 -536
- package/dist/engine/feature-ast/entity-field-types.d.ts +0 -13
- package/dist/engine/feature-ast/entity-field-types.js +0 -35
- package/dist/engine/feature-ast/extractors/ai-steps.d.ts +0 -6
- package/dist/engine/feature-ast/extractors/ai-steps.js +0 -254
- package/dist/engine/feature-ast/extractors/events.d.ts +0 -5
- package/dist/engine/feature-ast/extractors/events.js +0 -275
- package/dist/engine/feature-ast/extractors/handlers.d.ts +0 -24
- package/dist/engine/feature-ast/extractors/handlers.js +0 -358
- package/dist/engine/feature-ast/extractors/hooks.d.ts +0 -24
- package/dist/engine/feature-ast/extractors/hooks.js +0 -248
- package/dist/engine/feature-ast/extractors/index.d.ts +0 -14
- package/dist/engine/feature-ast/extractors/index.js +0 -13
- package/dist/engine/feature-ast/extractors/jobs-routes.d.ts +0 -7
- package/dist/engine/feature-ast/extractors/jobs-routes.js +0 -145
- package/dist/engine/feature-ast/extractors/projections-screens.d.ts +0 -10
- package/dist/engine/feature-ast/extractors/projections-screens.js +0 -195
- package/dist/engine/feature-ast/extractors/resolve-import.d.ts +0 -21
- package/dist/engine/feature-ast/extractors/resolve-import.js +0 -180
- package/dist/engine/feature-ast/extractors/round1.d.ts +0 -10
- package/dist/engine/feature-ast/extractors/round1.js +0 -81
- package/dist/engine/feature-ast/extractors/round2.d.ts +0 -7
- package/dist/engine/feature-ast/extractors/round2.js +0 -191
- package/dist/engine/feature-ast/extractors/round3.d.ts +0 -23
- package/dist/engine/feature-ast/extractors/round3.js +0 -292
- package/dist/engine/feature-ast/extractors/round5.d.ts +0 -9
- package/dist/engine/feature-ast/extractors/round5.js +0 -68
- package/dist/engine/feature-ast/extractors/round6.d.ts +0 -4
- package/dist/engine/feature-ast/extractors/round6.js +0 -17
- package/dist/engine/feature-ast/extractors/shared.d.ts +0 -94
- package/dist/engine/feature-ast/extractors/shared.js +0 -343
- package/dist/engine/feature-ast/index.d.ts +0 -16
- package/dist/engine/feature-ast/index.js +0 -10
- package/dist/engine/feature-ast/parse.d.ts +0 -31
- package/dist/engine/feature-ast/parse.js +0 -483
- package/dist/engine/feature-ast/patch.d.ts +0 -210
- package/dist/engine/feature-ast/patch.js +0 -610
- package/dist/engine/feature-ast/patcher.d.ts +0 -217
- package/dist/engine/feature-ast/patcher.js +0 -281
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +0 -1476
- package/dist/engine/feature-ast/pattern-change-schema.js +0 -1226
- package/dist/engine/feature-ast/pattern-id-matchers.d.ts +0 -7
- package/dist/engine/feature-ast/pattern-id-matchers.js +0 -210
- package/dist/engine/feature-ast/patterns.d.ts +0 -318
- package/dist/engine/feature-ast/patterns.js +0 -106
- package/dist/engine/feature-ast/render.d.ts +0 -47
- package/dist/engine/feature-ast/render.js +0 -662
- package/dist/engine/feature-ast/source-location.d.ts +0 -17
- package/dist/engine/feature-ast/source-location.js +0 -27
- package/dist/engine/pattern-library/index.d.ts +0 -2
- package/dist/engine/pattern-library/index.js +0 -3
- package/dist/engine/pattern-library/library.d.ts +0 -14
- package/dist/engine/pattern-library/library.js +0 -93
- package/dist/engine/pattern-library/mixed-schemas.d.ts +0 -16
- package/dist/engine/pattern-library/mixed-schemas.js +0 -559
- package/dist/engine/pattern-library/opaque-schemas.d.ts +0 -7
- package/dist/engine/pattern-library/opaque-schemas.js +0 -115
- package/dist/engine/pattern-library/shared-fields.d.ts +0 -122
- package/dist/engine/pattern-library/shared-fields.js +0 -88
- package/dist/engine/pattern-library/static-schemas.d.ts +0 -20
- package/dist/engine/pattern-library/static-schemas.js +0 -478
- package/dist/engine/pattern-library/types.d.ts +0 -158
- package/dist/engine/pattern-library/types.js +0 -36
- package/dist/engine/provider-plugin-context.d.ts +0 -3
- package/dist/engine/provider-plugin-context.js +0 -28
- package/dist/engine/resolve-config-or-param.d.ts +0 -18
- package/dist/engine/resolve-config-or-param.js +0 -76
- package/src/scripts/codemod/README.md +0 -187
- package/src/scripts/codemod/codemod-support.ts +0 -334
- package/src/scripts/codemod/collect-source-files.ts +0 -48
- package/src/scripts/codemod/crypto-shredding-testing-move.ts +0 -119
- package/src/scripts/codemod/db-factory.ts +0 -810
- package/src/scripts/codemod/feature-factory.ts +0 -429
- package/src/scripts/codemod/feature-layout.ts +0 -424
- package/src/scripts/codemod/import-extensions.ts +0 -118
- package/src/scripts/codemod/migrate-cross-tenant.ts +0 -328
- package/src/scripts/codemod/migrate-db-raw.ts +0 -401
- package/src/scripts/codemod/migrate-open-to-all.ts +0 -235
- package/src/scripts/codemod/object-form.ts +0 -746
- package/src/scripts/codemod/package-merge.ts +0 -299
- package/src/scripts/codemod/pii-personal-migration.ts +0 -816
- package/src/scripts/codemod/remove-engine-payload-casts.ts +0 -318
- package/src/scripts/codemod/s3-imports.ts +0 -236
- package/src/scripts/codemod/tenant-db-options.ts +0 -454
- package/src/scripts/codemod/testing-injection.ts +0 -215
- package/src/scripts/codemod/testing-move-targets.ts +0 -161
- package/src/scripts/codemod/testing-move.ts +0 -542
- package/src/scripts/codemod/unprocessable-error-details-reason.ts +0 -411
|
@@ -1,49 +1,3 @@
|
|
|
1
|
-
// applyEntityEvent — the ONLY write logic for r.entity tables from
|
|
2
|
-
// StoredEvents. Both callers use it:
|
|
3
|
-
//
|
|
4
|
-
// - createEventStoreExecutor (live, in the write TX) passes the just-appended
|
|
5
|
-
// StoredEvent directly; sensitive fields are table ciphertext in the payload
|
|
6
|
-
// (validated at boot as pii/encrypted).
|
|
7
|
-
// - rebuildProjection via ImplicitProjection (replay, in the rebuild TX)
|
|
8
|
-
// passes the same StoredEvent; apply copies ciphertext byte-for-byte,
|
|
9
|
-
// while bidx is recalculated below.
|
|
10
|
-
//
|
|
11
|
-
// Live==Rebuild equivalence therefore holds by construction for ALL fields —
|
|
12
|
-
// changed write logic needs to be maintained in only ONE place, with no sync
|
|
13
|
-
// contract. The only legitimate divergence is crypto-shredding
|
|
14
|
-
// (DEK erased → value unreadable, bidx NULL). Load-bearing test:
|
|
15
|
-
// db/__tests__/implicit-projection-equivalence.integration.test.ts.
|
|
16
|
-
//
|
|
17
|
-
// Tenant isolation: applyEntityEvent expects an ExecutorManagedRunner (tx or pool), NOT
|
|
18
|
-
// a TenantDb wrapper. Two sources keep it safe:
|
|
19
|
-
// 1. The live path reads the target row through the tenant-scoped TenantDb
|
|
20
|
-
// before writing — loadById() for update/delete/forget, db.fetchOne() for
|
|
21
|
-
// restore — so the aggregateId is already tenant-validated on arrival.
|
|
22
|
-
// 2. create sets tenantId explicitly from event.tenantId, never derived from
|
|
23
|
-
// the TenantDb wrapper default.
|
|
24
|
-
// The missing TenantDb wrapper in this function is therefore not a hole.
|
|
25
|
-
//
|
|
26
|
-
// Auto-verbs:
|
|
27
|
-
// <entity>.created → INSERT
|
|
28
|
-
// <entity>.updated → UPDATE WHERE id=aggregateId
|
|
29
|
-
// <entity>.deleted → soft-delete-UPDATE if entity.softDelete, otherwise hard-DELETE
|
|
30
|
-
// <entity>.restored → undelete-UPDATE (meaningful only for softDelete)
|
|
31
|
-
// <entity>.forgotten → always hard-DELETE (Art.17 purge, including softDelete);
|
|
32
|
-
// via executor.forget(). Rebuild replays created→forgotten
|
|
33
|
-
// → the row is gone, making this rebuild-safe (unlike a
|
|
34
|
-
// direct deleteMany).
|
|
35
|
-
//
|
|
36
|
-
// Domain events (r.defineEvent) on the same aggregate are NOT handled here —
|
|
37
|
-
// they never went through the Executor in the live path and must be handled by
|
|
38
|
-
// explicit r.projection-apply handlers or r.multiStreamProjection.
|
|
39
|
-
// ImplicitProjection registers the auto-verbs.
|
|
40
|
-
//
|
|
41
|
-
// Return shape: ApplyResult with `kind` and an optional `row`.
|
|
42
|
-
// - "applied" → the write completed. `row` contains the written row for
|
|
43
|
-
// create/update/soft-delete/restore. For hard-delete, `row` is null
|
|
44
|
-
// (DELETE statements do not provide a returning row).
|
|
45
|
-
// - "skipped" → the event is not an auto-verb (a domain event on the same
|
|
46
|
-
// aggregate). Caller no-op.
|
|
47
1
|
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
48
2
|
import { requestContext } from "../api/request-context.js";
|
|
49
3
|
import { collectLookupableFields, computeBlindIndexValues } from "../crypto/blind-index.js";
|
|
@@ -51,6 +5,8 @@ import { configuredPiiSubjectKms, decryptPiiFieldValues, isPiiCiphertext, PII_ER
|
|
|
51
5
|
import { collectPiiFileReferenceFields } from "../crypto/subject-resolver.js";
|
|
52
6
|
import { deleteMany, insertOne, updateMany } from "../db/query.js";
|
|
53
7
|
import { InternalError } from "../errors/index.js";
|
|
8
|
+
import { activeRowLevelSecurityScope, runInSystemRowLevelSecurityScope, } from "./row-level-security-scope.js";
|
|
9
|
+
import { asEntityTableMeta } from "./table-introspection.js";
|
|
54
10
|
const AUTO_VERBS = ["created", "updated", "deleted", "restored", "forgotten"];
|
|
55
11
|
/** Parses event.type into an AutoVerb when the event is an auto-verb
|
|
56
12
|
* for the given aggregate; otherwise returns null (a domain event). */
|
|
@@ -85,7 +41,19 @@ async function decryptFileReferenceCiphertext(entity, values) {
|
|
|
85
41
|
/** Applies an auto-event idempotently to the entity table.
|
|
86
42
|
* Called both during live append (inside the write TX) and during rebuild
|
|
87
43
|
* (inside the rebuild TX), using identical logic. */
|
|
88
|
-
|
|
44
|
+
// An ambient tenant scope stays, so a live write cannot land a row in another tenant (row-level security
|
|
45
|
+
// rejects it). Replay, rebuild and boot seeds run without a tenant scope and global tables live on the system
|
|
46
|
+
// tenant, so only those write system scoped.
|
|
47
|
+
export function applyEntityEvent(event, table, entity, tx) {
|
|
48
|
+
if (keepsAmbientTenantScope(table))
|
|
49
|
+
return applyEntityEventInScope(event, table, entity, tx);
|
|
50
|
+
return runInSystemRowLevelSecurityScope("replay, rebuild and global-table projection writes carry no tenant scope of their own", () => applyEntityEventInScope(event, table, entity, tx));
|
|
51
|
+
}
|
|
52
|
+
function keepsAmbientTenantScope(table) {
|
|
53
|
+
return (activeRowLevelSecurityScope()?.kind === "tenant" &&
|
|
54
|
+
asEntityTableMeta(table)?.tenancy !== "global");
|
|
55
|
+
}
|
|
56
|
+
async function applyEntityEventInScope(event, table, entity, tx) {
|
|
89
57
|
const verb = parseAutoVerb(event);
|
|
90
58
|
if (verb === null)
|
|
91
59
|
return { kind: "skipped" };
|
|
@@ -17,41 +17,47 @@ import { toSnakeCase } from "../utils/case.js";
|
|
|
17
17
|
import { resolveTableName } from "./entity-table-meta.js";
|
|
18
18
|
import { toPostgresArrayLiteral } from "./queries/postgres-array-literal.js";
|
|
19
19
|
import { executeRawQuery, executeRawQueryRead } from "./queries/raw-sql.js";
|
|
20
|
+
import { runInSystemRowLevelSecurityScope } from "./row-level-security-scope.js";
|
|
20
21
|
import { columnNamesOf, tableExists } from "./schema-inspection.js";
|
|
21
22
|
export async function nullBlindIndexesForSubject(db, features, subjectKey) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
for (const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
return runInSystemRowLevelSecurityScope("blind index nulling for a forgotten subject spans tenants", async () => {
|
|
24
|
+
const likePattern = subjectCiphertextLikePattern(subjectKey);
|
|
25
|
+
const candidates = [];
|
|
26
|
+
for (const feature of features.values()) {
|
|
27
|
+
for (const [entityName, entity] of Object.entries(feature.entities ?? {})) {
|
|
28
|
+
const lookupable = collectLookupableFields(entity);
|
|
29
|
+
if (lookupable.length === 0)
|
|
30
|
+
continue;
|
|
31
|
+
// No featureName prefix — the dispatcher builds entity tables without
|
|
32
|
+
// one (buildEntityTable with no featureName option), the sweep has to
|
|
33
|
+
// hit the same names.
|
|
34
|
+
candidates.push({
|
|
35
|
+
tableName: resolveTableName(entityName, entity, undefined),
|
|
36
|
+
lookupable,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
33
39
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (!existing.has(tableName))
|
|
43
|
-
continue;
|
|
44
|
-
// Same hazard for a migrated table that predates a later-added
|
|
45
|
-
// lookupable field: skip fields whose columns are missing instead of
|
|
46
|
-
// aborting the sweep for the remaining ones.
|
|
47
|
-
const columns = await columnNamesOf(db, tableName);
|
|
48
|
-
for (const fieldName of lookupable) {
|
|
49
|
-
const snake = toSnakeCase(fieldName);
|
|
50
|
-
if (!columns.has(snake) || !columns.has(`${snake}_bidx`))
|
|
40
|
+
// Skip entities whose projection was never migrated — same class as
|
|
41
|
+
// subjectRowExistsInTenant. A throw here runs AFTER eraseKey
|
|
42
|
+
// in forget-subject and would leave deterministic bidx columns still
|
|
43
|
+
// linkable. One batched existence query instead of one
|
|
44
|
+
// roundtrip per entity.
|
|
45
|
+
const existing = await existingTableNames(db, candidates.map((c) => c.tableName));
|
|
46
|
+
for (const { tableName, lookupable } of candidates) {
|
|
47
|
+
if (!existing.has(tableName))
|
|
51
48
|
continue;
|
|
52
|
-
|
|
49
|
+
// Same hazard for a migrated table that predates a later-added
|
|
50
|
+
// lookupable field: skip fields whose columns are missing instead of
|
|
51
|
+
// aborting the sweep for the remaining ones.
|
|
52
|
+
const columns = await columnNamesOf(db, tableName);
|
|
53
|
+
for (const fieldName of lookupable) {
|
|
54
|
+
const snake = toSnakeCase(fieldName);
|
|
55
|
+
if (!columns.has(snake) || !columns.has(`${snake}_bidx`))
|
|
56
|
+
continue;
|
|
57
|
+
await executeRawQuery(db, `UPDATE ${quoteIdent(tableName)} SET ${quoteIdent(`${snake}_bidx`)} = NULL WHERE ${quoteIdent(snake)} LIKE $1`, [likePattern]);
|
|
58
|
+
}
|
|
53
59
|
}
|
|
54
|
-
}
|
|
60
|
+
});
|
|
55
61
|
}
|
|
56
62
|
async function existingTableNames(db, tableNames) {
|
|
57
63
|
if (tableNames.length === 0)
|
|
@@ -75,37 +81,39 @@ async function existingTableNames(db, tableNames) {
|
|
|
75
81
|
// (`aggregate_id = subjectId` under the actor tenant) when apps need
|
|
76
82
|
// client-supplied ids.
|
|
77
83
|
export async function subjectRowExistsInTenant(db, features, subjectId, tenantId) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
for (const
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
return runInSystemRowLevelSecurityScope("subject existence check spans tenants", async () => {
|
|
85
|
+
// Prefetch which self-PII projection tables actually exist — probing a
|
|
86
|
+
// missing relation used to throw (and get swallowed), which both hid
|
|
87
|
+
// real schema bugs and risked poisoning the driver connection for the
|
|
88
|
+
// rest of the forget TX.
|
|
89
|
+
const candidateTables = [];
|
|
90
|
+
for (const feature of features.values()) {
|
|
91
|
+
for (const [entityName, entity] of Object.entries(feature.entities ?? {})) {
|
|
92
|
+
const hasSelfPiiField = Object.values(entity.fields).some(isSelfPiiField);
|
|
93
|
+
if (!hasSelfPiiField)
|
|
94
|
+
continue;
|
|
95
|
+
// Subject ids are uuids; comparing one to a serial id column makes
|
|
96
|
+
// postgres throw (invalid input syntax for integer) mid forget-TX.
|
|
97
|
+
if (entity.idType === "serial")
|
|
98
|
+
continue;
|
|
99
|
+
candidateTables.push(resolveTableName(entityName, entity, undefined));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const existing = new Set();
|
|
103
|
+
for (const tableName of candidateTables) {
|
|
104
|
+
const rows = await executeRawQueryRead(db, `SELECT to_regclass(quote_ident($1)) IS NOT NULL AS exists`, [tableName]);
|
|
105
|
+
if (rows[0]?.exists === true)
|
|
106
|
+
existing.add(tableName);
|
|
107
|
+
}
|
|
108
|
+
for (const tableName of candidateTables) {
|
|
109
|
+
if (!existing.has(tableName))
|
|
91
110
|
continue;
|
|
92
|
-
|
|
111
|
+
const rows = await executeRawQueryRead(db, `SELECT 1 FROM ${quoteIdent(tableName)} WHERE id = $1 AND tenant_id = $2 LIMIT 1`, [subjectId, tenantId]);
|
|
112
|
+
if (rows.length > 0)
|
|
113
|
+
return true;
|
|
93
114
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
for (const tableName of candidateTables) {
|
|
97
|
-
const rows = await executeRawQueryRead(db, `SELECT to_regclass(quote_ident($1)) IS NOT NULL AS exists`, [tableName]);
|
|
98
|
-
if (rows[0]?.exists === true)
|
|
99
|
-
existing.add(tableName);
|
|
100
|
-
}
|
|
101
|
-
for (const tableName of candidateTables) {
|
|
102
|
-
if (!existing.has(tableName))
|
|
103
|
-
continue;
|
|
104
|
-
const rows = await executeRawQueryRead(db, `SELECT 1 FROM ${quoteIdent(tableName)} WHERE id = $1 AND tenant_id = $2 LIMIT 1`, [subjectId, tenantId]);
|
|
105
|
-
if (rows.length > 0)
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
|
-
return false;
|
|
115
|
+
return false;
|
|
116
|
+
});
|
|
109
117
|
}
|
|
110
118
|
function findEntityByExactName(features, entityName) {
|
|
111
119
|
for (const feature of features.values()) {
|
|
@@ -123,14 +131,16 @@ function findEntityByExactName(features, entityName) {
|
|
|
123
131
|
// table (globally unique, unspoofable via a client-supplied explicitId
|
|
124
132
|
// at create) — a composite PK or shared table would break this.
|
|
125
133
|
export async function recordRowExistsInTenant(db, features, entityName, recordId, tenantId) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
return runInSystemRowLevelSecurityScope("record existence check spans tenants", async () => {
|
|
135
|
+
const entity = findEntityByExactName(features, entityName);
|
|
136
|
+
if (!entity)
|
|
137
|
+
return false;
|
|
138
|
+
const tableName = resolveTableName(entityName, entity, undefined);
|
|
139
|
+
if (!(await tableExists(db, tableName)))
|
|
140
|
+
return false;
|
|
141
|
+
const rows = await executeRawQueryRead(db, `SELECT 1 FROM ${quoteIdent(tableName)} WHERE id = $1 AND tenant_id = $2 LIMIT 1`, [recordId, tenantId]);
|
|
142
|
+
return rows.length > 0;
|
|
143
|
+
});
|
|
134
144
|
}
|
|
135
145
|
// Provenance fallback for record subjects whose projection row is gone (hard
|
|
136
146
|
// delete) or that have no registered entity at all (custom aggregates): the
|
|
@@ -138,20 +148,24 @@ export async function recordRowExistsInTenant(db, features, entityName, recordId
|
|
|
138
148
|
// and the subject key is global, so another tenant could open a stream under
|
|
139
149
|
// the same aggregate id; any foreign event voids the proof.
|
|
140
150
|
export async function recordEventsOwnedExclusivelyByTenant(db, aggregateType, aggregateId, tenantId) {
|
|
141
|
-
|
|
151
|
+
return runInSystemRowLevelSecurityScope("record event ownership check spans tenants", async () => {
|
|
152
|
+
const rows = await executeRawQueryRead(db, `SELECT 1 FROM "kumiko_events" WHERE aggregate_type = $1 AND aggregate_id = $2::uuid AND tenant_id = $3::uuid
|
|
142
153
|
AND NOT EXISTS (SELECT 1 FROM "kumiko_events" WHERE aggregate_type = $1 AND aggregate_id = $2::uuid AND tenant_id <> $3::uuid)
|
|
143
154
|
LIMIT 1`, [aggregateType, aggregateId, tenantId]);
|
|
144
|
-
|
|
155
|
+
return rows.length > 0;
|
|
156
|
+
});
|
|
145
157
|
}
|
|
146
158
|
// Owning tenant of a record subject's row, for callers that must evaluate
|
|
147
159
|
// tenant-scoped policy for the row's real owner (not the acting user's tenant).
|
|
148
160
|
export async function recordRowOwningTenantId(db, features, entityName, recordId) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
161
|
+
return runInSystemRowLevelSecurityScope("record owner lookup spans tenants", async () => {
|
|
162
|
+
const entity = findEntityByExactName(features, entityName);
|
|
163
|
+
if (!entity)
|
|
164
|
+
return undefined;
|
|
165
|
+
const tableName = resolveTableName(entityName, entity, undefined);
|
|
166
|
+
if (!(await tableExists(db, tableName)))
|
|
167
|
+
return undefined;
|
|
168
|
+
const rows = await executeRawQueryRead(db, `SELECT tenant_id FROM ${quoteIdent(tableName)} WHERE id = $1 LIMIT 1`, [recordId]);
|
|
169
|
+
return rows[0]?.tenant_id;
|
|
170
|
+
});
|
|
157
171
|
}
|
|
@@ -5,7 +5,7 @@ import { extractPgError } from "./pg-error.js";
|
|
|
5
5
|
// A pool can briefly keep handing out connections the server just closed.
|
|
6
6
|
// Each failed attempt discards one, so retries are bounded by pool size.
|
|
7
7
|
const CLOSED_CONNECTION_CODES = new Set([
|
|
8
|
-
"
|
|
8
|
+
"ERR_POSTGRES_CONNECTION_CLOSED", // Bun.SQL
|
|
9
9
|
"57P01", // PG SQLSTATE: admin_shutdown / terminated backend
|
|
10
10
|
]);
|
|
11
11
|
export function isClosedConnectionError(err) {
|
|
@@ -21,40 +21,33 @@ export function isPooledClient(raw) {
|
|
|
21
21
|
typeof propertyOf(raw, "savepoint") !== "function" &&
|
|
22
22
|
typeof propertyOf(raw, "release") !== "function");
|
|
23
23
|
}
|
|
24
|
-
const closedConnectionLog = createFallbackLogger("
|
|
25
|
-
// The driver rejects in-flight queries with this AbortError (no code) when the
|
|
26
|
-
// pool closes under them, so isClosedConnectionError can't see it.
|
|
27
|
-
const DRIVER_CLOSED_ABORT_MESSAGE = "The connection was closed.";
|
|
28
|
-
function isDriverClosedAbort(err) {
|
|
29
|
-
return (err instanceof Error && err.name === "AbortError" && err.message === DRIVER_CLOSED_ABORT_MESSAGE);
|
|
30
|
-
}
|
|
24
|
+
const closedConnectionLog = createFallbackLogger("bun-sql");
|
|
31
25
|
function isHandleLike(raw) {
|
|
32
26
|
return raw !== null && (typeof raw === "object" || typeof raw === "function");
|
|
33
27
|
}
|
|
34
|
-
//
|
|
28
|
+
// Bun.SQL pool and tx handles both carry `flush`.
|
|
35
29
|
function driverOf(raw) {
|
|
36
30
|
if (!isHandleLike(raw))
|
|
37
31
|
return "unknown";
|
|
38
|
-
if (typeof raw["
|
|
39
|
-
return "
|
|
32
|
+
if (typeof raw["flush"] === "function")
|
|
33
|
+
return "bun-sql";
|
|
40
34
|
return "unknown";
|
|
41
35
|
}
|
|
42
36
|
function positiveNumber(v) {
|
|
43
37
|
return typeof v === "number" && Number.isFinite(v) && v > 0 ? v : undefined;
|
|
44
38
|
}
|
|
45
|
-
//
|
|
39
|
+
// Bun.SQL reports its timeout options in milliseconds.
|
|
46
40
|
function timeoutOptionMs(options, driver, optionName) {
|
|
47
|
-
if (driver !== "
|
|
41
|
+
if (driver !== "bun-sql")
|
|
48
42
|
return undefined;
|
|
49
|
-
|
|
50
|
-
return seconds === undefined ? undefined : seconds * 1000;
|
|
43
|
+
return positiveNumber(options[optionName]);
|
|
51
44
|
}
|
|
52
45
|
function errorProp(err, name) {
|
|
53
46
|
return isHandleLike(err) ? err[name] : undefined;
|
|
54
47
|
}
|
|
55
48
|
// Deliberately excludes SQL text and parameters: app SQL can carry PII.
|
|
56
49
|
export function describeClosedConnectionFailure(raw, err, attempts) {
|
|
57
|
-
if (!isClosedConnectionError(err)
|
|
50
|
+
if (!isClosedConnectionError(err))
|
|
58
51
|
return undefined;
|
|
59
52
|
const driver = driverOf(raw);
|
|
60
53
|
const rawOptions = isHandleLike(raw) ? raw["options"] : undefined;
|
|
@@ -64,9 +57,9 @@ export function describeClosedConnectionFailure(raw, err, attempts) {
|
|
|
64
57
|
pooled: isPooledClient(raw),
|
|
65
58
|
attempts,
|
|
66
59
|
poolMax: positiveNumber(options["max"]),
|
|
67
|
-
idleTimeoutMs: timeoutOptionMs(options, driver, "
|
|
68
|
-
maxLifetimeMs: timeoutOptionMs(options, driver, "
|
|
69
|
-
connectionTimeoutMs: timeoutOptionMs(options, driver, "
|
|
60
|
+
idleTimeoutMs: timeoutOptionMs(options, driver, "idleTimeout"),
|
|
61
|
+
maxLifetimeMs: timeoutOptionMs(options, driver, "maxLifetime"),
|
|
62
|
+
connectionTimeoutMs: timeoutOptionMs(options, driver, "connectionTimeout"),
|
|
70
63
|
errorName: errorProp(err, "name"),
|
|
71
64
|
errorCode: extractPgError(err)?.code ?? errorProp(err, "code"),
|
|
72
65
|
errorErrno: errorProp(err, "errno"),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Canonical table source for both `kumiko schema generate` and setupTestStack
|
|
2
2
|
// (entities, storeTables, projections, multiStreamProjections). One shared
|
|
3
3
|
// source so the test-DB push and migrations cannot drift apart again.
|
|
4
|
-
import { compareByCodepoint } from "../utils/
|
|
4
|
+
import { compareByCodepoint } from "../utils/compare.js";
|
|
5
5
|
import { assertBackingTableSuperset, deriveEntityTableMeta, } from "./entity-table-meta.js";
|
|
6
6
|
import { enumerateFeatureTableSources } from "./feature-table-sources.js";
|
|
7
7
|
import { asEntityTableMeta } from "./query.js";
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
// Compound-type pipeline for the executor.
|
|
2
|
-
//
|
|
3
|
-
// A compound type is a field represented by ONE API object and MULTIPLE
|
|
4
|
-
// DB columns. Examples:
|
|
5
|
-
// - locatedTimestamp: { at, tz, utc } ↔ <name>Utc + <name>Tz
|
|
6
|
-
// - money: { amount, currency } ↔ <name> + <name>Currency
|
|
7
|
-
// - (upcoming) address: { street, zip, city } ↔ <name>Street + <name>Zip + <name>City
|
|
8
1
|
//
|
|
9
2
|
// Instead of nesting every helper in the executor at four different points,
|
|
10
3
|
// this function pipelines all compound-type conversions in one pass. Adding
|
package/dist/db/config-seed.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { ConfigSeedDef } from "@cosmicdrift/kumiko-types/config";
|
|
1
2
|
import type { EntityDefinition } from "../engine/index.ts";
|
|
2
|
-
import type {
|
|
3
|
+
import type { Registry } from "../engine/types/index.ts";
|
|
3
4
|
import type { EnvelopeCipher } from "../secrets/envelope-cipher.ts";
|
|
4
5
|
import type { DbConnection } from "./connection.ts";
|
|
5
6
|
import type { EntityTable } from "./table-builder.ts";
|
package/dist/db/config-seed.js
CHANGED
|
@@ -24,7 +24,12 @@ export async function seedConfigValues(seeds, table, entity, registry, db, ciphe
|
|
|
24
24
|
return { created, skipped };
|
|
25
25
|
const systemUser = createSystemUser(SYSTEM_TENANT_ID);
|
|
26
26
|
const executor = createEventStoreExecutor(table, entity, { entityName: "config-value" });
|
|
27
|
-
const tdb = createTenantDb({
|
|
27
|
+
const tdb = createTenantDb({
|
|
28
|
+
db,
|
|
29
|
+
tenantId: SYSTEM_TENANT_ID,
|
|
30
|
+
mode: "system",
|
|
31
|
+
reason: "config seeds are system-tenant defaults",
|
|
32
|
+
});
|
|
28
33
|
for (const seed of seeds) {
|
|
29
34
|
const keyDef = registry.getConfigKey(seed.key);
|
|
30
35
|
if (!keyDef) {
|
package/dist/db/connection.d.ts
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
import type { DbConnection, PgClient } from "@cosmicdrift/kumiko-types/db-connection";
|
|
2
|
-
import
|
|
1
|
+
import type { DbConnection, PgClient, PgListenClient } from "@cosmicdrift/kumiko-types/db-connection";
|
|
2
|
+
import { type RowLevelSecurityMode } from "./render-rls-ddl.ts";
|
|
3
3
|
export type * from "@cosmicdrift/kumiko-types/db-connection";
|
|
4
|
-
type
|
|
4
|
+
type BunSqlOptions = Bun.SQL.PostgresOrMySQLOptions;
|
|
5
5
|
export type DbConnectionOptions = {
|
|
6
6
|
readonly maxConnections?: number;
|
|
7
7
|
readonly idleTimeoutSeconds?: number;
|
|
8
8
|
readonly connectTimeoutSeconds?: number;
|
|
9
9
|
readonly closeTimeoutSeconds?: number;
|
|
10
|
-
readonly ssl?:
|
|
11
|
-
readonly onNotice?: (notice: postgres.Notice) => void;
|
|
10
|
+
readonly ssl?: BunSqlOptions["tls"];
|
|
12
11
|
readonly statementTimeoutMs?: number;
|
|
13
12
|
readonly idleInTransactionSessionTimeoutMs?: number;
|
|
14
13
|
readonly lockTimeoutMs?: number;
|
|
15
14
|
};
|
|
16
15
|
export type CreateDbConnectionOptions = DbConnectionOptions & {
|
|
17
16
|
readonly url: string;
|
|
17
|
+
readonly rowLevelSecurity?: RowLevelSecurityMode;
|
|
18
18
|
};
|
|
19
19
|
export type DbConnectionHandle = {
|
|
20
20
|
db: DbConnection;
|
|
21
21
|
client: PgClient;
|
|
22
|
+
listenClient: PgListenClient;
|
|
22
23
|
close: () => Promise<void>;
|
|
23
24
|
};
|
|
24
25
|
export declare const DEFAULT_DB_CLOSE_TIMEOUT_SECONDS = 5;
|
|
@@ -27,7 +28,7 @@ export type UnboundedSiblingPool = {
|
|
|
27
28
|
readonly close: (timeoutSeconds: number) => Promise<void>;
|
|
28
29
|
};
|
|
29
30
|
export declare function openUnboundedSiblingPool(db: unknown): UnboundedSiblingPool | undefined;
|
|
30
|
-
export declare function createDbConnection({ url, ...options }: CreateDbConnectionOptions): DbConnectionHandle;
|
|
31
|
+
export declare function createDbConnection({ url, rowLevelSecurity, ...options }: CreateDbConnectionOptions): DbConnectionHandle;
|
|
31
32
|
export declare function dbConnectionOptionsFromEnv(env?: Readonly<Record<string, string | undefined>>): DbConnectionOptions;
|
|
32
33
|
export declare const DEFAULT_SERVER_STATEMENT_TIMEOUT_MS = 60000;
|
|
33
34
|
export declare const DEFAULT_SERVER_IDLE_IN_TRANSACTION_TIMEOUT_MS = 300000;
|
package/dist/db/connection.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import postgres from "postgres";
|
|
2
1
|
import { readPositiveIntEnv } from "../utils/env-parse.js";
|
|
2
|
+
import { DEFAULT_ROW_LEVEL_SECURITY_MODE } from "./render-rls-ddl.js";
|
|
3
|
+
import { enforceRowLevelSecurity } from "./row-level-security-connection.js";
|
|
3
4
|
// A DB outage can leave an in-flight query referenced on a dead connection,
|
|
4
|
-
// so a plain
|
|
5
|
+
// so a plain close() (no timeout = wait for queries to finish) hangs forever —
|
|
5
6
|
// close() must always bound how long shutdown waits.
|
|
6
7
|
export const DEFAULT_DB_CLOSE_TIMEOUT_SECONDS = 5;
|
|
7
8
|
// Keyed by the pool so withoutStatementTimeout can open a sibling with identical connect options.
|
|
@@ -11,20 +12,28 @@ export function openUnboundedSiblingPool(db) {
|
|
|
11
12
|
return undefined;
|
|
12
13
|
return unboundedSiblingFactories.get(db)?.();
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
// Bun.SQL treats `true` and a TLS object without rejectUnauthorized as sslmode=require, which
|
|
16
|
+
// skips certificate checks; postgres-js verified in both cases, so keep that unless turned off.
|
|
17
|
+
function verifiedTls(ssl) {
|
|
18
|
+
if (ssl === true)
|
|
19
|
+
return "verify-full";
|
|
20
|
+
if (typeof ssl === "object" && !(ssl instanceof Blob) && ssl.rejectUnauthorized === undefined) {
|
|
21
|
+
return { ...ssl, rejectUnauthorized: true };
|
|
22
|
+
}
|
|
23
|
+
return ssl;
|
|
24
|
+
}
|
|
25
|
+
export function createDbConnection({ url, rowLevelSecurity = DEFAULT_ROW_LEVEL_SECURITY_MODE, ...options }) {
|
|
26
|
+
const sqlOptions = {};
|
|
16
27
|
if (options.maxConnections !== undefined)
|
|
17
|
-
|
|
28
|
+
sqlOptions.max = options.maxConnections;
|
|
18
29
|
if (options.idleTimeoutSeconds !== undefined) {
|
|
19
|
-
|
|
30
|
+
sqlOptions.idleTimeout = options.idleTimeoutSeconds;
|
|
20
31
|
}
|
|
21
32
|
if (options.connectTimeoutSeconds !== undefined) {
|
|
22
|
-
|
|
33
|
+
sqlOptions.connectionTimeout = options.connectTimeoutSeconds;
|
|
23
34
|
}
|
|
24
35
|
if (options.ssl !== undefined)
|
|
25
|
-
|
|
26
|
-
if (options.onNotice !== undefined)
|
|
27
|
-
pgOptions.onnotice = options.onNotice;
|
|
36
|
+
sqlOptions.tls = verifiedTls(options.ssl);
|
|
28
37
|
const serverParameters = {};
|
|
29
38
|
if (options.statementTimeoutMs !== undefined) {
|
|
30
39
|
serverParameters["statement_timeout"] = options.statementTimeoutMs;
|
|
@@ -36,48 +45,58 @@ export function createDbConnection({ url, ...options }) {
|
|
|
36
45
|
if (options.lockTimeoutMs !== undefined)
|
|
37
46
|
serverParameters["lock_timeout"] = options.lockTimeoutMs;
|
|
38
47
|
if (Object.keys(serverParameters).length > 0)
|
|
39
|
-
|
|
40
|
-
|
|
48
|
+
sqlOptions.connection = serverParameters;
|
|
49
|
+
// Bun.SQL opens all `max` connections on the first query, so a pool always costs `max` backends.
|
|
50
|
+
const rawClient = new Bun.SQL(url, sqlOptions);
|
|
51
|
+
const enforced = rowLevelSecurity === "on";
|
|
52
|
+
const client = enforced ? enforceRowLevelSecurity(rawClient) : rawClient;
|
|
41
53
|
// A run that never settles (a caller gave up on it via its own timeout) never reaches the
|
|
42
54
|
// sibling's own close; close() ends it so shutdown and DROP DATABASE are not blocked by it.
|
|
43
55
|
const openSiblings = new Set();
|
|
44
|
-
|
|
56
|
+
const registerFactory = (factory) => {
|
|
57
|
+
unboundedSiblingFactories.set(rawClient, factory);
|
|
58
|
+
if (client !== rawClient)
|
|
59
|
+
unboundedSiblingFactories.set(client, factory);
|
|
60
|
+
};
|
|
61
|
+
registerFactory(() => {
|
|
45
62
|
// Two connections are enough for lock-in-tx plus DDL; the cap keeps parallel rebuilds from exhausting max_connections.
|
|
46
|
-
const sibling =
|
|
47
|
-
...
|
|
63
|
+
const sibling = new Bun.SQL(url, {
|
|
64
|
+
...sqlOptions,
|
|
48
65
|
max: 4,
|
|
49
|
-
connection: { ...
|
|
66
|
+
connection: { ...sqlOptions.connection, statement_timeout: 0 },
|
|
50
67
|
});
|
|
51
68
|
openSiblings.add(sibling);
|
|
52
69
|
return {
|
|
53
|
-
client: sibling,
|
|
70
|
+
client: enforced ? enforceRowLevelSecurity(sibling) : sibling,
|
|
54
71
|
close: async (timeoutSeconds) => {
|
|
55
72
|
openSiblings.delete(sibling);
|
|
56
|
-
await sibling.
|
|
73
|
+
await sibling.close({ timeout: timeoutSeconds });
|
|
57
74
|
},
|
|
58
75
|
};
|
|
59
76
|
});
|
|
60
77
|
return {
|
|
61
78
|
db: client,
|
|
62
79
|
client,
|
|
80
|
+
listenClient: client,
|
|
63
81
|
close: async () => {
|
|
64
82
|
const timeout = options.closeTimeoutSeconds ?? DEFAULT_DB_CLOSE_TIMEOUT_SECONDS;
|
|
83
|
+
unboundedSiblingFactories.delete(rawClient);
|
|
65
84
|
unboundedSiblingFactories.delete(client);
|
|
66
|
-
const pools = [
|
|
85
|
+
const pools = [rawClient, ...openSiblings];
|
|
67
86
|
openSiblings.clear();
|
|
68
|
-
const results = await Promise.allSettled(pools.map((pool) => pool.
|
|
87
|
+
const results = await Promise.allSettled(pools.map((pool) => pool.close({ timeout })));
|
|
69
88
|
const failure = results.find((result) => result.status === "rejected");
|
|
70
89
|
if (failure !== undefined)
|
|
71
90
|
throw failure.reason;
|
|
72
91
|
},
|
|
73
92
|
};
|
|
74
93
|
}
|
|
75
|
-
// DB_PROVIDER
|
|
76
|
-
//
|
|
94
|
+
// DB_PROVIDER once selected between drivers; a config that still sets it is stale,
|
|
95
|
+
// so fail loudly instead of silently ignoring it.
|
|
77
96
|
function assertNoRemovedDbProvider(env) {
|
|
78
97
|
const provider = env["DB_PROVIDER"];
|
|
79
|
-
if (provider !== undefined && provider !== ""
|
|
80
|
-
throw new Error(`DB_PROVIDER="${provider}" is no longer supported:
|
|
98
|
+
if (provider !== undefined && provider !== "") {
|
|
99
|
+
throw new Error(`DB_PROVIDER="${provider}" is no longer supported: Bun.SQL is the only driver. Remove DB_PROVIDER from the environment.`);
|
|
81
100
|
}
|
|
82
101
|
}
|
|
83
102
|
export function dbConnectionOptionsFromEnv(env = process.env) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { EscapeHatchReporter } from "@cosmicdrift/kumiko-types/handlers";
|
|
1
2
|
import type { CrossTenantReads } from "@cosmicdrift/kumiko-types/tenant-db-types";
|
|
2
|
-
import type { EscapeHatchReporter } from "../engine/types/index.ts";
|
|
3
3
|
import { type CreateTenantDbOptions, type TenantDb } from "./tenant-db.ts";
|
|
4
4
|
export declare function createCrossTenantReads(systemModeDb: TenantDb, reason: string, report: EscapeHatchReporter): CrossTenantReads;
|
|
5
5
|
export declare function createHandlerCrossTenantReads(options: Omit<CreateTenantDbOptions, "mode"> & {
|
|
@@ -18,6 +18,6 @@ export function createCrossTenantReads(systemModeDb, reason, report) {
|
|
|
18
18
|
// Own system-mode TenantDb without unsafeRaw/globalWrites: the handler gets the read surface only.
|
|
19
19
|
export function createHandlerCrossTenantReads(options) {
|
|
20
20
|
const { reason, report, ...dbOptions } = options;
|
|
21
|
-
const systemModeDb = createTenantDb({ ...dbOptions, mode: "system" });
|
|
21
|
+
const systemModeDb = createTenantDb({ ...dbOptions, mode: "system", reason });
|
|
22
22
|
return createCrossTenantReads(systemModeDb, reason, report);
|
|
23
23
|
}
|
package/dist/db/dialect.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
|
4
4
|
import type { PgType } from "./entity-table-meta.ts";
|
|
5
5
|
export type { ColumnHandle, SchemaTable } from "@cosmicdrift/kumiko-types/schema-table-types";
|
|
6
6
|
export type TableColumns<_T = unknown> = SchemaTable;
|
|
7
|
-
export type SelectQuery = unknown;
|
|
8
7
|
export declare function pgTypeToSqlType(pgType: PgType): string;
|
|
9
8
|
type ColumnFinal = {
|
|
10
9
|
readonly sqlName: string;
|
|
@@ -46,10 +45,6 @@ export declare function timestamp(name: string, _opts?: {
|
|
|
46
45
|
withTimezone?: boolean;
|
|
47
46
|
mode?: "string" | "date";
|
|
48
47
|
}): ColumnBuilder<Temporal.Instant | string>;
|
|
49
|
-
export declare function numeric(name: string, _opts?: {
|
|
50
|
-
precision?: number;
|
|
51
|
-
scale?: number;
|
|
52
|
-
}): ColumnBuilder<string>;
|
|
53
48
|
export declare function instant(name: string, opts?: {
|
|
54
49
|
precision?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
55
50
|
}): ColumnBuilder<Temporal.Instant>;
|
package/dist/db/dialect.js
CHANGED
|
@@ -180,9 +180,6 @@ export function timestamp(name, _opts) {
|
|
|
180
180
|
}
|
|
181
181
|
// numeric → text (we don't currently use Decimal); kept for compat with
|
|
182
182
|
// legacy schema imports that won't actually instantiate at runtime.
|
|
183
|
-
export function numeric(name, _opts) {
|
|
184
|
-
return buildColumn(name, "text");
|
|
185
|
-
}
|
|
186
183
|
export function instant(name, opts) {
|
|
187
184
|
const pgType = opts?.precision === 3 ? "timestamptz(3)" : "timestamptz";
|
|
188
185
|
return buildColumn(name, pgType);
|