@cosmicdrift/kumiko-framework 0.1.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/README.md +159 -0
- package/package.json +91 -0
- package/src/__tests__/anonymous-access.integration.ts +325 -0
- package/src/__tests__/error-contract.integration.ts +435 -0
- package/src/__tests__/field-access.integration.ts +269 -0
- package/src/__tests__/full-stack.integration.ts +914 -0
- package/src/__tests__/ownership.integration.ts +449 -0
- package/src/__tests__/reference-data.integration.ts +198 -0
- package/src/__tests__/transition-guard.integration.ts +340 -0
- package/src/api/__tests__/api.test.ts +337 -0
- package/src/api/__tests__/auth-middleware-transport.test.ts +80 -0
- package/src/api/__tests__/auth-routes-cookie.test.ts +179 -0
- package/src/api/__tests__/batch.integration.ts +404 -0
- package/src/api/__tests__/body-limit.test.ts +88 -0
- package/src/api/__tests__/csrf-middleware.test.ts +97 -0
- package/src/api/__tests__/dispatcher-live.integration.ts +216 -0
- package/src/api/__tests__/metrics-endpoint.test.ts +126 -0
- package/src/api/__tests__/nested-write.integration.ts +213 -0
- package/src/api/__tests__/readiness.test.ts +76 -0
- package/src/api/__tests__/request-id-middleware.test.ts +72 -0
- package/src/api/__tests__/sse-broker.test.ts +58 -0
- package/src/api/__tests__/sse-route.test.ts +112 -0
- package/src/api/anonymous-cookie.ts +60 -0
- package/src/api/api-constants.ts +64 -0
- package/src/api/auth-middleware.ts +418 -0
- package/src/api/auth-routes.ts +982 -0
- package/src/api/csrf-middleware.ts +77 -0
- package/src/api/index.ts +31 -0
- package/src/api/jwt.ts +66 -0
- package/src/api/observability-middleware.ts +89 -0
- package/src/api/readiness.ts +132 -0
- package/src/api/request-context.ts +49 -0
- package/src/api/request-id-middleware.ts +50 -0
- package/src/api/route-registrars.ts +195 -0
- package/src/api/routes.ts +135 -0
- package/src/api/server.ts +640 -0
- package/src/api/sse-broker.ts +71 -0
- package/src/api/sse-route.ts +62 -0
- package/src/api/tokens.ts +16 -0
- package/src/db/__tests__/apply-entity-event-tenant.integration.ts +159 -0
- package/src/db/__tests__/compound-types.test.ts +114 -0
- package/src/db/__tests__/connection-options.test.ts +68 -0
- package/src/db/__tests__/cursor.test.ts +41 -0
- package/src/db/__tests__/db-helpers.test.ts +369 -0
- package/src/db/__tests__/dialect-instant.test.ts +50 -0
- package/src/db/__tests__/drizzle-helpers.integration.ts +186 -0
- package/src/db/__tests__/drizzle-table-types.test.ts +162 -0
- package/src/db/__tests__/encryption.test.ts +39 -0
- package/src/db/__tests__/event-store-executor-list.integration.ts +313 -0
- package/src/db/__tests__/event-store-executor.integration.ts +235 -0
- package/src/db/__tests__/implicit-projection-equivalence.integration.ts +304 -0
- package/src/db/__tests__/located-timestamp.test.ts +184 -0
- package/src/db/__tests__/money.test.ts +199 -0
- package/src/db/__tests__/multi-row-insert.integration.ts +76 -0
- package/src/db/__tests__/parse-auto-verb.test.ts +70 -0
- package/src/db/__tests__/required-not-null-migration-safety.integration.ts +105 -0
- package/src/db/__tests__/row-helpers.test.ts +59 -0
- package/src/db/__tests__/schema-migration.integration.ts +273 -0
- package/src/db/__tests__/table-builder-indexes.test.ts +153 -0
- package/src/db/__tests__/table-builder-required.test.ts +216 -0
- package/src/db/__tests__/tenant-db.integration.ts +606 -0
- package/src/db/__tests__/unique-violation-mapping.integration.ts +166 -0
- package/src/db/apply-entity-event.ts +188 -0
- package/src/db/assert-exists-in.ts +59 -0
- package/src/db/compound-types.ts +47 -0
- package/src/db/connection.ts +104 -0
- package/src/db/cursor.ts +83 -0
- package/src/db/dialect.ts +109 -0
- package/src/db/eagerload.ts +174 -0
- package/src/db/encryption.ts +39 -0
- package/src/db/event-store-executor.ts +906 -0
- package/src/db/index.ts +55 -0
- package/src/db/located-timestamp.ts +114 -0
- package/src/db/money.ts +120 -0
- package/src/db/pg-error.ts +46 -0
- package/src/db/reference-data.ts +77 -0
- package/src/db/row-helpers.ts +53 -0
- package/src/db/schema-inspection.ts +25 -0
- package/src/db/table-builder.ts +475 -0
- package/src/db/tenant-db.ts +434 -0
- package/src/engine/__tests__/auth-claims-registrar.test.ts +74 -0
- package/src/engine/__tests__/boot-validator-located-timestamps.test.ts +108 -0
- package/src/engine/__tests__/boot-validator.test.ts +1865 -0
- package/src/engine/__tests__/build-app-schema.test.ts +154 -0
- package/src/engine/__tests__/claim-keys.test.ts +274 -0
- package/src/engine/__tests__/config-helpers.test.ts +236 -0
- package/src/engine/__tests__/effective-features.test.ts +86 -0
- package/src/engine/__tests__/engine.test.ts +1461 -0
- package/src/engine/__tests__/entity-handlers.test.ts +274 -0
- package/src/engine/__tests__/event-helpers.test.ts +68 -0
- package/src/engine/__tests__/extends-registrar.test.ts +159 -0
- package/src/engine/__tests__/factories-long-text.test.ts +84 -0
- package/src/engine/__tests__/factories-time.test.ts +158 -0
- package/src/engine/__tests__/field-predicates.test.ts +48 -0
- package/src/engine/__tests__/hook-phases.test.ts +132 -0
- package/src/engine/__tests__/identifiers.test.ts +35 -0
- package/src/engine/__tests__/lifecycle-hooks.test.ts +237 -0
- package/src/engine/__tests__/nav.test.ts +267 -0
- package/src/engine/__tests__/ownership.test.ts +421 -0
- package/src/engine/__tests__/parse-ref-target.test.ts +43 -0
- package/src/engine/__tests__/projection-helpers.test.ts +62 -0
- package/src/engine/__tests__/projection.test.ts +191 -0
- package/src/engine/__tests__/qualified-name.test.ts +264 -0
- package/src/engine/__tests__/resolve-config-or-param.test.ts +315 -0
- package/src/engine/__tests__/run-in.test.ts +38 -0
- package/src/engine/__tests__/schema-builder.test.ts +380 -0
- package/src/engine/__tests__/screen.test.ts +408 -0
- package/src/engine/__tests__/state-machine.test.ts +148 -0
- package/src/engine/__tests__/system-user.test.ts +57 -0
- package/src/engine/__tests__/validation-hooks.test.ts +71 -0
- package/src/engine/access.ts +23 -0
- package/src/engine/boot-validator.ts +1528 -0
- package/src/engine/build-app-schema.ts +125 -0
- package/src/engine/config-helpers.ts +115 -0
- package/src/engine/constants.ts +85 -0
- package/src/engine/create-app.ts +98 -0
- package/src/engine/define-feature.ts +702 -0
- package/src/engine/define-handler.ts +78 -0
- package/src/engine/define-roles.ts +19 -0
- package/src/engine/effective-features.ts +87 -0
- package/src/engine/entity-handlers.ts +364 -0
- package/src/engine/event-helpers.ts +73 -0
- package/src/engine/factories.ts +328 -0
- package/src/engine/feature-ast/__tests__/canonical-form.test.ts +416 -0
- package/src/engine/feature-ast/__tests__/parse-happy-path.test.ts +197 -0
- package/src/engine/feature-ast/__tests__/parse-real-features.test.ts +128 -0
- package/src/engine/feature-ast/__tests__/parse.test.ts +888 -0
- package/src/engine/feature-ast/__tests__/patch.test.ts +360 -0
- package/src/engine/feature-ast/__tests__/patcher.test.ts +469 -0
- package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +287 -0
- package/src/engine/feature-ast/extractors.ts +2562 -0
- package/src/engine/feature-ast/index.ts +105 -0
- package/src/engine/feature-ast/parse.ts +369 -0
- package/src/engine/feature-ast/patch.ts +525 -0
- package/src/engine/feature-ast/patcher.ts +518 -0
- package/src/engine/feature-ast/patterns.ts +434 -0
- package/src/engine/feature-ast/render.ts +602 -0
- package/src/engine/feature-ast/source-location.ts +45 -0
- package/src/engine/field-access.ts +120 -0
- package/src/engine/index.ts +254 -0
- package/src/engine/ownership.ts +337 -0
- package/src/engine/parse-ref-target.ts +22 -0
- package/src/engine/pattern-library/__tests__/library.test.ts +351 -0
- package/src/engine/pattern-library/index.ts +24 -0
- package/src/engine/pattern-library/library.ts +1117 -0
- package/src/engine/pattern-library/types.ts +255 -0
- package/src/engine/projection-helpers.ts +85 -0
- package/src/engine/qualified-name.ts +122 -0
- package/src/engine/read-claim.ts +31 -0
- package/src/engine/registry.ts +1325 -0
- package/src/engine/resolve-config-or-param.ts +153 -0
- package/src/engine/run-in.ts +29 -0
- package/src/engine/schema-builder.ts +175 -0
- package/src/engine/screen-filter-ops.ts +51 -0
- package/src/engine/state-machine.ts +70 -0
- package/src/engine/system-user.ts +32 -0
- package/src/engine/types/config.ts +306 -0
- package/src/engine/types/event-type-map.ts +37 -0
- package/src/engine/types/feature.ts +574 -0
- package/src/engine/types/fields.ts +422 -0
- package/src/engine/types/handlers.ts +742 -0
- package/src/engine/types/hooks.ts +142 -0
- package/src/engine/types/http-route.ts +54 -0
- package/src/engine/types/identifiers.ts +47 -0
- package/src/engine/types/index.ts +208 -0
- package/src/engine/types/nav.ts +46 -0
- package/src/engine/types/projection.ts +132 -0
- package/src/engine/types/relations.ts +51 -0
- package/src/engine/types/screen.ts +452 -0
- package/src/engine/types/workspace.ts +42 -0
- package/src/engine/validation.ts +33 -0
- package/src/entrypoint/__tests__/entrypoint-job-wiring.integration.ts +173 -0
- package/src/entrypoint/__tests__/split-deploy.integration.ts +297 -0
- package/src/entrypoint/index.ts +442 -0
- package/src/errors/__tests__/classes.test.ts +371 -0
- package/src/errors/__tests__/write-failures.test.ts +109 -0
- package/src/errors/classes.ts +249 -0
- package/src/errors/i18n/de.yaml +83 -0
- package/src/errors/i18n/en.yaml +80 -0
- package/src/errors/index.ts +41 -0
- package/src/errors/kumiko-error.ts +67 -0
- package/src/errors/reasons.ts +36 -0
- package/src/errors/serialize.ts +136 -0
- package/src/errors/transition-details.ts +30 -0
- package/src/errors/write-error-info.ts +123 -0
- package/src/errors/zod-bridge.ts +49 -0
- package/src/event-store/__tests__/admin-api.integration.ts +361 -0
- package/src/event-store/__tests__/event-store.integration.ts +584 -0
- package/src/event-store/__tests__/get-stream-version-perf.integration.ts +83 -0
- package/src/event-store/__tests__/perf.integration.ts +255 -0
- package/src/event-store/__tests__/snapshot.integration.ts +267 -0
- package/src/event-store/__tests__/upcaster-dead-letter.integration.ts +204 -0
- package/src/event-store/__tests__/upcaster.integration.ts +460 -0
- package/src/event-store/admin-api.ts +257 -0
- package/src/event-store/archive.ts +106 -0
- package/src/event-store/errors.ts +35 -0
- package/src/event-store/event-store.ts +405 -0
- package/src/event-store/events-schema.ts +90 -0
- package/src/event-store/index.ts +50 -0
- package/src/event-store/snapshot.ts +210 -0
- package/src/event-store/upcaster-dead-letter.ts +119 -0
- package/src/event-store/upcaster.ts +147 -0
- package/src/files/__tests__/content-disposition.test.ts +123 -0
- package/src/files/__tests__/file-field-column.integration.ts +103 -0
- package/src/files/__tests__/file-field-pipeline.integration.ts +211 -0
- package/src/files/__tests__/file-handle.test.ts +122 -0
- package/src/files/__tests__/files.integration.ts +830 -0
- package/src/files/__tests__/storage-tracking.integration.ts +153 -0
- package/src/files/content-disposition.ts +55 -0
- package/src/files/file-handle.ts +63 -0
- package/src/files/file-ref-table.ts +22 -0
- package/src/files/file-routes.ts +353 -0
- package/src/files/in-memory-provider.ts +62 -0
- package/src/files/index.ts +29 -0
- package/src/files/local-provider.ts +35 -0
- package/src/files/storage-tracking.ts +60 -0
- package/src/files/types.ts +118 -0
- package/src/i18n/__tests__/i18n.test.ts +72 -0
- package/src/i18n/index.ts +29 -0
- package/src/jobs/__tests__/job-event-trigger.integration.ts +172 -0
- package/src/jobs/__tests__/job-multi-trigger.integration.ts +144 -0
- package/src/jobs/__tests__/jobs.integration.ts +566 -0
- package/src/jobs/index.ts +2 -0
- package/src/jobs/job-runner.ts +574 -0
- package/src/lifecycle/__tests__/create-test-lifecycle.ts +19 -0
- package/src/lifecycle/__tests__/lifecycle-server.integration.ts +108 -0
- package/src/lifecycle/__tests__/lifecycle.test.ts +212 -0
- package/src/lifecycle/__tests__/signal-handlers.test.ts +106 -0
- package/src/lifecycle/index.ts +13 -0
- package/src/lifecycle/lifecycle.ts +160 -0
- package/src/lifecycle/signal-handlers.ts +62 -0
- package/src/logging/__tests__/pino-trace-bridge.test.ts +50 -0
- package/src/logging/index.ts +3 -0
- package/src/logging/pino-logger.ts +64 -0
- package/src/logging/types.ts +7 -0
- package/src/migrations/__tests__/compare-snapshots.test.ts +150 -0
- package/src/migrations/__tests__/detect-drift.integration.ts +320 -0
- package/src/migrations/__tests__/detect-projections-to-rebuild.integration.ts +134 -0
- package/src/migrations/__tests__/rebuild-marker.test.ts +79 -0
- package/src/migrations/index.ts +28 -0
- package/src/migrations/projection-detection.ts +149 -0
- package/src/migrations/rebuild-marker.ts +64 -0
- package/src/migrations/schema-drift.ts +395 -0
- package/src/observability/__tests__/console-provider.test.ts +67 -0
- package/src/observability/__tests__/metric-validator.test.ts +87 -0
- package/src/observability/__tests__/noop-provider.test.ts +82 -0
- package/src/observability/__tests__/observability.integration.ts +559 -0
- package/src/observability/__tests__/prometheus-meter.test.ts +144 -0
- package/src/observability/__tests__/recording-meter.test.ts +101 -0
- package/src/observability/__tests__/recording-tracer.test.ts +110 -0
- package/src/observability/__tests__/sensitive-filter.test.ts +98 -0
- package/src/observability/console-provider.ts +130 -0
- package/src/observability/context.ts +26 -0
- package/src/observability/fallback.ts +34 -0
- package/src/observability/ids.ts +25 -0
- package/src/observability/index.ts +79 -0
- package/src/observability/metric-validator.ts +86 -0
- package/src/observability/metrics-handle.ts +56 -0
- package/src/observability/noop-provider.ts +146 -0
- package/src/observability/prometheus-meter.ts +284 -0
- package/src/observability/recording-meter.ts +156 -0
- package/src/observability/recording-tracer.ts +198 -0
- package/src/observability/redis-wrapper.ts +132 -0
- package/src/observability/sensitive-filter.ts +108 -0
- package/src/observability/standard-metrics.ts +213 -0
- package/src/observability/types/index.ts +29 -0
- package/src/observability/types/metric.ts +56 -0
- package/src/observability/types/provider.ts +32 -0
- package/src/observability/types/span.ts +64 -0
- package/src/pipeline/__tests__/archive-stream.integration.ts +220 -0
- package/src/pipeline/__tests__/auth-claims-resolver.test.ts +279 -0
- package/src/pipeline/__tests__/cascade-handler.integration.ts +419 -0
- package/src/pipeline/__tests__/cascade-handler.test.ts +52 -0
- package/src/pipeline/__tests__/causation-chain.integration.ts +206 -0
- package/src/pipeline/__tests__/ctx-bridge.integration.ts +234 -0
- package/src/pipeline/__tests__/dispatcher.test.ts +379 -0
- package/src/pipeline/__tests__/distributed-lock.integration.ts +67 -0
- package/src/pipeline/__tests__/domain-events-projections.integration.ts +323 -0
- package/src/pipeline/__tests__/event-dedup.integration.ts +153 -0
- package/src/pipeline/__tests__/event-define-event-strict.integration.ts +202 -0
- package/src/pipeline/__tests__/event-dispatcher-lifecycle.integration.ts +220 -0
- package/src/pipeline/__tests__/event-dispatcher-multi-instance.integration.ts +423 -0
- package/src/pipeline/__tests__/event-dispatcher-pg-listen.integration.ts +123 -0
- package/src/pipeline/__tests__/event-dispatcher-recovery.integration.ts +202 -0
- package/src/pipeline/__tests__/event-dispatcher-second-audit.integration.ts +290 -0
- package/src/pipeline/__tests__/event-dispatcher-strict.test.ts +65 -0
- package/src/pipeline/__tests__/event-dispatcher.integration.ts +287 -0
- package/src/pipeline/__tests__/event-retention.integration.ts +239 -0
- package/src/pipeline/__tests__/fetch-for-writing.integration.ts +281 -0
- package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +430 -0
- package/src/pipeline/__tests__/load-aggregate-query.integration.ts +266 -0
- package/src/pipeline/__tests__/msp-error-mode.integration.ts +149 -0
- package/src/pipeline/__tests__/msp-multi-hop.integration.ts +228 -0
- package/src/pipeline/__tests__/msp-rebuild.integration.ts +368 -0
- package/src/pipeline/__tests__/multi-stream-projection.integration.ts +341 -0
- package/src/pipeline/__tests__/perf-rebuild.integration.ts +147 -0
- package/src/pipeline/__tests__/projection-rebuild.integration.ts +551 -0
- package/src/pipeline/__tests__/query-projection.integration.ts +201 -0
- package/src/pipeline/__tests__/redis-pipeline.integration.ts +306 -0
- package/src/pipeline/append-event-core.ts +117 -0
- package/src/pipeline/auth-claims-resolver.ts +103 -0
- package/src/pipeline/cascade-handler.ts +113 -0
- package/src/pipeline/dispatcher.ts +1585 -0
- package/src/pipeline/distributed-lock.ts +37 -0
- package/src/pipeline/entity-cache.ts +113 -0
- package/src/pipeline/event-consumer-state.ts +108 -0
- package/src/pipeline/event-dedup.ts +23 -0
- package/src/pipeline/event-dispatcher.ts +1016 -0
- package/src/pipeline/event-retention.ts +154 -0
- package/src/pipeline/idempotency.ts +76 -0
- package/src/pipeline/index.ts +66 -0
- package/src/pipeline/lifecycle-pipeline.ts +409 -0
- package/src/pipeline/msp-rebuild.ts +242 -0
- package/src/pipeline/multi-stream-apply-context.ts +115 -0
- package/src/pipeline/projection-rebuild.ts +334 -0
- package/src/pipeline/projection-state.ts +72 -0
- package/src/pipeline/projections-runner.ts +56 -0
- package/src/pipeline/redis-keys.ts +11 -0
- package/src/pipeline/system-hooks.ts +190 -0
- package/src/random/__tests__/generate.test.ts +149 -0
- package/src/random/generate.ts +141 -0
- package/src/random/index.ts +8 -0
- package/src/random/words.ts +392 -0
- package/src/rate-limit/__tests__/dispatcher-l3.integration.ts +111 -0
- package/src/rate-limit/__tests__/middleware.integration.ts +189 -0
- package/src/rate-limit/__tests__/resolver.integration.ts +189 -0
- package/src/rate-limit/bucket.ts +36 -0
- package/src/rate-limit/index.ts +14 -0
- package/src/rate-limit/middleware.ts +152 -0
- package/src/rate-limit/resolver.ts +267 -0
- package/src/redis/__tests__/redis-options.test.ts +54 -0
- package/src/redis/index.ts +74 -0
- package/src/search/__tests__/meilisearch-adapter.integration.ts +236 -0
- package/src/search/__tests__/search-adapter.test.ts +256 -0
- package/src/search/in-memory-adapter.ts +123 -0
- package/src/search/index.ts +12 -0
- package/src/search/meilisearch-adapter.ts +106 -0
- package/src/search/types.ts +39 -0
- package/src/secrets/__tests__/dek-cache.test.ts +213 -0
- package/src/secrets/__tests__/env-master-key-provider.test.ts +119 -0
- package/src/secrets/__tests__/envelope.test.ts +74 -0
- package/src/secrets/__tests__/leak-guard.test.ts +92 -0
- package/src/secrets/__tests__/rotation.test.ts +149 -0
- package/src/secrets/dek-cache.ts +116 -0
- package/src/secrets/env-master-key-provider.ts +162 -0
- package/src/secrets/envelope.ts +55 -0
- package/src/secrets/index.ts +19 -0
- package/src/secrets/leak-guard.ts +87 -0
- package/src/secrets/rotation.ts +34 -0
- package/src/secrets/types.ts +107 -0
- package/src/stack/db.ts +104 -0
- package/src/stack/event-collector.ts +23 -0
- package/src/stack/index.ts +32 -0
- package/src/stack/redis.ts +44 -0
- package/src/stack/request-helper.ts +168 -0
- package/src/stack/table-helpers.ts +104 -0
- package/src/stack/test-stack.ts +357 -0
- package/src/stack/test-users.ts +37 -0
- package/src/testing/__tests__/e2e-generator.test.ts +230 -0
- package/src/testing/__tests__/ensure-entity-table.integration.ts +54 -0
- package/src/testing/access-assertions.ts +15 -0
- package/src/testing/assertions.ts +35 -0
- package/src/testing/e2e-generator.ts +465 -0
- package/src/testing/expect-error.ts +25 -0
- package/src/testing/handler-context.ts +125 -0
- package/src/testing/http-cookies.ts +52 -0
- package/src/testing/index.ts +41 -0
- package/src/testing/late-bound.ts +39 -0
- package/src/testing/mutable-master-key-provider.ts +31 -0
- package/src/testing/observability-recorder.ts +54 -0
- package/src/testing/shared-entities.ts +49 -0
- package/src/testing/utils.ts +1 -0
- package/src/testing/wait-for.ts +31 -0
- package/src/time/__tests__/polyfill.test.ts +73 -0
- package/src/time/__tests__/tz-context.test.ts +121 -0
- package/src/time/index.ts +21 -0
- package/src/time/polyfill.ts +70 -0
- package/src/time/tz-context.ts +107 -0
- package/src/ui-types/app-schema.ts +57 -0
- package/src/ui-types/index.ts +65 -0
- package/src/utils/__tests__/assert.test.ts +17 -0
- package/src/utils/__tests__/env-parse.test.ts +54 -0
- package/src/utils/assert.ts +18 -0
- package/src/utils/env-parse.ts +16 -0
- package/src/utils/ids.ts +16 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/safe-json.ts +30 -0
- package/src/utils/serialization.ts +7 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Central re-export for all PostgreSQL-specific imports.
|
|
2
|
+
// No other file in the framework should import from "drizzle-orm/pg-core" directly.
|
|
3
|
+
|
|
4
|
+
import { customType } from "drizzle-orm/pg-core";
|
|
5
|
+
|
|
6
|
+
export type {
|
|
7
|
+
PgSelect as SelectQuery,
|
|
8
|
+
PgTableWithColumns as TableColumns,
|
|
9
|
+
} from "drizzle-orm/pg-core";
|
|
10
|
+
export {
|
|
11
|
+
bigint,
|
|
12
|
+
bigserial,
|
|
13
|
+
boolean,
|
|
14
|
+
index,
|
|
15
|
+
integer,
|
|
16
|
+
jsonb,
|
|
17
|
+
numeric,
|
|
18
|
+
pgTable as table,
|
|
19
|
+
primaryKey,
|
|
20
|
+
serial,
|
|
21
|
+
text,
|
|
22
|
+
timestamp,
|
|
23
|
+
uniqueIndex,
|
|
24
|
+
uuid,
|
|
25
|
+
} from "drizzle-orm/pg-core";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Money column: BIGINT storing the integer minor unit (cents for EUR, yen
|
|
29
|
+
* for JPY). Range ±9.2e18 in the DB; JS `number` is safe to 2^53 ≈ 9e15
|
|
30
|
+
* minor units = ~90 trillion EUR. INTEGER would cap at ~21 million EUR per
|
|
31
|
+
* row which is too tight for real-world invoices, bank balances, or bills
|
|
32
|
+
* of sale — hence bigint.
|
|
33
|
+
*/
|
|
34
|
+
export const moneyAmount = customType<{ data: number; driverData: string | number }>({
|
|
35
|
+
dataType() {
|
|
36
|
+
return "bigint";
|
|
37
|
+
},
|
|
38
|
+
fromDriver(value: string | number): number {
|
|
39
|
+
// node-postgres returns BIGINT as string by default; Bun's pg returns
|
|
40
|
+
// number. Cast via Number() either way — safe because we stay under 2^53.
|
|
41
|
+
return typeof value === "number" ? value : Number(value);
|
|
42
|
+
},
|
|
43
|
+
toDriver(value: number): number {
|
|
44
|
+
return value;
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Instant column: TIMESTAMPTZ storing a UTC instant, surfaced to JS as
|
|
50
|
+
* `Temporal.Instant`. Replaces the old dual-mode situation (`mode:"date"`
|
|
51
|
+
* for base fields vs `mode:"string"` for user-defined timestamp fields)
|
|
52
|
+
* with a single round-trip type. See sprint-f-temporal.md for the migration.
|
|
53
|
+
*
|
|
54
|
+
* Driver-data is the ISO-8601 string the postgres driver actually exchanges.
|
|
55
|
+
* `fromDriver` reads what the driver returns (postgres-js gives strings for
|
|
56
|
+
* timestamptz) and parses through Temporal. `toDriver` writes the canonical
|
|
57
|
+
* Temporal.Instant.toString() — the spike confirmed `eq/lte/gt/orderBy/
|
|
58
|
+
* returning` accept Temporal.Instant directly without manual `.toString()`
|
|
59
|
+
* at the call site.
|
|
60
|
+
*
|
|
61
|
+
* Boot-order note: `Temporal` must exist on globalThis before any
|
|
62
|
+
* fromDriver/toDriver call. `ensureTemporalPolyfill()` runs at framework
|
|
63
|
+
* boot. The closures here are lazy — they fire on read/write, not on
|
|
64
|
+
* module load — so importing this file before the polyfill is safe.
|
|
65
|
+
*
|
|
66
|
+
* Optional `precision` (0..6) — fractional-second digits. Default 6 matches
|
|
67
|
+
* PG's default `timestamptz`. Pass 3 for the events-table (ms precision —
|
|
68
|
+
* matches what asOf-queries can compare reliably). Affects only CREATE TABLE
|
|
69
|
+
* DDL via drizzle-kit; runtime parse handles any precision via Temporal.
|
|
70
|
+
*/
|
|
71
|
+
// Forgiving overload: payloads from custom write-handlers sometimes
|
|
72
|
+
// arrive as ISO strings rather than Temporal.Instant (Zod insert-
|
|
73
|
+
// schemas use z.iso.datetime, not a Temporal validator). Coerce here
|
|
74
|
+
// at the boundary so the DB always sees a normalised string — and
|
|
75
|
+
// Temporal.Instant.from throws on bad input, which is the right failure
|
|
76
|
+
// mode (vs. the obscure "x.toString is not a function" crash that hit
|
|
77
|
+
// feature authors before this overload existed).
|
|
78
|
+
//
|
|
79
|
+
// Date-only strings (YYYY-MM-DD) coercen wir auf start-of-day UTC.
|
|
80
|
+
// Hintergrund: type:"date" ist heute auf instant() aliased (table-
|
|
81
|
+
// builder.ts TODO Sprint G), aber die Zod-Validation akzeptiert nur
|
|
82
|
+
// YYYY-MM-DD. Ohne diesen Coerce wirft Temporal mit "Cannot parse:
|
|
83
|
+
// 2026-04-10" und der Author bekommt einen internal_error 500 statt
|
|
84
|
+
// einen sauberen DB-Roundtrip.
|
|
85
|
+
//
|
|
86
|
+
// Exportiert für Tests; production-call läuft über instantBuilder.
|
|
87
|
+
export function instantToDriver(value: Temporal.Instant | string): string {
|
|
88
|
+
if (typeof value === "string") {
|
|
89
|
+
const dateOnly = /^\d{4}-\d{2}-\d{2}$/.test(value);
|
|
90
|
+
const iso = dateOnly ? `${value}T00:00:00Z` : value;
|
|
91
|
+
return Temporal.Instant.from(iso).toString();
|
|
92
|
+
}
|
|
93
|
+
return value.toString();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const instantBuilder = (config?: { precision?: 0 | 1 | 2 | 3 | 4 | 5 | 6 }) =>
|
|
97
|
+
customType<{ data: Temporal.Instant; driverData: string }>({
|
|
98
|
+
dataType() {
|
|
99
|
+
const p = config?.precision;
|
|
100
|
+
return p !== undefined ? `timestamp(${p}) with time zone` : "timestamptz";
|
|
101
|
+
},
|
|
102
|
+
fromDriver(value: string): Temporal.Instant {
|
|
103
|
+
return Temporal.Instant.from(value);
|
|
104
|
+
},
|
|
105
|
+
toDriver: instantToDriver,
|
|
106
|
+
});
|
|
107
|
+
export function instant(name: string, config?: { precision?: 0 | 1 | 2 | 3 | 4 | 5 | 6 }) {
|
|
108
|
+
return instantBuilder(config)(name);
|
|
109
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// Tier 2.7e Server-Side Eagerload für Reference-Felder.
|
|
2
|
+
//
|
|
3
|
+
// Nach `executor.list`/`detail` scannen wir die zurückgelieferten
|
|
4
|
+
// rows nach reference-Field-Values, sammeln pro Reference die UUIDs
|
|
5
|
+
// (deduped), führen einen einzigen WHERE id IN (...)-Lookup pro
|
|
6
|
+
// Referenced-Entity aus, und hängen die resolved Rows als
|
|
7
|
+
// `_refs.<fieldName>` (single) bzw. `_refs.<fieldName>: Row[]`
|
|
8
|
+
// (multiple) an die Original-Rows.
|
|
9
|
+
//
|
|
10
|
+
// Tenant-Scope: TenantDb hat den Tenant-Filter eingebaut (mode:
|
|
11
|
+
// "tenant"); der Lookup erbt das transparent. Cross-Feature-Refs
|
|
12
|
+
// landen automatisch im selben Tenant — falls ein referenced Item
|
|
13
|
+
// dem User nicht gehört, kommt es aus dem Lookup nicht zurück
|
|
14
|
+
// (TenantDb filtert), und der Renderer fällt auf UUID zurück.
|
|
15
|
+
//
|
|
16
|
+
// Limit: kein expliziter limit auf den Lookup-SELECT — wir
|
|
17
|
+
// fragen genau die UUIDs ab die in den main-Rows vorkommen, also
|
|
18
|
+
// O(n) pro Page (bei pageSize:50 mit 2 ref-Spalten = max 100 IDs).
|
|
19
|
+
// Render-Side limit:200-Workaround entfällt damit komplett.
|
|
20
|
+
//
|
|
21
|
+
// Diese Datei lebt im framework/db damit sie an einer Stelle
|
|
22
|
+
// zwischen executor und entity-handlers gemounted ist; sie nutzt
|
|
23
|
+
// keine framework-engine-Internals und kann auch von custom
|
|
24
|
+
// query-handlern manuell aufgerufen werden.
|
|
25
|
+
|
|
26
|
+
import { inArray } from "drizzle-orm";
|
|
27
|
+
import type { EntityDefinition, FieldDefinition, ReferenceFieldDef } from "../engine/types";
|
|
28
|
+
import { buildDrizzleTable } from "./table-builder";
|
|
29
|
+
import type { TenantDb } from "./tenant-db";
|
|
30
|
+
|
|
31
|
+
// Minimaler Registry-Lookup-Contract: pro entity-name → EntityDefinition.
|
|
32
|
+
// Wir importieren NICHT den ganzen Registry-Type weil das einen
|
|
33
|
+
// circular import zwischen db/ und engine/ erzeugen würde — der
|
|
34
|
+
// Caller (entity-handlers.ts) hat ctx.registry und reicht hier eine
|
|
35
|
+
// Closure rein.
|
|
36
|
+
export type EagerLoadEntityResolver = (entityName: string) => EntityDefinition | undefined;
|
|
37
|
+
|
|
38
|
+
// Tier 2.7e Audit-Fix #6: zentral typed Row-Shape mit _refs. Der
|
|
39
|
+
// `_refs`-Property ist Server-Eagerload-Output: pro reference-Field
|
|
40
|
+
// die resolved Row (single) oder ein Array resolved Rows (multiple).
|
|
41
|
+
// Eine reference-Spalte mit value=null hat _refs[fieldName]=undefined.
|
|
42
|
+
//
|
|
43
|
+
// Renderer/Cell-Code liest `row._refs?.[fieldName]` statt inline-Cast;
|
|
44
|
+
// Server-Code stempelt `_refs` über enrichWithReferences. Type ist
|
|
45
|
+
// strukturell — auch Apps die ihre eigenen Refs setzen (Custom-
|
|
46
|
+
// Handler) sollten das hier wiederverwenden.
|
|
47
|
+
export type EagerloadedRow<T extends Record<string, unknown> = Record<string, unknown>> = T & {
|
|
48
|
+
readonly _refs?: Readonly<
|
|
49
|
+
Record<string, Record<string, unknown> | ReadonlyArray<Record<string, unknown>> | undefined>
|
|
50
|
+
>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
type ReferenceFieldEntry = {
|
|
54
|
+
readonly fieldName: string;
|
|
55
|
+
readonly refEntityName: string;
|
|
56
|
+
readonly multiple: boolean;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function isReferenceField(field: FieldDefinition): field is ReferenceFieldDef {
|
|
60
|
+
return field.type === "reference";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function parseRefEntity(raw: string): string {
|
|
64
|
+
// Same-feature ("user") oder cross-feature ("users:user") — wir
|
|
65
|
+
// brauchen nur den entity-name (Names sind global eindeutig in
|
|
66
|
+
// entityMap). Der feature-prefix dient nur der Author-Klarheit.
|
|
67
|
+
const idx = raw.indexOf(":");
|
|
68
|
+
return idx < 0 ? raw : raw.slice(idx + 1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function collectReferenceFields(entity: EntityDefinition): readonly ReferenceFieldEntry[] {
|
|
72
|
+
const out: ReferenceFieldEntry[] = [];
|
|
73
|
+
for (const [fieldName, fieldDef] of Object.entries(entity.fields)) {
|
|
74
|
+
if (!isReferenceField(fieldDef)) continue;
|
|
75
|
+
out.push({
|
|
76
|
+
fieldName,
|
|
77
|
+
refEntityName: parseRefEntity(fieldDef.entity),
|
|
78
|
+
multiple: fieldDef.multiple === true,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Eagerload für eine Liste von Rows. Mutiert nicht — gibt eine
|
|
85
|
+
* flache Kopie der Rows mit hinzugefügtem `_refs`-Property zurück. */
|
|
86
|
+
export async function enrichWithReferences(
|
|
87
|
+
rows: ReadonlyArray<Record<string, unknown>>,
|
|
88
|
+
entity: EntityDefinition,
|
|
89
|
+
resolveEntity: EagerLoadEntityResolver,
|
|
90
|
+
db: TenantDb,
|
|
91
|
+
): Promise<Array<Record<string, unknown>>> {
|
|
92
|
+
const refFields = collectReferenceFields(entity);
|
|
93
|
+
if (refFields.length === 0 || rows.length === 0) {
|
|
94
|
+
return rows.map((r) => ({ ...r }));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Pro reference-Field: deduped Set der IDs sammeln, dann ein
|
|
98
|
+
// einziger SELECT WHERE id IN (...). Maps werden parallel gebaut
|
|
99
|
+
// damit die Lookups nicht serialisieren (Promise.all).
|
|
100
|
+
const lookupMaps = await Promise.all(
|
|
101
|
+
refFields.map(async (rf) => {
|
|
102
|
+
const ids = new Set<string>();
|
|
103
|
+
for (const row of rows) {
|
|
104
|
+
const v = row[rf.fieldName];
|
|
105
|
+
if (rf.multiple) {
|
|
106
|
+
if (Array.isArray(v)) {
|
|
107
|
+
for (const item of v) {
|
|
108
|
+
if (typeof item === "string" && item.length > 0) ids.add(item);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} else if (typeof v === "string" && v.length > 0) {
|
|
112
|
+
ids.add(v);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (ids.size === 0) return { fieldName: rf.fieldName, multiple: rf.multiple, map: new Map() };
|
|
116
|
+
const refEntity = resolveEntity(rf.refEntityName);
|
|
117
|
+
if (refEntity === undefined) {
|
|
118
|
+
// Author-Fehler oder Race-Condition (entity gerade umbenannt
|
|
119
|
+
// ohne registry-Reload). Boot-Validator hat das normalerweise
|
|
120
|
+
// gepinnt; Runtime-Defense: leere Map → Renderer fällt auf
|
|
121
|
+
// UUID zurück, kein Crash.
|
|
122
|
+
return { fieldName: rf.fieldName, multiple: rf.multiple, map: new Map() };
|
|
123
|
+
}
|
|
124
|
+
const refTable = buildDrizzleTable(rf.refEntityName, refEntity);
|
|
125
|
+
const idCol = refTable["id"];
|
|
126
|
+
if (idCol === undefined) {
|
|
127
|
+
return { fieldName: rf.fieldName, multiple: rf.multiple, map: new Map() };
|
|
128
|
+
}
|
|
129
|
+
const idArray = [...ids];
|
|
130
|
+
const refRows = (await db
|
|
131
|
+
.select()
|
|
132
|
+
.from(refTable)
|
|
133
|
+
.where(inArray(idCol, idArray as never /* @cast-boundary db-operator */))) as Array<
|
|
134
|
+
Record<string, unknown>
|
|
135
|
+
>; // @cast-boundary db-row
|
|
136
|
+
const map = new Map<string, Record<string, unknown>>();
|
|
137
|
+
for (const r of refRows) {
|
|
138
|
+
const id = r["id"];
|
|
139
|
+
if (typeof id === "string") map.set(id, r);
|
|
140
|
+
}
|
|
141
|
+
return { fieldName: rf.fieldName, multiple: rf.multiple, map };
|
|
142
|
+
}),
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
return rows.map((row) => {
|
|
146
|
+
const refs: Record<string, unknown> = {};
|
|
147
|
+
for (const lookup of lookupMaps) {
|
|
148
|
+
const v = row[lookup.fieldName];
|
|
149
|
+
if (lookup.multiple) {
|
|
150
|
+
const ids = Array.isArray(v) ? v : [];
|
|
151
|
+
const resolved = ids
|
|
152
|
+
.map((id) => (typeof id === "string" ? lookup.map.get(id) : undefined))
|
|
153
|
+
.filter((r) => r !== undefined);
|
|
154
|
+
refs[lookup.fieldName] = resolved;
|
|
155
|
+
} else if (typeof v === "string" && v.length > 0) {
|
|
156
|
+
refs[lookup.fieldName] = lookup.map.get(v);
|
|
157
|
+
} else {
|
|
158
|
+
refs[lookup.fieldName] = undefined;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return { ...row, _refs: refs };
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Single-Row-Variante für detail-Calls. */
|
|
166
|
+
export async function enrichRowWithReferences(
|
|
167
|
+
row: Record<string, unknown>,
|
|
168
|
+
entity: EntityDefinition,
|
|
169
|
+
resolveEntity: EagerLoadEntityResolver,
|
|
170
|
+
db: TenantDb,
|
|
171
|
+
): Promise<Record<string, unknown>> {
|
|
172
|
+
const enriched = await enrichWithReferences([row], entity, resolveEntity, db);
|
|
173
|
+
return enriched[0] ?? { ...row, _refs: {} };
|
|
174
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
const ALGORITHM = "aes-256-gcm";
|
|
4
|
+
const IV_LENGTH = 12;
|
|
5
|
+
const TAG_LENGTH = 16;
|
|
6
|
+
|
|
7
|
+
export type EncryptionProvider = {
|
|
8
|
+
encrypt(plaintext: string): string;
|
|
9
|
+
decrypt(ciphertext: string): string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function createEncryptionProvider(key: string): EncryptionProvider {
|
|
13
|
+
// Key must be 32 bytes for AES-256
|
|
14
|
+
const keyBuffer = Buffer.from(key, "base64");
|
|
15
|
+
if (keyBuffer.length !== 32) {
|
|
16
|
+
throw new Error("ENCRYPTION_KEY must be 32 bytes (base64 encoded)");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
encrypt(plaintext: string): string {
|
|
21
|
+
const iv = randomBytes(IV_LENGTH);
|
|
22
|
+
const cipher = createCipheriv(ALGORITHM, keyBuffer, iv);
|
|
23
|
+
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
24
|
+
const tag = cipher.getAuthTag();
|
|
25
|
+
// Format: base64(iv + tag + ciphertext)
|
|
26
|
+
return Buffer.concat([iv, tag, encrypted]).toString("base64");
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
decrypt(ciphertext: string): string {
|
|
30
|
+
const data = Buffer.from(ciphertext, "base64");
|
|
31
|
+
const iv = data.subarray(0, IV_LENGTH);
|
|
32
|
+
const tag = data.subarray(IV_LENGTH, IV_LENGTH + TAG_LENGTH);
|
|
33
|
+
const encrypted = data.subarray(IV_LENGTH + TAG_LENGTH);
|
|
34
|
+
const decipher = createDecipheriv(ALGORITHM, keyBuffer, iv);
|
|
35
|
+
decipher.setAuthTag(tag);
|
|
36
|
+
return decipher.update(encrypted) + decipher.final("utf8");
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|