@cosmicdrift/kumiko-framework 0.389.0 → 0.390.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -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/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 +3 -3
- package/agents/examples/managed-config.md +4 -4
- package/agents/examples/managed-pages.md +1 -1
- 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 +2 -2
- 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 +4 -4
- package/agents/features.md +1 -1
- package/dist/api/auth-middleware.js +1 -1
- package/dist/api/auth-routes-invite.js +2 -1
- package/dist/api/entity-read-access.d.ts +3 -1
- package/dist/api/export/export-columns.d.ts +3 -1
- package/dist/api/export/export-route.js +3 -2
- package/dist/api/export/export-screen.d.ts +2 -1
- package/dist/api/extra-route.d.ts +3 -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/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.js +7 -3
- 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 +2 -1
- package/dist/api/server-middleware.js +3 -1
- package/dist/api/server-routes.d.ts +2 -1
- package/dist/api/server-wiring.js +6 -4
- package/dist/api/server.d.ts +2 -1
- package/dist/api/sse-route.d.ts +2 -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 +10 -10
- 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.js +6 -1
- 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/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 +3 -1
- package/dist/db/connection.js +15 -5
- 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-value-preparation.js +23 -2
- 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-filter.d.ts +1 -1
- 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.js +8 -7
- package/dist/db/event-store-executor-write-steps.d.ts +3 -2
- package/dist/db/event-store-executor-write.js +7 -4
- package/dist/db/event-store-executor.d.ts +1 -1
- package/dist/db/executor-managed-tables.d.ts +7 -1
- package/dist/db/executor-managed-tables.js +11 -2
- 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 +4 -0
- 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 +11 -0
- 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 +9 -25
- package/dist/db/queries/test-stack.d.ts +4 -1
- package/dist/db/queries/test-stack.js +84 -2
- package/dist/db/query.js +11 -7
- package/dist/db/reference-data.d.ts +1 -1
- package/dist/db/reference-data.js +6 -1
- 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 +6 -0
- package/dist/db/row-level-security-boot-check.js +86 -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/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.d.ts +17 -12
- package/dist/db/tenant-db.js +49 -120
- 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/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 +1 -1
- package/dist/engine/boot-validator/list-table-aggregate-options.d.ts +1 -1
- package/dist/engine/boot-validator/list-table-aggregate-query.d.ts +2 -1
- 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/query-refs.d.ts +2 -1
- package/dist/engine/boot-validator/screens-forms.d.ts +2 -1
- package/dist/engine/boot-validator/screens-kind-shared.d.ts +3 -1
- 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/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 +1 -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 +8 -1
- 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.js +1 -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 +2 -1
- package/dist/engine/handler-field-references.d.ts +0 -4
- package/dist/engine/index.d.ts +57 -54
- package/dist/engine/index.js +22 -27
- 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 +0 -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/template-variables.d.ts +2 -1
- package/dist/engine/transfer-adjacency.d.ts +1 -1
- package/dist/engine/types/index.d.ts +8 -31
- package/dist/engine/types/index.js +2 -10
- package/dist/engine/validation.d.ts +2 -1
- package/dist/entrypoint/index.d.ts +7 -35
- package/dist/entrypoint/index.js +7 -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.js +41 -34
- 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/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 +3 -3
- package/dist/files/create-file-ref.d.ts +3 -2
- package/dist/files/create-file-ref.js +2 -2
- 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 +1 -1
- package/dist/files/file-routes.js +12 -2
- 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 +5 -4
- package/dist/files/provider-resolver.js +4 -4
- 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/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/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 +9 -5
- 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-types.d.ts +2 -1
- package/dist/jobs/job-runner.js +19 -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/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/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 +31 -18
- 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 -198
- 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 +4 -2
- package/dist/pipeline/event-dispatcher.d.ts +2 -2
- package/dist/pipeline/event-dispatcher.js +7 -3
- 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 +13 -7
- package/dist/pipeline/handler-context-dispatch.js +9 -8
- package/dist/pipeline/handler-context-events.d.ts +22 -36
- package/dist/pipeline/handler-context-events.js +36 -31
- 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 +3 -1
- package/dist/pipeline/system-hooks.js +6 -4
- 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/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 +12 -2
- package/dist/schema-cli/shared.js +24 -2
- package/dist/schema-cli/status-command.js +8 -2
- package/dist/schema-cli/validate-command.js +48 -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 +93 -87
- 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 -2
- package/dist/upgrade-changelog.js +12 -8
- package/dist/upgrade-cli-internals.d.ts +8 -0
- package/dist/upgrade-cli-internals.js +63 -0
- package/dist/upgrade-cli.d.ts +2 -9
- package/dist/upgrade-cli.js +28 -70
- 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 +23 -19
- package/src/changes.json +236 -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 -11
- package/dist/engine/feature-ast/extractors/round1.js +0 -87
- 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 -485
- package/dist/engine/feature-ast/patch.d.ts +0 -212
- package/dist/engine/feature-ast/patch.js +0 -611
- 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 -1238
- package/dist/engine/feature-ast/pattern-change-schema.js +0 -1072
- package/dist/engine/feature-ast/pattern-id-matchers.d.ts +0 -7
- package/dist/engine/feature-ast/pattern-id-matchers.js +0 -211
- package/dist/engine/feature-ast/pattern-id-schema.d.ts +0 -251
- package/dist/engine/feature-ast/pattern-id-schema.js +0 -166
- package/dist/engine/feature-ast/patterns.d.ts +0 -322
- package/dist/engine/feature-ast/patterns.js +0 -107
- package/dist/engine/feature-ast/render.d.ts +0 -47
- package/dist/engine/feature-ast/render.js +0 -666
- 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 -94
- 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 -21
- package/dist/engine/pattern-library/static-schemas.js +0 -487
- package/dist/engine/pattern-library/types.d.ts +0 -158
- package/dist/engine/pattern-library/types.js +0 -36
- 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
|
@@ -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
|
}
|
|
@@ -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,4 +1,5 @@
|
|
|
1
1
|
import type { DbConnection, PgClient, PgListenClient } from "@cosmicdrift/kumiko-types/db-connection";
|
|
2
|
+
import { type RowLevelSecurityMode } from "./render-rls-ddl.ts";
|
|
2
3
|
export type * from "@cosmicdrift/kumiko-types/db-connection";
|
|
3
4
|
type BunSqlOptions = Bun.SQL.PostgresOrMySQLOptions;
|
|
4
5
|
export type DbConnectionOptions = {
|
|
@@ -13,6 +14,7 @@ export type DbConnectionOptions = {
|
|
|
13
14
|
};
|
|
14
15
|
export type CreateDbConnectionOptions = DbConnectionOptions & {
|
|
15
16
|
readonly url: string;
|
|
17
|
+
readonly rowLevelSecurity?: RowLevelSecurityMode;
|
|
16
18
|
};
|
|
17
19
|
export type DbConnectionHandle = {
|
|
18
20
|
db: DbConnection;
|
|
@@ -26,7 +28,7 @@ export type UnboundedSiblingPool = {
|
|
|
26
28
|
readonly close: (timeoutSeconds: number) => Promise<void>;
|
|
27
29
|
};
|
|
28
30
|
export declare function openUnboundedSiblingPool(db: unknown): UnboundedSiblingPool | undefined;
|
|
29
|
-
export declare function createDbConnection({ url, ...options }: CreateDbConnectionOptions): DbConnectionHandle;
|
|
31
|
+
export declare function createDbConnection({ url, rowLevelSecurity, ...options }: CreateDbConnectionOptions): DbConnectionHandle;
|
|
30
32
|
export declare function dbConnectionOptionsFromEnv(env?: Readonly<Record<string, string | undefined>>): DbConnectionOptions;
|
|
31
33
|
export declare const DEFAULT_SERVER_STATEMENT_TIMEOUT_MS = 60000;
|
|
32
34
|
export declare const DEFAULT_SERVER_IDLE_IN_TRANSACTION_TIMEOUT_MS = 300000;
|
package/dist/db/connection.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
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";
|
|
2
4
|
// A DB outage can leave an in-flight query referenced on a dead connection,
|
|
3
5
|
// so a plain close() (no timeout = wait for queries to finish) hangs forever —
|
|
4
6
|
// close() must always bound how long shutdown waits.
|
|
@@ -20,7 +22,7 @@ function verifiedTls(ssl) {
|
|
|
20
22
|
}
|
|
21
23
|
return ssl;
|
|
22
24
|
}
|
|
23
|
-
export function createDbConnection({ url, ...options }) {
|
|
25
|
+
export function createDbConnection({ url, rowLevelSecurity = DEFAULT_ROW_LEVEL_SECURITY_MODE, ...options }) {
|
|
24
26
|
const sqlOptions = {};
|
|
25
27
|
if (options.maxConnections !== undefined)
|
|
26
28
|
sqlOptions.max = options.maxConnections;
|
|
@@ -45,11 +47,18 @@ export function createDbConnection({ url, ...options }) {
|
|
|
45
47
|
if (Object.keys(serverParameters).length > 0)
|
|
46
48
|
sqlOptions.connection = serverParameters;
|
|
47
49
|
// Bun.SQL opens all `max` connections on the first query, so a pool always costs `max` backends.
|
|
48
|
-
const
|
|
50
|
+
const rawClient = new Bun.SQL(url, sqlOptions);
|
|
51
|
+
const enforced = rowLevelSecurity === "on";
|
|
52
|
+
const client = enforced ? enforceRowLevelSecurity(rawClient) : rawClient;
|
|
49
53
|
// A run that never settles (a caller gave up on it via its own timeout) never reaches the
|
|
50
54
|
// sibling's own close; close() ends it so shutdown and DROP DATABASE are not blocked by it.
|
|
51
55
|
const openSiblings = new Set();
|
|
52
|
-
|
|
56
|
+
const registerFactory = (factory) => {
|
|
57
|
+
unboundedSiblingFactories.set(rawClient, factory);
|
|
58
|
+
if (client !== rawClient)
|
|
59
|
+
unboundedSiblingFactories.set(client, factory);
|
|
60
|
+
};
|
|
61
|
+
registerFactory(() => {
|
|
53
62
|
// Two connections are enough for lock-in-tx plus DDL; the cap keeps parallel rebuilds from exhausting max_connections.
|
|
54
63
|
const sibling = new Bun.SQL(url, {
|
|
55
64
|
...sqlOptions,
|
|
@@ -58,7 +67,7 @@ export function createDbConnection({ url, ...options }) {
|
|
|
58
67
|
});
|
|
59
68
|
openSiblings.add(sibling);
|
|
60
69
|
return {
|
|
61
|
-
client: sibling,
|
|
70
|
+
client: enforced ? enforceRowLevelSecurity(sibling) : sibling,
|
|
62
71
|
close: async (timeoutSeconds) => {
|
|
63
72
|
openSiblings.delete(sibling);
|
|
64
73
|
await sibling.close({ timeout: timeoutSeconds });
|
|
@@ -71,8 +80,9 @@ export function createDbConnection({ url, ...options }) {
|
|
|
71
80
|
listenClient: client,
|
|
72
81
|
close: async () => {
|
|
73
82
|
const timeout = options.closeTimeoutSeconds ?? DEFAULT_DB_CLOSE_TIMEOUT_SECONDS;
|
|
83
|
+
unboundedSiblingFactories.delete(rawClient);
|
|
74
84
|
unboundedSiblingFactories.delete(client);
|
|
75
|
-
const pools = [
|
|
85
|
+
const pools = [rawClient, ...openSiblings];
|
|
76
86
|
openSiblings.clear();
|
|
77
87
|
const results = await Promise.allSettled(pools.map((pool) => pool.close({ timeout })));
|
|
78
88
|
const failure = results.find((result) => result.status === "rejected");
|
|
@@ -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);
|
|
@@ -65,9 +65,30 @@ const INTEGER_PG_TYPES = new Set([
|
|
|
65
65
|
export function instantToTimestamptzParam(instant) {
|
|
66
66
|
return Temporal.Instant.fromEpochMilliseconds(instant.epochMilliseconds).toString();
|
|
67
67
|
}
|
|
68
|
+
function instantOfIsoString(value) {
|
|
69
|
+
try {
|
|
70
|
+
return Temporal.Instant.from(value);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// skip: not an ISO instant, Postgres parses (or rejects) the text itself
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Event payloads carry instants as ISO strings with microseconds; a projection rebuild binds them
|
|
78
|
+
// as-is, so without the same truncation the replayed row differs from the live one.
|
|
79
|
+
function timestamptzInstantOf(value) {
|
|
80
|
+
if (isTemporalInstant(value))
|
|
81
|
+
return value;
|
|
82
|
+
if (typeof value === "string")
|
|
83
|
+
return instantOfIsoString(value);
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
68
86
|
function prepareDriverNeutralScalar(value, pgType) {
|
|
69
|
-
if (
|
|
70
|
-
|
|
87
|
+
if (pgType === "timestamptz" || pgType === "timestamptz(3)") {
|
|
88
|
+
const instant = timestamptzInstantOf(value);
|
|
89
|
+
if (instant !== undefined) {
|
|
90
|
+
return { kind: "param", sql: "", bound: instantToTimestamptzParam(instant) };
|
|
91
|
+
}
|
|
71
92
|
}
|
|
72
93
|
// Sent as text, so Postgres rejects 3.5 for an integer column instead of rounding it.
|
|
73
94
|
if (typeof value === "number" && pgType !== undefined && INTEGER_PG_TYPES.has(pgType)) {
|
package/dist/db/eagerload.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { EntityDefinition
|
|
1
|
+
import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
|
|
2
|
+
import type { SessionUser } from "../engine/types/index.ts";
|
|
2
3
|
import { type ParentVisibilityOption } from "./parent-ref-clause.ts";
|
|
3
4
|
import type { TenantDb } from "./tenant-db.ts";
|
|
4
5
|
export type EagerLoadEntityResolver = (entityName: string) => EntityDefinition | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EntityDefinition } from "
|
|
1
|
+
import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
|
|
2
2
|
import { type Logger } from "../logging/index.ts";
|
|
3
3
|
import type { EnvelopeCipher } from "../secrets/envelope-cipher.ts";
|
|
4
4
|
export declare function collectEncryptedFieldNames(entity: EntityDefinition): ReadonlySet<string>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Writable } from "@cosmicdrift/kumiko-types/executor-brand";
|
|
2
|
+
import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
|
|
2
3
|
import type { EntityTenancy, TenancyBrand } from "@cosmicdrift/kumiko-types/tenancy-brand";
|
|
3
|
-
import type { EntityDefinition } from "../engine/types/index.ts";
|
|
4
4
|
import type { BuildEntityTableMetaOptions, EntityTableMeta, UnmanagedTableInput } from "./entity-table-meta-types.ts";
|
|
5
5
|
export type { BuildEntityTableMetaOptions, ColumnMeta, CompositePrimaryKeyMeta, EntityTableMeta, IndexMeta, PgType, UnmanagedTableInput, } from "./entity-table-meta-types.ts";
|
|
6
6
|
export declare function resolveTableName(entityName: string, entity: EntityDefinition, featureName: string | undefined): string;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
|
|
2
|
+
import type { EntityId } from "@cosmicdrift/kumiko-types/identifiers";
|
|
1
3
|
import type { ErasedSubjectHandling } from "../crypto/pii-field-encryption.ts";
|
|
2
4
|
import type { WhereObject } from "../db/query.ts";
|
|
3
|
-
import type {
|
|
5
|
+
import type { SessionUser, TenantId } from "../engine/types/index.ts";
|
|
4
6
|
import { type WriteFailure } from "../errors/index.ts";
|
|
5
7
|
import { type EventMetadata } from "../event-store/index.ts";
|
|
6
8
|
import type { EntityCache } from "../pipeline/entity-cache.ts";
|
|
@@ -5,7 +5,8 @@ import { executeRawQueryRead } from "../db/queries/raw-sql.js";
|
|
|
5
5
|
import { shiftParams } from "../engine/ownership.js";
|
|
6
6
|
import { SYSTEM_TENANT_ID } from "../engine/types/identifiers.js";
|
|
7
7
|
import { AccessDeniedError, InternalError, subjectErasedConflict, UniqueViolationError, writeFailure, } from "../errors/index.js";
|
|
8
|
-
import { ArchivedStreamError
|
|
8
|
+
import { ArchivedStreamError } from "../event-store/errors.js";
|
|
9
|
+
import { isStreamArchived } from "../event-store/index.js";
|
|
9
10
|
import { assertUnreachable } from "../utils/index.js";
|
|
10
11
|
import { rehydrateCompoundTypes } from "./compound-types.js";
|
|
11
12
|
import { collectEncryptedFieldNames, decryptEntityFieldValues, encryptEntityFieldValues, logUndecryptableField, resolveEntityFieldEncryption, } from "./entity-field-encryption.js";
|
|
@@ -120,7 +121,7 @@ export function buildExecutorContext(table, entity, options) {
|
|
|
120
121
|
// old hardcoded user.tenantId). Single source of truth for the stream key.
|
|
121
122
|
const streamTenantFor = (user, requestedStreamTenantId) => entity.systemStream ? SYSTEM_TENANT_ID : (requestedStreamTenantId ?? user.tenantId);
|
|
122
123
|
// Only a system-mode db (r.systemScope() / acknowledged cross-tenant) or a tenant-mode db bound
|
|
123
|
-
// to exactly that stream tenant (ctx.
|
|
124
|
+
// to exactly that stream tenant (ctx.tenants.writeTarget) may address another tenant's stream, and only
|
|
124
125
|
// the tenant of the row it just loaded.
|
|
125
126
|
const streamTenantOverrideFailure = (db, row, requestedStreamTenantId) => {
|
|
126
127
|
if (requestedStreamTenantId === undefined)
|
|
@@ -293,7 +294,7 @@ export function buildExecutorContext(table, entity, options) {
|
|
|
293
294
|
return await decryptForRead(rehydrateCompoundTypes(row, entity));
|
|
294
295
|
}
|
|
295
296
|
// Archive guard for the CRUD write paths. Archived streams are read-only —
|
|
296
|
-
// ctx.
|
|
297
|
+
// ctx.events.append (append-event-core) already enforces this, but the
|
|
297
298
|
// executor appends directly via append() and getStreamVersion() ignores
|
|
298
299
|
// the archive flag, so without this check a PATCH/DELETE on an archived
|
|
299
300
|
// entity would silently land an event and break the read-only contract
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
|
|
1
2
|
import { type OwnershipClause } from "../engine/ownership.ts";
|
|
2
|
-
import type { EntityDefinition } from "../engine/types/index.ts";
|
|
3
3
|
import { type Table } from "./event-store-executor-context.ts";
|
|
4
4
|
export type ListWhere = {
|
|
5
5
|
readonly whereSql: string[];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { EntityDefinition
|
|
1
|
+
import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
|
|
2
|
+
import type { TenantId } from "../engine/types/index.ts";
|
|
2
3
|
import type { TableInfo } from "./table-info.ts";
|
|
3
4
|
export declare function withStreamVersion(row: Record<string, unknown>, streamVersion: number): Record<string, unknown>;
|
|
4
5
|
export declare function coerceAndRehydrateRow(raw: Record<string, unknown>, tableInfo: TableInfo, entity: EntityDefinition): Record<string, unknown>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
|
|
1
2
|
import { type OwnershipClause } from "../engine/ownership.ts";
|
|
2
|
-
import type {
|
|
3
|
+
import type { SessionUser, TenantId } from "../engine/types/index.ts";
|
|
3
4
|
import type { SearchAdapter } from "../search/types.ts";
|
|
4
5
|
import type { EventStoreExecutor } from "./event-store-executor.ts";
|
|
5
6
|
import type { Table } from "./event-store-executor-context.ts";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { EntityDefinition } from "@cosmicdrift/kumiko-types/fields";
|
|
1
2
|
import { type OwnershipClause } from "../engine/ownership.ts";
|
|
2
|
-
import type {
|
|
3
|
+
import type { SessionUser } from "../engine/types/index.ts";
|
|
3
4
|
import type { EventStoreExecutor } from "./event-store-executor.ts";
|
|
4
5
|
import type { Table } from "./event-store-executor-context.ts";
|
|
5
6
|
import type { ListWhere } from "./event-store-executor-read-filter.ts";
|
|
@@ -59,7 +59,7 @@ export function createReadVerbs(ctx) {
|
|
|
59
59
|
const tableInfo = extractTableInfo(table);
|
|
60
60
|
const encryptedRows = rawRows.map((r) => coerceAndRehydrateRow(r, tableInfo, entity));
|
|
61
61
|
const decryptedRows = await Promise.all(encryptedRows.map((r) => decryptForRead(r, { degradeUndecryptable: () => { } })));
|
|
62
|
-
// Stream version is authoritative (see detail()): ctx.
|
|
62
|
+
// Stream version is authoritative (see detail()): ctx.events.append bumps the
|
|
63
63
|
// stream without touching row.version, so the raw row version would hand
|
|
64
64
|
// clients a stale optimistic-lock base. Rows without events keep theirs.
|
|
65
65
|
const rows = decryptedRows.map((r, i) => withStreamVersion(r, streamVersions[i] ?? 0));
|
|
@@ -101,7 +101,7 @@ export function createReadVerbs(ctx) {
|
|
|
101
101
|
return null;
|
|
102
102
|
const idWhere = idFilter(payload.id);
|
|
103
103
|
// Stream-version authoritative (same policy as update/Block 0):
|
|
104
|
-
// ctx.
|
|
104
|
+
// ctx.events.append (lifecycle-writes like incident:post-update) bumps
|
|
105
105
|
// the stream WITHOUT touching row.version — a detail-read that hands
|
|
106
106
|
// out the stale row.version dooms the next CRUD update built on it
|
|
107
107
|
// (entityEdit loads detail.version as its optimistic-lock base) to a
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import { runnerCanBeginTransaction } from "./query.js";
|
|
2
|
-
import { runInOwnTransaction } from "./tenant-db.js";
|
|
3
2
|
import { isBoundTenantDb, tenantDbRunner, tenantDbWritesDeniedReason } from "./tenant-db-runner.js";
|
|
3
|
+
import { runInOwnSavepoint, runInOwnTransaction } from "./tenant-db-scoped-rebind.js";
|
|
4
4
|
// Aborts the surrounding transaction when a verb returns a failure instead of throwing.
|
|
5
5
|
class RollbackOnFailure extends Error {
|
|
6
6
|
}
|
|
7
|
-
//
|
|
8
|
-
//
|
|
7
|
+
// Append, projection and hooks commit or roll back together, also when a handler swallows a failed
|
|
8
|
+
// verb inside its transaction. Savepoints on one tx are serialized, so parallel verbs cannot interleave.
|
|
9
9
|
async function runVerbAtomically(db, run) {
|
|
10
10
|
// Unbound and denied handles keep their original error, a fresh transaction would reword it.
|
|
11
|
-
if (!isBoundTenantDb(db) ||
|
|
12
|
-
tenantDbWritesDeniedReason(db) !== undefined ||
|
|
13
|
-
!runnerCanBeginTransaction(tenantDbRunner(db))) {
|
|
11
|
+
if (!isBoundTenantDb(db) || tenantDbWritesDeniedReason(db) !== undefined) {
|
|
14
12
|
return run(db);
|
|
15
13
|
}
|
|
14
|
+
const runInOwnScope = runnerCanBeginTransaction(tenantDbRunner(db))
|
|
15
|
+
? runInOwnTransaction
|
|
16
|
+
: runInOwnSavepoint;
|
|
16
17
|
let failure;
|
|
17
18
|
try {
|
|
18
|
-
return await
|
|
19
|
+
return await runInOwnScope(db, async (txDb) => {
|
|
19
20
|
const result = await run(txDb);
|
|
20
21
|
if (!result.isSuccess) {
|
|
21
22
|
failure = result;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { EntityId } from "@cosmicdrift/kumiko-types/identifiers";
|
|
1
2
|
import type { TenantDb } from "@cosmicdrift/kumiko-types/tenant-db-types";
|
|
2
3
|
import type { ZodType } from "zod";
|
|
3
4
|
import type { UnavailableSelectOption } from "../engine/screen-helpers.ts";
|
|
4
|
-
import type {
|
|
5
|
-
import { type
|
|
5
|
+
import type { UnprocessableOpts } from "../errors/classes.ts";
|
|
6
|
+
import { type WriteFailure, writeFailure } from "../errors/index.ts";
|
|
6
7
|
import type { StoredEvent } from "../event-store/index.ts";
|
|
7
8
|
import type { DbRow, DbRunner } from "./connection.ts";
|
|
8
9
|
export declare function projectEntityEvent(db: TenantDb, runner: DbRunner, event: StoredEvent): Promise<void>;
|