@cosmicdrift/kumiko-framework 0.14.0 → 0.15.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/package.json +6 -6
- package/src/__tests__/{anonymous-access.integration.ts → anonymous-access.integration.test.ts} +12 -9
- package/src/__tests__/{error-contract.integration.ts → error-contract.integration.test.ts} +5 -4
- package/src/__tests__/{field-access.integration.ts → field-access.integration.test.ts} +3 -3
- package/src/__tests__/{full-stack.integration.ts → full-stack.integration.test.ts} +7 -16
- package/src/__tests__/{ownership.integration.ts → ownership.integration.test.ts} +3 -2
- package/src/__tests__/{raw-table.integration.ts → raw-table.integration.test.ts} +18 -30
- package/src/__tests__/{reference-data.integration.ts → reference-data.integration.test.ts} +24 -11
- package/src/__tests__/{transition-guard.integration.ts → transition-guard.integration.test.ts} +12 -10
- package/src/api/__tests__/api.test.ts +1 -1
- package/src/api/__tests__/auth-middleware-transport.test.ts +1 -1
- package/src/api/__tests__/auth-routes-cookie.test.ts +1 -1
- package/src/api/__tests__/{batch.integration.ts → batch.integration.test.ts} +30 -30
- package/src/api/__tests__/body-limit.test.ts +1 -1
- package/src/api/__tests__/csrf-middleware.test.ts +1 -1
- package/src/api/__tests__/{dispatcher-live.integration.ts → dispatcher-live.integration.test.ts} +10 -9
- package/src/api/__tests__/metrics-endpoint.test.ts +1 -1
- package/src/api/__tests__/{nested-write.integration.ts → nested-write.integration.test.ts} +13 -16
- package/src/api/__tests__/readiness.test.ts +1 -1
- package/src/api/__tests__/request-id-middleware.test.ts +1 -1
- package/src/api/__tests__/sse-broker.test.ts +12 -12
- package/src/api/__tests__/sse-route.test.ts +1 -1
- package/src/api/readiness.ts +2 -2
- package/src/auth/__tests__/roles.test.ts +2 -2
- package/src/bun-db/__tests__/PATTERN.md +73 -0
- package/src/bun-db/__tests__/_helpers.ts +103 -0
- package/src/bun-db/__tests__/batch-methods.integration.test.ts +143 -0
- package/src/bun-db/__tests__/batch-methods.test.ts +20 -0
- package/src/bun-db/__tests__/bun-test-db.ts +19 -0
- package/src/bun-db/__tests__/bun-test-stack.ts +6 -0
- package/src/bun-db/__tests__/column-types.integration.test.ts +132 -0
- package/src/bun-db/__tests__/compound-types.integration.test.ts +134 -0
- package/src/bun-db/__tests__/jsonb-edge-cases.integration.test.ts +235 -0
- package/src/bun-db/__tests__/smoke.integration.test.ts +43 -0
- package/src/bun-db/__tests__/sql-methods.integration.test.ts +231 -0
- package/src/bun-db/__tests__/where-patterns.integration.test.ts +185 -0
- package/src/bun-db/connection.ts +84 -0
- package/src/bun-db/index.ts +31 -0
- package/src/bun-db/query.ts +845 -0
- package/src/compliance/__tests__/duration-spec.test.ts +1 -1
- package/src/compliance/__tests__/profiles.test.ts +1 -1
- package/src/compliance/__tests__/sub-processors.test.ts +1 -1
- package/src/db/__tests__/{apply-entity-event-tenant.integration.ts → apply-entity-event-tenant.integration.test.ts} +13 -11
- package/src/db/__tests__/big-int-field.test.ts +15 -14
- package/src/db/__tests__/column-ddl.integration.test.ts +113 -0
- package/src/db/__tests__/compound-types.test.ts +1 -1
- package/src/db/__tests__/{config-seed.integration.ts → config-seed.integration.test.ts} +32 -27
- package/src/db/__tests__/connection-options.test.ts +1 -1
- package/src/db/__tests__/dialect-instant.test.ts +1 -1
- package/src/db/__tests__/encryption.test.ts +1 -1
- package/src/db/__tests__/{drizzle-table-types.test.ts → entity-table-types.test.ts} +16 -16
- package/src/db/__tests__/{event-store-executor-list.integration.ts → event-store-executor-list.integration.test.ts} +12 -7
- package/src/db/__tests__/{event-store-executor.integration.ts → event-store-executor.integration.test.ts} +19 -12
- package/src/db/__tests__/{implicit-projection-equivalence.integration.ts → implicit-projection-equivalence.integration.test.ts} +35 -29
- package/src/db/__tests__/located-timestamp.test.ts +1 -1
- package/src/db/__tests__/money.test.ts +1 -1
- package/src/db/__tests__/{multi-row-insert.integration.ts → multi-row-insert.integration.test.ts} +18 -11
- package/src/db/__tests__/parse-auto-verb.test.ts +1 -1
- package/src/db/__tests__/{required-not-null-migration-safety.integration.ts → required-not-null-migration-safety.integration.test.ts} +28 -24
- package/src/db/__tests__/{schema-migration.integration.ts → schema-migration.integration.test.ts} +32 -28
- package/src/db/__tests__/sql-inventory.test.ts +56 -0
- package/src/db/__tests__/table-builder-indexes.test.ts +30 -11
- package/src/db/__tests__/table-builder-required.test.ts +20 -22
- package/src/db/__tests__/{tenant-db.integration.ts → tenant-db.integration.test.ts} +106 -144
- package/src/db/__tests__/{unique-violation-mapping.integration.ts → unique-violation-mapping.integration.test.ts} +13 -8
- package/src/db/api.ts +46 -0
- package/src/db/apply-entity-event.ts +45 -36
- package/src/db/assert-exists-in.ts +5 -16
- package/src/db/bun-provider.ts +37 -0
- package/src/db/config-seed.ts +4 -4
- package/src/db/connection.ts +14 -57
- package/src/db/cursor.ts +5 -56
- package/src/db/dialect.ts +472 -99
- package/src/db/eagerload.ts +5 -12
- package/src/db/entity-table-meta.ts +390 -0
- package/src/db/event-store-executor.ts +158 -100
- package/src/db/index.ts +33 -5
- package/src/db/migrate-generator.ts +350 -0
- package/src/db/migrate-runner.ts +206 -0
- package/src/db/postgres-provider.ts +25 -0
- package/src/db/queries/entity-read.ts +15 -0
- package/src/db/queries/es-ops.ts +17 -0
- package/src/db/queries/event-consumer.ts +170 -0
- package/src/db/queries/event-store-admin.ts +127 -0
- package/src/db/queries/event-store.ts +155 -0
- package/src/db/queries/projection-rebuild.ts +59 -0
- package/src/db/queries/raw-sql.ts +15 -0
- package/src/db/queries/schema-drift.ts +35 -0
- package/src/db/queries/seed-context.ts +58 -0
- package/src/db/queries/table-ops.ts +11 -0
- package/src/db/queries/test-stack.ts +56 -0
- package/src/db/query-api.ts +22 -0
- package/src/db/query.ts +30 -0
- package/src/db/reference-data.ts +19 -22
- package/src/db/render-ddl.ts +57 -0
- package/src/db/row-helpers.ts +3 -52
- package/src/db/schema-inspection.ts +17 -4
- package/src/db/sql-inventory.ts +208 -0
- package/src/db/table-builder.ts +48 -40
- package/src/db/tenant-db.ts +105 -326
- package/src/engine/__tests__/auth-claims-registrar.test.ts +1 -1
- package/src/engine/__tests__/boot-validator-api-exposure.test.ts +3 -3
- package/src/engine/__tests__/boot-validator-located-timestamps.test.ts +1 -1
- package/src/engine/__tests__/boot-validator-pii-retention.test.ts +5 -5
- package/src/engine/__tests__/boot-validator-s0-integration.test.ts +3 -3
- package/src/engine/__tests__/boot-validator.test.ts +4 -3
- package/src/engine/__tests__/build-app-schema.test.ts +1 -1
- package/src/engine/__tests__/build-target.test.ts +1 -1
- package/src/engine/__tests__/claim-keys.test.ts +1 -1
- package/src/engine/__tests__/codemod-pipeline.test.ts +3 -3
- package/src/engine/__tests__/config-helpers.test.ts +1 -1
- package/src/engine/__tests__/effective-features.test.ts +1 -1
- package/src/engine/__tests__/engine.test.ts +1 -1
- package/src/engine/__tests__/entity-handlers.test.ts +3 -3
- package/src/engine/__tests__/event-helpers.test.ts +3 -3
- package/src/engine/__tests__/extends-registrar.test.ts +4 -4
- package/src/engine/__tests__/factories-long-text.test.ts +1 -1
- package/src/engine/__tests__/factories-time.test.ts +1 -1
- package/src/engine/__tests__/field-predicates.test.ts +1 -1
- package/src/engine/__tests__/hook-phases.test.ts +1 -1
- package/src/engine/__tests__/identifiers.test.ts +1 -1
- package/src/engine/__tests__/lifecycle-hooks.test.ts +1 -1
- package/src/engine/__tests__/nav.test.ts +1 -1
- package/src/engine/__tests__/ownership.test.ts +10 -11
- package/src/engine/__tests__/parse-ref-target.test.ts +1 -1
- package/src/engine/__tests__/pipeline-engine.test.ts +1 -1
- package/src/engine/__tests__/{pipeline-handler.integration.ts → pipeline-handler.integration.test.ts} +38 -52
- package/src/engine/__tests__/{pipeline-observability.integration.ts → pipeline-observability.integration.test.ts} +1 -1
- package/src/engine/__tests__/{pipeline-performance.integration.ts → pipeline-performance.integration.test.ts} +1 -1
- package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +1 -1
- package/src/engine/__tests__/post-query-hook.test.ts +1 -1
- package/src/engine/__tests__/projection-helpers.test.ts +25 -17
- package/src/engine/__tests__/projection.test.ts +4 -4
- package/src/engine/__tests__/qualified-name.test.ts +1 -1
- package/src/engine/__tests__/raw-table.test.ts +9 -8
- package/src/engine/__tests__/resolve-config-or-param.test.ts +5 -5
- package/src/engine/__tests__/run-in.test.ts +1 -1
- package/src/engine/__tests__/schema-builder.test.ts +1 -1
- package/src/engine/__tests__/screen.test.ts +1 -1
- package/src/engine/__tests__/search-payload-extension.test.ts +3 -3
- package/src/engine/__tests__/state-machine.test.ts +1 -1
- package/src/engine/__tests__/steps-aggregate-append-event.test.ts +7 -7
- package/src/engine/__tests__/steps-aggregate-create.test.ts +4 -4
- package/src/engine/__tests__/steps-aggregate-update.test.ts +3 -3
- package/src/engine/__tests__/steps-call-feature.test.ts +5 -5
- package/src/engine/__tests__/steps-mail-send.test.ts +7 -7
- package/src/engine/__tests__/steps-read.test.ts +34 -40
- package/src/engine/__tests__/steps-resolver-utils.test.ts +6 -6
- package/src/engine/__tests__/steps-unsafe-projection-delete.test.ts +24 -19
- package/src/engine/__tests__/steps-unsafe-projection-upsert.test.ts +28 -17
- package/src/engine/__tests__/steps-webhook-send.test.ts +6 -6
- package/src/engine/__tests__/steps-workflow.test.ts +7 -7
- package/src/engine/__tests__/system-user.test.ts +1 -1
- package/src/engine/__tests__/validate-projection-allowlist.test.ts +4 -5
- package/src/engine/__tests__/validation-hooks.test.ts +1 -1
- package/src/engine/__tests__/visual-tree-patterns.test.ts +1 -1
- package/src/engine/boot-validator/entity-handler.ts +3 -3
- package/src/engine/boot-validator/ownership.ts +1 -1
- package/src/engine/define-feature.ts +1 -2
- package/src/engine/entity-handlers.ts +5 -5
- package/src/engine/factories.ts +1 -1
- package/src/engine/feature-ast/__tests__/canonical-form.test.ts +1 -1
- package/src/engine/feature-ast/__tests__/parse-happy-path.test.ts +1 -1
- package/src/engine/feature-ast/__tests__/parse-real-features.test.ts +2 -2
- package/src/engine/feature-ast/__tests__/parse.test.ts +1 -1
- package/src/engine/feature-ast/__tests__/patch.test.ts +1 -1
- package/src/engine/feature-ast/__tests__/patcher.test.ts +1 -1
- package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +1 -1
- package/src/engine/feature-ast/__tests__/visual-tree-parse.test.ts +1 -1
- package/src/engine/ownership.ts +113 -41
- package/src/engine/pattern-library/__tests__/library.test.ts +2 -2
- package/src/engine/projection-helpers.ts +2 -11
- package/src/engine/registry.ts +2 -2
- package/src/engine/steps/read-find-many.ts +13 -13
- package/src/engine/steps/read-find-one.ts +7 -9
- package/src/engine/steps/unsafe-projection-delete.ts +4 -5
- package/src/engine/steps/unsafe-projection-upsert.ts +63 -31
- package/src/engine/types/feature.ts +7 -2
- package/src/engine/types/fields.ts +4 -5
- package/src/engine/types/step.ts +10 -10
- package/src/engine/validate-projection-allowlist.ts +23 -3
- package/src/entrypoint/__tests__/{entrypoint-job-wiring.integration.ts → entrypoint-job-wiring.integration.test.ts} +4 -3
- package/src/entrypoint/__tests__/{split-deploy.integration.ts → split-deploy.integration.test.ts} +4 -3
- package/src/env/__tests__/compose-env-schema.test.ts +1 -1
- package/src/env/__tests__/dry-run.test.ts +1 -1
- package/src/errors/__tests__/classes.test.ts +1 -1
- package/src/errors/__tests__/write-failures.test.ts +1 -1
- package/src/es-ops/__tests__/{context.integration.ts → context.integration.test.ts} +43 -29
- package/src/es-ops/__tests__/{runner.integration.ts → runner.integration.test.ts} +25 -23
- package/src/es-ops/__tests__/runner.test.ts +29 -19
- package/src/es-ops/context.ts +9 -43
- package/src/es-ops/operations-schema.ts +2 -2
- package/src/es-ops/runner.ts +12 -26
- package/src/event-store/__tests__/{admin-api.integration.ts → admin-api.integration.test.ts} +71 -45
- package/src/event-store/__tests__/{event-store.integration.ts → event-store.integration.test.ts} +7 -5
- package/src/event-store/__tests__/{get-stream-version-perf.integration.ts → get-stream-version-perf.integration.test.ts} +5 -3
- package/src/event-store/__tests__/{perf.integration.ts → perf.integration.test.ts} +24 -16
- package/src/event-store/__tests__/{snapshot.integration.ts → snapshot.integration.test.ts} +34 -28
- package/src/event-store/__tests__/{upcaster-dead-letter.integration.ts → upcaster-dead-letter.integration.test.ts} +11 -12
- package/src/event-store/__tests__/{upcaster.integration.ts → upcaster.integration.test.ts} +19 -32
- package/src/event-store/admin-api.ts +55 -83
- package/src/event-store/archive.ts +15 -39
- package/src/event-store/event-store.ts +92 -86
- package/src/event-store/events-schema.ts +2 -1
- package/src/event-store/index.ts +1 -0
- package/src/event-store/snapshot.ts +26 -24
- package/src/event-store/upcaster-dead-letter.ts +19 -18
- package/src/files/__tests__/content-disposition.test.ts +1 -1
- package/src/files/__tests__/{file-field-pipeline.integration.ts → file-field-pipeline.integration.test.ts} +8 -5
- package/src/files/__tests__/file-handle.test.ts +1 -1
- package/src/files/__tests__/{files.integration.ts → files.integration.test.ts} +32 -17
- package/src/files/__tests__/read-stream.test.ts +1 -1
- package/src/files/__tests__/{storage-tracking.integration.ts → storage-tracking.integration.test.ts} +26 -30
- package/src/files/__tests__/write-stream.test.ts +1 -1
- package/src/files/__tests__/zip-stream.test.ts +1 -1
- package/src/files/file-ref-table.ts +2 -2
- package/src/files/file-routes.ts +7 -9
- package/src/files/storage-tracking.ts +9 -17
- package/src/i18n/__tests__/i18n.test.ts +1 -1
- package/src/jobs/__tests__/{job-event-trigger.integration.ts → job-event-trigger.integration.test.ts} +6 -3
- package/src/jobs/__tests__/{job-multi-trigger.integration.ts → job-multi-trigger.integration.test.ts} +6 -3
- package/src/jobs/__tests__/{jobs.integration.ts → jobs.integration.test.ts} +5 -7
- package/src/lifecycle/__tests__/{lifecycle-server.integration.ts → lifecycle-server.integration.test.ts} +1 -1
- package/src/lifecycle/__tests__/lifecycle.test.ts +6 -6
- package/src/lifecycle/__tests__/signal-handlers.test.ts +6 -6
- package/src/logging/__tests__/pino-trace-bridge.test.ts +1 -1
- package/src/migrations/__tests__/compare-snapshots.test.ts +1 -1
- package/src/migrations/__tests__/{detect-drift.integration.ts → detect-drift.integration.test.ts} +34 -26
- package/src/migrations/__tests__/{detect-projections-to-rebuild.integration.ts → detect-projections-to-rebuild.integration.test.ts} +1 -1
- package/src/migrations/__tests__/rebuild-marker.test.ts +1 -1
- package/src/migrations/projection-detection.ts +12 -1
- package/src/migrations/schema-drift.ts +7 -23
- package/src/observability/__tests__/console-provider.test.ts +1 -1
- package/src/observability/__tests__/metric-validator.test.ts +1 -1
- package/src/observability/__tests__/noop-provider.test.ts +1 -1
- package/src/observability/__tests__/{observability.integration.ts → observability.integration.test.ts} +5 -8
- package/src/observability/__tests__/prometheus-meter.test.ts +1 -1
- package/src/observability/__tests__/recording-meter.test.ts +1 -1
- package/src/observability/__tests__/recording-tracer.test.ts +1 -1
- package/src/observability/__tests__/sensitive-filter.test.ts +1 -1
- package/src/pipeline/__tests__/{archive-stream.integration.ts → archive-stream.integration.test.ts} +3 -3
- package/src/pipeline/__tests__/auth-claims-resolver.test.ts +9 -9
- package/src/pipeline/__tests__/{cascade-handler.integration.ts → cascade-handler.integration.test.ts} +18 -15
- package/src/pipeline/__tests__/cascade-handler.test.ts +1 -1
- package/src/pipeline/__tests__/{causation-chain.integration.ts → causation-chain.integration.test.ts} +12 -13
- package/src/pipeline/__tests__/{ctx-bridge.integration.ts → ctx-bridge.integration.test.ts} +12 -11
- package/src/pipeline/__tests__/dispatcher.test.ts +2 -2
- package/src/pipeline/__tests__/{distributed-lock.integration.ts → distributed-lock.integration.test.ts} +1 -1
- package/src/pipeline/__tests__/{domain-events-projections.integration.ts → domain-events-projections.integration.test.ts} +13 -15
- package/src/pipeline/__tests__/{event-dedup.integration.ts → event-dedup.integration.test.ts} +1 -1
- package/src/pipeline/__tests__/{event-define-event-strict.integration.ts → event-define-event-strict.integration.test.ts} +6 -16
- package/src/pipeline/__tests__/{event-dispatcher-lifecycle.integration.ts → event-dispatcher-lifecycle.integration.test.ts} +1 -1
- package/src/pipeline/__tests__/{event-dispatcher-multi-instance.integration.ts → event-dispatcher-multi-instance.integration.test.ts} +3 -2
- package/src/pipeline/__tests__/{event-dispatcher-pg-listen.integration.ts → event-dispatcher-pg-listen.integration.test.ts} +1 -1
- package/src/pipeline/__tests__/{event-dispatcher-recovery.integration.ts → event-dispatcher-recovery.integration.test.ts} +2 -2
- package/src/pipeline/__tests__/{event-dispatcher-second-audit.integration.ts → event-dispatcher-second-audit.integration.test.ts} +17 -16
- package/src/pipeline/__tests__/event-dispatcher-strict.test.ts +14 -12
- package/src/pipeline/__tests__/{event-dispatcher.integration.ts → event-dispatcher.integration.test.ts} +8 -15
- package/src/pipeline/__tests__/{event-retention.integration.ts → event-retention.integration.test.ts} +28 -25
- package/src/pipeline/__tests__/{fetch-for-writing.integration.ts → fetch-for-writing.integration.test.ts} +6 -6
- package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +4 -4
- package/src/pipeline/__tests__/{load-aggregate-query.integration.ts → load-aggregate-query.integration.test.ts} +9 -5
- package/src/pipeline/__tests__/{msp-error-mode.integration.ts → msp-error-mode.integration.test.ts} +1 -1
- package/src/pipeline/__tests__/{msp-multi-hop.integration.ts → msp-multi-hop.integration.test.ts} +9 -8
- package/src/pipeline/__tests__/{msp-rebuild.integration.ts → msp-rebuild.integration.test.ts} +47 -55
- package/src/pipeline/__tests__/{multi-stream-projection.integration.ts → multi-stream-projection.integration.test.ts} +19 -53
- package/src/pipeline/__tests__/{perf-rebuild.integration.ts → perf-rebuild.integration.test.ts} +36 -34
- package/src/pipeline/__tests__/{post-query-hook.integration.ts → post-query-hook.integration.test.ts} +1 -1
- package/src/pipeline/__tests__/{projection-rebuild.integration.ts → projection-rebuild.integration.test.ts} +21 -30
- package/src/pipeline/__tests__/{query-projection.integration.ts → query-projection.integration.test.ts} +6 -5
- package/src/pipeline/__tests__/{redis-pipeline.integration.ts → redis-pipeline.integration.test.ts} +3 -1
- package/src/pipeline/cascade-handler.ts +13 -21
- package/src/pipeline/dispatcher.ts +43 -48
- package/src/pipeline/event-consumer-state.ts +11 -2
- package/src/pipeline/event-dispatcher.ts +86 -146
- package/src/pipeline/event-retention.ts +14 -24
- package/src/pipeline/msp-rebuild.ts +54 -78
- package/src/pipeline/projection-rebuild.ts +65 -67
- package/src/pipeline/projection-state.ts +2 -2
- package/src/random/__tests__/generate.test.ts +13 -13
- package/src/rate-limit/__tests__/{dispatcher-l3.integration.ts → dispatcher-l3.integration.test.ts} +1 -1
- package/src/rate-limit/__tests__/{middleware.integration.ts → middleware.integration.test.ts} +1 -1
- package/src/rate-limit/__tests__/{resolver.integration.ts → resolver.integration.test.ts} +1 -1
- package/src/redis/__tests__/redis-options.test.ts +1 -1
- package/src/search/__tests__/{meilisearch-adapter.integration.ts → meilisearch-adapter.integration.test.ts} +1 -1
- package/src/search/__tests__/search-adapter.test.ts +1 -1
- package/src/secrets/__tests__/dek-cache.test.ts +1 -3
- package/src/secrets/__tests__/env-master-key-provider.test.ts +1 -1
- package/src/secrets/__tests__/envelope.test.ts +1 -1
- package/src/secrets/__tests__/leak-guard.test.ts +1 -1
- package/src/secrets/__tests__/rotation.test.ts +1 -1
- package/src/stack/db.ts +25 -48
- package/src/stack/push-entity-projection-tables.ts +2 -4
- package/src/stack/table-helpers.ts +98 -61
- package/src/stack/test-stack.ts +8 -7
- package/src/testing/__tests__/db-cleanup.test.ts +40 -0
- package/src/testing/__tests__/e2e-generator.test.ts +1 -1
- package/src/testing/__tests__/{ensure-entity-table.integration.ts → ensure-entity-table.integration.test.ts} +7 -14
- package/src/testing/db-cleanup.ts +44 -0
- package/src/testing/expect-error.ts +1 -1
- package/src/testing/index.ts +2 -0
- package/src/testing/multipart-helper.ts +94 -0
- package/src/testing/shared-entities.ts +5 -5
- package/src/time/__tests__/polyfill.test.ts +1 -1
- package/src/time/__tests__/tz-context.test.ts +1 -1
- package/src/utils/__tests__/assert.test.ts +1 -1
- package/src/utils/__tests__/env-parse.test.ts +1 -1
- package/CHANGELOG.md +0 -474
- package/src/db/__tests__/cursor.test.ts +0 -41
- package/src/db/__tests__/db-helpers.test.ts +0 -369
- package/src/db/__tests__/drizzle-helpers.integration.ts +0 -186
- package/src/db/__tests__/row-helpers.test.ts +0 -59
- package/src/engine/steps/_drizzle-boundary.ts +0 -19
- package/src/files/__tests__/file-field-column.integration.ts +0 -103
package/src/db/tenant-db.ts
CHANGED
|
@@ -1,24 +1,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
asRawClient,
|
|
3
|
+
deleteMany as bunDeleteMany,
|
|
4
|
+
fetchOne as bunFetchOne,
|
|
5
|
+
insertOne as bunInsertOne,
|
|
6
|
+
selectMany as bunSelectMany,
|
|
7
|
+
updateMany as bunUpdateMany,
|
|
8
|
+
type SelectOptions,
|
|
9
|
+
type WhereObject,
|
|
10
|
+
} from "../db/query";
|
|
2
11
|
import { SYSTEM_TENANT_ID, type TenantId } from "../engine/types/identifiers";
|
|
3
12
|
import { emitDbQuery, type Meter, registerStandardMetrics, type Tracer } from "../observability";
|
|
4
13
|
import type { DbRunner } from "./connection";
|
|
5
14
|
import type { TableColumns } from "./dialect";
|
|
6
15
|
|
|
7
|
-
// biome-ignore lint/suspicious/noExplicitAny:
|
|
16
|
+
// biome-ignore lint/suspicious/noExplicitAny: Dynamic tables — keys depend on user-defined schema.
|
|
8
17
|
type Table = TableColumns<any>;
|
|
9
18
|
|
|
10
|
-
// biome-ignore lint/suspicious/noExplicitAny: Drizzle column selection
|
|
11
|
-
type ColumnSelection = Record<string, any>;
|
|
12
|
-
|
|
13
19
|
/**
|
|
14
20
|
* TenantDb scope modes:
|
|
15
21
|
*
|
|
16
|
-
* - "tenant" (default): SELECT/UPDATE/DELETE filtered by tenantId + reference data (tenantId=
|
|
22
|
+
* - "tenant" (default): SELECT/UPDATE/DELETE filtered by tenantId + reference data (tenantId=SYSTEM_TENANT_ID).
|
|
17
23
|
* INSERT forces tenantId — handler cannot override.
|
|
18
24
|
*
|
|
19
25
|
* - "system" (r.systemScope()): No tenant filter on reads/updates/deletes.
|
|
20
|
-
* INSERT uses tenantId as default but handler can override
|
|
21
|
-
* cross-tenant row to a shared sentinel like SYSTEM_TENANT_ID).
|
|
26
|
+
* INSERT uses tenantId as default but handler can override.
|
|
22
27
|
*
|
|
23
28
|
* Tables without a tenantId column are always unfiltered regardless of mode.
|
|
24
29
|
*/
|
|
@@ -29,143 +34,62 @@ export type TenantDb = {
|
|
|
29
34
|
readonly mode: TenantDbMode;
|
|
30
35
|
/**
|
|
31
36
|
* Underlying DbRunner. Framework-internal use (event-store, migrations) —
|
|
32
|
-
* bypasses tenant-filter. Feature code
|
|
33
|
-
*
|
|
37
|
+
* bypasses tenant-filter. Feature code uses the typed helpers above so the
|
|
38
|
+
* automatic scoping stays intact.
|
|
34
39
|
*/
|
|
35
40
|
readonly raw: DbRunner;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
where(condition: WhereCondition): TenantSelectQuery;
|
|
53
|
-
limit(n: number): TenantSelectQuery;
|
|
54
|
-
offset(n: number): TenantSelectQuery;
|
|
55
|
-
orderBy(...columns: (SQL | Column)[]): TenantSelectQuery;
|
|
56
|
-
/** Row-level locking (FOR UPDATE / FOR SHARE). Must be called inside a tx. */
|
|
57
|
-
for(strength: RowLockStrength): TenantSelectQuery;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
type TenantInsert = {
|
|
61
|
-
values(data: Record<string, unknown>): TenantInsertValues;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
type ConflictTarget = Column | readonly Column[];
|
|
65
|
-
type ConflictUpdate = {
|
|
66
|
-
target: ConflictTarget;
|
|
67
|
-
set: Record<string, unknown>;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
type TenantInsertValues = PromiseLike<void> & {
|
|
71
|
-
returning(): PromiseLike<Record<string, unknown>[]>;
|
|
72
|
-
onConflictDoUpdate(spec: ConflictUpdate): PromiseLike<void>;
|
|
73
|
-
onConflictDoNothing(spec?: { target: ConflictTarget }): PromiseLike<void>;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
type TenantUpdate = {
|
|
77
|
-
set(data: Record<string, unknown>): TenantUpdateSet;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
type TenantUpdateSet = PromiseLike<void> & {
|
|
81
|
-
where(condition: WhereCondition): TenantUpdateWhere;
|
|
82
|
-
returning(): PromiseLike<Record<string, unknown>[]>;
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
type TenantUpdateWhere = PromiseLike<void> & {
|
|
86
|
-
returning(): PromiseLike<Record<string, unknown>[]>;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
type TenantDelete = {
|
|
90
|
-
where(condition: WhereCondition): PromiseLike<void>;
|
|
41
|
+
selectMany<T = Record<string, unknown>>(
|
|
42
|
+
table: Table,
|
|
43
|
+
where?: WhereObject,
|
|
44
|
+
options?: SelectOptions,
|
|
45
|
+
): Promise<readonly T[]>;
|
|
46
|
+
fetchOne<T = Record<string, unknown>>(table: Table, where: WhereObject): Promise<T | undefined>;
|
|
47
|
+
insertOne<T = Record<string, unknown>>(
|
|
48
|
+
table: Table,
|
|
49
|
+
values: Record<string, unknown>,
|
|
50
|
+
): Promise<T | undefined>;
|
|
51
|
+
updateMany<T = Record<string, unknown>>(
|
|
52
|
+
table: Table,
|
|
53
|
+
set: Record<string, unknown>,
|
|
54
|
+
where: WhereObject,
|
|
55
|
+
): Promise<readonly T[]>;
|
|
56
|
+
deleteMany(table: Table, where: WhereObject): Promise<void>;
|
|
91
57
|
};
|
|
92
58
|
|
|
93
|
-
/**
|
|
94
|
-
* Cast helper for the `Record<string, unknown>[]` rows that
|
|
95
|
-
* `TenantDb.select()` returns.
|
|
96
|
-
*
|
|
97
|
-
* Usage:
|
|
98
|
-
* const rows = castTenantRows<MyRow>(
|
|
99
|
-
* await ctx.db.select({...}).from(myTable),
|
|
100
|
-
* );
|
|
101
|
-
*
|
|
102
|
-
* Why this exists: drizzle's `.select({col1: t.col1, ...})` natively
|
|
103
|
-
* returns `Array<{col1: T1, ...}>`, but our TenantDb wrapper erases
|
|
104
|
-
* that shape to `Record<string, unknown>[]` so it can centralize tenant-
|
|
105
|
-
* scoping. Until the wrapper preserves the typed-row shape (see memory:
|
|
106
|
-
* project_tenant_db_typed_rows), call sites need to assert the column
|
|
107
|
-
* shape they just selected. This helper:
|
|
108
|
-
* - centralises the cast (single grep target for the future refactor)
|
|
109
|
-
* - tags it with `@cast-boundary tenant-db-row` for the as-cast audit
|
|
110
|
-
* - documents the trade-off once instead of N times
|
|
111
|
-
*
|
|
112
|
-
* Removal plan: when TenantSelectQuery becomes generic over the
|
|
113
|
-
* column-shape, every `castTenantRows<T>(...)` call is just `await ...`
|
|
114
|
-
* and this helper goes away.
|
|
115
|
-
*/
|
|
116
59
|
// @cast-boundary tenant-db-row
|
|
117
60
|
export function castTenantRows<T>(rows: readonly Record<string, unknown>[]): readonly T[] {
|
|
118
61
|
return rows as unknown as readonly T[];
|
|
119
62
|
}
|
|
120
63
|
|
|
64
|
+
const KUMIKO_NAME_SYMBOL = Symbol.for("kumiko:schema:Name");
|
|
65
|
+
|
|
66
|
+
function tableNameOf(table: Table): string {
|
|
67
|
+
const sym = (table as unknown as Record<symbol, unknown>)[KUMIKO_NAME_SYMBOL];
|
|
68
|
+
return typeof sym === "string" ? sym : "<unknown>";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function hasTenantColumn(table: Table): boolean {
|
|
72
|
+
return (table as Record<string, unknown>)["tenantId"] !== undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
121
75
|
export function createTenantDb(
|
|
122
76
|
db: DbRunner,
|
|
123
77
|
tenantId: TenantId,
|
|
124
78
|
mode: TenantDbMode = "tenant",
|
|
125
79
|
tracer?: Tracer,
|
|
126
80
|
meter?: Meter,
|
|
127
|
-
// Pre-flight cancellation: when set, every query check
|
|
128
|
-
// `signal.throwIfAborted()` BEFORE issuing the SQL. The currently
|
|
129
|
-
// running query is not actively cancelled (postgres-js connection
|
|
130
|
-
// cancel is a separate, riskier feature). This still saves the bulk
|
|
131
|
-
// of the wasted work in handlers that fire many sequential queries
|
|
132
|
-
// — once the client disconnects, the next query throws and the rest
|
|
133
|
-
// of the chain falls away.
|
|
134
81
|
signal?: AbortSignal,
|
|
135
82
|
): TenantDb {
|
|
136
|
-
// If a meter was passed, make sure standard metrics are registered on it
|
|
137
|
-
// before we try to emit. Idempotent — buildServer typically registers them
|
|
138
|
-
// up front; this guards against test call-sites that wire up a TenantDb
|
|
139
|
-
// directly with a fresh meter.
|
|
140
83
|
if (meter) registerStandardMetrics(meter);
|
|
141
84
|
|
|
142
|
-
function hasTenantColumn(table: Table): boolean {
|
|
143
|
-
return table["tenantId"] !== undefined;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Drizzle's terminal builders (insert, update().where, delete().where) are
|
|
147
|
-
// thenable — `.then` is there so `await` works — but the declared return
|
|
148
|
-
// types don't include PromiseLike. Cast via this helper so the double-
|
|
149
|
-
// cast is named and lives in exactly one place per scope.
|
|
150
|
-
function asDrizzleThenable<T>(builder: unknown): PromiseLike<T> {
|
|
151
|
-
return builder as PromiseLike<T>; // @cast-boundary engine-bridge
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Wrap a DB query promise in a `db.query` span + emit the DB duration
|
|
155
|
-
// histogram. Row count is recorded when the result is an array (SELECTs
|
|
156
|
-
// + *.returning()). Metric is emitted both on success and on throw so
|
|
157
|
-
// slow failing queries show up too.
|
|
158
85
|
function withDbSpan<T>(
|
|
159
86
|
operation: "select" | "insert" | "update" | "delete",
|
|
160
87
|
table: Table,
|
|
161
|
-
|
|
162
|
-
):
|
|
163
|
-
// Pre-flight cancellation. Sits above the early-return so the check
|
|
164
|
-
// fires regardless of observability config — cancellation is a
|
|
165
|
-
// correctness feature, not an observability one.
|
|
88
|
+
runner: () => Promise<T>,
|
|
89
|
+
): Promise<T> {
|
|
166
90
|
signal?.throwIfAborted();
|
|
167
|
-
if (!tracer && !meter) return
|
|
168
|
-
const tableName =
|
|
91
|
+
if (!tracer && !meter) return runner();
|
|
92
|
+
const tableName = tableNameOf(table);
|
|
169
93
|
const start = performance.now();
|
|
170
94
|
const emitMetric = () => {
|
|
171
95
|
if (meter) {
|
|
@@ -174,10 +98,9 @@ export function createTenantDb(
|
|
|
174
98
|
};
|
|
175
99
|
|
|
176
100
|
if (!tracer) {
|
|
177
|
-
// Tracer absent but meter present: just time + emit, no span.
|
|
178
101
|
return (async () => {
|
|
179
102
|
try {
|
|
180
|
-
return await
|
|
103
|
+
return await runner();
|
|
181
104
|
} finally {
|
|
182
105
|
emitMetric();
|
|
183
106
|
}
|
|
@@ -196,7 +119,7 @@ export function createTenantDb(
|
|
|
196
119
|
},
|
|
197
120
|
async (span) => {
|
|
198
121
|
try {
|
|
199
|
-
const result = await
|
|
122
|
+
const result = await runner();
|
|
200
123
|
if (Array.isArray(result)) {
|
|
201
124
|
span.setAttribute("db.row_count", result.length);
|
|
202
125
|
}
|
|
@@ -208,227 +131,83 @@ export function createTenantDb(
|
|
|
208
131
|
);
|
|
209
132
|
}
|
|
210
133
|
|
|
211
|
-
//
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (!hasTenantColumn(table)) {
|
|
218
|
-
return extra.length > 0 ? and(...extra) : undefined;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if (mode === "system") {
|
|
222
|
-
// System mode: no tenant restriction, only pass through extra conditions
|
|
223
|
-
return extra.length > 0 ? and(...extra) : undefined;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Tenant mode: own data + reference data (zero-UUID tenantId for global rows).
|
|
227
|
-
// Drizzle's `or()` is typed `SQL | undefined` (variadic-empty case); both
|
|
228
|
-
// `eq()` args always produce SQL, so the cast documents that assumption.
|
|
229
|
-
const ownOrGlobal = or(
|
|
230
|
-
eq(table["tenantId"], tenantId),
|
|
231
|
-
eq(table["tenantId"], SYSTEM_TENANT_ID),
|
|
232
|
-
) as SQL; // @cast-boundary db-operator
|
|
233
|
-
return extra.length > 0 ? and(ownOrGlobal, ...extra) : ownOrGlobal;
|
|
134
|
+
// Reads see own-tenant rows + reference data (tenantId === SYSTEM_TENANT_ID).
|
|
135
|
+
// Writes never touch reference rows — those are system-mode only.
|
|
136
|
+
function readWhere(table: Table, where?: WhereObject): WhereObject | undefined {
|
|
137
|
+
if (!hasTenantColumn(table) || mode === "system") return where;
|
|
138
|
+
const tenantFilter: WhereObject = { tenantId: [tenantId, SYSTEM_TENANT_ID] };
|
|
139
|
+
return where ? { ...tenantFilter, ...where } : tenantFilter;
|
|
234
140
|
}
|
|
235
141
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
// could mutate global data by coincidence of id/condition. Only system-scope
|
|
240
|
-
// (r.systemScope()) may modify reference data.
|
|
241
|
-
|
|
242
|
-
function writeFilter(table: Table, ...extra: SQL[]): SQL | undefined {
|
|
243
|
-
if (!hasTenantColumn(table)) {
|
|
244
|
-
return extra.length > 0 ? and(...extra) : undefined;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (mode === "system") {
|
|
248
|
-
return extra.length > 0 ? and(...extra) : undefined;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const ownOnly = eq(table["tenantId"], tenantId);
|
|
252
|
-
return extra.length > 0 ? and(ownOnly, ...extra) : ownOnly;
|
|
142
|
+
function writeWhere(table: Table, where: WhereObject): WhereObject {
|
|
143
|
+
if (!hasTenantColumn(table) || mode === "system") return where;
|
|
144
|
+
return { tenantId, ...where };
|
|
253
145
|
}
|
|
254
146
|
|
|
255
|
-
// --- Write values (INSERT tenantId handling) ---
|
|
256
|
-
|
|
257
147
|
function insertValues(table: Table, data: Record<string, unknown>): Record<string, unknown> {
|
|
258
148
|
if (!hasTenantColumn(table)) return data;
|
|
259
|
-
|
|
260
|
-
if (mode === "system") {
|
|
261
|
-
// System mode: tenantId is a default the handler can override —
|
|
262
|
-
// e.g. to write a cross-tenant row under SYSTEM_TENANT_ID, or to
|
|
263
|
-
// target a foreign tenant's projection from a SystemAdmin action.
|
|
264
|
-
return { tenantId, ...data };
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Tenant mode: tenantId is forced, handler cannot override
|
|
149
|
+
if (mode === "system") return { tenantId, ...data };
|
|
268
150
|
return { ...data, tenantId };
|
|
269
151
|
}
|
|
270
152
|
|
|
271
|
-
// --- Select wrapper (lazy filter + chainable) ---
|
|
272
|
-
|
|
273
|
-
function wrapSelect(
|
|
274
|
-
// biome-ignore lint/suspicious/noExplicitAny: Drizzle internal query type
|
|
275
|
-
query: any,
|
|
276
|
-
table: Table,
|
|
277
|
-
filtered: boolean,
|
|
278
|
-
): TenantSelectQuery {
|
|
279
|
-
function ensureFiltered() {
|
|
280
|
-
if (filtered) return query;
|
|
281
|
-
const filter = readFilter(table);
|
|
282
|
-
return filter ? query.where(filter) : query;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return {
|
|
286
|
-
where(condition: SQL) {
|
|
287
|
-
const filter = readFilter(table, condition);
|
|
288
|
-
return wrapSelect(filter ? query.where(filter) : query.where(condition), table, true);
|
|
289
|
-
},
|
|
290
|
-
limit(n: number) {
|
|
291
|
-
return wrapSelect(ensureFiltered().limit(n), table, true);
|
|
292
|
-
},
|
|
293
|
-
offset(n: number) {
|
|
294
|
-
return wrapSelect(ensureFiltered().offset(n), table, true);
|
|
295
|
-
},
|
|
296
|
-
orderBy(...columns: SQL[]) {
|
|
297
|
-
return wrapSelect(ensureFiltered().orderBy(...columns), table, true);
|
|
298
|
-
},
|
|
299
|
-
for(strength: RowLockStrength) {
|
|
300
|
-
return wrapSelect(ensureFiltered().for(strength), table, true);
|
|
301
|
-
},
|
|
302
|
-
// biome-ignore lint/suspicious/noThenProperty: thenable for await
|
|
303
|
-
then(
|
|
304
|
-
resolve: ((value: Record<string, unknown>[]) => void) | null,
|
|
305
|
-
reject: ((reason: unknown) => void) | null,
|
|
306
|
-
) {
|
|
307
|
-
return withDbSpan<Record<string, unknown>[]>("select", table, () => ensureFiltered()).then(
|
|
308
|
-
(rows) => resolve?.(rows),
|
|
309
|
-
reject ?? undefined,
|
|
310
|
-
);
|
|
311
|
-
},
|
|
312
|
-
} as TenantSelectQuery; // @cast-boundary drizzle-bridge
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// --- Where helper for update/delete ---
|
|
316
|
-
|
|
317
|
-
function whereClause(table: Table, condition: SQL): SQL {
|
|
318
|
-
const filter = writeFilter(table, condition);
|
|
319
|
-
return filter ?? condition;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
153
|
return {
|
|
323
154
|
tenantId,
|
|
324
155
|
mode,
|
|
325
156
|
raw: db,
|
|
326
157
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
158
|
+
selectMany<T = Record<string, unknown>>(
|
|
159
|
+
table: Table,
|
|
160
|
+
where?: WhereObject,
|
|
161
|
+
options?: SelectOptions,
|
|
162
|
+
): Promise<readonly T[]> {
|
|
163
|
+
const filter = readWhere(table, where);
|
|
164
|
+
return withDbSpan("select", table, async () => bunSelectMany<T>(db, table, filter, options));
|
|
334
165
|
},
|
|
335
166
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
return withDbSpan<Record<string, unknown>[]>(
|
|
343
|
-
"insert",
|
|
344
|
-
table,
|
|
345
|
-
() => q.returning() as PromiseLike<Record<string, unknown>[]>, // @cast-boundary db-runner
|
|
346
|
-
);
|
|
347
|
-
},
|
|
348
|
-
onConflictDoUpdate(spec: ConflictUpdate) {
|
|
349
|
-
return withDbSpan<void>("insert", table, () =>
|
|
350
|
-
(
|
|
351
|
-
q as unknown as {
|
|
352
|
-
onConflictDoUpdate: (s: ConflictUpdate) => PromiseLike<void>;
|
|
353
|
-
}
|
|
354
|
-
).onConflictDoUpdate(spec),
|
|
355
|
-
);
|
|
356
|
-
},
|
|
357
|
-
onConflictDoNothing(spec?: { target: ConflictTarget }) {
|
|
358
|
-
return withDbSpan<void>("insert", table, () =>
|
|
359
|
-
(
|
|
360
|
-
q as unknown as {
|
|
361
|
-
onConflictDoNothing: (s?: { target: ConflictTarget }) => PromiseLike<void>;
|
|
362
|
-
}
|
|
363
|
-
).onConflictDoNothing(spec),
|
|
364
|
-
);
|
|
365
|
-
},
|
|
366
|
-
// biome-ignore lint/suspicious/noThenProperty: thenable for await
|
|
367
|
-
then(resolve, reject) {
|
|
368
|
-
return withDbSpan<void>("insert", table, () => asDrizzleThenable<void>(q)).then(
|
|
369
|
-
resolve,
|
|
370
|
-
reject,
|
|
371
|
-
);
|
|
372
|
-
},
|
|
373
|
-
} as TenantInsertValues; // @cast-boundary drizzle-bridge
|
|
374
|
-
},
|
|
375
|
-
};
|
|
167
|
+
fetchOne<T = Record<string, unknown>>(
|
|
168
|
+
table: Table,
|
|
169
|
+
where: WhereObject,
|
|
170
|
+
): Promise<T | undefined> {
|
|
171
|
+
const filter = readWhere(table, where) ?? {};
|
|
172
|
+
return withDbSpan("select", table, async () => bunFetchOne<T>(db, table, filter));
|
|
376
173
|
},
|
|
377
174
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
const wq = q.where(whereClause(table, condition));
|
|
385
|
-
return {
|
|
386
|
-
returning() {
|
|
387
|
-
return withDbSpan<Record<string, unknown>[]>(
|
|
388
|
-
"update",
|
|
389
|
-
table,
|
|
390
|
-
() => wq.returning() as PromiseLike<Record<string, unknown>[]>, // @cast-boundary db-runner
|
|
391
|
-
);
|
|
392
|
-
},
|
|
393
|
-
// biome-ignore lint/suspicious/noThenProperty: thenable for await
|
|
394
|
-
then(resolve, reject) {
|
|
395
|
-
return withDbSpan<void>("update", table, () => asDrizzleThenable<void>(wq)).then(
|
|
396
|
-
resolve,
|
|
397
|
-
reject,
|
|
398
|
-
);
|
|
399
|
-
},
|
|
400
|
-
} as TenantUpdateWhere; // @cast-boundary drizzle-bridge
|
|
401
|
-
},
|
|
402
|
-
returning(): PromiseLike<Record<string, unknown>[]> {
|
|
403
|
-
return Promise.reject(
|
|
404
|
-
new Error(
|
|
405
|
-
"TenantDb.update().set().returning() without .where() would mass-update all tenant rows. " +
|
|
406
|
-
"Add .where(...) first, or call .set(...).where(...).returning().",
|
|
407
|
-
),
|
|
408
|
-
);
|
|
409
|
-
},
|
|
410
|
-
// biome-ignore lint/suspicious/noThenProperty: thenable for await
|
|
411
|
-
then(resolve, reject) {
|
|
412
|
-
return Promise.reject(
|
|
413
|
-
new Error(
|
|
414
|
-
"TenantDb.update().set() awaited without .where() would mass-update all tenant rows. " +
|
|
415
|
-
"Add .where(...) before awaiting.",
|
|
416
|
-
),
|
|
417
|
-
).then(resolve, reject);
|
|
418
|
-
},
|
|
419
|
-
} as TenantUpdateSet; // @cast-boundary drizzle-bridge
|
|
420
|
-
},
|
|
421
|
-
};
|
|
175
|
+
insertOne<T = Record<string, unknown>>(
|
|
176
|
+
table: Table,
|
|
177
|
+
values: Record<string, unknown>,
|
|
178
|
+
): Promise<T | undefined> {
|
|
179
|
+
const data = insertValues(table, values);
|
|
180
|
+
return withDbSpan("insert", table, async () => bunInsertOne<T>(db, table, data));
|
|
422
181
|
},
|
|
423
182
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
183
|
+
updateMany<T = Record<string, unknown>>(
|
|
184
|
+
table: Table,
|
|
185
|
+
set: Record<string, unknown>,
|
|
186
|
+
where: WhereObject,
|
|
187
|
+
): Promise<readonly T[]> {
|
|
188
|
+
if (!where || Object.keys(where).length === 0) {
|
|
189
|
+
return Promise.reject(
|
|
190
|
+
new Error(
|
|
191
|
+
"TenantDb.updateMany without where would mass-update all tenant rows. Pass at least one where condition.",
|
|
192
|
+
),
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
const filter = writeWhere(table, where);
|
|
196
|
+
return withDbSpan("update", table, async () => bunUpdateMany<T>(db, table, set, filter));
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
deleteMany(table: Table, where: WhereObject): Promise<void> {
|
|
200
|
+
if (!where || Object.keys(where).length === 0) {
|
|
201
|
+
return Promise.reject(
|
|
202
|
+
new Error(
|
|
203
|
+
"TenantDb.deleteMany without where would mass-delete all tenant rows. Pass at least one where condition.",
|
|
204
|
+
),
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
const filter = writeWhere(table, where);
|
|
208
|
+
return withDbSpan("delete", table, async () => bunDeleteMany(db, table, filter));
|
|
432
209
|
},
|
|
433
210
|
};
|
|
434
211
|
}
|
|
212
|
+
|
|
213
|
+
export { asRawClient };
|
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
// Soft-Warning (console.warn):
|
|
10
10
|
// - Feature ruft eigene exposesApi via usesApi (Refactor-Leftover)
|
|
11
11
|
|
|
12
|
-
import { afterEach, beforeEach, describe, expect,
|
|
12
|
+
import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
|
|
13
13
|
import { validateBoot } from "../boot-validator";
|
|
14
14
|
import { defineFeature } from "../define-feature";
|
|
15
15
|
|
|
16
16
|
describe("validateBoot — r.exposesApi / r.usesApi", () => {
|
|
17
|
-
let warnSpy: ReturnType<typeof
|
|
17
|
+
let warnSpy: ReturnType<typeof spyOn>;
|
|
18
18
|
|
|
19
19
|
beforeEach(() => {
|
|
20
|
-
warnSpy =
|
|
20
|
+
warnSpy = spyOn(console, "warn").mockImplementation(() => {});
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
afterEach(() => {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// in derselben Entity referenzieren. Sonst silent data loss — wir wollen
|
|
5
5
|
// fail-fast beim Boot.
|
|
6
6
|
|
|
7
|
-
import { describe, expect, test } from "
|
|
7
|
+
import { describe, expect, test } from "bun:test";
|
|
8
8
|
import { validateBoot } from "../boot-validator";
|
|
9
9
|
import { defineFeature } from "../define-feature";
|
|
10
10
|
import { createEntity, createTimestampField, createTzField, locatedTimestamp } from "../factories";
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
//
|
|
15
15
|
// allowPlaintext-Marker unterdrückt Heuristik-Warnings.
|
|
16
16
|
|
|
17
|
-
import { afterEach, beforeEach, describe, expect,
|
|
17
|
+
import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
|
|
18
18
|
import { z } from "zod";
|
|
19
19
|
import { validateBoot } from "../boot-validator";
|
|
20
20
|
import { defineFeature } from "../define-feature";
|
|
@@ -48,10 +48,10 @@ function stubListHandler(r: any, entityName: string): void {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
describe("validateBoot — PII annotations", () => {
|
|
51
|
-
let warnSpy: ReturnType<typeof
|
|
51
|
+
let warnSpy: ReturnType<typeof spyOn>;
|
|
52
52
|
|
|
53
53
|
beforeEach(() => {
|
|
54
|
-
warnSpy =
|
|
54
|
+
warnSpy = spyOn(console, "warn").mockImplementation(() => {});
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
afterEach(() => {
|
|
@@ -428,10 +428,10 @@ describe("validateBoot — PII annotations", () => {
|
|
|
428
428
|
});
|
|
429
429
|
|
|
430
430
|
describe("validateBoot — retention", () => {
|
|
431
|
-
let warnSpy: ReturnType<typeof
|
|
431
|
+
let warnSpy: ReturnType<typeof spyOn>;
|
|
432
432
|
|
|
433
433
|
beforeEach(() => {
|
|
434
|
-
warnSpy =
|
|
434
|
+
warnSpy = spyOn(console, "warn").mockImplementation(() => {});
|
|
435
435
|
});
|
|
436
436
|
|
|
437
437
|
afterEach(() => {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// + retention.blockDelete + anonymize-Funktion
|
|
14
14
|
// + handler-access mit ROLES.TenantAdmin
|
|
15
15
|
|
|
16
|
-
import { afterEach, beforeEach, describe, expect,
|
|
16
|
+
import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
|
|
17
17
|
import { z } from "zod";
|
|
18
18
|
import { ROLES } from "../../auth";
|
|
19
19
|
import { validateBoot } from "../boot-validator";
|
|
@@ -27,10 +27,10 @@ import {
|
|
|
27
27
|
} from "../index";
|
|
28
28
|
|
|
29
29
|
describe("S0 Integration — full surface stack", () => {
|
|
30
|
-
let warnSpy: ReturnType<typeof
|
|
30
|
+
let warnSpy: ReturnType<typeof spyOn>;
|
|
31
31
|
|
|
32
32
|
beforeEach(() => {
|
|
33
|
-
warnSpy =
|
|
33
|
+
warnSpy = spyOn(console, "warn").mockImplementation(() => {});
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
afterEach(() => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { describe, expect, test } from "vitest";
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
3
2
|
import { z } from "zod";
|
|
3
|
+
import type { SchemaTable } from "../../db/dialect";
|
|
4
|
+
import { table, text } from "../../db/dialect";
|
|
4
5
|
import { validateBoot } from "../boot-validator";
|
|
5
6
|
import { createSystemConfig, createTenantConfig } from "../config-helpers";
|
|
6
7
|
import {
|
|
@@ -904,7 +905,7 @@ describe("boot-validator", () => {
|
|
|
904
905
|
// --- MultiStreamProjection delivery invariant (Welle 2.7) ---
|
|
905
906
|
|
|
906
907
|
describe("MultiStreamProjection delivery", () => {
|
|
907
|
-
const sinkTable =
|
|
908
|
+
const sinkTable = table("sink", { id: text("id").primaryKey() }) as unknown as SchemaTable;
|
|
908
909
|
|
|
909
910
|
test("rejects delivery='per-instance' combined with a backing table", () => {
|
|
910
911
|
const features = [
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// projection-Schritt rausgefiltert — sonst landet Server-Runtime
|
|
8
8
|
// im Browser-Bundle.
|
|
9
9
|
|
|
10
|
-
import { describe, expect, test } from "
|
|
10
|
+
import { describe, expect, test } from "bun:test";
|
|
11
11
|
import { buildAppSchema } from "../build-app-schema";
|
|
12
12
|
import { defineFeature } from "../define-feature";
|
|
13
13
|
import { createRegistry } from "../registry";
|