@cosmicdrift/kumiko-framework 0.14.0 → 0.16.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/auth-routes.ts +2 -5
- 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 +842 -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/compliance/profiles.ts +1 -4
- 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__/cursor.test.ts +8 -32
- 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__/migrate-generator.test.ts +71 -0
- package/src/db/__tests__/migrate-runner.test.ts +19 -0
- 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__/pg-error.test.ts +43 -0
- 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 +54 -46
- 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__/duration-utils.test.ts +16 -0
- 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-access.test.ts +38 -0
- 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__/no-return-guard.test.ts +17 -0
- 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__/unmanaged-table.test.ts +98 -0
- 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 +37 -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/feature-ast/extractors/shared.ts +2 -3
- 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 +21 -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 +47 -2
- package/src/engine/types/fields.ts +4 -5
- package/src/engine/types/index.ts +2 -0
- 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__/error-helpers.test.ts +44 -0
- package/src/errors/__tests__/field-issue-compat.test.ts +16 -0
- package/src/errors/__tests__/write-failures.test.ts +1 -1
- package/src/errors/classes.ts +5 -19
- package/src/errors/field-issue.ts +11 -0
- package/src/errors/index.ts +1 -0
- package/src/errors/zod-bridge.ts +3 -2
- 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 +11 -56
- 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-utils.test.ts +107 -0
- 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-keys.test.ts +12 -0
- 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-utils.ts +8 -7
- 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 +10 -9
- 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__/case.test.ts +16 -0
- package/src/utils/__tests__/env-parse.test.ts +1 -1
- package/src/utils/__tests__/is-plain-object.test.ts +16 -0
- package/src/utils/__tests__/parse-string-array-json.test.ts +16 -0
- package/src/utils/__tests__/safe-json.test.ts +22 -0
- package/src/utils/case.ts +6 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/is-plain-object.ts +4 -0
- package/src/utils/parse-string-array-json.ts +14 -0
- package/CHANGELOG.md +0 -474
- 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,4 +1,4 @@
|
|
|
1
|
-
import { afterAll, beforeAll, describe, expect, test } from "
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
2
|
import { requestContext } from "../../api/request-context";
|
|
3
3
|
import { createRegistry, defineFeature } from "../../engine";
|
|
4
4
|
import type { AppContext, Registry } from "../../engine/types";
|
|
@@ -192,7 +192,7 @@ describe("scenario 2: scheduled job", () => {
|
|
|
192
192
|
|
|
193
193
|
// BullMQ's repeatable scheduler needs a second or two to register its
|
|
194
194
|
// first tick — a generous delay schedule covers the startup window.
|
|
195
|
-
test("cron job fires via BullMQ scheduler",
|
|
195
|
+
test("cron job fires via BullMQ scheduler", async () => {
|
|
196
196
|
clearLog();
|
|
197
197
|
await withRunner(async () => {
|
|
198
198
|
await waitFor(
|
|
@@ -278,7 +278,7 @@ describe("concurrency: skip", () => {
|
|
|
278
278
|
});
|
|
279
279
|
|
|
280
280
|
describe("concurrency: sequential", () => {
|
|
281
|
-
test("same-name dispatches run strictly one after the other",
|
|
281
|
+
test("same-name dispatches run strictly one after the other", async () => {
|
|
282
282
|
clearLog();
|
|
283
283
|
await withRunner(async (runner) => {
|
|
284
284
|
// Three rapid dispatches. Parallel mode would land all entries
|
|
@@ -318,7 +318,7 @@ describe("concurrency: sequential", () => {
|
|
|
318
318
|
});
|
|
319
319
|
});
|
|
320
320
|
|
|
321
|
-
test("lock is released even when the handler throws",
|
|
321
|
+
test("lock is released even when the handler throws", async () => {
|
|
322
322
|
clearLog();
|
|
323
323
|
await withRunner(async (runner) => {
|
|
324
324
|
// First dispatch fails. If the finally-path didn't release the lock,
|
|
@@ -355,9 +355,7 @@ describe("concurrency: sequential", () => {
|
|
|
355
355
|
});
|
|
356
356
|
});
|
|
357
357
|
|
|
358
|
-
test("lock release is value-matched: foreign tokens survive expiration races", {
|
|
359
|
-
timeout: 5_000,
|
|
360
|
-
}, async () => {
|
|
358
|
+
test("lock release is value-matched: foreign tokens survive expiration races", async () => {
|
|
361
359
|
// Pin the contract that distributed-lock's release script enforces:
|
|
362
360
|
// a release call from a worker whose token has already expired and
|
|
363
361
|
// been claimed by someone else must NOT delete the new owner's lock.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Full-stack proof for lifecycle ↔ buildServer wiring.
|
|
2
2
|
// Drives drain() directly — SIGTERM plumbing has its own unit test.
|
|
3
3
|
|
|
4
|
-
import { afterAll, afterEach, beforeAll, describe, expect, test } from "
|
|
4
|
+
import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test";
|
|
5
5
|
import { defineFeature } from "../../engine";
|
|
6
6
|
import { setupTestStack, type TestStack } from "../../stack";
|
|
7
7
|
import { sharedWidgetEntity } from "../../testing";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
2
|
import { createLifecycle } from "../lifecycle";
|
|
3
3
|
|
|
4
4
|
describe("lifecycle — state machine", () => {
|
|
@@ -20,7 +20,7 @@ describe("lifecycle — state machine", () => {
|
|
|
20
20
|
|
|
21
21
|
test("markReady from 'ready' is a no-op", () => {
|
|
22
22
|
const lc = createLifecycle({ startReady: true });
|
|
23
|
-
const listener =
|
|
23
|
+
const listener = mock();
|
|
24
24
|
lc.onStateChange(listener);
|
|
25
25
|
lc.markReady();
|
|
26
26
|
expect(listener).not.toHaveBeenCalled();
|
|
@@ -70,7 +70,7 @@ describe("lifecycle — shutdown hooks", () => {
|
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
test("one failing hook does not block the others, and the error is logged", async () => {
|
|
73
|
-
const logger = { error:
|
|
73
|
+
const logger = { error: mock() };
|
|
74
74
|
const lc = createLifecycle({ startReady: true, logger });
|
|
75
75
|
const calls: string[] = [];
|
|
76
76
|
lc.registerShutdownHook("healthy-a", async () => {
|
|
@@ -171,7 +171,7 @@ describe("lifecycle — onStateChange", () => {
|
|
|
171
171
|
|
|
172
172
|
test("unsubscribe stops further callbacks", () => {
|
|
173
173
|
const lc = createLifecycle();
|
|
174
|
-
const cb =
|
|
174
|
+
const cb = mock();
|
|
175
175
|
const unsubscribe = lc.onStateChange(cb);
|
|
176
176
|
lc.markReady();
|
|
177
177
|
expect(cb).toHaveBeenCalledTimes(1);
|
|
@@ -184,9 +184,9 @@ describe("lifecycle — onStateChange", () => {
|
|
|
184
184
|
});
|
|
185
185
|
|
|
186
186
|
test("broken listener does not break others, and the error is logged", () => {
|
|
187
|
-
const logger = { error:
|
|
187
|
+
const logger = { error: mock() };
|
|
188
188
|
const lc = createLifecycle({ logger });
|
|
189
|
-
const healthy =
|
|
189
|
+
const healthy = mock();
|
|
190
190
|
lc.onStateChange(() => {
|
|
191
191
|
throw new Error("subscriber exploded");
|
|
192
192
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
2
|
import { createLifecycle } from "../lifecycle";
|
|
3
3
|
import { attachSignalHandlers } from "../signal-handlers";
|
|
4
4
|
import { createTestLifecycle } from "./create-test-lifecycle";
|
|
@@ -6,7 +6,7 @@ import { createTestLifecycle } from "./create-test-lifecycle";
|
|
|
6
6
|
describe("attachSignalHandlers", () => {
|
|
7
7
|
test("SIGTERM triggers drain and calls exit(0)", async () => {
|
|
8
8
|
const lc = createLifecycle({ startReady: true });
|
|
9
|
-
const exit =
|
|
9
|
+
const exit = mock();
|
|
10
10
|
const hookCalls: string[] = [];
|
|
11
11
|
lc.registerShutdownHook("spy", async (signal) => {
|
|
12
12
|
hookCalls.push(signal);
|
|
@@ -25,7 +25,7 @@ describe("attachSignalHandlers", () => {
|
|
|
25
25
|
|
|
26
26
|
test("SIGINT path drains with the right signal label", async () => {
|
|
27
27
|
const lc = createLifecycle({ startReady: true });
|
|
28
|
-
const exit =
|
|
28
|
+
const exit = mock();
|
|
29
29
|
const seen: string[] = [];
|
|
30
30
|
lc.registerShutdownHook("spy", async (signal) => {
|
|
31
31
|
seen.push(signal);
|
|
@@ -44,7 +44,7 @@ describe("attachSignalHandlers", () => {
|
|
|
44
44
|
|
|
45
45
|
test("multiple SIGTERMs still call exit exactly once", async () => {
|
|
46
46
|
const lc = createLifecycle({ startReady: true });
|
|
47
|
-
const exit =
|
|
47
|
+
const exit = mock();
|
|
48
48
|
// Slow hook so we can fire additional signals while drain is in-flight.
|
|
49
49
|
lc.registerShutdownHook("slow", async () => {
|
|
50
50
|
await new Promise((r) => setTimeout(r, 30));
|
|
@@ -74,7 +74,7 @@ describe("attachSignalHandlers", () => {
|
|
|
74
74
|
throw new Error("drain itself exploded");
|
|
75
75
|
},
|
|
76
76
|
});
|
|
77
|
-
const exit =
|
|
77
|
+
const exit = mock();
|
|
78
78
|
const handle = attachSignalHandlers(brokenLifecycle, { exit, signals: ["SIGTERM"] });
|
|
79
79
|
try {
|
|
80
80
|
process.emit("SIGTERM");
|
|
@@ -87,7 +87,7 @@ describe("attachSignalHandlers", () => {
|
|
|
87
87
|
|
|
88
88
|
test("detach() removes the process listeners", () => {
|
|
89
89
|
const lc = createLifecycle({ startReady: true });
|
|
90
|
-
const exit =
|
|
90
|
+
const exit = mock();
|
|
91
91
|
const before = process.listenerCount("SIGTERM");
|
|
92
92
|
const handle = attachSignalHandlers(lc, { exit, signals: ["SIGTERM"] });
|
|
93
93
|
expect(process.listenerCount("SIGTERM")).toBe(before + 1);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Projection-Tabelle muss der Detector die richtigen Tabellen-Namen
|
|
4
4
|
// melden, damit migrate apply den richtigen Rebuild triggert.
|
|
5
5
|
|
|
6
|
-
import { describe, expect, test } from "
|
|
6
|
+
import { describe, expect, test } from "bun:test";
|
|
7
7
|
import { compareSnapshots } from "../projection-detection";
|
|
8
8
|
import type { Snapshot, SnapshotTable } from "../schema-drift";
|
|
9
9
|
|
package/src/migrations/__tests__/{detect-drift.integration.ts → detect-drift.integration.test.ts}
RENAMED
|
@@ -4,18 +4,20 @@
|
|
|
4
4
|
// hier blockiert Container-Starts; jeder False-Negative lässt
|
|
5
5
|
// Schema-Drift unentdeckt durch.
|
|
6
6
|
|
|
7
|
+
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
7
8
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
8
9
|
import { tmpdir } from "node:os";
|
|
9
10
|
import { join } from "node:path";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
|
|
12
|
+
import { asRawClient } from "../../db/query";
|
|
13
|
+
import { ensureTemporalPolyfill } from "../../time/polyfill";
|
|
13
14
|
import { detectDrift } from "../schema-drift";
|
|
14
15
|
|
|
15
|
-
let testDb:
|
|
16
|
+
let testDb: BunTestDb;
|
|
16
17
|
let migrationsDir: string;
|
|
17
18
|
|
|
18
19
|
beforeAll(async () => {
|
|
20
|
+
await ensureTemporalPolyfill();
|
|
19
21
|
testDb = await createTestDb();
|
|
20
22
|
});
|
|
21
23
|
|
|
@@ -82,8 +84,8 @@ function writeSnapshotSimple(idx: number, tableNames: string[]): void {
|
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
async function ensureDrizzleMigrationsTable(): Promise<void> {
|
|
85
|
-
await testDb.db.
|
|
86
|
-
await testDb.db.
|
|
87
|
+
await asRawClient(testDb.db).unsafe(`CREATE SCHEMA IF NOT EXISTS drizzle`);
|
|
88
|
+
await asRawClient(testDb.db).unsafe(`
|
|
87
89
|
CREATE TABLE IF NOT EXISTS drizzle.__drizzle_migrations (
|
|
88
90
|
id serial PRIMARY KEY,
|
|
89
91
|
hash text NOT NULL,
|
|
@@ -93,12 +95,13 @@ async function ensureDrizzleMigrationsTable(): Promise<void> {
|
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
async function dropDrizzleMigrationsTable(): Promise<void> {
|
|
96
|
-
await testDb.db.
|
|
98
|
+
await asRawClient(testDb.db).unsafe(`DROP TABLE IF EXISTS drizzle.__drizzle_migrations`);
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
async function insertAppliedMigration(hash: string): Promise<void> {
|
|
100
|
-
await testDb.db.
|
|
101
|
-
|
|
102
|
+
await asRawClient(testDb.db).unsafe(
|
|
103
|
+
`INSERT INTO drizzle.__drizzle_migrations (hash, created_at) VALUES ($1, $2)`,
|
|
104
|
+
[hash, Date.now()],
|
|
102
105
|
);
|
|
103
106
|
}
|
|
104
107
|
|
|
@@ -106,8 +109,8 @@ describe("detectDrift", () => {
|
|
|
106
109
|
beforeEach(async () => {
|
|
107
110
|
await dropDrizzleMigrationsTable();
|
|
108
111
|
// Cleanup test tables that might still exist from earlier runs
|
|
109
|
-
await testDb.db.
|
|
110
|
-
await testDb.db.
|
|
112
|
+
await asRawClient(testDb.db).unsafe(`DROP TABLE IF EXISTS drift_test_users`);
|
|
113
|
+
await asRawClient(testDb.db).unsafe(`DROP TABLE IF EXISTS drift_test_orders`);
|
|
111
114
|
});
|
|
112
115
|
|
|
113
116
|
test("frische DB ohne __drizzle_migrations + 1 Migration im Journal → 1 pending + table missing", async () => {
|
|
@@ -124,7 +127,7 @@ describe("detectDrift", () => {
|
|
|
124
127
|
test("alle Migrations applied + alle Tabellen existieren → ok", async () => {
|
|
125
128
|
writeJournal([{ idx: 0, tag: "0000_init" }]);
|
|
126
129
|
writeSnapshotSimple(0, ["drift_test_users"]);
|
|
127
|
-
await testDb.db.
|
|
130
|
+
await asRawClient(testDb.db).unsafe(`CREATE TABLE drift_test_users (id uuid PRIMARY KEY)`);
|
|
128
131
|
await ensureDrizzleMigrationsTable();
|
|
129
132
|
await insertAppliedMigration("hash-0000");
|
|
130
133
|
|
|
@@ -140,8 +143,8 @@ describe("detectDrift", () => {
|
|
|
140
143
|
{ idx: 1, tag: "0001_add_orders" },
|
|
141
144
|
]);
|
|
142
145
|
writeSnapshotSimple(1, ["drift_test_users", "drift_test_orders"]);
|
|
143
|
-
await testDb.db.
|
|
144
|
-
await testDb.db.
|
|
146
|
+
await asRawClient(testDb.db).unsafe(`CREATE TABLE drift_test_users (id uuid PRIMARY KEY)`);
|
|
147
|
+
await asRawClient(testDb.db).unsafe(`CREATE TABLE drift_test_orders (id uuid PRIMARY KEY)`);
|
|
145
148
|
await ensureDrizzleMigrationsTable();
|
|
146
149
|
await insertAppliedMigration("hash-0000"); // nur eine applied
|
|
147
150
|
|
|
@@ -155,7 +158,7 @@ describe("detectDrift", () => {
|
|
|
155
158
|
test("alle Migrations applied aber Tabelle fehlt manuell → drift", async () => {
|
|
156
159
|
writeJournal([{ idx: 0, tag: "0000_init" }]);
|
|
157
160
|
writeSnapshotSimple(0, ["drift_test_users", "drift_test_orders"]);
|
|
158
|
-
await testDb.db.
|
|
161
|
+
await asRawClient(testDb.db).unsafe(`CREATE TABLE drift_test_users (id uuid PRIMARY KEY)`);
|
|
159
162
|
// drift_test_orders bewusst NICHT angelegt (simuliert manuellen DROP)
|
|
160
163
|
await ensureDrizzleMigrationsTable();
|
|
161
164
|
await insertAppliedMigration("hash-0000");
|
|
@@ -179,7 +182,9 @@ describe("detectDrift", () => {
|
|
|
179
182
|
},
|
|
180
183
|
]);
|
|
181
184
|
// DB hat email NULLABLE — drift.
|
|
182
|
-
await testDb.db.
|
|
185
|
+
await asRawClient(testDb.db).unsafe(
|
|
186
|
+
`CREATE TABLE drift_test_users (id uuid PRIMARY KEY, email text)`,
|
|
187
|
+
);
|
|
183
188
|
await ensureDrizzleMigrationsTable();
|
|
184
189
|
await insertAppliedMigration("hash-0000");
|
|
185
190
|
|
|
@@ -204,7 +209,7 @@ describe("detectDrift", () => {
|
|
|
204
209
|
},
|
|
205
210
|
]);
|
|
206
211
|
// DB hat KEINE email-Spalte.
|
|
207
|
-
await testDb.db.
|
|
212
|
+
await asRawClient(testDb.db).unsafe(`CREATE TABLE drift_test_users (id uuid PRIMARY KEY)`);
|
|
208
213
|
await ensureDrizzleMigrationsTable();
|
|
209
214
|
await insertAppliedMigration("hash-0000");
|
|
210
215
|
|
|
@@ -226,8 +231,8 @@ describe("detectDrift", () => {
|
|
|
226
231
|
},
|
|
227
232
|
]);
|
|
228
233
|
// DB hat zusätzliche Spalte (z.B. manueller ALTER TABLE in Prod).
|
|
229
|
-
await testDb.db.
|
|
230
|
-
|
|
234
|
+
await asRawClient(testDb.db).unsafe(
|
|
235
|
+
`CREATE TABLE drift_test_users (id uuid PRIMARY KEY, secret_legacy text)`,
|
|
231
236
|
);
|
|
232
237
|
await ensureDrizzleMigrationsTable();
|
|
233
238
|
await insertAppliedMigration("hash-0000");
|
|
@@ -251,7 +256,9 @@ describe("detectDrift", () => {
|
|
|
251
256
|
},
|
|
252
257
|
]);
|
|
253
258
|
// DB hat age als TEXT statt INTEGER.
|
|
254
|
-
await testDb.db.
|
|
259
|
+
await asRawClient(testDb.db).unsafe(
|
|
260
|
+
`CREATE TABLE drift_test_users (id uuid PRIMARY KEY, age text)`,
|
|
261
|
+
);
|
|
255
262
|
await ensureDrizzleMigrationsTable();
|
|
256
263
|
await insertAppliedMigration("hash-0000");
|
|
257
264
|
|
|
@@ -278,7 +285,7 @@ describe("detectDrift", () => {
|
|
|
278
285
|
},
|
|
279
286
|
},
|
|
280
287
|
]);
|
|
281
|
-
await testDb.db.
|
|
288
|
+
await asRawClient(testDb.db).unsafe(`
|
|
282
289
|
CREATE TABLE drift_test_users (
|
|
283
290
|
id uuid PRIMARY KEY,
|
|
284
291
|
email text NOT NULL,
|
|
@@ -297,10 +304,10 @@ describe("detectDrift", () => {
|
|
|
297
304
|
test("public.__drizzle_migrations Fallback (Pre-0.20-Drizzle)", async () => {
|
|
298
305
|
writeJournal([{ idx: 0, tag: "0000_init" }]);
|
|
299
306
|
writeSnapshotSimple(0, ["drift_test_users"]);
|
|
300
|
-
await testDb.db.
|
|
307
|
+
await asRawClient(testDb.db).unsafe(`CREATE TABLE drift_test_users (id uuid PRIMARY KEY)`);
|
|
301
308
|
// Legacy: Tabelle in public-Schema statt drizzle-Schema
|
|
302
309
|
await dropDrizzleMigrationsTable();
|
|
303
|
-
await testDb.db.
|
|
310
|
+
await asRawClient(testDb.db).unsafe(`
|
|
304
311
|
CREATE TABLE public.__drizzle_migrations (
|
|
305
312
|
id serial PRIMARY KEY,
|
|
306
313
|
hash text NOT NULL,
|
|
@@ -308,13 +315,14 @@ describe("detectDrift", () => {
|
|
|
308
315
|
)
|
|
309
316
|
`);
|
|
310
317
|
try {
|
|
311
|
-
await testDb.db.
|
|
312
|
-
|
|
318
|
+
await asRawClient(testDb.db).unsafe(
|
|
319
|
+
`INSERT INTO public.__drizzle_migrations (hash, created_at) VALUES ('hash-0000', $1)`,
|
|
320
|
+
[Date.now()],
|
|
313
321
|
);
|
|
314
322
|
const report = await detectDrift(testDb.db, migrationsDir);
|
|
315
323
|
expect(report.ok).toBe(true);
|
|
316
324
|
} finally {
|
|
317
|
-
await testDb.db.
|
|
325
|
+
await asRawClient(testDb.db).unsafe(`DROP TABLE public.__drizzle_migrations`);
|
|
318
326
|
}
|
|
319
327
|
});
|
|
320
328
|
});
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
// Sprint-G-Pieces nebeneinander leben aber sich nicht treffen — Marker
|
|
9
9
|
// wäre leer, kein Rebuild würde ausgelöst.
|
|
10
10
|
|
|
11
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
11
12
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
12
13
|
import { tmpdir } from "node:os";
|
|
13
14
|
import { join } from "node:path";
|
|
14
|
-
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
|
15
15
|
import { createBooleanField, createEntity, createTextField, defineFeature } from "../../engine";
|
|
16
16
|
import { createRegistry } from "../../engine/registry";
|
|
17
17
|
import { detectProjectionsToRebuild } from "../projection-detection";
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
// - schemaVersion-Mismatch wirft (verhindert dass alte Markers gegen
|
|
5
5
|
// neue Lese-Logik fahren)
|
|
6
6
|
|
|
7
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
7
8
|
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
8
9
|
import { tmpdir } from "node:os";
|
|
9
10
|
import { join } from "node:path";
|
|
10
|
-
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
|
11
11
|
import { readRebuildMarker, writeRebuildMarker } from "../rebuild-marker";
|
|
12
12
|
|
|
13
13
|
let tmpDir: string;
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
// Damit muss niemand Tabellen-Namen doppelt pflegen (Truth liegt in der
|
|
12
12
|
// Projection-Definition).
|
|
13
13
|
|
|
14
|
-
import { getTableName } from "drizzle-orm";
|
|
15
14
|
import type { Registry } from "../engine/types/feature";
|
|
16
15
|
import {
|
|
17
16
|
type ColumnSpec,
|
|
@@ -90,6 +89,18 @@ function sameColumns(
|
|
|
90
89
|
return true;
|
|
91
90
|
}
|
|
92
91
|
|
|
92
|
+
const KUMIKO_NAME_SYMBOL = Symbol.for("kumiko:schema:Name");
|
|
93
|
+
function getTableName(table: unknown): string {
|
|
94
|
+
if (typeof table !== "object" || table === null) {
|
|
95
|
+
throw new Error("projection-detection: table is not a pgTable object");
|
|
96
|
+
}
|
|
97
|
+
const name = (table as Record<symbol, unknown>)[KUMIKO_NAME_SYMBOL];
|
|
98
|
+
if (typeof name !== "string") {
|
|
99
|
+
throw new Error("projection-detection: table missing drizzle name symbol");
|
|
100
|
+
}
|
|
101
|
+
return name;
|
|
102
|
+
}
|
|
103
|
+
|
|
93
104
|
/** Index `tableName → projection-name` aus der Registry. Nur Projections
|
|
94
105
|
* mit table-Definition (single-stream + multi-stream-with-table) zählen.
|
|
95
106
|
* Side-effect-only MSPs (table omitted) haben keinen Rebuild-Sinn. */
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// 3. Column-Diff: information_schema-Vergleich gegen Snapshot —
|
|
10
10
|
// missing-/extra-column, type-mismatch, nullability-mismatch. Fängt
|
|
11
11
|
// manuelle ALTER TABLEs in Prod sowie doppelte pgTable-Definitionen
|
|
12
|
-
// pro Tabelle (eine hand-written, eine via
|
|
12
|
+
// pro Tabelle (eine hand-written, eine via buildEntityTable), die
|
|
13
13
|
// stillschweigend gegen den Snapshot driften.
|
|
14
14
|
//
|
|
15
15
|
// Drizzle-kit's eigene Garantie: nach `migrate apply` ist der DB-Stand
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
|
|
20
20
|
import { readFileSync } from "node:fs";
|
|
21
21
|
import { resolve } from "node:path";
|
|
22
|
-
import { sql } from "drizzle-orm";
|
|
23
22
|
import type { DbConnection } from "../db/connection";
|
|
23
|
+
import { selectAppliedMigrations, selectPublicTableColumns } from "../db/queries/schema-drift";
|
|
24
24
|
import { tableExists } from "../db/schema-inspection";
|
|
25
25
|
import { parseJsonOrThrow } from "../utils/safe-json";
|
|
26
26
|
|
|
@@ -125,16 +125,10 @@ export async function loadAppliedMigrations(db: DbConnection): Promise<AppliedMi
|
|
|
125
125
|
? false
|
|
126
126
|
: await tableExists(db, "public.__drizzle_migrations");
|
|
127
127
|
if (!drizzleSchemaExists && !publicSchemaExists) return [];
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
: sql`public.__drizzle_migrations`;
|
|
133
|
-
const rows = await db.execute<{ hash: string; created_at: bigint | number | null }>(sql`
|
|
134
|
-
SELECT hash, created_at
|
|
135
|
-
FROM ${tableRef}
|
|
136
|
-
ORDER BY id
|
|
137
|
-
`);
|
|
128
|
+
const rows = await selectAppliedMigrations(
|
|
129
|
+
db,
|
|
130
|
+
drizzleSchemaExists ? "drizzle.__drizzle_migrations" : "public.__drizzle_migrations",
|
|
131
|
+
);
|
|
138
132
|
return rows.map((r) => ({
|
|
139
133
|
hash: r.hash,
|
|
140
134
|
createdAt: typeof r.created_at === "bigint" ? Number(r.created_at) : (r.created_at ?? 0),
|
|
@@ -143,12 +137,6 @@ export async function loadAppliedMigrations(db: DbConnection): Promise<AppliedMi
|
|
|
143
137
|
|
|
144
138
|
// --- Column-Diff (Welle 2 Boot-Gate Layer 3) ---
|
|
145
139
|
|
|
146
|
-
type DbColumnRow = {
|
|
147
|
-
readonly column_name: string;
|
|
148
|
-
readonly data_type: string;
|
|
149
|
-
readonly is_nullable: "YES" | "NO";
|
|
150
|
-
};
|
|
151
|
-
|
|
152
140
|
/** Liest information_schema.columns für eine Tabelle im public-Schema.
|
|
153
141
|
* Map by column_name. Default-Werte werden bewusst ausgelassen — die
|
|
154
142
|
* drift'en über drizzle-Versionen / PG-Reformulierungen hinweg ohne dass
|
|
@@ -158,11 +146,7 @@ async function loadDbColumns(
|
|
|
158
146
|
db: DbConnection,
|
|
159
147
|
tableName: string,
|
|
160
148
|
): Promise<ReadonlyMap<string, { type: string; notNull: boolean }>> {
|
|
161
|
-
const rows = await db
|
|
162
|
-
SELECT column_name, data_type, is_nullable
|
|
163
|
-
FROM information_schema.columns
|
|
164
|
-
WHERE table_schema = 'public' AND table_name = ${tableName}
|
|
165
|
-
`);
|
|
149
|
+
const rows = await selectPublicTableColumns(db, tableName);
|
|
166
150
|
const map = new Map<string, { type: string; notNull: boolean }>();
|
|
167
151
|
for (const r of rows) {
|
|
168
152
|
map.set(r.column_name, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "
|
|
1
|
+
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "bun:test";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import {
|
|
3
|
+
import { buildEntityTable } from "../../db/table-builder";
|
|
4
4
|
import { createRegistry, defineFeature } from "../../engine";
|
|
5
5
|
import type { AppContext, SaveContext } from "../../engine/types";
|
|
6
6
|
import { createJobRunner } from "../../jobs";
|
|
@@ -65,18 +65,15 @@ const todoEntity = {
|
|
|
65
65
|
|
|
66
66
|
let postSaveInvocations = 0;
|
|
67
67
|
const todoFeature = defineFeature("todo", (r) => {
|
|
68
|
-
const todoTable =
|
|
68
|
+
const todoTable = buildEntityTable("todo", todoEntity);
|
|
69
69
|
r.entity("todo", todoEntity);
|
|
70
70
|
|
|
71
71
|
r.writeHandler(
|
|
72
72
|
"create",
|
|
73
73
|
z.object({ title: z.string() }),
|
|
74
74
|
async (event, ctx) => {
|
|
75
|
-
const rows = await ctx.db
|
|
76
|
-
|
|
77
|
-
.values({ title: event.payload.title })
|
|
78
|
-
.returning();
|
|
79
|
-
const row = rows[0] as { id: number; title: string };
|
|
75
|
+
const rows = await ctx.db.insertOne(todoTable, { title: event.payload.title });
|
|
76
|
+
const row = rows as { id: number; title: string };
|
|
80
77
|
return {
|
|
81
78
|
isSuccess: true,
|
|
82
79
|
data: {
|
package/src/pipeline/__tests__/{archive-stream.integration.ts → archive-stream.integration.test.ts}
RENAMED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
// Kumiko carries this as a sparse kumiko_archived_streams table so active
|
|
6
6
|
// streams never pay for extra metadata writes.
|
|
7
7
|
|
|
8
|
-
import { afterAll, afterEach, beforeAll, describe, expect, test } from "
|
|
8
|
+
import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { createEventStoreExecutor } from "../../db/event-store-executor";
|
|
11
|
-
import {
|
|
11
|
+
import { buildEntityTable } from "../../db/table-builder";
|
|
12
12
|
import { createEntity, createTextField, defineFeature } from "../../engine";
|
|
13
13
|
import {
|
|
14
14
|
ArchivedStreamError,
|
|
@@ -27,7 +27,7 @@ const itemEntity = createEntity({
|
|
|
27
27
|
table: "read_arch_items",
|
|
28
28
|
fields: { label: createTextField({ required: true }) },
|
|
29
29
|
});
|
|
30
|
-
const itemTable =
|
|
30
|
+
const itemTable = buildEntityTable("arch-item", itemEntity);
|
|
31
31
|
|
|
32
32
|
const archFeature = defineFeature("archtest", (r) => {
|
|
33
33
|
r.entity("arch-item", itemEntity);
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
2
|
import type { AuthClaimsContext, AuthClaimsHookDef, SessionUser } from "../../engine/types";
|
|
3
3
|
import type { Logger } from "../../logging/types";
|
|
4
4
|
import { resolveAuthClaims } from "../auth-claims-resolver";
|
|
5
5
|
|
|
6
6
|
type TestLogger = {
|
|
7
7
|
readonly log: Logger;
|
|
8
|
-
readonly warn: ReturnType<typeof
|
|
8
|
+
readonly warn: ReturnType<typeof mock>;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
function makeTestLogger(): TestLogger {
|
|
12
|
-
const warn =
|
|
13
|
-
const info =
|
|
14
|
-
const error =
|
|
15
|
-
const debug =
|
|
12
|
+
const warn = mock();
|
|
13
|
+
const info = mock();
|
|
14
|
+
const error = mock();
|
|
15
|
+
const debug = mock();
|
|
16
16
|
const logger: Logger = {
|
|
17
17
|
warn,
|
|
18
18
|
info,
|
|
@@ -45,7 +45,7 @@ function hooks(...entries: AuthClaimsHookDef[]): readonly AuthClaimsHookDef[] {
|
|
|
45
45
|
|
|
46
46
|
describe("resolveAuthClaims — empty", () => {
|
|
47
47
|
test("zero hooks registered → empty record, contextFactory not called", async () => {
|
|
48
|
-
const factory =
|
|
48
|
+
const factory = mock();
|
|
49
49
|
const result = await resolveAuthClaims({
|
|
50
50
|
user: testUser,
|
|
51
51
|
hooks: [],
|
|
@@ -73,8 +73,8 @@ describe("resolveAuthClaims — single hook", () => {
|
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
test("receives the user and context handed in", async () => {
|
|
76
|
-
const fn =
|
|
77
|
-
const factory =
|
|
76
|
+
const fn = mock(async () => ({}));
|
|
77
|
+
const factory = mock(() => stubContext);
|
|
78
78
|
await resolveAuthClaims({
|
|
79
79
|
user: testUser,
|
|
80
80
|
hooks: hooks({ featureName: "any", fn }),
|