@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
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { emitDbQuery } from "../observability/standard-metrics.js";
|
|
2
|
+
import { runInRowLevelSecurityScope, tenantDbRowLevelSecurityScope, } from "./row-level-security-scope.js";
|
|
3
|
+
import { tableNameOf } from "./table-introspection.js";
|
|
4
|
+
export function createTenantDbStatementRunner({ tracer, meter, signal, mode, reason, tenantId, }) {
|
|
5
|
+
return function withDbSpan(operation, table, runner) {
|
|
6
|
+
signal?.throwIfAborted();
|
|
7
|
+
const scope = tenantDbRowLevelSecurityScope(mode === "system" ? { mode, reason } : { mode }, tenantId, table);
|
|
8
|
+
const scopedRunner = () => runInRowLevelSecurityScope(scope, runner);
|
|
9
|
+
if (!tracer && !meter)
|
|
10
|
+
return scopedRunner();
|
|
11
|
+
const tableName = tableNameOf(table);
|
|
12
|
+
const start = performance.now();
|
|
13
|
+
const emitMetric = () => {
|
|
14
|
+
if (meter) {
|
|
15
|
+
emitDbQuery(meter, { operation, table: tableName }, (performance.now() - start) / 1000);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
if (!tracer) {
|
|
19
|
+
return (async () => {
|
|
20
|
+
try {
|
|
21
|
+
return await scopedRunner();
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
emitMetric();
|
|
25
|
+
}
|
|
26
|
+
})();
|
|
27
|
+
}
|
|
28
|
+
return tracer.withSpan("db.query", {
|
|
29
|
+
kind: "client",
|
|
30
|
+
attributes: {
|
|
31
|
+
"db.system": "postgresql",
|
|
32
|
+
"db.operation": operation,
|
|
33
|
+
"db.table": tableName,
|
|
34
|
+
},
|
|
35
|
+
}, async (span) => {
|
|
36
|
+
try {
|
|
37
|
+
const result = await scopedRunner();
|
|
38
|
+
if (Array.isArray(result)) {
|
|
39
|
+
span.setAttribute("db.row_count", result.length);
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
emitMetric();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { TenantDbMode, TenantDbUpsertOptions } from "@cosmicdrift/kumiko-types/tenant-db-types";
|
|
2
|
+
import { AccessDeniedError } from "../errors/index.ts";
|
|
3
|
+
import { type AnyDb, type TableLike } from "./query.ts";
|
|
4
|
+
export declare function upsertTableDenied(table: TableLike, mode: TenantDbMode): AccessDeniedError | undefined;
|
|
5
|
+
export declare function upsertTenantRow<TRow>(db: AnyDb, table: TableLike, values: Record<string, unknown>, options: TenantDbUpsertOptions): Promise<TRow | undefined>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { quoteIdent } from "../crypto/ciphertext-pattern.js";
|
|
2
|
+
import { AccessDeniedError } from "../errors/index.js";
|
|
3
|
+
import { isExecutorManagedTable } from "./executor-managed-tables.js";
|
|
4
|
+
import { buildInsertSql, coerceRow, conflictUpdateParts, insertEntries, unsafeLoggingClosedConnection, } from "./query.js";
|
|
5
|
+
import { extractTableInfo } from "./table-info.js";
|
|
6
|
+
import { asEntityTableMeta, tableNameOf } from "./table-introspection.js";
|
|
7
|
+
const TENANT_COLUMN = "tenant_id";
|
|
8
|
+
// Upsert is never allowed on event-fed tables (even with the write capability) and has no tenant-mode meaning for "global" ones.
|
|
9
|
+
export function upsertTableDenied(table, mode) {
|
|
10
|
+
if (isExecutorManagedTable(table)) {
|
|
11
|
+
return new AccessDeniedError({
|
|
12
|
+
message: `ctx.db.upsert(${tableNameOf(table)}): table is fed by events; upsert is not supported on it.`,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
if (mode === "tenant" && asEntityTableMeta(table)?.tenancy === "global") {
|
|
16
|
+
return new AccessDeniedError({
|
|
17
|
+
message: `ctx.db.upsert(${tableNameOf(table)}): "global" table rows must carry SYSTEM_TENANT_ID; ` +
|
|
18
|
+
"use db.global(table) with escapeHatch instead.",
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
function sameColumnSet(a, b) {
|
|
24
|
+
return a.length === b.length && new Set(a).size === a.length && a.every((c) => b.includes(c));
|
|
25
|
+
}
|
|
26
|
+
function declaredUniqueColumnSets(meta) {
|
|
27
|
+
const sets = meta.indexes
|
|
28
|
+
.filter((i) => i.unique === true && i.whereSql === undefined && i.needsManualWhere !== true)
|
|
29
|
+
.map((i) => i.columns);
|
|
30
|
+
if (meta.compositePrimaryKey)
|
|
31
|
+
sets.push(meta.compositePrimaryKey.columns);
|
|
32
|
+
const primaryKeyColumns = meta.columns.filter((c) => c.primaryKey).map((c) => c.name);
|
|
33
|
+
if (primaryKeyColumns.length > 0)
|
|
34
|
+
sets.push(primaryKeyColumns);
|
|
35
|
+
return sets;
|
|
36
|
+
}
|
|
37
|
+
function primaryKeyColumnsOf(meta) {
|
|
38
|
+
return [
|
|
39
|
+
...meta.columns.filter((c) => c.primaryKey).map((c) => c.name),
|
|
40
|
+
...(meta.compositePrimaryKey?.columns ?? []),
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
function assertTenantSafeConflictTarget(tableName, meta, conflictCols) {
|
|
44
|
+
if (conflictCols.length === 0 || !conflictCols.includes(TENANT_COLUMN)) {
|
|
45
|
+
throw new Error(`tenantDb.upsert(${tableName}): conflictTarget must include "tenantId" — a target without it ` +
|
|
46
|
+
"could match, and overwrite, another tenant's row.");
|
|
47
|
+
}
|
|
48
|
+
if (!declaredUniqueColumnSets(meta).some((set) => sameColumnSet(set, conflictCols))) {
|
|
49
|
+
throw new Error(`tenantDb.upsert(${tableName}): conflictTarget (${conflictCols.join(", ")}) matches no ` +
|
|
50
|
+
"declared unique index (without a WHERE clause) or primary key.");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Handles a tenant's own row only: values carry the tenant already, and the DO UPDATE guard
|
|
54
|
+
// compares against the tenant being inserted, so a conflict on another tenant's row updates nothing.
|
|
55
|
+
export async function upsertTenantRow(db, table, values, options) {
|
|
56
|
+
const info = extractTableInfo(table);
|
|
57
|
+
const meta = asEntityTableMeta(table);
|
|
58
|
+
if (!info.hasColumn("tenantId")) {
|
|
59
|
+
throw new Error(`tenantDb.upsert(${info.name}): table has no tenant column — use the free upsertOnConflict ` +
|
|
60
|
+
"helper on an unsafeRaw runner instead.");
|
|
61
|
+
}
|
|
62
|
+
if (!meta) {
|
|
63
|
+
throw new Error(`tenantDb.upsert(${info.name}): needs an EntityTableMeta table (declared unique indexes and primary key), got a table without meta.`);
|
|
64
|
+
}
|
|
65
|
+
const conflictCols = options.conflictTarget.map((field) => info.columnOf(field));
|
|
66
|
+
assertTenantSafeConflictTarget(info.name, meta, conflictCols);
|
|
67
|
+
if (options.update &&
|
|
68
|
+
Object.keys(options.update).some((f) => info.columnOf(f) === TENANT_COLUMN)) {
|
|
69
|
+
throw new Error(`tenantDb.upsert(${info.name}): update must not set "tenantId" — it would move the row into another tenant.`);
|
|
70
|
+
}
|
|
71
|
+
const entries = insertEntries(info, values);
|
|
72
|
+
if (!entries.some((e) => e.col === TENANT_COLUMN)) {
|
|
73
|
+
throw new Error(`tenantDb.upsert(${info.name}): values carry no tenantId.`);
|
|
74
|
+
}
|
|
75
|
+
const { sqlPrefix, params } = buildInsertSql(info, entries);
|
|
76
|
+
const skipCols = new Set([...conflictCols, ...primaryKeyColumnsOf(meta), TENANT_COLUMN]);
|
|
77
|
+
const setParts = conflictUpdateParts(info, entries, options.update, skipCols, params);
|
|
78
|
+
if (setParts.length === 0) {
|
|
79
|
+
throw new Error(`tenantDb.upsert(${info.name}): nothing to update on conflict`);
|
|
80
|
+
}
|
|
81
|
+
const tenantRef = quoteIdent(TENANT_COLUMN);
|
|
82
|
+
const sqlText = `${sqlPrefix} ON CONFLICT (${conflictCols.map((c) => quoteIdent(c)).join(", ")}) ` +
|
|
83
|
+
`DO UPDATE SET ${setParts.join(", ")} ` +
|
|
84
|
+
`WHERE ${quoteIdent(info.name)}.${tenantRef} = EXCLUDED.${tenantRef} RETURNING *`;
|
|
85
|
+
const rows = await unsafeLoggingClosedConnection(db, sqlText, params, {
|
|
86
|
+
operation: "tenantDb.upsert",
|
|
87
|
+
table: info.name,
|
|
88
|
+
});
|
|
89
|
+
const first = rows[0];
|
|
90
|
+
return first ? coerceRow(first, info) : undefined;
|
|
91
|
+
}
|
package/dist/db/tenant-db.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { EntityTableMeta } from "@cosmicdrift/kumiko-types/entity-table-meta-types";
|
|
2
2
|
import type { ExecutorManagedRunner } from "@cosmicdrift/kumiko-types/executor-brand";
|
|
3
|
-
import type { EscapeHatchDeclaration, EscapeHatchReporter, EscapeHatchTarget } from "@cosmicdrift/kumiko-types/handlers";
|
|
3
|
+
import type { EscapeHatchDeclaration, EscapeHatchReporter, EscapeHatchTarget, HandlerDb } from "@cosmicdrift/kumiko-types/handlers";
|
|
4
|
+
import type { Meter, Tracer } from "@cosmicdrift/kumiko-types/observability";
|
|
4
5
|
import type { SchemaTable } from "@cosmicdrift/kumiko-types/schema-table-types";
|
|
5
|
-
import { type ExecutorManagedTenantDb, type TenantDb, type
|
|
6
|
+
import { type ExecutorManagedTenantDb, type TenantDb, type UncheckedSystemDb } from "@cosmicdrift/kumiko-types/tenant-db-types";
|
|
6
7
|
import { asRawClient } from "../db/query.ts";
|
|
7
8
|
import type { EntityDefinition } from "../engine/types/fields.ts";
|
|
8
9
|
import { type TenantId } from "../engine/types/identifiers.ts";
|
|
9
10
|
import type { Registry } from "../engine/types/index.ts";
|
|
10
|
-
import { type Meter, type Tracer } from "../observability/index.ts";
|
|
11
11
|
import type { DbRunner } from "./connection.ts";
|
|
12
12
|
import { tableNameOf } from "./table-introspection.ts";
|
|
13
13
|
type Table = SchemaTable;
|
|
@@ -19,10 +19,9 @@ export declare function unsafeRawForDeclaredStep(holder: TenantDb | UncheckedSys
|
|
|
19
19
|
export declare function withSystemDbUnsafeRawGrant<T extends object>(systemDb: T, grant: EscapeHatchDeclaration | undefined, deniedCallerLabel: string): T | UncheckedSystemDb;
|
|
20
20
|
export declare function createUncheckedSystemDb(db: TenantDb, dbOutsideTransaction?: TenantDb, report?: EscapeHatchReporter): UncheckedSystemDb;
|
|
21
21
|
export declare function createSystemDbView(db: TenantDb, dbOutsideTransaction?: TenantDb, report?: EscapeHatchReporter): UncheckedSystemDb;
|
|
22
|
-
export declare function scopeTenantDbToTenant(db: TenantDb, tenantId: TenantId): TenantDb;
|
|
23
22
|
export declare function castTenantRows<T>(rows: readonly Record<string, unknown>[]): readonly T[];
|
|
23
|
+
export { requestedTenantIds } from "./tenant-id-filter.ts";
|
|
24
24
|
export { tableNameOf };
|
|
25
|
-
export declare function requestedTenantIds(requested: unknown): readonly unknown[] | undefined;
|
|
26
25
|
export declare function hasTenantColumn(table: Table | EntityTableMeta): boolean;
|
|
27
26
|
export type TenantDbGrants = {
|
|
28
27
|
readonly globalWrites?: EscapeHatchDeclaration;
|
|
@@ -40,26 +39,32 @@ export declare function acknowledgeConventionCrossTenant(tenantDb: TenantDb, rea
|
|
|
40
39
|
}): TenantDb;
|
|
41
40
|
export declare function reportConventionCrossTenantUse(acknowledged: TenantDb, target?: EscapeHatchTarget): void;
|
|
42
41
|
export type OutsideTransactionContext = {
|
|
43
|
-
readonly
|
|
42
|
+
readonly db: Pick<HandlerDb, "outsideTransaction">;
|
|
44
43
|
};
|
|
45
|
-
export declare function hasOutsideTransactionDb(ctx: OutsideTransactionContext): ctx is
|
|
46
|
-
readonly
|
|
44
|
+
export declare function hasOutsideTransactionDb(ctx: OutsideTransactionContext): ctx is {
|
|
45
|
+
readonly db: {
|
|
46
|
+
readonly outsideTransaction: TenantDb;
|
|
47
|
+
};
|
|
47
48
|
};
|
|
48
49
|
export declare function requireOutsideTransactionDb(ctx: OutsideTransactionContext): TenantDb;
|
|
49
|
-
export declare function runInOwnTransaction<T>(tenantDb: TenantDb, fn: (txDb: TenantDb) => Promise<T>): Promise<T>;
|
|
50
50
|
export type CreateTenantDbOptions = {
|
|
51
51
|
readonly db: DbRunner;
|
|
52
52
|
readonly tenantId: TenantId;
|
|
53
|
-
readonly mode?: TenantDbMode;
|
|
54
53
|
readonly tracer?: Tracer;
|
|
55
54
|
readonly meter?: Meter;
|
|
56
55
|
readonly signal?: AbortSignal;
|
|
57
56
|
readonly grants?: TenantDbGrants;
|
|
58
|
-
}
|
|
57
|
+
} & ({
|
|
58
|
+
readonly mode?: "tenant";
|
|
59
|
+
readonly reason?: undefined;
|
|
60
|
+
} | {
|
|
61
|
+
readonly mode: "system";
|
|
62
|
+
readonly reason: string;
|
|
63
|
+
});
|
|
59
64
|
export declare function createTenantDb(options: CreateTenantDbOptions & {
|
|
60
65
|
readonly db: ExecutorManagedRunner;
|
|
61
66
|
}): ExecutorManagedTenantDb;
|
|
62
67
|
export declare function createTenantDb(options: CreateTenantDbOptions): TenantDb;
|
|
63
|
-
export declare function createTenantDataDestroyDb(options: Omit<CreateTenantDbOptions, "mode">): ExecutorManagedTenantDb;
|
|
68
|
+
export declare function createTenantDataDestroyDb(options: Omit<CreateTenantDbOptions, "mode" | "reason">): ExecutorManagedTenantDb;
|
|
64
69
|
export declare function createProjectingTenantDb(db: DbRunner, tenantId: TenantId, projectionRegistry: Registry | undefined): TenantDb;
|
|
65
70
|
export { asRawClient };
|
package/dist/db/tenant-db.js
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import { SYSTEM_SCOPE_CHECK_BRAND, } from "@cosmicdrift/kumiko-types/tenant-db-types";
|
|
2
|
-
import {
|
|
3
|
-
import { asEntityTableMeta, asRawClient, aggregateWhere as bunAggregateWhere, countWhere as bunCountWhere, deleteMany as bunDeleteMany, fetchOne as bunFetchOne, insertOne as bunInsertOne, selectMany as bunSelectMany, updateMany as bunUpdateMany, runInNewTransaction, } from "../db/query.js";
|
|
2
|
+
import { asEntityTableMeta, asRawClient, aggregateWhere as bunAggregateWhere, countWhere as bunCountWhere, deleteMany as bunDeleteMany, fetchOne as bunFetchOne, insertOne as bunInsertOne, selectMany as bunSelectMany, updateMany as bunUpdateMany, } from "../db/query.js";
|
|
4
3
|
import { assertReason } from "../engine/escape-hatch-reason.js";
|
|
5
4
|
import { SYSTEM_TENANT_ID } from "../engine/types/identifiers.js";
|
|
6
|
-
import { AccessDeniedError, InternalError
|
|
7
|
-
import {
|
|
5
|
+
import { AccessDeniedError, InternalError } from "../errors/index.js";
|
|
6
|
+
import { memberResolutionReadOnlyDenied } from "../errors/member-resolution.js";
|
|
7
|
+
import { fallbackEscapeHatchReporter } from "../observability/escape-hatch-report.js";
|
|
8
|
+
import { registerStandardMetrics } from "../observability/standard-metrics.js";
|
|
8
9
|
import { assertWritableTable, executorManagedWriteDenied, hasExecutorManagedWriteCapability, registerExecutorManagedWriteRunner, } from "./executor-managed-tables.js";
|
|
9
10
|
import { bindProjectionRegistry, projectionRegistryOf } from "./projection-registry-binding.js";
|
|
10
11
|
import { acquireNamespacedAdvisoryLock } from "./queries/advisory-lock.js";
|
|
11
12
|
import { queryDatabaseNow } from "./queries/database-now.js";
|
|
13
|
+
import { systemModeOptions, unsafeRawScopedRunner, unscopedRunnerOf, } from "./row-level-security-scope.js";
|
|
12
14
|
import { hasFieldProperty, tableNameOf } from "./table-introspection.js";
|
|
13
|
-
import { bindTenantDbRunner, denyTenantDbWrites, tenantDbRunner, tenantDbWritesDeniedError,
|
|
15
|
+
import { bindTenantDbRunner, denyTenantDbWrites, tenantDbRunner, tenantDbWritesDeniedError, } from "./tenant-db-runner.js";
|
|
16
|
+
import { registerScopedRebinder } from "./tenant-db-scoped-rebind.js";
|
|
17
|
+
import { createTenantDbStatementRunner } from "./tenant-db-statement-runner.js";
|
|
18
|
+
import { upsertTableDenied, upsertTenantRow } from "./tenant-db-upsert.js";
|
|
19
|
+
import { isForeignTenantId, requestedTenantIds, tenantIdsAfterExclusion, } from "./tenant-id-filter.js";
|
|
20
|
+
import { registerRunnerProxyTarget } from "./transaction-serialization.js";
|
|
14
21
|
export { SYSTEM_SCOPE_CHECK_BRAND, } from "@cosmicdrift/kumiko-types/tenant-db-types";
|
|
15
22
|
const declaredUnsafeRawRunners = new WeakMap();
|
|
16
23
|
// The CRUD executor writes through tenantDbRunner, not insertOne, so it asks the
|
|
@@ -36,7 +43,7 @@ function boundRunner(runner, gate, projectionRegistry, executorManagedWrites) {
|
|
|
36
43
|
if (typeof value !== "function")
|
|
37
44
|
return value;
|
|
38
45
|
if (prop === "reserve") {
|
|
39
|
-
//
|
|
46
|
+
// The driver hands back a raw connection; re-wrap it so the gate survives.
|
|
40
47
|
return async (...args) => {
|
|
41
48
|
const reserved = await Reflect.apply(value, target, args);
|
|
42
49
|
return boundRunner(reserved, gate, projectionRegistry, executorManagedWrites);
|
|
@@ -66,6 +73,7 @@ function boundRunner(runner, gate, projectionRegistry, executorManagedWrites) {
|
|
|
66
73
|
return value.bind(target);
|
|
67
74
|
},
|
|
68
75
|
});
|
|
76
|
+
registerRunnerProxyTarget(proxy, runner);
|
|
69
77
|
if (gate)
|
|
70
78
|
runnerPersonalDataGates.set(proxy, gate);
|
|
71
79
|
if (projectionRegistry)
|
|
@@ -165,9 +173,10 @@ function buildUncheckedSystemDb(db, dbOutsideTransaction, report, gate) {
|
|
|
165
173
|
`${gate.deniedCallerLabel} to allow unsafeRaw.`,
|
|
166
174
|
});
|
|
167
175
|
}
|
|
176
|
+
// A granted unsafeRaw is a declared, audited bypass of the TenantDb filter, so row-level security does not narrow it either.
|
|
168
177
|
// Engine-forwarded steps carry their own declared reason; a caller-supplied one never reaches the audit trail.
|
|
169
178
|
report("unsafe-raw", forwardedDeclaredStep ? reason : (gate?.grant?.reason ?? reason));
|
|
170
|
-
const runner = withoutExecutorManagedWrites(tenantDbRunner(db));
|
|
179
|
+
const runner = unsafeRawScopedRunner(withoutExecutorManagedWrites(tenantDbRunner(db)));
|
|
171
180
|
return boundRunner(runner, personalDataGates.get(db), projectionRegistryOf(db), false);
|
|
172
181
|
}
|
|
173
182
|
const uncheckedSystemDb = {
|
|
@@ -233,10 +242,9 @@ function buildUncheckedSystemDb(db, dbOutsideTransaction, report, gate) {
|
|
|
233
242
|
// own tenantId, never a foreign one.
|
|
234
243
|
//
|
|
235
244
|
// dbOutsideTransaction is optional so every existing single-arg call site
|
|
236
|
-
// (jobs/job-runner.ts, tests) keeps compiling — those callers have no
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
// is reachable through `ctx.systemDb`.
|
|
245
|
+
// (jobs/job-runner.ts, tests) keeps compiling — those callers have no outside-tx
|
|
246
|
+
// source to hand in. Only buildHandlerContext passes it, which is also the only
|
|
247
|
+
// place `.outsideTransaction` is reachable through `ctx.systemDb`.
|
|
240
248
|
//
|
|
241
249
|
// Ungated here (the handler's own systemScope() is the grant); a hook's own
|
|
242
250
|
// escapeHatch is layered on afterwards via withSystemDbUnsafeRawGrant. Public
|
|
@@ -251,55 +259,16 @@ export function createUncheckedSystemDb(db, dbOutsideTransaction, report = fallb
|
|
|
251
259
|
export function createSystemDbView(db, dbOutsideTransaction, report = fallbackEscapeHatchReporter(db.tenantId)) {
|
|
252
260
|
return buildUncheckedSystemDb(db, dbOutsideTransaction, report, { kind: "source-tenant-db" });
|
|
253
261
|
}
|
|
254
|
-
// Narrows a (possibly "system"-mode) TenantDb to a "tenant"-mode handle for one tenant on the
|
|
255
|
-
// same connection/transaction. For code that must run caller-supplied callbacks (e.g. config
|
|
256
|
-
// validators) without handing them a cross-tenant view.
|
|
257
|
-
export function scopeTenantDbToTenant(db, tenantId) {
|
|
258
|
-
const writesDeniedReason = tenantDbWritesDeniedReason(db);
|
|
259
|
-
return createTenantDb({
|
|
260
|
-
db: tenantDbRunner(db),
|
|
261
|
-
tenantId,
|
|
262
|
-
mode: "tenant",
|
|
263
|
-
...(writesDeniedReason !== undefined && { grants: { writesDeniedReason } }),
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
262
|
// @cast-boundary tenant-db-row
|
|
267
263
|
export function castTenantRows(rows) {
|
|
268
264
|
return rows;
|
|
269
265
|
}
|
|
266
|
+
export { requestedTenantIds } from "./tenant-id-filter.js";
|
|
270
267
|
export { tableNameOf };
|
|
271
268
|
// A grant is only real when its reason is non-empty — `{ reason: "" }` must not silently unlock it.
|
|
272
269
|
function hasGrant(decl) {
|
|
273
270
|
return decl !== undefined && decl.reason.trim().length > 0;
|
|
274
271
|
}
|
|
275
|
-
// Tenant ids left over when the caller excludes some via ne/notIn; undefined for other operator forms.
|
|
276
|
-
function tenantIdsAfterExclusion(requested, allowed) {
|
|
277
|
-
if (!isPlainObject(requested))
|
|
278
|
-
return undefined;
|
|
279
|
-
if ("ne" in requested)
|
|
280
|
-
return allowed.filter((t) => t !== requested["ne"]);
|
|
281
|
-
if (Array.isArray(requested["notIn"])) {
|
|
282
|
-
const excluded = requested["notIn"];
|
|
283
|
-
return allowed.filter((t) => !excluded.includes(t));
|
|
284
|
-
}
|
|
285
|
-
return undefined;
|
|
286
|
-
}
|
|
287
|
-
// Tenant ids a where.tenantId filter asks for; undefined for operator forms
|
|
288
|
-
// that cannot narrow to a tenant list (ne, like, ...).
|
|
289
|
-
export function requestedTenantIds(requested) {
|
|
290
|
-
if (Array.isArray(requested))
|
|
291
|
-
return requested;
|
|
292
|
-
if (!isPlainObject(requested))
|
|
293
|
-
return [requested];
|
|
294
|
-
if (Array.isArray(requested["in"]))
|
|
295
|
-
return requested["in"];
|
|
296
|
-
if ("eq" in requested)
|
|
297
|
-
return [requested["eq"]];
|
|
298
|
-
return undefined;
|
|
299
|
-
}
|
|
300
|
-
function isForeignTenantId(tenantIdValue) {
|
|
301
|
-
return tenantIdValue !== undefined && tenantIdValue !== SYSTEM_TENANT_ID;
|
|
302
|
-
}
|
|
303
272
|
// Checks the canonical EntityTableMeta (branded EntityTable's KUMIKO_META_SYMBOL
|
|
304
273
|
// or a plain deriveEntityTableMeta/defineUnmanagedTable result), not a direct
|
|
305
274
|
// `table.tenantId` property read — the latter only exists on branded EntityTables
|
|
@@ -346,28 +315,16 @@ export function reportConventionCrossTenantUse(acknowledged, target) {
|
|
|
346
315
|
}
|
|
347
316
|
reporter(target);
|
|
348
317
|
}
|
|
349
|
-
const ownTransactionRebinders = new WeakMap();
|
|
350
318
|
export function hasOutsideTransactionDb(ctx) {
|
|
351
|
-
return ctx.
|
|
319
|
+
return ctx.db.outsideTransaction !== undefined;
|
|
352
320
|
}
|
|
353
321
|
export function requireOutsideTransactionDb(ctx) {
|
|
354
322
|
if (!hasOutsideTransactionDb(ctx)) {
|
|
355
323
|
throw new InternalError({
|
|
356
|
-
message: "ctx.
|
|
324
|
+
message: "ctx.db.outsideTransaction is not available in this dispatch phase (member-resolution, before-transaction denial) or metering tenant mode.",
|
|
357
325
|
});
|
|
358
326
|
}
|
|
359
|
-
return ctx.
|
|
360
|
-
}
|
|
361
|
-
export async function runInOwnTransaction(tenantDb, fn) {
|
|
362
|
-
const rebind = ownTransactionRebinders.get(tenantDb);
|
|
363
|
-
if (!rebind) {
|
|
364
|
-
throw new InternalError({
|
|
365
|
-
message: "runInOwnTransaction received a TenantDb without a transaction rebinder: either it was not " +
|
|
366
|
-
"built by createTenantDb, or it is the fail-closed ctx.db/ctx.dbOutsideTransaction of an " +
|
|
367
|
-
"r.systemScope() handler (use ctx.systemDb.outsideTransaction there).",
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
return rebind(fn);
|
|
327
|
+
return ctx.db.outsideTransaction;
|
|
371
328
|
}
|
|
372
329
|
export function createTenantDb(options) {
|
|
373
330
|
return buildTenantDb(options);
|
|
@@ -379,7 +336,9 @@ export function createTenantDataDestroyDb(options) {
|
|
|
379
336
|
// Untyped JS callers or an `as` cast can still hand in undefined; a scoped handle
|
|
380
337
|
// without a tenant would bind `tenant_id = NULL` instead of failing loudly.
|
|
381
338
|
function buildTenantDb(options) {
|
|
382
|
-
const { db, tenantId, mode = "tenant", tracer, meter, signal, grants } = options;
|
|
339
|
+
const { db: givenDb, tenantId, mode = "tenant", reason: systemReason, tracer, meter, signal, grants, } = options;
|
|
340
|
+
const modeOptions = mode === "system" ? systemModeOptions(systemReason) : { mode: "tenant" };
|
|
341
|
+
const db = mode === "system" ? givenDb : unscopedRunnerOf(givenDb);
|
|
383
342
|
if (tenantId === undefined) {
|
|
384
343
|
throw new InternalError({ message: "createTenantDb: tenantId is required" });
|
|
385
344
|
}
|
|
@@ -392,47 +351,13 @@ function buildTenantDb(options) {
|
|
|
392
351
|
const rebindGrants = projectionRegistry && grants?.projectionRegistry === undefined
|
|
393
352
|
? { ...grants, projectionRegistry }
|
|
394
353
|
: grants;
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
if (meter) {
|
|
403
|
-
emitDbQuery(meter, { operation, table: tableName }, (performance.now() - start) / 1000);
|
|
404
|
-
}
|
|
405
|
-
};
|
|
406
|
-
if (!tracer) {
|
|
407
|
-
return (async () => {
|
|
408
|
-
try {
|
|
409
|
-
return await runner();
|
|
410
|
-
}
|
|
411
|
-
finally {
|
|
412
|
-
emitMetric();
|
|
413
|
-
}
|
|
414
|
-
})();
|
|
415
|
-
}
|
|
416
|
-
return tracer.withSpan("db.query", {
|
|
417
|
-
kind: "client",
|
|
418
|
-
attributes: {
|
|
419
|
-
"db.system": "postgresql",
|
|
420
|
-
"db.operation": operation,
|
|
421
|
-
"db.table": tableName,
|
|
422
|
-
},
|
|
423
|
-
}, async (span) => {
|
|
424
|
-
try {
|
|
425
|
-
const result = await runner();
|
|
426
|
-
if (Array.isArray(result)) {
|
|
427
|
-
span.setAttribute("db.row_count", result.length);
|
|
428
|
-
}
|
|
429
|
-
return result;
|
|
430
|
-
}
|
|
431
|
-
finally {
|
|
432
|
-
emitMetric();
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
}
|
|
354
|
+
const withDbSpan = createTenantDbStatementRunner({
|
|
355
|
+
tracer,
|
|
356
|
+
meter,
|
|
357
|
+
signal,
|
|
358
|
+
...modeOptions,
|
|
359
|
+
tenantId,
|
|
360
|
+
});
|
|
436
361
|
// Reads see own-tenant rows + reference data (tenantId === SYSTEM_TENANT_ID).
|
|
437
362
|
// Writes never touch reference rows — those are system-mode only.
|
|
438
363
|
// A caller-supplied `where.tenantId` may only NARROW the enforced scope
|
|
@@ -612,7 +537,7 @@ function buildTenantDb(options) {
|
|
|
612
537
|
}
|
|
613
538
|
// Engine-forwarded steps carry their own declared reason; otherwise the grant's reason is audited.
|
|
614
539
|
report("unsafe-raw", declaredStepReason ?? grants?.unsafeRaw?.reason ?? "");
|
|
615
|
-
return boundRunner(withoutExecutorManagedWrites(db), personalDataGate, projectionRegistry, false);
|
|
540
|
+
return boundRunner(unsafeRawScopedRunner(withoutExecutorManagedWrites(db)), personalDataGate, projectionRegistry, false);
|
|
616
541
|
}
|
|
617
542
|
const tenantDb = {
|
|
618
543
|
tenantId,
|
|
@@ -662,6 +587,15 @@ function buildTenantDb(options) {
|
|
|
662
587
|
const data = insertValues(table, values);
|
|
663
588
|
return withDbSpan("insert", table, async () => insertRow(table, data));
|
|
664
589
|
},
|
|
590
|
+
upsert(table, values, options) {
|
|
591
|
+
const denied = writesDenied("ctx.db.upsert") ??
|
|
592
|
+
upsertTableDenied(table, mode) ??
|
|
593
|
+
personalDataDenied(table, [...Object.keys(values), ...Object.keys(options.update ?? {})]);
|
|
594
|
+
if (denied)
|
|
595
|
+
return Promise.reject(denied);
|
|
596
|
+
const data = insertValues(table, values);
|
|
597
|
+
return withDbSpan("insert", table, () => upsertTenantRow(db, table, data, options));
|
|
598
|
+
},
|
|
665
599
|
updateMany(table, set, where) {
|
|
666
600
|
const writeDenied = writesDenied("ctx.db.updateMany");
|
|
667
601
|
if (writeDenied)
|
|
@@ -702,7 +636,7 @@ function buildTenantDb(options) {
|
|
|
702
636
|
unsafeRawRebinders.set(tenantDb, (grant) => createTenantDb({
|
|
703
637
|
db,
|
|
704
638
|
tenantId,
|
|
705
|
-
|
|
639
|
+
...modeOptions,
|
|
706
640
|
tracer,
|
|
707
641
|
meter,
|
|
708
642
|
signal,
|
|
@@ -720,6 +654,7 @@ function buildTenantDb(options) {
|
|
|
720
654
|
db,
|
|
721
655
|
tenantId,
|
|
722
656
|
mode: "system",
|
|
657
|
+
reason,
|
|
723
658
|
tracer,
|
|
724
659
|
meter,
|
|
725
660
|
signal,
|
|
@@ -728,18 +663,22 @@ function buildTenantDb(options) {
|
|
|
728
663
|
crossTenantUseReporters.set(acknowledged, (target) => report("acknowledge-cross-tenant", reason, target));
|
|
729
664
|
return acknowledged;
|
|
730
665
|
});
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
666
|
+
// Carries a runner-bound gate (grants.personalDataGate unset, resolved via the fallback above) forward explicitly, since a fresh tx-handle isn't itself registered in runnerPersonalDataGates.
|
|
667
|
+
const scopedGrants = personalDataGate ? { ...rebindGrants, personalDataGate } : rebindGrants;
|
|
668
|
+
registerScopedRebinder(tenantDb, {
|
|
669
|
+
memberReadOnly: grants?.memberReadOnly === true,
|
|
670
|
+
rebind: (runner) => createTenantDb({
|
|
671
|
+
db: runner,
|
|
672
|
+
tenantId,
|
|
673
|
+
...modeOptions,
|
|
674
|
+
tracer,
|
|
675
|
+
meter,
|
|
676
|
+
signal,
|
|
677
|
+
grants: scopedGrants,
|
|
678
|
+
}),
|
|
741
679
|
});
|
|
742
|
-
|
|
680
|
+
// Framework code reading through the raw runner (executor list/detail) must follow the db's own mode, not the ambient scope.
|
|
681
|
+
bindTenantDbRunner(tenantDb, db, modeOptions, tenantId);
|
|
743
682
|
if (grants?.writesDeniedReason !== undefined) {
|
|
744
683
|
denyTenantDbWrites(tenantDb, grants.writesDeniedReason);
|
|
745
684
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function tenantIdsAfterExclusion(requested: unknown, allowed: readonly unknown[]): readonly unknown[] | undefined;
|
|
2
|
+
export declare function requestedTenantIds(requested: unknown): readonly unknown[] | undefined;
|
|
3
|
+
export declare function isForeignTenantId(tenantIdValue: unknown): boolean;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
2
|
+
import { SYSTEM_TENANT_ID } from "../engine/types/identifiers.js";
|
|
3
|
+
// Tenant ids left over when the caller excludes some via ne/notIn; undefined for other operator forms.
|
|
4
|
+
export function tenantIdsAfterExclusion(requested, allowed) {
|
|
5
|
+
if (!isPlainObject(requested))
|
|
6
|
+
return undefined;
|
|
7
|
+
if ("ne" in requested)
|
|
8
|
+
return allowed.filter((t) => t !== requested["ne"]);
|
|
9
|
+
if (Array.isArray(requested["notIn"])) {
|
|
10
|
+
const excluded = requested["notIn"];
|
|
11
|
+
return allowed.filter((t) => !excluded.includes(t));
|
|
12
|
+
}
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
// Tenant ids a where.tenantId filter asks for; undefined for operator forms
|
|
16
|
+
// that cannot narrow to a tenant list (ne, like, ...).
|
|
17
|
+
export function requestedTenantIds(requested) {
|
|
18
|
+
if (Array.isArray(requested))
|
|
19
|
+
return requested;
|
|
20
|
+
if (!isPlainObject(requested))
|
|
21
|
+
return [requested];
|
|
22
|
+
if (Array.isArray(requested["in"]))
|
|
23
|
+
return requested["in"];
|
|
24
|
+
if ("eq" in requested)
|
|
25
|
+
return [requested["eq"]];
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
export function isForeignTenantId(tenantIdValue) {
|
|
29
|
+
return tenantIdValue !== undefined && tenantIdValue !== SYSTEM_TENANT_ID;
|
|
30
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type HandleKey = object;
|
|
2
|
+
export declare function registerRunnerProxyTarget(proxy: HandleKey, target: HandleKey): void;
|
|
3
|
+
export declare function openSerializedSavepoint<T>(transactionHandle: HandleKey & {
|
|
4
|
+
savepoint: <R>(fn: (sp: unknown) => Promise<R>) => Promise<R>;
|
|
5
|
+
}, fn: (sp: unknown) => Promise<T>): Promise<T>;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
const runnerProxyTargets = new WeakMap();
|
|
3
|
+
const savepointTransactionKeys = new WeakMap();
|
|
4
|
+
const rootChains = new WeakMap();
|
|
5
|
+
const nestedChainsInScope = new AsyncLocalStorage();
|
|
6
|
+
function isHandleKey(value) {
|
|
7
|
+
return typeof value === "function" || (typeof value === "object" && value !== null);
|
|
8
|
+
}
|
|
9
|
+
export function registerRunnerProxyTarget(proxy, target) {
|
|
10
|
+
runnerProxyTargets.set(proxy, target);
|
|
11
|
+
}
|
|
12
|
+
function driverHandleOf(handle) {
|
|
13
|
+
let current = handle;
|
|
14
|
+
let target = runnerProxyTargets.get(current);
|
|
15
|
+
while (target) {
|
|
16
|
+
current = target;
|
|
17
|
+
target = runnerProxyTargets.get(current);
|
|
18
|
+
}
|
|
19
|
+
return current;
|
|
20
|
+
}
|
|
21
|
+
function transactionKeyOf(handle) {
|
|
22
|
+
const driverHandle = driverHandleOf(handle);
|
|
23
|
+
return savepointTransactionKeys.get(driverHandle) ?? driverHandle;
|
|
24
|
+
}
|
|
25
|
+
function chainFor(transactionKey) {
|
|
26
|
+
const nested = nestedChainsInScope.getStore()?.get(transactionKey);
|
|
27
|
+
if (nested)
|
|
28
|
+
return nested;
|
|
29
|
+
const root = rootChains.get(transactionKey);
|
|
30
|
+
if (root)
|
|
31
|
+
return root;
|
|
32
|
+
const created = { tail: Promise.resolve() };
|
|
33
|
+
rootChains.set(transactionKey, created);
|
|
34
|
+
return created;
|
|
35
|
+
}
|
|
36
|
+
async function runOnChain(transactionKey, work) {
|
|
37
|
+
const chain = chainFor(transactionKey);
|
|
38
|
+
const previous = chain.tail;
|
|
39
|
+
let release = () => { };
|
|
40
|
+
chain.tail = new Promise((resolve) => {
|
|
41
|
+
release = resolve;
|
|
42
|
+
});
|
|
43
|
+
await previous;
|
|
44
|
+
try {
|
|
45
|
+
const scope = new Map(nestedChainsInScope.getStore());
|
|
46
|
+
scope.set(transactionKey, { tail: Promise.resolve() });
|
|
47
|
+
return await nestedChainsInScope.run(scope, work);
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
release();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function openSerializedSavepoint(transactionHandle, fn) {
|
|
54
|
+
const transactionKey = transactionKeyOf(transactionHandle);
|
|
55
|
+
return runOnChain(transactionKey, () => transactionHandle.savepoint((sp) => {
|
|
56
|
+
if (isHandleKey(sp))
|
|
57
|
+
savepointTransactionKeys.set(driverHandleOf(sp), transactionKey);
|
|
58
|
+
return fn(sp);
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
@@ -72,7 +72,7 @@ export async function deleteStoredFileAndDerivatives(originalKey, store, errorCo
|
|
|
72
72
|
}
|
|
73
73
|
return failedKeys;
|
|
74
74
|
}
|
|
75
|
-
// deleteStoredFileAndDerivatives through ctx.files for a key read from a tenant-filtered fileRef
|
|
75
|
+
// deleteStoredFileAndDerivatives through ctx.services.files for a key read from a tenant-filtered fileRef
|
|
76
76
|
// row. After a tenant handover the key keeps the previous tenant's prefix, which ref() and list()
|
|
77
77
|
// reject; every key the helper deletes is the original or one of its derivatives.
|
|
78
78
|
export function deleteFileRefBytes(files, row, errorContext) {
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
export
|
|
2
|
-
export { deleteFileRefBytes, deleteStoredFileAndDerivatives, listDerivativeKeys, } from "./delete-stored-file.ts";
|
|
3
|
-
export type { DerivativesContextDeps } from "./derivatives-context.ts";
|
|
4
|
-
export { createDerivativesContext, resolveRenderer } from "./derivatives-context.ts";
|
|
1
|
+
export { deleteFileRefBytes, deleteStoredFileAndDerivatives, } from "./delete-stored-file.ts";
|
|
5
2
|
export { resolveFieldVariant } from "./field-variants.ts";
|
|
6
|
-
export {
|
|
3
|
+
export { derivativeListPrefix, parseDerivativeKey, variantSuffix, } from "./variant-key.ts";
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export { deleteFileRefBytes, deleteStoredFileAndDerivatives,
|
|
2
|
-
export { createDerivativesContext, resolveRenderer } from "./derivatives-context.js";
|
|
1
|
+
export { deleteFileRefBytes, deleteStoredFileAndDerivatives, } from "./delete-stored-file.js";
|
|
3
2
|
export { resolveFieldVariant } from "./field-variants.js";
|
|
4
|
-
export {
|
|
3
|
+
export { derivativeListPrefix, parseDerivativeKey, variantSuffix, } from "./variant-key.js";
|