@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,5 +1,6 @@
|
|
|
1
|
-
import { and, getTableName, inArray, lt, sql } from "drizzle-orm";
|
|
2
1
|
import type { DbConnection } from "../db/connection";
|
|
2
|
+
import { lockEventConsumersShareMode } from "../db/queries/event-consumer";
|
|
3
|
+
import { deleteMany, selectMany, transaction } from "../db/query";
|
|
3
4
|
import { eventsTable } from "../event-store";
|
|
4
5
|
import { eventConsumerStateTable } from "./event-consumer-state";
|
|
5
6
|
|
|
@@ -83,7 +84,7 @@ export async function pruneEvents(
|
|
|
83
84
|
const aggregateTypes = options.aggregateTypes;
|
|
84
85
|
const dryRun = options.dryRun === true;
|
|
85
86
|
|
|
86
|
-
return
|
|
87
|
+
return transaction(db, async (tx) => {
|
|
87
88
|
// Serialise against consumer-bootstrap INSERTs. Without this, the race
|
|
88
89
|
// is: prune reads consumers (snapshot misses a consumer bootstrapping
|
|
89
90
|
// in a parallel tx) → consumer commits its row with
|
|
@@ -96,21 +97,13 @@ export async function pruneEvents(
|
|
|
96
97
|
// (cursor advances) do too, but prune is measured in milliseconds and
|
|
97
98
|
// pausing cursor advances for that window is cheap insurance against
|
|
98
99
|
// a silent data-loss bug.
|
|
99
|
-
|
|
100
|
-
// Drizzle can't express LOCK TABLE — drop to raw SQL with the table
|
|
101
|
-
// name identifier so a future table-rename is caught at compile time.
|
|
102
|
-
await tx.execute(sql.raw(`LOCK TABLE ${getTableName(eventConsumerStateTable)} IN SHARE MODE`));
|
|
100
|
+
await lockEventConsumersShareMode(tx);
|
|
103
101
|
|
|
104
102
|
// Step 1 — collect candidate event ids.
|
|
105
|
-
const candidates = await tx
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
and(
|
|
110
|
-
inArray(eventsTable.aggregateType, [...aggregateTypes]),
|
|
111
|
-
lt(eventsTable.createdAt, cutoff),
|
|
112
|
-
),
|
|
113
|
-
);
|
|
103
|
+
const candidates = await selectMany<{ id: bigint }>(tx, eventsTable, {
|
|
104
|
+
aggregateType: [...aggregateTypes],
|
|
105
|
+
createdAt: { lt: cutoff },
|
|
106
|
+
});
|
|
114
107
|
|
|
115
108
|
if (candidates.length === 0) {
|
|
116
109
|
return { deletedCount: 0, cutoff, aggregateTypes, dryRun };
|
|
@@ -127,10 +120,10 @@ export async function pruneEvents(
|
|
|
127
120
|
// The SHARE lock above guarantees this SELECT sees a complete view:
|
|
128
121
|
// no new consumer can INSERT a fresh-cursor row between here and the
|
|
129
122
|
// DELETE below.
|
|
130
|
-
const activeConsumers = await
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
123
|
+
const activeConsumers = await selectMany<{
|
|
124
|
+
name: string;
|
|
125
|
+
lastProcessedEventId: bigint;
|
|
126
|
+
}>(tx, eventConsumerStateTable, { status: { ne: "disabled" } });
|
|
134
127
|
|
|
135
128
|
for (const consumer of activeConsumers) {
|
|
136
129
|
if (consumer.lastProcessedEventId < maxCandidateId) {
|
|
@@ -144,11 +137,8 @@ export async function pruneEvents(
|
|
|
144
137
|
|
|
145
138
|
// Step 3 — actual delete, bounded to the candidate set.
|
|
146
139
|
const candidateIds = candidates.map((c) => c.id);
|
|
147
|
-
|
|
148
|
-
.delete(eventsTable)
|
|
149
|
-
.where(inArray(eventsTable.id, candidateIds))
|
|
150
|
-
.returning({ id: eventsTable.id });
|
|
140
|
+
await deleteMany(tx, eventsTable, { id: candidateIds });
|
|
151
141
|
|
|
152
|
-
return { deletedCount:
|
|
142
|
+
return { deletedCount: candidateIds.length, cutoff, aggregateTypes, dryRun: false };
|
|
153
143
|
});
|
|
154
144
|
}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { DbConnection, DbRunner, DbTx } from "../db/connection";
|
|
2
|
+
import {
|
|
3
|
+
markConsumerRebuildFailed,
|
|
4
|
+
resetConsumerForMspRebuild,
|
|
5
|
+
selectConsumerForUpdate,
|
|
6
|
+
updateConsumerRebuildCursor,
|
|
7
|
+
} from "../db/queries/event-consumer";
|
|
8
|
+
import { truncateTable } from "../db/queries/table-ops";
|
|
9
|
+
import { selectMany } from "../db/query";
|
|
3
10
|
import type { Registry, TenantId } from "../engine/types";
|
|
4
11
|
import { InternalError } from "../errors";
|
|
5
12
|
import { eventsTable, type StoredEvent, upcastStoredEvent } from "../event-store";
|
|
@@ -7,7 +14,7 @@ import { loadAggregate, loadAggregateAsOf } from "../event-store/event-store";
|
|
|
7
14
|
import { upcastStoredEvents } from "../event-store/upcaster";
|
|
8
15
|
import { emitProjectionRebuild } from "../observability/standard-metrics";
|
|
9
16
|
import type { Meter } from "../observability/types/metric";
|
|
10
|
-
import {
|
|
17
|
+
import { SHARED_INSTANCE_SENTINEL } from "./event-consumer-state";
|
|
11
18
|
import type { MultiStreamApplyContext } from "./multi-stream-apply-context";
|
|
12
19
|
import type { RebuildResult } from "./projection-rebuild";
|
|
13
20
|
|
|
@@ -101,57 +108,35 @@ export async function rebuildMultiStreamProjection(
|
|
|
101
108
|
let lastProcessedEventId = 0n;
|
|
102
109
|
|
|
103
110
|
try {
|
|
104
|
-
await db.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
// table, so the guard above refuses them anyway), and rebuild's
|
|
108
|
-
// purpose is to rematerialize one persistent read-model, not fan
|
|
109
|
-
// out a local cache reset across instances. The FOR UPDATE on the
|
|
110
|
-
// next SELECT is what blocks concurrent rebuilds of the same MSP;
|
|
111
|
-
// live dispatcher passes use SKIP LOCKED on this row and will bail
|
|
112
|
-
// silently while we hold it.
|
|
113
|
-
await tx
|
|
114
|
-
.insert(eventConsumerStateTable)
|
|
115
|
-
.values({
|
|
116
|
-
name: mspName,
|
|
117
|
-
instanceId: SHARED_INSTANCE_SENTINEL,
|
|
118
|
-
lastProcessedEventId: 0n,
|
|
119
|
-
status: "idle",
|
|
120
|
-
})
|
|
121
|
-
.onConflictDoUpdate({
|
|
122
|
-
target: [eventConsumerStateTable.name, eventConsumerStateTable.instanceId],
|
|
123
|
-
set: {
|
|
124
|
-
lastProcessedEventId: 0n,
|
|
125
|
-
status: "idle",
|
|
126
|
-
attempts: 0,
|
|
127
|
-
lastError: null,
|
|
128
|
-
updatedAt: sql`now()`,
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
await tx
|
|
132
|
-
.select()
|
|
133
|
-
.from(eventConsumerStateTable)
|
|
134
|
-
.where(
|
|
135
|
-
and(
|
|
136
|
-
eq(eventConsumerStateTable.name, mspName),
|
|
137
|
-
eq(eventConsumerStateTable.instanceId, SHARED_INSTANCE_SENTINEL),
|
|
138
|
-
),
|
|
139
|
-
)
|
|
140
|
-
.for("update");
|
|
111
|
+
await db.begin(async (tx: DbTx) => {
|
|
112
|
+
await resetConsumerForMspRebuild(tx, mspName, SHARED_INSTANCE_SENTINEL);
|
|
113
|
+
await selectConsumerForUpdate(tx, mspName, SHARED_INSTANCE_SENTINEL);
|
|
141
114
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const tableName = getTableName(msp.table as NonNullable<typeof msp.table>); // @cast-boundary db-operator
|
|
146
|
-
await tx.execute(sql.raw(`TRUNCATE TABLE ${quoteIdent(tableName)}`));
|
|
115
|
+
const mspTable = msp.table as NonNullable<typeof msp.table>;
|
|
116
|
+
const tableName = getTableName(mspTable);
|
|
117
|
+
await truncateTable(tx, tableName);
|
|
147
118
|
|
|
148
119
|
const subscribedTypes = Object.keys(msp.apply);
|
|
149
120
|
if (subscribedTypes.length > 0) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
121
|
+
type EventRow = {
|
|
122
|
+
id: bigint;
|
|
123
|
+
aggregateId: string;
|
|
124
|
+
aggregateType: string;
|
|
125
|
+
tenantId: TenantId;
|
|
126
|
+
version: number;
|
|
127
|
+
type: string;
|
|
128
|
+
eventVersion: number;
|
|
129
|
+
payload: Record<string, unknown>;
|
|
130
|
+
metadata: import("../event-store/event-store").EventMetadata;
|
|
131
|
+
createdAt: Temporal.Instant;
|
|
132
|
+
createdBy: string;
|
|
133
|
+
};
|
|
134
|
+
const events = await selectMany<EventRow>(
|
|
135
|
+
tx,
|
|
136
|
+
eventsTable,
|
|
137
|
+
{ type: [...subscribedTypes] },
|
|
138
|
+
{ orderBy: { col: "id", direction: "asc" } },
|
|
139
|
+
);
|
|
155
140
|
|
|
156
141
|
const upcasters = registry.getEventUpcasters();
|
|
157
142
|
for (const row of events) {
|
|
@@ -170,44 +155,27 @@ export async function rebuildMultiStreamProjection(
|
|
|
170
155
|
};
|
|
171
156
|
const storedEvent = await upcastStoredEvent(raw, upcasters, {
|
|
172
157
|
db: tx,
|
|
173
|
-
tenantId: row.tenantId
|
|
158
|
+
tenantId: row.tenantId,
|
|
174
159
|
});
|
|
175
160
|
const applyFn = msp.apply[row.type];
|
|
176
161
|
if (!applyFn) continue;
|
|
177
|
-
const rebuildCtx = createRebuildCtx(registry, tx, row.tenantId
|
|
162
|
+
const rebuildCtx = createRebuildCtx(registry, tx, row.tenantId);
|
|
178
163
|
await applyFn(storedEvent, tx, rebuildCtx);
|
|
179
164
|
eventsProcessed++;
|
|
180
165
|
lastProcessedEventId = row.id;
|
|
181
166
|
}
|
|
182
167
|
}
|
|
183
168
|
|
|
184
|
-
await
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
lastError: null,
|
|
191
|
-
updatedAt: sql`now()`,
|
|
192
|
-
})
|
|
193
|
-
.where(
|
|
194
|
-
and(
|
|
195
|
-
eq(eventConsumerStateTable.name, mspName),
|
|
196
|
-
eq(eventConsumerStateTable.instanceId, SHARED_INSTANCE_SENTINEL),
|
|
197
|
-
),
|
|
198
|
-
);
|
|
169
|
+
await updateConsumerRebuildCursor(
|
|
170
|
+
tx,
|
|
171
|
+
mspName,
|
|
172
|
+
SHARED_INSTANCE_SENTINEL,
|
|
173
|
+
lastProcessedEventId,
|
|
174
|
+
);
|
|
199
175
|
});
|
|
200
176
|
} catch (e) {
|
|
201
177
|
const message = e instanceof Error ? e.message : String(e);
|
|
202
|
-
await db
|
|
203
|
-
.update(eventConsumerStateTable)
|
|
204
|
-
.set({ status: "dead", lastError: message, updatedAt: sql`now()` })
|
|
205
|
-
.where(
|
|
206
|
-
and(
|
|
207
|
-
eq(eventConsumerStateTable.name, mspName),
|
|
208
|
-
eq(eventConsumerStateTable.instanceId, SHARED_INSTANCE_SENTINEL),
|
|
209
|
-
),
|
|
210
|
-
);
|
|
178
|
+
await markConsumerRebuildFailed(db, mspName, SHARED_INSTANCE_SENTINEL, message);
|
|
211
179
|
if (deps.meter) {
|
|
212
180
|
emitProjectionRebuild(
|
|
213
181
|
deps.meter,
|
|
@@ -237,6 +205,14 @@ export async function rebuildMultiStreamProjection(
|
|
|
237
205
|
return result;
|
|
238
206
|
}
|
|
239
207
|
|
|
240
|
-
|
|
241
|
-
|
|
208
|
+
const KUMIKO_NAME_SYMBOL = Symbol.for("kumiko:schema:Name");
|
|
209
|
+
function getTableName(table: unknown): string {
|
|
210
|
+
if (typeof table !== "object" || table === null) {
|
|
211
|
+
throw new InternalError({ message: "msp-rebuild: msp.table is not a pgTable object" });
|
|
212
|
+
}
|
|
213
|
+
const name = (table as Record<symbol, unknown>)[KUMIKO_NAME_SYMBOL];
|
|
214
|
+
if (typeof name !== "string") {
|
|
215
|
+
throw new InternalError({ message: "msp-rebuild: msp.table missing drizzle name symbol" });
|
|
216
|
+
}
|
|
217
|
+
return name;
|
|
242
218
|
}
|
|
@@ -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";
|