@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
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { DbConnection, DbTx } from "../db/connection";
|
|
2
|
+
import {
|
|
3
|
+
finalizeProjectionRebuild,
|
|
4
|
+
markProjectionRebuildFailed,
|
|
5
|
+
markProjectionRebuilding,
|
|
6
|
+
selectEventsForProjectionRebuild,
|
|
7
|
+
} from "../db/queries/projection-rebuild";
|
|
8
|
+
import { truncateTable } from "../db/queries/table-ops";
|
|
9
|
+
import { coerceRow, extractTableInfo, selectMany } from "../db/query";
|
|
3
10
|
import type { Registry, TenantId } from "../engine/types";
|
|
4
11
|
import {
|
|
5
12
|
eventsTable,
|
|
@@ -86,53 +93,43 @@ export async function rebuildProjection(
|
|
|
86
93
|
let lastProcessedEventId = 0n;
|
|
87
94
|
|
|
88
95
|
try {
|
|
89
|
-
await db.
|
|
90
|
-
|
|
91
|
-
// gets a row. FOR UPDATE would need the row to exist — upsert-first
|
|
92
|
-
// keeps it idempotent.
|
|
93
|
-
await tx
|
|
94
|
-
.insert(projectionStateTable)
|
|
95
|
-
.values({ name: projectionName, status: "rebuilding" })
|
|
96
|
-
.onConflictDoUpdate({
|
|
97
|
-
target: projectionStateTable.name,
|
|
98
|
-
set: {
|
|
99
|
-
status: "rebuilding",
|
|
100
|
-
lastError: null,
|
|
101
|
-
updatedAt: sql`now()`,
|
|
102
|
-
},
|
|
103
|
-
});
|
|
96
|
+
await db.begin(async (tx: DbTx) => {
|
|
97
|
+
await markProjectionRebuilding(tx, projectionName);
|
|
104
98
|
|
|
105
|
-
// Wipe the projection table. drizzle-orm's public API doesn't expose
|
|
106
|
-
// TRUNCATE, so we issue raw SQL — but `getTableName()` is the public
|
|
107
|
-
// accessor for the table's registered name, avoiding Symbol.for()
|
|
108
|
-
// internal lookups. The identifier is still quoted defensively.
|
|
109
99
|
const tableName = getTableName(projection.table);
|
|
110
|
-
await tx
|
|
100
|
+
await truncateTable(tx, tableName);
|
|
111
101
|
|
|
112
102
|
// Stream events in chronological order for every source. The event
|
|
113
|
-
// type filter
|
|
114
|
-
// doesn't care about early — important when a single source has more
|
|
115
|
-
// event types than the projection subscribes to.
|
|
103
|
+
// type filter prunes events the projection doesn't care about early.
|
|
116
104
|
const subscribed = Object.keys(projection.apply);
|
|
117
105
|
if (subscribed.length === 0) {
|
|
118
106
|
// nothing to replay, just mark idle — projection exists but doesn't
|
|
119
107
|
// subscribe to any event types on its sources yet.
|
|
120
108
|
} else {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
109
|
+
type EventRow = {
|
|
110
|
+
id: bigint;
|
|
111
|
+
aggregateId: string;
|
|
112
|
+
aggregateType: string;
|
|
113
|
+
tenantId: string;
|
|
114
|
+
version: number;
|
|
115
|
+
type: string;
|
|
116
|
+
eventVersion: number;
|
|
117
|
+
payload: Record<string, unknown>;
|
|
118
|
+
metadata: import("../event-store/event-store").EventMetadata;
|
|
119
|
+
createdAt: Temporal.Instant;
|
|
120
|
+
createdBy: string;
|
|
121
|
+
};
|
|
122
|
+
const sourcesList = [...sources];
|
|
123
|
+
const subscribedList = [...subscribed];
|
|
124
|
+
const rawEvents = await selectEventsForProjectionRebuild(tx, sourcesList, subscribedList);
|
|
125
|
+
const events = rawEvents.map((r) => {
|
|
126
|
+
const info = extractTableInfo(eventsTable);
|
|
127
|
+
return coerceRow(r, info) as EventRow;
|
|
128
|
+
});
|
|
131
129
|
|
|
132
130
|
// Upcasters run at read time: older stored payloads get walked
|
|
133
131
|
// through the registered r.eventMigration chain until their shape
|
|
134
|
-
// matches the current event version.
|
|
135
|
-
// v3 shape stays oblivious to v1 payloads still on disk.
|
|
132
|
+
// matches the current event version.
|
|
136
133
|
const upcasters = registry.getEventUpcasters();
|
|
137
134
|
for (const row of events) {
|
|
138
135
|
deps.signal?.throwIfAborted();
|
|
@@ -163,30 +160,13 @@ export async function rebuildProjection(
|
|
|
163
160
|
}
|
|
164
161
|
}
|
|
165
162
|
|
|
166
|
-
|
|
167
|
-
await tx
|
|
168
|
-
.update(projectionStateTable)
|
|
169
|
-
.set({
|
|
170
|
-
lastProcessedEventId,
|
|
171
|
-
status: "idle",
|
|
172
|
-
lastRebuildAt: sql`now()`,
|
|
173
|
-
lastError: null,
|
|
174
|
-
updatedAt: sql`now()`,
|
|
175
|
-
})
|
|
176
|
-
.where(eq(projectionStateTable.name, projectionName));
|
|
163
|
+
await finalizeProjectionRebuild(tx, projectionName, lastProcessedEventId);
|
|
177
164
|
});
|
|
178
165
|
} catch (e) {
|
|
179
166
|
// Outer catch: TX has been rolled back by Postgres already. Record the
|
|
180
|
-
// failure in a SEPARATE write so ops can see what happened
|
|
181
|
-
// rolled-back status change is gone, so we write failed+error now.
|
|
167
|
+
// failure in a SEPARATE write so ops can see what happened.
|
|
182
168
|
const message = e instanceof Error ? e.message : String(e);
|
|
183
|
-
await db
|
|
184
|
-
.insert(projectionStateTable)
|
|
185
|
-
.values({ name: projectionName, status: "failed", lastError: message })
|
|
186
|
-
.onConflictDoUpdate({
|
|
187
|
-
target: projectionStateTable.name,
|
|
188
|
-
set: { status: "failed", lastError: message, updatedAt: sql`now()` },
|
|
189
|
-
});
|
|
169
|
+
await markProjectionRebuildFailed(db, projectionName, message);
|
|
190
170
|
// Failure metric: duration until throw, 0 events "delivered" (the replayed
|
|
191
171
|
// rows were rolled back — counting them would overstate live delivery).
|
|
192
172
|
// success=false label distinguishes these in Prom dashboards.
|
|
@@ -219,11 +199,16 @@ export async function rebuildProjection(
|
|
|
219
199
|
return result;
|
|
220
200
|
}
|
|
221
201
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
202
|
+
const KUMIKO_NAME_SYMBOL = Symbol.for("kumiko:schema:Name");
|
|
203
|
+
function getTableName(table: unknown): string {
|
|
204
|
+
if (typeof table !== "object" || table === null) {
|
|
205
|
+
throw new Error("projection-rebuild: projection.table is not a pgTable object");
|
|
206
|
+
}
|
|
207
|
+
const name = (table as Record<symbol, unknown>)[KUMIKO_NAME_SYMBOL];
|
|
208
|
+
if (typeof name !== "string") {
|
|
209
|
+
throw new Error("projection-rebuild: projection.table missing drizzle name symbol");
|
|
210
|
+
}
|
|
211
|
+
return name;
|
|
227
212
|
}
|
|
228
213
|
|
|
229
214
|
// Read-only status for one projection. Returns null if the projection was
|
|
@@ -239,10 +224,15 @@ export async function getProjectionState(
|
|
|
239
224
|
readonly lastError: string | null;
|
|
240
225
|
readonly updatedAt: Temporal.Instant;
|
|
241
226
|
} | null> {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
227
|
+
type Row = {
|
|
228
|
+
readonly name: string;
|
|
229
|
+
readonly status: string;
|
|
230
|
+
readonly lastProcessedEventId: bigint;
|
|
231
|
+
readonly lastRebuildAt: Temporal.Instant | null;
|
|
232
|
+
readonly lastError: string | null;
|
|
233
|
+
readonly updatedAt: Temporal.Instant;
|
|
234
|
+
};
|
|
235
|
+
const [row] = await selectMany<Row>(db, projectionStateTable, { name: projectionName });
|
|
246
236
|
if (!row) return null;
|
|
247
237
|
return {
|
|
248
238
|
name: row.name,
|
|
@@ -277,8 +267,16 @@ export async function listProjectionsWithState(
|
|
|
277
267
|
readonly lastError: string | null;
|
|
278
268
|
}>
|
|
279
269
|
> {
|
|
270
|
+
type StateRow = {
|
|
271
|
+
name: string;
|
|
272
|
+
status: string;
|
|
273
|
+
lastProcessedEventId: bigint;
|
|
274
|
+
lastRebuildAt: Temporal.Instant | null;
|
|
275
|
+
lastError: string | null;
|
|
276
|
+
updatedAt: Temporal.Instant;
|
|
277
|
+
};
|
|
280
278
|
const projections = registry.getAllProjections();
|
|
281
|
-
const stateRows = await db
|
|
279
|
+
const stateRows = await selectMany<StateRow>(db, projectionStateTable);
|
|
282
280
|
const stateByName = new Map(stateRows.map((r) => [r.name, r]));
|
|
283
281
|
|
|
284
282
|
return [...projections.values()]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
// sql now comes from native dialect
|
|
2
2
|
import type { DbConnection } from "../db/connection";
|
|
3
|
-
import { bigint, index, instant, table as pgTable, text } from "../db/dialect";
|
|
3
|
+
import { bigint, index, instant, table as pgTable, sql, text } from "../db/dialect";
|
|
4
4
|
import { tableExists } from "../db/schema-inspection";
|
|
5
5
|
import { unsafePushTables } from "../stack";
|
|
6
6
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, test } from "
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import {
|
|
3
3
|
ADJECTIVES,
|
|
4
4
|
generateAdjNounName,
|
|
@@ -12,16 +12,16 @@ describe("generateAdjNounName", () => {
|
|
|
12
12
|
const name = generateAdjNounName();
|
|
13
13
|
const [adj, noun, ...rest] = name.split("-");
|
|
14
14
|
expect(rest).toEqual([]);
|
|
15
|
-
expect(ADJECTIVES).toContain(adj);
|
|
16
|
-
expect(NOUNS).toContain(noun);
|
|
15
|
+
expect(ADJECTIVES).toContain(adj!);
|
|
16
|
+
expect(NOUNS).toContain(noun!);
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
test("custom separator", () => {
|
|
20
20
|
const name = generateAdjNounName({ separator: "_" });
|
|
21
21
|
expect(name).toMatch(/^[a-z]+_[a-z]+$/);
|
|
22
22
|
const [adj, noun] = name.split("_");
|
|
23
|
-
expect(ADJECTIVES).toContain(adj);
|
|
24
|
-
expect(NOUNS).toContain(noun);
|
|
23
|
+
expect(ADJECTIVES).toContain(adj!);
|
|
24
|
+
expect(NOUNS).toContain(noun!);
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
test("mit suffix: <adj>-<noun>-<suffix>", () => {
|
|
@@ -29,11 +29,11 @@ describe("generateAdjNounName", () => {
|
|
|
29
29
|
const parts = name.split("-");
|
|
30
30
|
expect(parts).toHaveLength(3);
|
|
31
31
|
const [adj, noun, suffix] = parts;
|
|
32
|
-
expect(ADJECTIVES).toContain(adj);
|
|
33
|
-
expect(NOUNS).toContain(noun);
|
|
34
|
-
expect(suffix).toMatch(/^[a-z2-9]{3}$/);
|
|
32
|
+
expect(ADJECTIVES).toContain(adj!);
|
|
33
|
+
expect(NOUNS).toContain(noun!);
|
|
34
|
+
expect(suffix!).toMatch(/^[a-z2-9]{3}$/);
|
|
35
35
|
// No-confusable: keine 0/1/o/l/i
|
|
36
|
-
expect(suffix).not.toMatch(/[01oli]/);
|
|
36
|
+
expect(suffix!).not.toMatch(/[01oli]/);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
test("custom adjectives + nouns Wahl", () => {
|
|
@@ -94,7 +94,7 @@ describe("generateUniqueName", () => {
|
|
|
94
94
|
},
|
|
95
95
|
});
|
|
96
96
|
expect(seen).toHaveLength(1);
|
|
97
|
-
expect(name).toBe(seen[0]);
|
|
97
|
+
expect(name).toBe(seen[0]!);
|
|
98
98
|
// Clean = nur 2 Teile (adj + noun), kein suffix
|
|
99
99
|
expect(name.split("-")).toHaveLength(2);
|
|
100
100
|
});
|
|
@@ -112,11 +112,11 @@ describe("generateUniqueName", () => {
|
|
|
112
112
|
expect(tried).toHaveLength(4);
|
|
113
113
|
// Erste 3 ohne suffix
|
|
114
114
|
for (let i = 0; i < 3; i++) {
|
|
115
|
-
expect(tried[i]
|
|
115
|
+
expect(tried[i]!.split("-")).toHaveLength(2);
|
|
116
116
|
}
|
|
117
117
|
// 4. Versuch hat suffix
|
|
118
|
-
expect(tried[3]
|
|
119
|
-
expect(name).toBe(tried[3]);
|
|
118
|
+
expect(tried[3]!.split("-")).toHaveLength(3);
|
|
119
|
+
expect(name).toBe(tried[3]!);
|
|
120
120
|
});
|
|
121
121
|
|
|
122
122
|
test("wirft wenn maxTotalAttempts erschöpft", async () => {
|
package/src/rate-limit/__tests__/{dispatcher-l3.integration.ts → dispatcher-l3.integration.test.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
1
2
|
import { defineFeature, defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { createTestUser, setupTestStack, type TestStack, TestUsers } from "../../stack";
|
|
5
5
|
|
package/src/rate-limit/__tests__/{middleware.integration.ts → middleware.integration.test.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
1
2
|
import { Hono } from "hono";
|
|
2
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
|
3
3
|
import { createTestRedis, type TestRedis } from "../../stack";
|
|
4
4
|
import { authEndpointRateLimit, globalIpRateLimit } from "../middleware";
|
|
5
5
|
import { createRateLimitResolver, type RateLimitResolver } from "../resolver";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "
|
|
1
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
import { RateLimitError } from "../../errors";
|
|
3
3
|
import { createTestRedis, type TestRedis } from "../../stack";
|
|
4
4
|
import {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
1
2
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
3
|
import { generateId as uuid } from "@cosmicdrift/kumiko-framework/utils";
|
|
3
4
|
import { Meilisearch } from "meilisearch";
|
|
4
|
-
import { afterAll, beforeAll, describe, expect, test } from "vitest";
|
|
5
5
|
import { createMeilisearchAdapter } from "../meilisearch-adapter";
|
|
6
6
|
import type { SearchAdapter } from "../types";
|
|
7
7
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
1
2
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { describe, expect, test, vi } from "vitest";
|
|
3
3
|
import { createDekCache } from "../dek-cache";
|
|
4
4
|
import type { MasterKeyProvider } from "../types";
|
|
5
5
|
|
|
@@ -153,8 +153,6 @@ describe("DekCache", () => {
|
|
|
153
153
|
t = 5 * 60 * 1000 + 1; // just past 5 min
|
|
154
154
|
await cache.unwrapDek(Buffer.from("x"), 1, provider);
|
|
155
155
|
expect(provider.unwrapCallCount()).toBe(2);
|
|
156
|
-
// avoid vitest unused-import warning
|
|
157
|
-
void vi;
|
|
158
156
|
});
|
|
159
157
|
|
|
160
158
|
test("LRU: evicts oldest when maxEntries is reached", async () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
1
2
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { describe, expect, test } from "vitest";
|
|
3
3
|
import { createEnvMasterKeyProvider } from "../env-master-key-provider";
|
|
4
4
|
|
|
5
5
|
function env(vars: Record<string, string>): Record<string, string> {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
1
2
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { describe, expect, test } from "vitest";
|
|
3
3
|
import { createEnvMasterKeyProvider } from "../env-master-key-provider";
|
|
4
4
|
import { decryptValue, encryptValue } from "../envelope";
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
1
2
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { describe, expect, test } from "vitest";
|
|
3
3
|
import { createEnvMasterKeyProvider } from "../env-master-key-provider";
|
|
4
4
|
import { decryptValue, encryptValue } from "../envelope";
|
|
5
5
|
import { rewrapDek } from "../rotation";
|
package/src/stack/db.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Test-DB Factory: CREATE DATABASE → connect → createEventsTable.
|
|
2
|
+
// Provider-agnostic via createConnection (DB_PROVIDER env).
|
|
3
|
+
// postgres-js = default. DB_PROVIDER=bun = Bun.SQL (experimentell).
|
|
4
|
+
|
|
5
|
+
import { createConnection } from "../db/api";
|
|
6
|
+
import { createDatabase, databaseExists, dropDatabaseIfExists } from "../db/queries/test-stack";
|
|
7
|
+
import { ensureTemporalPolyfill } from "../time/polyfill";
|
|
3
8
|
import { generateId } from "../utils";
|
|
4
9
|
|
|
5
10
|
function requireEnv(name: string): string {
|
|
@@ -13,88 +18,60 @@ function requireEnv(name: string): string {
|
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
export type TestDb = {
|
|
16
|
-
db:
|
|
17
|
-
client:
|
|
21
|
+
db: unknown;
|
|
22
|
+
client: unknown;
|
|
18
23
|
dbName: string;
|
|
19
24
|
cleanup: () => Promise<void>;
|
|
20
25
|
};
|
|
21
26
|
|
|
22
27
|
export type CreateTestDbOptions = {
|
|
23
|
-
/** Override TEST_DATABASE_URL. Rare — mostly for tests that want a
|
|
24
|
-
* non-default Postgres (e.g. a read-replica probe). */
|
|
25
28
|
readonly baseUrl?: string;
|
|
26
|
-
/** Use a specific DB name instead of the default
|
|
27
|
-
* `kumiko_test_<8chars>`. Combined with `persistent: true`, lets a
|
|
28
|
-
* dev server keep state across restarts. Must be a legal Postgres
|
|
29
|
-
* identifier — the caller is responsible for matching the usual
|
|
30
|
-
* [a-z_0-9]+ shape. */
|
|
31
29
|
readonly dbName?: string;
|
|
32
|
-
/** When true, cleanup() is a no-op and the DB survives. Also
|
|
33
|
-
* changes CREATE DATABASE to IF-NOT-EXISTS semantics so restarts
|
|
34
|
-
* reuse the same storage. Default false (test contract: fresh DB
|
|
35
|
-
* per call, dropped on cleanup). */
|
|
36
30
|
readonly persistent?: boolean;
|
|
37
31
|
};
|
|
38
32
|
|
|
39
33
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* call `createTestDb()` with no args; only dev-server and niche tests
|
|
43
|
-
* need the options form.
|
|
34
|
+
* Provider-agnostische Test-DB. createConnection liest DB_PROVIDER.
|
|
35
|
+
* Für Bun.SQL: DB_PROVIDER=bun setzen (experimentell — siehe db/bun-provider.ts).
|
|
44
36
|
*/
|
|
45
37
|
export async function createTestDb(arg?: string | CreateTestDbOptions): Promise<TestDb> {
|
|
38
|
+
await ensureTemporalPolyfill();
|
|
46
39
|
const opts: CreateTestDbOptions = typeof arg === "string" ? { baseUrl: arg } : (arg ?? {});
|
|
47
40
|
const url = opts.baseUrl ?? requireEnv("TEST_DATABASE_URL");
|
|
48
|
-
// slice(-8) — the last 8 hex chars of a UUIDv7 are pure random (the
|
|
49
|
-
// front 48 bits are a timestamp, which would collide across workers
|
|
50
|
-
// that start within the same millisecond).
|
|
51
41
|
const dbName = opts.dbName ?? `kumiko_test_${generateId().slice(-8)}`;
|
|
52
42
|
const adminUrl = url.replace(/\/[^/]+$/, "/postgres");
|
|
53
43
|
|
|
54
|
-
const
|
|
44
|
+
const admin = await createConnection(adminUrl, { maxConnections: 1 });
|
|
55
45
|
try {
|
|
56
46
|
if (opts.persistent) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const existing = await adminClient<{ exists: boolean }[]>`
|
|
60
|
-
SELECT EXISTS (SELECT 1 FROM pg_database WHERE datname = ${dbName}) AS exists
|
|
61
|
-
`;
|
|
62
|
-
if (!existing[0]?.exists) {
|
|
63
|
-
await adminClient.unsafe(`CREATE DATABASE "${dbName}"`);
|
|
47
|
+
if (!(await databaseExists(admin.db, dbName))) {
|
|
48
|
+
await createDatabase(admin.db, dbName);
|
|
64
49
|
}
|
|
65
50
|
} else {
|
|
66
|
-
await
|
|
51
|
+
await createDatabase(admin.db, dbName);
|
|
67
52
|
}
|
|
68
53
|
} finally {
|
|
69
|
-
await
|
|
54
|
+
await admin.close();
|
|
70
55
|
}
|
|
71
56
|
|
|
72
57
|
const testUrl = url.replace(/\/[^/]+$/, `/${dbName}`);
|
|
73
|
-
const
|
|
74
|
-
const db = drizzle(client);
|
|
58
|
+
const conn = await createConnection(testUrl);
|
|
75
59
|
|
|
76
|
-
// Every ES-entity writes events; auto-create the events table so tests that
|
|
77
|
-
// go straight to createTestDb (not setupTestStack) also work out of the box.
|
|
78
|
-
// In persistent mode this is idempotent: createEventsTable emits IF NOT
|
|
79
|
-
// EXISTS so a second boot is a no-op.
|
|
80
60
|
const { createEventsTable } = await import("../event-store");
|
|
81
|
-
await createEventsTable(db);
|
|
61
|
+
await createEventsTable(conn.db);
|
|
82
62
|
|
|
83
63
|
return {
|
|
84
|
-
db,
|
|
85
|
-
client,
|
|
64
|
+
db: conn.db,
|
|
65
|
+
client: conn.client,
|
|
86
66
|
dbName,
|
|
87
67
|
cleanup: async () => {
|
|
88
|
-
await
|
|
89
|
-
// Persistent mode: dev-server owns the DB lifecycle — don't drop
|
|
90
|
-
// on process exit. `yarn kumiko clean-test-dbs` is the escape
|
|
91
|
-
// hatch when you really want to start over.
|
|
68
|
+
await conn.close();
|
|
92
69
|
if (!opts.persistent) {
|
|
93
|
-
const
|
|
70
|
+
const admin2 = await createConnection(adminUrl, { maxConnections: 1 });
|
|
94
71
|
try {
|
|
95
|
-
await
|
|
72
|
+
await dropDatabaseIfExists(admin2.db, dbName);
|
|
96
73
|
} finally {
|
|
97
|
-
await
|
|
74
|
+
await admin2.close();
|
|
98
75
|
}
|
|
99
76
|
}
|
|
100
77
|
},
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { getTableName } from "drizzle-orm";
|
|
2
1
|
import { tableExists } from "../db/schema-inspection";
|
|
3
2
|
import type { Registry } from "../engine/types";
|
|
4
3
|
import { unsafePushTables } from "./table-helpers";
|
|
@@ -35,9 +34,8 @@ export async function pushEntityProjectionTables(
|
|
|
35
34
|
if (!proj.isImplicit) continue;
|
|
36
35
|
if (seen.has(proj.table)) continue;
|
|
37
36
|
seen.add(proj.table);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const physical = getTableName(proj.table as Parameters<typeof getTableName>[0]);
|
|
37
|
+
const tableRec = proj.table as unknown as Record<symbol, unknown>;
|
|
38
|
+
const physical = tableRec[Symbol.for("kumiko:schema:Name")] as string;
|
|
41
39
|
if (await tableExists(stack.db, `public.${physical}`)) {
|
|
42
40
|
logInfo(`[kumiko-stack] table ${physical} already exists — skipping create`);
|
|
43
41
|
continue;
|