@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
|
@@ -6,8 +6,10 @@ import { selectEventsHeadId, selectPendingAndNewEventRows, } from "../db/queries
|
|
|
6
6
|
import { coerceRow } from "../db/query.js";
|
|
7
7
|
import { extractTableInfo } from "../db/table-info.js";
|
|
8
8
|
import { qnScope } from "../engine/qualified-name.js";
|
|
9
|
-
import { eventsTable
|
|
10
|
-
import {
|
|
9
|
+
import { eventsTable } from "../event-store/index.js";
|
|
10
|
+
import { toStoredEvent as rowToStoredEvent } from "../event-store/row-to-stored-event.js";
|
|
11
|
+
import { getFallbackMeter } from "../observability/fallback.js";
|
|
12
|
+
import { emitDispatcherError, emitEventConsumerLag, emitEventConsumerPendingGaps, } from "../observability/standard-metrics.js";
|
|
11
13
|
import { ConsumerStatuses, eventConsumerStateTable, SHARED_INSTANCE_SENTINEL, } from "./event-consumer-state.js";
|
|
12
14
|
import { parseWriteOrigin } from "./write-origin.js";
|
|
13
15
|
// Fails closed without throwing: a throw would poison the event for every consumer.
|
|
@@ -212,9 +214,11 @@ export async function deliverEvents(consumer, events, context, maxAttempts, stat
|
|
|
212
214
|
resolve(row.id);
|
|
213
215
|
continue;
|
|
214
216
|
}
|
|
215
|
-
attempts += 1;
|
|
216
217
|
lastError = errMessage;
|
|
217
218
|
failedEventId = row.id;
|
|
219
|
+
if (consumer.errorPolicy?.isTransientError?.(e))
|
|
220
|
+
break;
|
|
221
|
+
attempts += 1;
|
|
218
222
|
if (attempts >= effectiveMaxAttempts)
|
|
219
223
|
deadLettered = true;
|
|
220
224
|
break;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import type { Meter, Tracer } from "@cosmicdrift/kumiko-types/observability";
|
|
1
2
|
import type { PgListenClient } from "../db/connection.ts";
|
|
2
3
|
import type { AppContext } from "../engine/types/index.ts";
|
|
3
|
-
import {
|
|
4
|
-
import { type Meter, type Tracer } from "../observability/index.ts";
|
|
4
|
+
import type { StoredEvent } from "../event-store/index.ts";
|
|
5
5
|
export type EventConsumerHandler = (event: StoredEvent, ctx: AppContext) => Promise<void>;
|
|
6
6
|
export type EventConsumerBatchHandler = (events: readonly StoredEvent[], ctx: AppContext) => Promise<void>;
|
|
7
7
|
export type EventConsumerErrorPolicy = {
|
|
8
8
|
readonly skipApplyErrors?: boolean;
|
|
9
9
|
readonly maxAttempts?: number;
|
|
10
|
+
readonly isTransientError?: (error: unknown) => boolean;
|
|
10
11
|
};
|
|
11
12
|
export type EventConsumer = {
|
|
12
13
|
readonly name: string;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { extractPgError } from "../db/pg-error.js";
|
|
2
2
|
import { recordConsumerPassFailure, selectSnapshotXmax, selectSnapshotXmin, } from "../db/queries/event-consumer.js";
|
|
3
|
+
import { assertNoActiveRowLevelSecurityScope, runInSystemRowLevelSecurityScope, } from "../db/row-level-security-scope.js";
|
|
3
4
|
import { SYSTEM_TENANT_ID } from "../engine/types/identifiers.js";
|
|
4
|
-
import { EVENTS_PUBSUB_CHANNEL } from "../event-store/
|
|
5
|
+
import { EVENTS_PUBSUB_CHANNEL } from "../event-store/event-store.js";
|
|
5
6
|
import { createFallbackLogger } from "../logging/utils.js";
|
|
6
|
-
import {
|
|
7
|
+
import { getFallbackMeter, getFallbackTracer } from "../observability/fallback.js";
|
|
8
|
+
import { emitEventConsumerPassOutcome, emitEventConsumerPassSkipped, emitEventConsumerRearmExhausted, emitEventDispatcherListenConnected, } from "../observability/standard-metrics.js";
|
|
7
9
|
import { SHARED_INSTANCE_SENTINEL } from "./event-consumer-state.js";
|
|
8
10
|
import { acquireConsumerState, consumerInstanceId, deliverEvents, emitLagFromTx, fetchPendingEvents, markProcessing, persistConsumerOutcome, preRegisterConsumers, selectIdleConsumerKeys, } from "./event-dispatcher-delivery.js";
|
|
9
11
|
import { mergeContiguousGapRanges, partitionBurntGaps, splitRangeExcludingIds, toIdRanges, } from "./pending-gap-ranges.js";
|
|
@@ -16,12 +18,10 @@ const PENDING_GAPS_WARN_THRESHOLD = 1000;
|
|
|
16
18
|
// Only these say the database socket itself is gone; timeouts, schema or pool errors do not
|
|
17
19
|
// imply the dedicated LISTEN connection dropped.
|
|
18
20
|
const CONNECTION_LOSS_CODES = new Set([
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"ECONNREFUSED",
|
|
24
|
-
"ECONNRESET",
|
|
21
|
+
"ERR_POSTGRES_CONNECTION_CLOSED",
|
|
22
|
+
"ERR_POSTGRES_CONNECTION_REFUSED",
|
|
23
|
+
"ERR_POSTGRES_CONNECTION_TIMEOUT",
|
|
24
|
+
"ERR_POSTGRES_CONNECTION_FAILED",
|
|
25
25
|
"57P01",
|
|
26
26
|
]);
|
|
27
27
|
function isConnectionLossError(e) {
|
|
@@ -111,7 +111,7 @@ export function createEventDispatcher(options) {
|
|
|
111
111
|
let pgSubscription = null;
|
|
112
112
|
// Bounds how many consumer turns hold a DB transaction at once. Each turn's
|
|
113
113
|
// db.begin() checks out one pool connection, plus any writes the handler
|
|
114
|
-
// itself makes via context.db — the app db pool defaults to
|
|
114
|
+
// itself makes via context.db — the app db pool defaults to Bun.SQL's
|
|
115
115
|
// max=10 (db/connection.ts, no DATABASE_POOL_MAX override) and is shared
|
|
116
116
|
// with HTTP request handlers, so turns can't be allowed to claim it all.
|
|
117
117
|
// A pool no larger than this limit can deadlock: every connection held by
|
|
@@ -223,7 +223,8 @@ export function createEventDispatcher(options) {
|
|
|
223
223
|
if (!preRegistered) {
|
|
224
224
|
throw new Error("EventDispatcher.runOnce() called before start() — consumer state rows are not registered. Call start() first (production) or ensureRegistered() (tests after truncating kumiko_event_consumers).");
|
|
225
225
|
}
|
|
226
|
-
|
|
226
|
+
// Per pass, never around start(): the poll loop itself must stay outside any scope.
|
|
227
|
+
const pass = runInSystemRowLevelSecurityScope("event dispatcher pass runs consumers for every tenant", doPass);
|
|
227
228
|
inFlightPasses.add(pass);
|
|
228
229
|
try {
|
|
229
230
|
return await pass;
|
|
@@ -459,6 +460,7 @@ export function createEventDispatcher(options) {
|
|
|
459
460
|
return {
|
|
460
461
|
consumers,
|
|
461
462
|
async start() {
|
|
463
|
+
assertNoActiveRowLevelSecurityScope("EventDispatcher");
|
|
462
464
|
// skip: already running, idempotent
|
|
463
465
|
if (running)
|
|
464
466
|
return;
|
|
@@ -493,8 +495,7 @@ export function createEventDispatcher(options) {
|
|
|
493
495
|
const sub = await options.pgClient.listen(EVENTS_PUBSUB_CHANNEL, runBackgroundPass, () => {
|
|
494
496
|
// Fires on initial connect and again when the driver's own
|
|
495
497
|
// re-LISTEN lands after a drop; during an outage that re-LISTEN
|
|
496
|
-
// waits in
|
|
497
|
-
// after the DB returns), polling covers
|
|
498
|
+
// waits in the driver's reconnect backoff, polling covers
|
|
498
499
|
// delivery meanwhile. Never unlisten()
|
|
499
500
|
// + listen() by hand: that leaves duplicate listeners.
|
|
500
501
|
// ponytail: no own LISTEN reconnect, the backoff gap is polled;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="temporal-polyfill/global" preserve="true" />
|
|
2
2
|
import { lockEventConsumersShareMode } from "../db/queries/event-consumer.js";
|
|
3
3
|
import { deleteMany, selectMany, transaction } from "../db/query.js";
|
|
4
|
+
import { runInSystemRowLevelSecurityScope } from "../db/row-level-security-scope.js";
|
|
4
5
|
import { eventsTable } from "../event-store/index.js";
|
|
5
6
|
import { eventConsumerStateTable } from "./event-consumer-state.js";
|
|
6
7
|
export class ConsumerLagError extends Error {
|
|
@@ -27,64 +28,66 @@ function resolveCutoff(opts) {
|
|
|
27
28
|
return Temporal.Now.instant().subtract({ hours: days * 24 });
|
|
28
29
|
}
|
|
29
30
|
export async function pruneEvents(db, options) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (options.aggregateIds !== undefined && options.aggregateIds.length === 0) {
|
|
35
|
-
throw new Error("pruneEvents: aggregateIds must be non-empty when set.");
|
|
36
|
-
}
|
|
37
|
-
if (options.batchSize !== undefined &&
|
|
38
|
-
(!Number.isInteger(options.batchSize) || options.batchSize < 1)) {
|
|
39
|
-
throw new Error("pruneEvents: batchSize must be a positive integer.");
|
|
40
|
-
}
|
|
41
|
-
const aggregateTypes = options.aggregateTypes;
|
|
42
|
-
const aggregateIds = options.aggregateIds;
|
|
43
|
-
const dryRun = options.dryRun === true;
|
|
44
|
-
return transaction(db, async (tx) => {
|
|
45
|
-
// Serialise against consumer-bootstrap INSERTs. Without this, the race
|
|
46
|
-
// is: prune reads consumers (snapshot misses a consumer bootstrapping
|
|
47
|
-
// in a parallel tx) → consumer commits its row with
|
|
48
|
-
// lastProcessedEventId=0 → prune deletes events below its new cursor
|
|
49
|
-
// → first pass of the new consumer silently skips the deleted events.
|
|
50
|
-
//
|
|
51
|
-
// SHARE is the lightest table-mode that conflicts with ROW EXCLUSIVE
|
|
52
|
-
// (the mode INSERT/UPDATE/DELETE take). Concurrent INSERTs on the
|
|
53
|
-
// consumer table queue until this TX commits; concurrent UPDATEs
|
|
54
|
-
// (cursor advances) do too, but prune is measured in milliseconds and
|
|
55
|
-
// pausing cursor advances for that window is cheap insurance against
|
|
56
|
-
// a silent data-loss bug.
|
|
57
|
-
await lockEventConsumersShareMode(tx);
|
|
58
|
-
// Step 1 — collect candidate event ids.
|
|
59
|
-
const candidates = await selectMany(tx, eventsTable, {
|
|
60
|
-
aggregateType: [...aggregateTypes],
|
|
61
|
-
...(aggregateIds ? { aggregateId: [...aggregateIds] } : {}),
|
|
62
|
-
createdAt: { lt: cutoff },
|
|
63
|
-
}, options.batchSize === undefined
|
|
64
|
-
? undefined
|
|
65
|
-
: { orderBy: { col: "id", direction: "asc" }, limit: options.batchSize });
|
|
66
|
-
if (candidates.length === 0) {
|
|
67
|
-
return { deletedCount: 0, cutoff, aggregateTypes, dryRun };
|
|
31
|
+
return runInSystemRowLevelSecurityScope("event retention pruning deletes expired events of all tenants", async () => {
|
|
32
|
+
const cutoff = resolveCutoff(options);
|
|
33
|
+
if (!options.aggregateTypes || options.aggregateTypes.length === 0) {
|
|
34
|
+
throw new Error("pruneEvents: aggregateTypes is required and must be non-empty. Pruning the event log is destructive — name the aggregate types to delete explicitly.");
|
|
68
35
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
// the candidate set. Disabled consumers are intentionally excluded —
|
|
72
|
-
// ops disables them precisely to park them while pruning happens.
|
|
73
|
-
// The SHARE lock above guarantees this SELECT sees a complete view:
|
|
74
|
-
// no new consumer can INSERT a fresh-cursor row between here and the
|
|
75
|
-
// DELETE below.
|
|
76
|
-
const activeConsumers = await selectMany(tx, eventConsumerStateTable, { status: { ne: "disabled" } });
|
|
77
|
-
for (const consumer of activeConsumers) {
|
|
78
|
-
if (consumer.lastProcessedEventId < maxCandidateId) {
|
|
79
|
-
throw new ConsumerLagError(consumer.name, consumer.lastProcessedEventId, maxCandidateId);
|
|
80
|
-
}
|
|
36
|
+
if (options.aggregateIds !== undefined && options.aggregateIds.length === 0) {
|
|
37
|
+
throw new Error("pruneEvents: aggregateIds must be non-empty when set.");
|
|
81
38
|
}
|
|
82
|
-
if (
|
|
83
|
-
|
|
39
|
+
if (options.batchSize !== undefined &&
|
|
40
|
+
(!Number.isInteger(options.batchSize) || options.batchSize < 1)) {
|
|
41
|
+
throw new Error("pruneEvents: batchSize must be a positive integer.");
|
|
84
42
|
}
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
return
|
|
43
|
+
const aggregateTypes = options.aggregateTypes;
|
|
44
|
+
const aggregateIds = options.aggregateIds;
|
|
45
|
+
const dryRun = options.dryRun === true;
|
|
46
|
+
return transaction(db, async (tx) => {
|
|
47
|
+
// Serialise against consumer-bootstrap INSERTs. Without this, the race
|
|
48
|
+
// is: prune reads consumers (snapshot misses a consumer bootstrapping
|
|
49
|
+
// in a parallel tx) → consumer commits its row with
|
|
50
|
+
// lastProcessedEventId=0 → prune deletes events below its new cursor
|
|
51
|
+
// → first pass of the new consumer silently skips the deleted events.
|
|
52
|
+
//
|
|
53
|
+
// SHARE is the lightest table-mode that conflicts with ROW EXCLUSIVE
|
|
54
|
+
// (the mode INSERT/UPDATE/DELETE take). Concurrent INSERTs on the
|
|
55
|
+
// consumer table queue until this TX commits; concurrent UPDATEs
|
|
56
|
+
// (cursor advances) do too, but prune is measured in milliseconds and
|
|
57
|
+
// pausing cursor advances for that window is cheap insurance against
|
|
58
|
+
// a silent data-loss bug.
|
|
59
|
+
await lockEventConsumersShareMode(tx);
|
|
60
|
+
// Step 1 — collect candidate event ids.
|
|
61
|
+
const candidates = await selectMany(tx, eventsTable, {
|
|
62
|
+
aggregateType: [...aggregateTypes],
|
|
63
|
+
...(aggregateIds ? { aggregateId: [...aggregateIds] } : {}),
|
|
64
|
+
createdAt: { lt: cutoff },
|
|
65
|
+
}, options.batchSize === undefined
|
|
66
|
+
? undefined
|
|
67
|
+
: { orderBy: { col: "id", direction: "asc" }, limit: options.batchSize });
|
|
68
|
+
if (candidates.length === 0) {
|
|
69
|
+
return { deletedCount: 0, cutoff, aggregateTypes, dryRun };
|
|
70
|
+
}
|
|
71
|
+
const maxCandidateId = candidates.reduce((acc, row) => (row.id > acc ? row.id : acc), candidates[0]?.id ?? 0n);
|
|
72
|
+
// Step 2 — safety guard: check every ACTIVE consumer has moved past
|
|
73
|
+
// the candidate set. Disabled consumers are intentionally excluded —
|
|
74
|
+
// ops disables them precisely to park them while pruning happens.
|
|
75
|
+
// The SHARE lock above guarantees this SELECT sees a complete view:
|
|
76
|
+
// no new consumer can INSERT a fresh-cursor row between here and the
|
|
77
|
+
// DELETE below.
|
|
78
|
+
const activeConsumers = await selectMany(tx, eventConsumerStateTable, { status: { ne: "disabled" } });
|
|
79
|
+
for (const consumer of activeConsumers) {
|
|
80
|
+
if (consumer.lastProcessedEventId < maxCandidateId) {
|
|
81
|
+
throw new ConsumerLagError(consumer.name, consumer.lastProcessedEventId, maxCandidateId);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (dryRun) {
|
|
85
|
+
return { deletedCount: candidates.length, cutoff, aggregateTypes, dryRun: true };
|
|
86
|
+
}
|
|
87
|
+
// Step 3 — actual delete, bounded to the candidate set.
|
|
88
|
+
const candidateIds = candidates.map((c) => c.id);
|
|
89
|
+
await deleteMany(tx, eventsTable, { id: candidateIds });
|
|
90
|
+
return { deletedCount: candidateIds.length, cutoff, aggregateTypes, dryRun: false };
|
|
91
|
+
});
|
|
89
92
|
});
|
|
90
93
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isAnonymousSessionUser } from "../engine/system-user.js";
|
|
2
|
-
import {
|
|
2
|
+
import { UnauthenticatedError } from "../errors/classes.js";
|
|
3
|
+
import { AccessDeniedError } from "../errors/index.js";
|
|
3
4
|
// Anonymous callers get 401 so a client whose session lapsed (browser dropped
|
|
4
5
|
// the expired auth cookie) can tell "sign in again" apart from "signed in but
|
|
5
6
|
// not allowed", which stays 403.
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { MemberReader } from "@cosmicdrift/kumiko-types/handlers";
|
|
2
|
+
import type { HandlerAuth } from "../engine/types/index.ts";
|
|
3
3
|
import type { HandlerContextScope } from "./handler-context-scope.ts";
|
|
4
4
|
export declare function buildAuthFacet(scope: HandlerContextScope): {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
hasFeature: (featureName: string) => Promise<boolean>;
|
|
5
|
+
readonly auth: HandlerAuth;
|
|
6
|
+
readonly queryAsMember: MemberReader;
|
|
7
|
+
readonly hasFeature: (featureName: string) => Promise<boolean>;
|
|
9
8
|
};
|
|
@@ -12,13 +12,13 @@ export function buildAuthFacet(scope) {
|
|
|
12
12
|
ungatedMemberReader ??= createMemberReaderFn(ctx, user.tenantId, tx);
|
|
13
13
|
return ungatedMemberReader(userId, qn, payload);
|
|
14
14
|
}, { ...identitySwitchAudit, tenantId: user.tenantId });
|
|
15
|
-
|
|
15
|
+
const auth = {
|
|
16
16
|
// Thin pass-through: one resolve impl lives on the dispatcher, the
|
|
17
17
|
// handler surface just forwards the call so both entry points (login
|
|
18
|
-
// handler via ctx.
|
|
18
|
+
// handler via ctx.auth.resolveClaims, switch-tenant route via
|
|
19
19
|
// dispatcher.resolveAuthClaims) cannot drift.
|
|
20
|
-
|
|
21
|
-
// Thin pass-through, same reasoning as
|
|
20
|
+
resolveClaims: (claimsUser) => resolveAuthClaimsFn(ctx, claimsUser), // @wrapper-known semantic-alias
|
|
21
|
+
// Thin pass-through, same reasoning as resolveClaims above — one
|
|
22
22
|
// resolve impl lives on the dispatcher so callers can't drift apart.
|
|
23
23
|
// Internally queries memberships as SYSTEM, so it needs the same grant
|
|
24
24
|
// as a SYSTEM queryAs/writeAs (r.systemScope() or escapeHatch).
|
|
@@ -27,6 +27,9 @@ export function buildAuthFacet(scope) {
|
|
|
27
27
|
throw systemIdentitySwitchDenied(`handler "${type}"`);
|
|
28
28
|
return resolveActiveMembershipFn(ctx, userId, tenantId, INTERACTIVE_SIGN_IN_POLICY); // @wrapper-known semantic-alias
|
|
29
29
|
},
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
auth,
|
|
30
33
|
// Needs the same grant as a SYSTEM queryAs.
|
|
31
34
|
queryAsMember,
|
|
32
35
|
// Feature-effective check for in-handler opt-in logic. Scope:
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
+
import type { ConfigAccessor } from "@cosmicdrift/kumiko-types/config";
|
|
1
2
|
import { type TenantDb } from "../db/tenant-db.ts";
|
|
2
3
|
import type { TenantId } from "../engine/types/identifiers.ts";
|
|
3
|
-
import type { ConfigAccessor } from "../engine/types/index.ts";
|
|
4
4
|
import { createTzContext } from "../time/index.ts";
|
|
5
5
|
import type { HandlerContextScope } from "./handler-context-scope.ts";
|
|
6
6
|
export declare function buildConfigFacet(scope: HandlerContextScope, db: TenantDb | undefined): {
|
|
7
7
|
config: ConfigAccessor | undefined;
|
|
8
|
-
|
|
9
|
-
config?: ConfigAccessor | undefined;
|
|
10
|
-
configFor?: ((targetTenantId: TenantId) => ConfigAccessor) | undefined;
|
|
11
|
-
};
|
|
8
|
+
configFor: ((targetTenantId: TenantId) => ConfigAccessor) | undefined;
|
|
12
9
|
};
|
|
13
10
|
export declare function resolveHandlerTz(scope: HandlerContextScope, config: ConfigAccessor | undefined): Promise<ReturnType<typeof createTzContext>>;
|
|
14
11
|
export declare function resolveHandlerLocale(scope: HandlerContextScope): string;
|
|
@@ -18,7 +18,7 @@ function buildConfigFor(scope, configFactory, config) {
|
|
|
18
18
|
return config;
|
|
19
19
|
if (!isSystemIdentity(user) && !user.roles.includes("SystemAdmin")) {
|
|
20
20
|
throw new AccessDeniedError({
|
|
21
|
-
message: "ctx.
|
|
21
|
+
message: "ctx.tenants.config(otherTenant) requires the system role or SystemAdmin",
|
|
22
22
|
details: { reason: "config_for_requires_system_admin" },
|
|
23
23
|
});
|
|
24
24
|
}
|
|
@@ -50,7 +50,7 @@ export function buildConfigFacet(scope, db) {
|
|
|
50
50
|
})
|
|
51
51
|
: undefined;
|
|
52
52
|
const configFor = buildConfigFor(scope, configFactory, config);
|
|
53
|
-
return { config,
|
|
53
|
+
return { config, configFor };
|
|
54
54
|
}
|
|
55
55
|
async function readTenantTimezone(scope, config) {
|
|
56
56
|
const { ctx, user, tx } = scope;
|
|
@@ -1,23 +1,48 @@
|
|
|
1
|
+
import type { TenantWriteTarget } from "@cosmicdrift/kumiko-types/handlers";
|
|
1
2
|
import type { DbConnection, DbTx } from "../db/connection.ts";
|
|
2
3
|
import { type TenantDb } from "../db/tenant-db.ts";
|
|
3
4
|
import type { TenantId } from "../engine/types/identifiers.ts";
|
|
4
|
-
import type { TenantWriteTarget } from "../engine/types/index.ts";
|
|
5
5
|
import type { HandlerContextScope } from "./handler-context-scope.ts";
|
|
6
6
|
export declare function buildTenantScopedDb(scope: HandlerContextScope, source: DbConnection | DbTx, signal: AbortSignal | undefined, foreignTenantId?: TenantId, denyWrites?: boolean): TenantDb;
|
|
7
7
|
export declare function buildHandlerDbFacet(scope: HandlerContextScope): {
|
|
8
8
|
db: TenantDb | undefined;
|
|
9
|
-
exposedDb:
|
|
9
|
+
exposedDb: ({
|
|
10
|
+
readonly tenantId: TenantId;
|
|
11
|
+
readonly mode: import("@cosmicdrift/kumiko-types/tenant-db-types").TenantDbMode;
|
|
12
|
+
unsafeRaw(): import("@cosmicdrift/kumiko-types/db-connection").DbRunner;
|
|
13
|
+
global<TTable extends (import("@cosmicdrift/kumiko-types/schema-table-types").SchemaTable | import("@cosmicdrift/kumiko-types/entity-table-meta-types").EntityTableMeta) & import("@cosmicdrift/kumiko-types/tenancy-brand").TenancyBrand<"global">>(table: TTable): import("@cosmicdrift/kumiko-types/tenant-db-types").GlobalTableDb<TTable>;
|
|
14
|
+
selectMany<T = Record<string, unknown>>(table: import("@cosmicdrift/kumiko-types/schema-table-types").SchemaTable | import("@cosmicdrift/kumiko-types/entity-table-meta-types").EntityTableMeta, where?: import("@cosmicdrift/kumiko-types/where-clause-types").WhereObject, options?: import("@cosmicdrift/kumiko-types/where-clause-types").SelectOptions): Promise<readonly T[]>;
|
|
15
|
+
fetchOne<T = Record<string, unknown>>(table: import("@cosmicdrift/kumiko-types/schema-table-types").SchemaTable | import("@cosmicdrift/kumiko-types/entity-table-meta-types").EntityTableMeta, where: import("@cosmicdrift/kumiko-types/where-clause-types").WhereObject): Promise<T | undefined>;
|
|
16
|
+
count(table: import("@cosmicdrift/kumiko-types/schema-table-types").SchemaTable | import("@cosmicdrift/kumiko-types/entity-table-meta-types").EntityTableMeta, where?: import("@cosmicdrift/kumiko-types/where-clause-types").WhereObject): Promise<number>;
|
|
17
|
+
advisoryXactLock(namespace: number, key: string): Promise<void>;
|
|
18
|
+
databaseNow(): Promise<Date>;
|
|
19
|
+
aggregate(table: import("@cosmicdrift/kumiko-types/schema-table-types").SchemaTable | import("@cosmicdrift/kumiko-types/entity-table-meta-types").EntityTableMeta, spec: import("@cosmicdrift/kumiko-types/aggregate-types").AggregateSpec, where?: import("@cosmicdrift/kumiko-types/where-clause-types").WhereObject): Promise<readonly import("@cosmicdrift/kumiko-types/aggregate-types").AggregateRow[]>;
|
|
20
|
+
upsert<T = Record<string, unknown>>(table: import("@cosmicdrift/kumiko-types/executor-brand").WritableTable, values: Record<string, unknown>, options: import("@cosmicdrift/kumiko-types/tenant-db-types").TenantDbUpsertOptions): Promise<T | undefined>;
|
|
21
|
+
} & import("@cosmicdrift/kumiko-types/tenant-db-types").TenantDbWrites<import("@cosmicdrift/kumiko-types/executor-brand").WritableTable> & {
|
|
22
|
+
readonly outsideTransaction: TenantDb | undefined;
|
|
23
|
+
}) | undefined;
|
|
10
24
|
outsideTxSource: DbConnection | undefined;
|
|
11
25
|
fragment: {
|
|
12
|
-
db:
|
|
13
|
-
|
|
26
|
+
db: ({
|
|
27
|
+
readonly tenantId: TenantId;
|
|
28
|
+
readonly mode: import("@cosmicdrift/kumiko-types/tenant-db-types").TenantDbMode;
|
|
29
|
+
unsafeRaw(): import("@cosmicdrift/kumiko-types/db-connection").DbRunner;
|
|
30
|
+
global<TTable extends (import("@cosmicdrift/kumiko-types/schema-table-types").SchemaTable | import("@cosmicdrift/kumiko-types/entity-table-meta-types").EntityTableMeta) & import("@cosmicdrift/kumiko-types/tenancy-brand").TenancyBrand<"global">>(table: TTable): import("@cosmicdrift/kumiko-types/tenant-db-types").GlobalTableDb<TTable>;
|
|
31
|
+
selectMany<T = Record<string, unknown>>(table: import("@cosmicdrift/kumiko-types/schema-table-types").SchemaTable | import("@cosmicdrift/kumiko-types/entity-table-meta-types").EntityTableMeta, where?: import("@cosmicdrift/kumiko-types/where-clause-types").WhereObject, options?: import("@cosmicdrift/kumiko-types/where-clause-types").SelectOptions): Promise<readonly T[]>;
|
|
32
|
+
fetchOne<T = Record<string, unknown>>(table: import("@cosmicdrift/kumiko-types/schema-table-types").SchemaTable | import("@cosmicdrift/kumiko-types/entity-table-meta-types").EntityTableMeta, where: import("@cosmicdrift/kumiko-types/where-clause-types").WhereObject): Promise<T | undefined>;
|
|
33
|
+
count(table: import("@cosmicdrift/kumiko-types/schema-table-types").SchemaTable | import("@cosmicdrift/kumiko-types/entity-table-meta-types").EntityTableMeta, where?: import("@cosmicdrift/kumiko-types/where-clause-types").WhereObject): Promise<number>;
|
|
34
|
+
advisoryXactLock(namespace: number, key: string): Promise<void>;
|
|
35
|
+
databaseNow(): Promise<Date>;
|
|
36
|
+
aggregate(table: import("@cosmicdrift/kumiko-types/schema-table-types").SchemaTable | import("@cosmicdrift/kumiko-types/entity-table-meta-types").EntityTableMeta, spec: import("@cosmicdrift/kumiko-types/aggregate-types").AggregateSpec, where?: import("@cosmicdrift/kumiko-types/where-clause-types").WhereObject): Promise<readonly import("@cosmicdrift/kumiko-types/aggregate-types").AggregateRow[]>;
|
|
37
|
+
upsert<T = Record<string, unknown>>(table: import("@cosmicdrift/kumiko-types/executor-brand").WritableTable, values: Record<string, unknown>, options: import("@cosmicdrift/kumiko-types/tenant-db-types").TenantDbUpsertOptions): Promise<T | undefined>;
|
|
38
|
+
} & import("@cosmicdrift/kumiko-types/tenant-db-types").TenantDbWrites<import("@cosmicdrift/kumiko-types/executor-brand").WritableTable> & {
|
|
39
|
+
readonly outsideTransaction: TenantDb | undefined;
|
|
40
|
+
}) | undefined;
|
|
14
41
|
systemDb?: import("@cosmicdrift/kumiko-types/tenant-db-types").UncheckedSystemDb | undefined;
|
|
15
42
|
crossTenantReads?: import("@cosmicdrift/kumiko-types/tenant-db-types").CrossTenantReads | undefined;
|
|
16
43
|
};
|
|
17
44
|
};
|
|
18
|
-
export declare function
|
|
19
|
-
forTenant?: ((targetTenantId: TenantId) => TenantWriteTarget) | undefined;
|
|
20
|
-
};
|
|
45
|
+
export declare function buildWriteTarget(scope: HandlerContextScope, exposedDb: TenantDb | undefined, readOnlyForTenant: boolean): ((targetTenantId: TenantId) => TenantWriteTarget) | undefined;
|
|
21
46
|
export declare function buildFileFragment(scope: HandlerContextScope, db: TenantDb | undefined): {
|
|
22
47
|
files?: import("@cosmicdrift/kumiko-types/file-handle-types").FileContext | undefined;
|
|
23
48
|
derivatives?: import("@cosmicdrift/kumiko-types/derivatives-types").DerivativesContext | undefined;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { crossTenantReadsGrantFor, escapeHatchFor } from "@cosmicdrift/kumiko-types/handlers";
|
|
2
|
-
import { isAgentToolCallScope } from "../api/request-context.js";
|
|
2
|
+
import { isAgentToolCallScope } from "../api/request-context-scopes.js";
|
|
3
3
|
import { createHandlerCrossTenantReads } from "../db/cross-tenant-reads.js";
|
|
4
4
|
import { createTenantDb, createUncheckedSystemDb } from "../db/tenant-db.js";
|
|
5
5
|
import { createDerivativesContext } from "../derivatives/derivatives-context.js";
|
|
6
6
|
import { mayOverrideTenant } from "../engine/cross-tenant.js";
|
|
7
7
|
import { AccessDeniedError, FrameworkReasons } from "../errors/index.js";
|
|
8
8
|
import { createTenantFileContext } from "../files/file-handle.js";
|
|
9
|
+
import { attachOutsideTransaction } from "./attach-outside-transaction.js";
|
|
9
10
|
import { createSystemScopedDbGuard, isMemberResolutionPrincipal, resolveAppPoolDb, } from "./dispatch-shared.js";
|
|
10
11
|
import { buildPersonalDataGate } from "./write-origin.js";
|
|
11
12
|
export function buildTenantScopedDb(scope, source, signal, foreignTenantId, denyWrites = false) {
|
|
12
13
|
const { user, context, registry, origin, isSystem, writeEscapeHatch, handlerEscapeHatch } = scope;
|
|
13
|
-
|
|
14
|
+
const options = {
|
|
14
15
|
db: source,
|
|
15
16
|
tenantId: foreignTenantId ?? user.tenantId,
|
|
16
|
-
mode: isSystem && foreignTenantId === undefined ? "system" : "tenant",
|
|
17
17
|
tracer: context.tracer,
|
|
18
18
|
meter: context.meter,
|
|
19
19
|
signal,
|
|
@@ -28,7 +28,14 @@ export function buildTenantScopedDb(scope, source, signal, foreignTenantId, deny
|
|
|
28
28
|
projectionRegistry: registry,
|
|
29
29
|
...(denyWrites && { writesDeniedReason: "without an open transaction" }),
|
|
30
30
|
},
|
|
31
|
-
}
|
|
31
|
+
};
|
|
32
|
+
return isSystem && foreignTenantId === undefined
|
|
33
|
+
? createTenantDb({
|
|
34
|
+
...options,
|
|
35
|
+
mode: "system",
|
|
36
|
+
reason: "system-scope handler dispatch reads and writes across tenants by design",
|
|
37
|
+
})
|
|
38
|
+
: createTenantDb(options);
|
|
32
39
|
}
|
|
33
40
|
function buildHandlerCrossTenantReads(scope) {
|
|
34
41
|
const { user, context, registry, origin, dbSource, reqCtx, handlerEscapeHatch } = scope;
|
|
@@ -77,26 +84,28 @@ export function buildHandlerDbFacet(scope) {
|
|
|
77
84
|
? createUncheckedSystemDb(db, rawDbOutsideTransaction, scope.reportEscapeHatch)
|
|
78
85
|
: undefined;
|
|
79
86
|
const crossTenantReads = buildHandlerCrossTenantReads(scope);
|
|
87
|
+
// Same fail-closed treatment as `exposedDb` — a system handler reaching
|
|
88
|
+
// ctx.db.outsideTransaction directly would be a second, unguarded door to
|
|
89
|
+
// the same cross-tenant access ctx.db closes off.
|
|
90
|
+
const dbOutsideTransaction = isSystem && rawDbOutsideTransaction
|
|
91
|
+
? createSystemScopedDbGuard(type, "db.outsideTransaction", "ctx.systemDb.outsideTransaction.assertTenantMatch(...) or " +
|
|
92
|
+
"ctx.systemDb.outsideTransaction.acknowledgeCrossTenant(...)")
|
|
93
|
+
: rawDbOutsideTransaction;
|
|
80
94
|
// Exposed as ctx.db below — the internal `db` above stays the real,
|
|
81
95
|
// working TenantDb for config/derivatives accessors and systemDb
|
|
82
96
|
// construction.
|
|
83
|
-
const
|
|
97
|
+
const unattachedExposedDb = isSystem && db
|
|
84
98
|
? createSystemScopedDbGuard(type, "db", "ctx.systemDb.assertTenantMatch(...) or ctx.systemDb.acknowledgeCrossTenant(...)")
|
|
85
99
|
: db;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const dbOutsideTransaction = isSystem && rawDbOutsideTransaction
|
|
90
|
-
? createSystemScopedDbGuard(type, "dbOutsideTransaction", "ctx.systemDb.outsideTransaction.assertTenantMatch(...) or " +
|
|
91
|
-
"ctx.systemDb.outsideTransaction.acknowledgeCrossTenant(...)")
|
|
92
|
-
: rawDbOutsideTransaction;
|
|
100
|
+
const exposedDb = unattachedExposedDb
|
|
101
|
+
? attachOutsideTransaction(unattachedExposedDb, dbOutsideTransaction)
|
|
102
|
+
: undefined;
|
|
93
103
|
return {
|
|
94
104
|
db,
|
|
95
105
|
exposedDb,
|
|
96
106
|
outsideTxSource,
|
|
97
107
|
fragment: {
|
|
98
108
|
db: exposedDb,
|
|
99
|
-
dbOutsideTransaction,
|
|
100
109
|
...(systemDb && { systemDb }),
|
|
101
110
|
...(crossTenantReads && { crossTenantReads }),
|
|
102
111
|
},
|
|
@@ -106,7 +115,7 @@ export function buildHandlerDbFacet(scope) {
|
|
|
106
115
|
// dispatcher's tenantIdOverride predicate, so no caller reaches more than it could via
|
|
107
116
|
// tenantIdOverride. The foreign db carries no unsafeRaw/globalWrites grant and is tenant mode
|
|
108
117
|
// bound to the target; the executor accepts streamTenantId only on a db bound to that tenant.
|
|
109
|
-
export function
|
|
118
|
+
export function buildWriteTarget(scope, exposedDb, readOnlyForTenant) {
|
|
110
119
|
const { user, isSystem, dbSource, isWriteHandler, reqCtx, denyDbWrites, isPerCommand } = scope;
|
|
111
120
|
const forTenant = !isSystem && dbSource && isWriteHandler
|
|
112
121
|
? (targetTenantId) => {
|
|
@@ -128,9 +137,9 @@ export function buildForTenantFragment(scope, exposedDb, readOnlyForTenant) {
|
|
|
128
137
|
};
|
|
129
138
|
}
|
|
130
139
|
: undefined;
|
|
131
|
-
return
|
|
140
|
+
return forTenant;
|
|
132
141
|
}
|
|
133
|
-
// ctx.files resolved per-tenant through file-foundation (lazy — the
|
|
142
|
+
// ctx.services.files resolved per-tenant through file-foundation (lazy — the
|
|
134
143
|
// provider is only resolved when a handle actually does I/O). Boot wires
|
|
135
144
|
// _fileProviderResolver when a file-provider plugin is mounted; falls back
|
|
136
145
|
// to a statically-injected context.files (tests).
|
|
@@ -138,7 +147,7 @@ export function buildFileFragment(scope, db) {
|
|
|
138
147
|
const { context, registry, user } = scope;
|
|
139
148
|
const fileResolver = context._fileProviderResolver;
|
|
140
149
|
const files = fileResolver ? createTenantFileContext(fileResolver, user.tenantId) : context.files;
|
|
141
|
-
// ctx.derivatives builds on ctx.files (needs a FileContext to read the
|
|
150
|
+
// ctx.services.derivatives builds on ctx.services.files (needs a FileContext to read the
|
|
142
151
|
// original + write the variant) plus db (to look up the FileRef row) — so
|
|
143
152
|
// it can only be constructed exactly when files+db both resolved; falls
|
|
144
153
|
// back to a statically-injected context.derivatives otherwise (tests).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type { WriteResult } from "
|
|
2
|
-
import {
|
|
1
|
+
import type { WriteResult } from "@cosmicdrift/kumiko-types/handlers";
|
|
2
|
+
import type { HandlerDispatch } from "../engine/types/index.ts";
|
|
3
|
+
import { type AfterRollbackHook } from "./after-rollback-sink.ts";
|
|
4
|
+
import type { HandlerTransactionScope } from "./dispatch-shared.ts";
|
|
3
5
|
import { type AfterCommitHook } from "./dispatcher-utils.ts";
|
|
4
6
|
import type { HandlerContextScope } from "./handler-context-scope.ts";
|
|
5
7
|
export type CommitSinks = {
|
|
@@ -8,14 +10,20 @@ export type CommitSinks = {
|
|
|
8
10
|
readonly notifyCommitSink: AfterCommitHook[] | undefined;
|
|
9
11
|
};
|
|
10
12
|
export declare function createCommitSinks(afterCommitHooks: AfterCommitHook[] | undefined, transactionScope: HandlerTransactionScope, outerNotifySink: AfterCommitHook[] | undefined): CommitSinks;
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
+
export type DispatchFacetInput = Pick<HandlerDispatch, "queryAsMember" | "queryProjection">;
|
|
14
|
+
export declare function buildDispatchFacet(scope: HandlerContextScope, sinks: CommitSinks, readers: DispatchFacetInput): {
|
|
15
|
+
dispatch: {
|
|
16
|
+
queryAsMember: import("@cosmicdrift/kumiko-types/handlers").MemberReader;
|
|
17
|
+
queryProjection: <T = Record<string, unknown>>(qualifiedName: string, options?: {
|
|
18
|
+
readonly unsafeAllTenants?: boolean;
|
|
19
|
+
}) => Promise<readonly T[]>;
|
|
13
20
|
query: (targetType: string, payload: unknown) => Promise<unknown>;
|
|
14
21
|
queryAs: import("./system-identity-switch.ts").QueryAsFn;
|
|
15
22
|
write: (targetType: string, payload: unknown) => Promise<WriteResult>;
|
|
16
23
|
writeAs: import("./system-identity-switch.ts").WriteAsFn;
|
|
17
24
|
};
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
commitHooks: {
|
|
26
|
+
afterCommit?: ((hook: AfterCommitHook) => void) | undefined;
|
|
27
|
+
afterRollback?: ((hook: AfterRollbackHook) => void) | undefined;
|
|
20
28
|
};
|
|
21
29
|
};
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import { runWithWriteOrigin } from "../api/request-context-scopes.js";
|
|
1
2
|
import { runInSavepoint } from "../db/query.js";
|
|
2
3
|
import { writeFailure } from "../errors/index.js";
|
|
4
|
+
import { afterRollbackHooksOf, carrySavepointRollbackHooks, } from "./after-rollback-sink.js";
|
|
3
5
|
import { runBatch, unwrapSingle } from "./dispatch-batch.js";
|
|
4
6
|
import { executeQuery } from "./dispatch-query.js";
|
|
5
|
-
import { perCommandNestedDenied } from "./dispatch-shared.js";
|
|
6
7
|
import { executeWrite } from "./dispatch-write.js";
|
|
7
8
|
import { isFailedWriteResult, NestedWriteRollback, } from "./dispatcher-utils.js";
|
|
9
|
+
import { perCommandNestedDenied } from "./handler-context-phase-denials.js";
|
|
8
10
|
import { createGatedIdentitySwitch } from "./system-identity-switch.js";
|
|
9
11
|
export function createCommitSinks(afterCommitHooks, transactionScope, outerNotifySink) {
|
|
10
|
-
// Cross-feature bridge: ctx.query/write share the current tx + afterCommitHooks sink.
|
|
12
|
+
// Cross-feature bridge: ctx.dispatch.query/write share the current tx + afterCommitHooks sink.
|
|
11
13
|
// queryAs/writeAs to anyone but the caller itself needs r.systemScope() or { escapeHatch } (system-identity-switch.ts).
|
|
12
14
|
const bridgeSink = afterCommitHooks ?? [];
|
|
13
15
|
// Query bridges and post-commit contexts get a throwaway sink: hooks pushed there never run.
|
|
@@ -40,9 +42,11 @@ async function runNestedWriteInSavepoint(scope, sinks, asUser, targetType, paylo
|
|
|
40
42
|
return innerResult;
|
|
41
43
|
});
|
|
42
44
|
bridgeSink.push(...innerAfterCommitHooks);
|
|
45
|
+
carrySavepointRollbackHooks(innerAfterCommitHooks, bridgeSink, false);
|
|
43
46
|
return result;
|
|
44
47
|
}
|
|
45
48
|
catch (e) {
|
|
49
|
+
carrySavepointRollbackHooks(innerAfterCommitHooks, bridgeSink, true);
|
|
46
50
|
if (e instanceof NestedWriteRollback)
|
|
47
51
|
return e.failure;
|
|
48
52
|
throw e;
|
|
@@ -59,11 +63,14 @@ function createSerializedNestedWriter(scope, sinks) {
|
|
|
59
63
|
return run;
|
|
60
64
|
};
|
|
61
65
|
}
|
|
62
|
-
export function buildDispatchFacet(scope, sinks) {
|
|
66
|
+
export function buildDispatchFacet(scope, sinks, readers) {
|
|
63
67
|
const { ctx, type, user, origin, tx, transactionScope, hasIdentitySwitchGrant, identitySwitchAudit, } = scope;
|
|
64
68
|
const scheduleAfterCommit = (hook) => {
|
|
65
69
|
sinks.bridgeSink.push(hook);
|
|
66
70
|
};
|
|
71
|
+
const scheduleAfterRollback = (hook) => {
|
|
72
|
+
afterRollbackHooksOf(sinks.bridgeSink).push(() => runWithWriteOrigin(origin, hook));
|
|
73
|
+
};
|
|
67
74
|
const executeNestedWriteInSavepoint = createSerializedNestedWriter(scope, sinks);
|
|
68
75
|
// Without an open transaction (post-commit hooks, stream/query handlers, perCommand handlers) a bare
|
|
69
76
|
// executeWrite would auto-commit event append, projections and inTransaction hooks separately and its
|
|
@@ -78,12 +85,18 @@ export function buildDispatchFacet(scope, sinks) {
|
|
|
78
85
|
writeAs: writeAsUser,
|
|
79
86
|
}, identitySwitchAudit);
|
|
80
87
|
return {
|
|
81
|
-
|
|
88
|
+
dispatch: {
|
|
89
|
+
...readers,
|
|
82
90
|
query: (targetType, payload) => executeQuery(ctx, targetType, payload, user, origin, tx, sinks.notifyCommitSink), // @wrapper-known semantic-alias
|
|
83
91
|
queryAs: identitySwitch.queryAs,
|
|
84
92
|
write: (targetType, payload) => writeAsUser(user, targetType, payload),
|
|
85
93
|
writeAs: identitySwitch.writeAs,
|
|
86
94
|
},
|
|
87
|
-
|
|
95
|
+
commitHooks: {
|
|
96
|
+
...(sinks.hasLiveCommitSink && { afterCommit: scheduleAfterCommit }),
|
|
97
|
+
// perCommand and no-tx paths have earlier commands already committed: nothing to compensate on rollback.
|
|
98
|
+
...(sinks.hasLiveCommitSink &&
|
|
99
|
+
transactionScope === "inTransaction" && { afterRollback: scheduleAfterRollback }),
|
|
100
|
+
},
|
|
88
101
|
};
|
|
89
102
|
}
|