@cosmicdrift/kumiko-framework 0.386.0 → 0.388.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/AGENTS.md +6 -1
- package/LICENSE +21 -57
- package/README.md +2 -2
- package/agents/examples/anonymous-access.md +1 -1
- package/agents/examples/apex-landing.md +204 -0
- package/agents/examples/api-worker-split.md +64 -0
- package/agents/examples/app-config.md +14 -0
- package/agents/examples/auth-mfa.md +115 -0
- package/agents/examples/env-composition.md +28 -1
- package/agents/examples/legal-pages.md +1 -1
- package/agents/examples/prod-boot-options.md +16 -0
- package/agents/examples/rate-limiting.md +48 -0
- package/agents/examples/record-detail-layout.md +692 -0
- package/agents/examples/seed-migration.md +212 -0
- package/agents/examples/wizard-form.md +941 -0
- package/agents/examples/writeform-section.md +548 -0
- package/agents/features.md +19 -2
- package/agents/widgets.md +40 -39
- package/dist/api/api-constants.js +2 -2
- package/dist/api/auth-middleware.js +7 -7
- package/dist/api/auth-rate-limiters.js +7 -3
- package/dist/api/auth-routes-mfa.js +1 -1
- package/dist/api/auth-routes-session.js +1 -1
- package/dist/api/auth-routes-shared.js +7 -3
- package/dist/api/auth-routes-signup.js +1 -1
- package/dist/api/client-error-route.js +8 -2
- package/dist/api/client-ip.js +3 -1
- package/dist/api/extra-route.js +1 -1
- package/dist/api/index.d.ts +3 -1
- package/dist/api/index.js +3 -1
- package/dist/api/pii-leak-guard.js +4 -2
- package/dist/api/readiness.js +5 -5
- package/dist/api/redis-sse-broker.js +8 -6
- package/dist/api/request-context.d.ts +1 -1
- package/dist/api/request-context.js +1 -1
- package/dist/api/routes.js +2 -2
- package/dist/api/server-boot-checks.d.ts +10 -0
- package/dist/api/server-boot-checks.js +157 -0
- package/dist/api/server-event-dispatcher.d.ts +26 -0
- package/dist/api/server-event-dispatcher.js +208 -0
- package/dist/api/server-extra-routes.d.ts +11 -0
- package/dist/api/server-extra-routes.js +203 -0
- package/dist/api/server-middleware.d.ts +20 -0
- package/dist/api/server-middleware.js +211 -0
- package/dist/api/server-routes.d.ts +19 -0
- package/dist/api/server-routes.js +293 -0
- package/dist/api/server-websocket-routes.d.ts +4 -0
- package/dist/api/server-websocket-routes.js +108 -0
- package/dist/api/server-wiring.d.ts +32 -0
- package/dist/api/server-wiring.js +236 -0
- package/dist/api/server.d.ts +10 -15
- package/dist/api/server.js +48 -1250
- package/dist/api/session-cookies.js +4 -4
- package/dist/api/sse-broker.js +1 -1
- package/dist/api/sse-route.d.ts +13 -13
- package/dist/api/sse-route.js +31 -27
- package/dist/auth/index.js +4 -4
- package/dist/auth/roles.d.ts +4 -4
- package/dist/auth/roles.js +24 -24
- package/dist/compliance/duration-spec.js +18 -20
- package/dist/compliance/index.js +2 -3
- package/dist/compliance/override-schema.d.ts +4 -4
- package/dist/compliance/override-schema.js +19 -22
- package/dist/compliance/profiles.d.ts +26 -29
- package/dist/compliance/profiles.js +62 -69
- package/dist/compliance/sub-processors.d.ts +28 -30
- package/dist/compliance/sub-processors.js +17 -21
- package/dist/consumer-cli.js +4 -4
- package/dist/crypto/blind-index.js +18 -17
- package/dist/crypto/ciphertext-pattern.js +1 -1
- package/dist/crypto/event-pii.js +3 -3
- package/dist/crypto/kek-source.d.ts +1 -1
- package/dist/crypto/kek-source.js +3 -2
- package/dist/crypto/kms-adapter.d.ts +1 -1
- package/dist/crypto/kms-adapter.js +4 -6
- package/dist/crypto/kms-wiring.d.ts +1 -2
- package/dist/crypto/kms-wiring.js +2 -2
- package/dist/crypto/owner-transition-rekey.d.ts +5 -1
- package/dist/crypto/owner-transition-rekey.js +1 -1
- package/dist/crypto/pg-kms-adapter.js +2 -6
- package/dist/crypto/pii-field-encryption.d.ts +5 -1
- package/dist/crypto/pii-field-encryption.js +7 -5
- package/dist/crypto/subject-resolver.d.ts +1 -1
- package/dist/crypto/subject-resolver.js +2 -2
- package/dist/db/aggregate-sql.d.ts +21 -0
- package/dist/db/aggregate-sql.js +155 -0
- package/dist/db/apply-entity-event.d.ts +6 -6
- package/dist/db/apply-entity-event.js +55 -55
- package/dist/db/blind-index-cleanup.js +7 -7
- package/dist/db/closed-connection.d.ts +9 -0
- package/dist/db/closed-connection.js +95 -0
- package/dist/db/collect-table-metas.js +10 -11
- package/dist/db/compound-types.d.ts +1 -1
- package/dist/db/compound-types.js +12 -12
- package/dist/db/cursor.js +3 -4
- package/dist/db/dialect.d.ts +2 -2
- package/dist/db/dialect.js +3 -3
- package/dist/db/distinct-on.d.ts +1 -1
- package/dist/db/driver-row-coercion.d.ts +2 -0
- package/dist/db/driver-row-coercion.js +186 -0
- package/dist/db/driver-value-preparation.d.ts +16 -0
- package/dist/db/driver-value-preparation.js +99 -0
- package/dist/db/eagerload.d.ts +3 -3
- package/dist/db/eagerload.js +21 -12
- package/dist/db/entity-field-encryption.d.ts +1 -0
- package/dist/db/entity-field-encryption.js +1 -1
- package/dist/db/entity-table-meta.d.ts +2 -2
- package/dist/db/entity-table-meta.js +105 -112
- package/dist/db/event-store-executor-context.d.ts +3 -2
- package/dist/db/event-store-executor-context.js +13 -13
- package/dist/db/event-store-executor-read-aggregate.d.ts +8 -0
- package/dist/db/event-store-executor-read-aggregate.js +112 -0
- package/dist/db/event-store-executor-read-filter.d.ts +31 -0
- package/dist/db/event-store-executor-read-filter.js +203 -0
- package/dist/db/event-store-executor-read-paging.d.ts +14 -0
- package/dist/db/event-store-executor-read-paging.js +99 -0
- package/dist/db/event-store-executor-read-rows.d.ts +6 -0
- package/dist/db/event-store-executor-read-rows.js +32 -0
- package/dist/db/event-store-executor-read-search.d.ts +28 -0
- package/dist/db/event-store-executor-read-search.js +221 -0
- package/dist/db/event-store-executor-read-sort.d.ts +41 -0
- package/dist/db/event-store-executor-read-sort.js +130 -0
- package/dist/db/event-store-executor-read-where.d.ts +24 -0
- package/dist/db/event-store-executor-read-where.js +81 -0
- package/dist/db/event-store-executor-read.d.ts +2 -9
- package/dist/db/event-store-executor-read.js +50 -704
- package/dist/db/event-store-executor-write-steps.d.ts +20 -0
- package/dist/db/event-store-executor-write-steps.js +100 -0
- package/dist/db/event-store-executor-write.js +401 -510
- package/dist/db/index.d.ts +6 -2
- package/dist/db/index.js +5 -1
- package/dist/db/list-aggregate-labels.d.ts +14 -0
- package/dist/db/list-aggregate-labels.js +26 -0
- package/dist/db/located-timestamp.d.ts +11 -11
- package/dist/db/located-timestamp.js +29 -30
- package/dist/db/migrate-generator.d.ts +1 -1
- package/dist/db/migrate-generator.js +97 -105
- package/dist/db/migrate-runner.js +29 -26
- package/dist/db/money.d.ts +6 -6
- package/dist/db/money.js +29 -27
- package/dist/db/queries/backfill-pii.js +5 -5
- package/dist/db/queries/ddl.js +1 -1
- package/dist/db/queries/es-ops.d.ts +1 -1
- package/dist/db/queries/es-ops.js +1 -1
- package/dist/db/queries/event-consumer.js +3 -3
- package/dist/db/queries/event-store.js +4 -2
- package/dist/db/queries/projection-rebuild.js +3 -3
- package/dist/db/queries/raw-sql.d.ts +2 -2
- package/dist/db/queries/raw-sql.js +2 -2
- package/dist/db/queries/shadow-swap.js +19 -19
- package/dist/db/query.d.ts +3 -16
- package/dist/db/query.js +8 -761
- package/dist/db/rebuild-marker.js +20 -21
- package/dist/db/reference-data.js +6 -1
- package/dist/db/replay-migration-sql.js +2 -2
- package/dist/db/schema-inspection.js +4 -5
- package/dist/db/table-builder.d.ts +1 -2
- package/dist/db/table-builder.js +137 -190
- package/dist/db/table-info.d.ts +10 -0
- package/dist/db/table-info.js +45 -0
- package/dist/db/temporal-value-guards.d.ts +6 -0
- package/dist/db/temporal-value-guards.js +15 -0
- package/dist/db/tenant-db-runner.d.ts +1 -0
- package/dist/db/tenant-db-runner.js +3 -0
- package/dist/db/tenant-db.d.ts +2 -2
- package/dist/db/tenant-db.js +48 -11
- package/dist/db/where-clause.d.ts +6 -0
- package/dist/db/where-clause.js +236 -0
- package/dist/derivatives/delete-stored-file.js +6 -2
- package/dist/derivatives/derivatives-context.js +1 -1
- package/dist/engine/boot-validator/access-roles.js +4 -3
- package/dist/engine/boot-validator/api-ext.js +14 -14
- package/dist/engine/boot-validator/boot-check.js +1 -1
- package/dist/engine/boot-validator/config-deps.js +7 -6
- package/dist/engine/boot-validator/detail-screens.js +4 -4
- package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
- package/dist/engine/boot-validator/entity-handler.js +77 -61
- package/dist/engine/boot-validator/form-derivations.js +35 -1
- package/dist/engine/boot-validator/gdpr-storage.js +4 -4
- package/dist/engine/boot-validator/i18n-keys.js +6 -4
- package/dist/engine/boot-validator/index.d.ts +2 -2
- package/dist/engine/boot-validator/index.js +22 -20
- package/dist/engine/boot-validator/nav.js +5 -5
- package/dist/engine/boot-validator/ownership.js +12 -9
- package/dist/engine/boot-validator/parent-ref.js +1 -1
- package/dist/engine/boot-validator/pii-retention.js +258 -187
- package/dist/engine/boot-validator/projection-list-screens.js +7 -7
- package/dist/engine/boot-validator/query-refs.js +1 -1
- package/dist/engine/boot-validator/screens-actions.d.ts +1 -0
- package/dist/engine/boot-validator/screens-actions.js +23 -17
- package/dist/engine/boot-validator/screens-dashboard.js +2 -5
- package/dist/engine/boot-validator/screens-forms.js +12 -17
- package/dist/engine/boot-validator/screens-kind-config-edit.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-config-edit.js +88 -0
- package/dist/engine/boot-validator/screens-kind-entity-edit.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-entity-edit.js +55 -0
- package/dist/engine/boot-validator/screens-kind-entity-list.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-entity-list.js +228 -0
- package/dist/engine/boot-validator/screens-kind-projection-detail.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-projection-detail.js +235 -0
- package/dist/engine/boot-validator/screens-kind-projection-list.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-projection-list.js +52 -0
- package/dist/engine/boot-validator/screens-kind-secret-mint.d.ts +3 -0
- package/dist/engine/boot-validator/screens-kind-secret-mint.js +28 -0
- package/dist/engine/boot-validator/screens-kind-shared.d.ts +37 -0
- package/dist/engine/boot-validator/screens-kind-shared.js +142 -0
- package/dist/engine/boot-validator/screens-lists.d.ts +2 -1
- package/dist/engine/boot-validator/screens-lists.js +26 -24
- package/dist/engine/boot-validator/screens.d.ts +0 -2
- package/dist/engine/boot-validator/screens.js +102 -869
- package/dist/engine/boot-validator/security-baseline.js +3 -2
- package/dist/engine/boot-validator/transfer-graph.js +2 -2
- package/dist/engine/boot-validator/workspaces.js +3 -3
- package/dist/engine/bridge-stub.js +3 -4
- package/dist/engine/build-app-schema.js +130 -170
- package/dist/engine/build-config-feature-schema.js +50 -50
- package/dist/engine/build-target.js +20 -23
- package/dist/engine/codemod/pipeline-codemod.js +20 -13
- package/dist/engine/config-helpers.js +4 -4
- package/dist/engine/constants.js +2 -2
- package/dist/engine/create-app.js +2 -2
- package/dist/engine/define-feature.js +21 -13
- package/dist/engine/define-handler.js +13 -11
- package/dist/engine/define-step.js +1 -1
- package/dist/engine/define-workflow.d.ts +5 -5
- package/dist/engine/define-workflow.js +6 -6
- package/dist/engine/entity-handlers.d.ts +13 -0
- package/dist/engine/entity-handlers.js +22 -34
- package/dist/engine/entity-list-runtime-options.d.ts +8 -0
- package/dist/engine/entity-list-runtime-options.js +18 -0
- package/dist/engine/event-def.js +2 -2
- package/dist/engine/extension-names.d.ts +16 -17
- package/dist/engine/extension-names.js +37 -38
- package/dist/engine/extensions/escape-hatch-usage.js +1 -1
- package/dist/engine/extensions/tenant-data.js +2 -2
- package/dist/engine/extensions/user-data.d.ts +23 -24
- package/dist/engine/factories.d.ts +14 -13
- package/dist/engine/factories.js +38 -37
- package/dist/engine/feature-ast/entity-field-types.js +1 -1
- package/dist/engine/feature-ast/extractors/handlers.js +2 -1
- package/dist/engine/feature-ast/extractors/hooks.js +1 -1
- package/dist/engine/feature-ast/extractors/round2.js +25 -6
- package/dist/engine/feature-ast/extractors/shared.d.ts +2 -3
- package/dist/engine/feature-ast/extractors/shared.js +2 -3
- package/dist/engine/feature-ast/parse.js +13 -13
- package/dist/engine/feature-ast/patch.js +4 -182
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +3 -0
- package/dist/engine/feature-ast/pattern-change-schema.js +2 -1
- package/dist/engine/feature-ast/pattern-id-matchers.d.ts +7 -0
- package/dist/engine/feature-ast/pattern-id-matchers.js +210 -0
- package/dist/engine/feature-ast/patterns.d.ts +11 -2
- package/dist/engine/feature-ast/patterns.js +43 -51
- package/dist/engine/feature-ast/render.js +50 -89
- package/dist/engine/feature-config-events-jobs.js +15 -12
- package/dist/engine/feature-entity-handlers.js +12 -5
- package/dist/engine/feature-manifest.d.ts +9 -9
- package/dist/engine/feature-manifest.js +6 -7
- package/dist/engine/feature-ui-extensions.d.ts +3 -3
- package/dist/engine/feature-ui-extensions.js +30 -22
- package/dist/engine/field-access.js +5 -6
- package/dist/engine/form-derivations.js +18 -7
- package/dist/engine/handler-field-references.js +1 -1
- package/dist/engine/index.d.ts +1 -0
- package/dist/engine/index.js +4 -4
- package/dist/engine/object-form.d.ts +1 -0
- package/dist/engine/object-form.js +14 -4
- package/dist/engine/ownership.js +3 -3
- package/dist/engine/parse-ref-target.js +5 -7
- package/dist/engine/pattern-library/index.js +2 -2
- package/dist/engine/pattern-library/library.js +2 -2
- package/dist/engine/pattern-library/types.js +3 -3
- package/dist/engine/pipeline.js +2 -2
- package/dist/engine/project-app-schema-for-roles.d.ts +2 -0
- package/dist/engine/project-app-schema-for-roles.js +7 -5
- package/dist/engine/projection-helpers.js +9 -9
- package/dist/engine/qualified-name.js +2 -2
- package/dist/engine/registry-facade.js +1 -1
- package/dist/engine/registry-ingest.js +13 -13
- package/dist/engine/registry-state.js +17 -19
- package/dist/engine/registry-validate.js +12 -12
- package/dist/engine/run-in.js +2 -2
- package/dist/engine/run-pipeline.js +7 -0
- package/dist/engine/schema-builder.js +251 -233
- package/dist/engine/screen-filter-ops.js +17 -20
- package/dist/engine/screen-helpers.js +4 -3
- package/dist/engine/screen-kind.d.ts +7 -0
- package/dist/engine/screen-kind.js +1 -0
- package/dist/engine/state-machine.d.ts +12 -12
- package/dist/engine/state-machine.js +7 -7
- package/dist/engine/steps/_resolver-utils.js +4 -3
- package/dist/engine/steps/for-each.js +4 -4
- package/dist/engine/steps/unsafe-projection-delete.js +1 -1
- package/dist/engine/steps/unsafe-projection-upsert.js +4 -4
- package/dist/engine/steps/wait-for-event.js +4 -4
- package/dist/engine/steps/workflow-system-events.js +3 -3
- package/dist/engine/strip-disabled-features-from-app-schema.js +51 -14
- package/dist/engine/tier-resolver-extension.d.ts +29 -27
- package/dist/engine/tier-resolver-extension.js +26 -26
- package/dist/engine/transfer-adjacency.js +2 -2
- package/dist/engine/validate-projection-allowlist.js +9 -28
- package/dist/engine/walk-step-instances.d.ts +2 -0
- package/dist/engine/walk-step-instances.js +38 -0
- package/dist/engine/where-rule-lint.js +1 -1
- package/dist/engine/write-handler-qn-extract.js +1 -1
- package/dist/entrypoint/index.d.ts +4 -1
- package/dist/entrypoint/index.js +10 -4
- package/dist/env/index.d.ts +7 -6
- package/dist/env/index.js +10 -9
- package/dist/errors/docs-url.d.ts +1 -1
- package/dist/errors/docs-url.js +22 -2
- package/dist/errors/transition-details.js +7 -7
- package/dist/errors/write-error-info.d.ts +6 -6
- package/dist/errors/write-error-info.js +6 -6
- package/dist/es-ops/context.d.ts +9 -9
- package/dist/es-ops/context.js +29 -29
- package/dist/es-ops/index.js +7 -9
- package/dist/es-ops/operations-schema.js +4 -10
- package/dist/es-ops/runner.d.ts +11 -11
- package/dist/es-ops/runner.js +49 -53
- package/dist/es-ops/types.d.ts +55 -55
- package/dist/es-ops/types.js +11 -11
- package/dist/event-store/admin-api.js +7 -8
- package/dist/event-store/errors.js +1 -1
- package/dist/event-store/event-store.js +12 -6
- package/dist/event-store/rebuild-dead-letter.js +1 -1
- package/dist/files/bind-pending-file-refs.js +3 -3
- package/dist/files/create-file-ref-from-bytes.js +4 -1
- package/dist/files/feature.js +9 -10
- package/dist/files/file-handle.js +7 -9
- package/dist/files/file-ref-entity.js +15 -15
- package/dist/files/file-ref-table.js +7 -8
- package/dist/files/file-routes.d.ts +7 -0
- package/dist/files/file-routes.js +22 -13
- package/dist/files/in-memory-provider.js +8 -9
- package/dist/files/local-provider.js +18 -25
- package/dist/files/provider-resolver.d.ts +7 -7
- package/dist/files/resolve-content-type.js +43 -0
- package/dist/files/storage-tracking.js +2 -2
- package/dist/files/upload-ledger.js +7 -0
- package/dist/files/zip-stream.d.ts +12 -19
- package/dist/files/zip-stream.js +44 -63
- package/dist/i18n/required-surface-keys.d.ts +1 -1
- package/dist/i18n/required-surface-keys.js +188 -212
- package/dist/internal/stack-support.d.ts +2 -1
- package/dist/internal/stack-support.js +1 -1
- package/dist/jobs/job-runner-context.d.ts +32 -0
- package/dist/jobs/job-runner-context.js +305 -0
- package/dist/jobs/job-runner-execution.d.ts +7 -0
- package/dist/jobs/job-runner-execution.js +160 -0
- package/dist/jobs/job-runner-gates.d.ts +19 -0
- package/dist/jobs/job-runner-gates.js +198 -0
- package/dist/jobs/job-runner-helpers.d.ts +17 -3
- package/dist/jobs/job-runner-helpers.js +69 -4
- package/dist/jobs/job-runner-schedulers.d.ts +1 -1
- package/dist/jobs/job-runner-schedulers.js +3 -4
- package/dist/jobs/job-runner-scope.d.ts +44 -0
- package/dist/jobs/job-runner-scope.js +102 -0
- package/dist/jobs/job-runner-types.d.ts +12 -0
- package/dist/jobs/job-runner.d.ts +0 -1
- package/dist/jobs/job-runner.js +57 -669
- package/dist/lifecycle/lifecycle.js +3 -5
- package/dist/lifecycle/signal-handlers.js +5 -4
- package/dist/logging/utils.d.ts +2 -1
- package/dist/logging/utils.js +38 -10
- package/dist/migrations/index.js +1 -1
- package/dist/migrations/kumiko-drift.js +14 -13
- package/dist/migrations/pending-rebuilds.d.ts +21 -20
- package/dist/migrations/pending-rebuilds.js +27 -27
- package/dist/migrations/projection-table-index.d.ts +3 -3
- package/dist/migrations/projection-table-index.js +8 -8
- package/dist/observability/escape-hatch-report.d.ts +3 -0
- package/dist/observability/escape-hatch-report.js +12 -1
- package/dist/observability/metric-validator.js +1 -1
- package/dist/observability/metrics-handle.js +3 -3
- package/dist/pipeline/append-event-core.js +11 -2
- package/dist/pipeline/cascade-handler.d.ts +1 -1
- package/dist/pipeline/cascade-handler.js +165 -138
- package/dist/pipeline/dispatch-batch.js +2 -2
- package/dist/pipeline/dispatch-query.js +20 -13
- package/dist/pipeline/dispatch-shared.d.ts +14 -3
- package/dist/pipeline/dispatch-shared.js +67 -721
- package/dist/pipeline/dispatch-stream.js +1 -1
- package/dist/pipeline/dispatch-write.js +297 -190
- package/dist/pipeline/dispatcher-utils.d.ts +1 -1
- package/dist/pipeline/dispatcher-utils.js +1 -1
- package/dist/pipeline/event-consumer-state.js +2 -2
- package/dist/pipeline/event-dispatcher-admin.js +2 -1
- package/dist/pipeline/event-dispatcher-delivery.js +4 -6
- package/dist/pipeline/event-dispatcher.js +17 -28
- package/dist/pipeline/handler-context-auth.d.ts +9 -0
- package/dist/pipeline/handler-context-auth.js +54 -0
- package/dist/pipeline/handler-context-config.d.ts +14 -0
- package/dist/pipeline/handler-context-config.js +113 -0
- package/dist/pipeline/handler-context-db.d.ts +24 -0
- package/dist/pipeline/handler-context-db.js +149 -0
- package/dist/pipeline/handler-context-dispatch.d.ts +21 -0
- package/dist/pipeline/handler-context-dispatch.js +89 -0
- package/dist/pipeline/handler-context-events.d.ts +43 -0
- package/dist/pipeline/handler-context-events.js +237 -0
- package/dist/pipeline/handler-context-notify.d.ts +12 -0
- package/dist/pipeline/handler-context-notify.js +55 -0
- package/dist/pipeline/handler-context-observability.d.ts +7 -0
- package/dist/pipeline/handler-context-observability.js +21 -0
- package/dist/pipeline/handler-context-scope.d.ts +48 -0
- package/dist/pipeline/handler-context-scope.js +88 -0
- package/dist/pipeline/irreversible-operation-gate.js +1 -1
- package/dist/pipeline/lifecycle-pipeline.js +9 -9
- package/dist/pipeline/member-read-only-transaction.js +1 -3
- package/dist/pipeline/msp-rebuild.js +1 -1
- package/dist/pipeline/notify-scope.js +12 -1
- package/dist/pipeline/pre-handler-gates.js +20 -4
- package/dist/pipeline/pre-transaction-reservations.d.ts +1 -0
- package/dist/pipeline/pre-transaction-reservations.js +9 -3
- package/dist/pipeline/projection-rebuild.js +15 -13
- package/dist/pipeline/system-hooks.js +39 -41
- package/dist/pipeline/system-identity-switch.js +1 -3
- package/dist/pipeline/tenant-timezone-cache.js +1 -1
- package/dist/random/generate.d.ts +29 -29
- package/dist/random/generate.js +30 -30
- package/dist/random/words.js +20 -20
- package/dist/rate-limit/middleware.js +9 -5
- package/dist/redis/client.js +2 -2
- package/dist/redis/pubsub-signal.js +14 -17
- package/dist/schema-cli/apply-command.d.ts +2 -0
- package/dist/schema-cli/apply-command.js +97 -0
- package/dist/schema-cli/baseline-command.d.ts +2 -0
- package/dist/schema-cli/baseline-command.js +33 -0
- package/dist/schema-cli/generate-command.d.ts +2 -0
- package/dist/schema-cli/generate-command.js +90 -0
- package/dist/schema-cli/shared.d.ts +39 -0
- package/dist/schema-cli/shared.js +95 -0
- package/dist/schema-cli/status-command.d.ts +2 -0
- package/dist/schema-cli/status-command.js +73 -0
- package/dist/schema-cli/validate-boot-input.d.ts +5 -0
- package/dist/schema-cli/validate-boot-input.js +130 -0
- package/dist/schema-cli/validate-command.d.ts +2 -0
- package/dist/schema-cli/validate-command.js +130 -0
- package/dist/schema-cli.d.ts +2 -18
- package/dist/schema-cli.js +47 -510
- package/dist/search/index.js +4 -4
- package/dist/search/meilisearch-adapter.js +2 -1
- package/dist/search/purge-subject.js +4 -5
- package/dist/search/reindex-entity.js +6 -9
- package/dist/time/boot-tz-warning.d.ts +2 -2
- package/dist/time/boot-tz-warning.js +8 -8
- package/dist/time/iana.js +10 -10
- package/dist/time/index.js +9 -9
- package/dist/time/legacy-date.d.ts +2 -2
- package/dist/time/legacy-date.js +6 -8
- package/dist/time/polyfill.js +1 -1
- package/dist/time/tz-context.d.ts +1 -1
- package/dist/time/tz-context.js +1 -1
- package/dist/ui-types/app-schema.js +7 -7
- package/dist/ui-types/list-row-meta.js +4 -4
- package/dist/upgrade-cli.js +159 -130
- package/dist/utils/compare.js +1 -1
- package/package.json +5 -5
- package/src/changes.json +358 -0
- package/src/scripts/codemod/crypto-shredding-testing-move.ts +2 -2
- package/src/scripts/codemod/feature-layout.ts +10 -2
- package/src/scripts/codemod/migrate-cross-tenant.ts +1 -1
- package/src/scripts/codemod/migrate-open-to-all.ts +1 -1
- package/src/scripts/codemod/object-form.ts +41 -4
- package/src/scripts/codemod/package-merge.ts +58 -8
- package/src/scripts/codemod/pii-personal-migration.ts +192 -153
- package/src/scripts/codemod/testing-injection.ts +122 -42
- package/src/scripts/codemod/testing-move.ts +29 -7
- package/src/scripts/codemod/unprocessable-error-details-reason.ts +2 -2
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
10
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
11
|
+
import { assertUniqueTableNames, loadSnapshotJson } from "../db/index.js";
|
|
12
|
+
import { isEntityTableMeta } from "../db/table-introspection.js";
|
|
13
|
+
export const SNAPSHOT_FILENAME = ".snapshot.json";
|
|
14
|
+
export function describeThrown(e) {
|
|
15
|
+
return e instanceof Error ? e.message : String(e);
|
|
16
|
+
}
|
|
17
|
+
function tableNameHint(entry) {
|
|
18
|
+
if (isPlainObject(entry) && typeof entry["tableName"] === "string")
|
|
19
|
+
return entry["tableName"];
|
|
20
|
+
return "unknown";
|
|
21
|
+
}
|
|
22
|
+
// Single load-path for kumiko/schema.ts (512/3) — `generate` and `validate`
|
|
23
|
+
// both need ENTITY_METAS (and validate additionally wants FEATURES); this is
|
|
24
|
+
// the one place that enforces "ENTITY_METAS must be an array" so the two
|
|
25
|
+
// commands can't drift on the check or its wording. Throws — callers that
|
|
26
|
+
// want a graceful exit code (validate) catch it themselves.
|
|
27
|
+
async function loadSchemaModFromApp(schemaFile) {
|
|
28
|
+
// bun imports TS directly — no spawn needed.
|
|
29
|
+
const mod = await import(__rewriteRelativeImportExtension(schemaFile));
|
|
30
|
+
const entityMetas = mod["ENTITY_METAS"];
|
|
31
|
+
if (!Array.isArray(entityMetas)) {
|
|
32
|
+
throw new Error(`Schema file ${schemaFile} must declare \`export const ENTITY_METAS: EntityTableMeta[]\`.`);
|
|
33
|
+
}
|
|
34
|
+
const validMetas = [];
|
|
35
|
+
for (const [index, entry] of entityMetas.entries()) {
|
|
36
|
+
if (!isEntityTableMeta(entry)) {
|
|
37
|
+
throw new Error(`Schema file ${schemaFile}: ENTITY_METAS[${index}] is not an EntityTableMeta (table: ${tableNameHint(entry)}).`);
|
|
38
|
+
}
|
|
39
|
+
validMetas.push(entry);
|
|
40
|
+
}
|
|
41
|
+
assertUniqueTableNames(validMetas);
|
|
42
|
+
return {
|
|
43
|
+
entityMetas: validMetas,
|
|
44
|
+
features: mod["FEATURES"],
|
|
45
|
+
validateBootOptions: mod["VALIDATE_BOOT_OPTIONS"],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export function nextSequenceNumber(migrationsDir) {
|
|
49
|
+
if (!existsSync(migrationsDir))
|
|
50
|
+
return 1;
|
|
51
|
+
const files = readdirSync(migrationsDir).filter((f) => f.endsWith(".sql"));
|
|
52
|
+
let max = 0;
|
|
53
|
+
for (const f of files) {
|
|
54
|
+
const m = f.match(/^(\d+)_/);
|
|
55
|
+
if (m) {
|
|
56
|
+
const n = Number(m[1]);
|
|
57
|
+
if (n > max)
|
|
58
|
+
max = n;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return max + 1;
|
|
62
|
+
}
|
|
63
|
+
export function loadPreviousSnapshot(snapshotPath) {
|
|
64
|
+
return existsSync(snapshotPath) ? loadSnapshotJson(snapshotPath) : null;
|
|
65
|
+
}
|
|
66
|
+
export async function loadSchemaModOrReport(ctx) {
|
|
67
|
+
const { out, schemaFile } = ctx;
|
|
68
|
+
if (!existsSync(schemaFile)) {
|
|
69
|
+
out.err(` ${schemaFile} is missing.`);
|
|
70
|
+
out.err(" App convention: kumiko/schema.ts with");
|
|
71
|
+
out.err(" export const ENTITY_METAS: EntityTableMeta[] = [...]");
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
return await loadSchemaModFromApp(schemaFile);
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
out.err(` ${describeThrown(e)}`);
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export function readDatabaseUrlOrReport(out) {
|
|
83
|
+
const dbUrl = process.env["DATABASE_URL"];
|
|
84
|
+
if (!dbUrl) {
|
|
85
|
+
out.err(" DATABASE_URL not set.");
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
return dbUrl;
|
|
89
|
+
}
|
|
90
|
+
export function migrationsDirExistsOrReport(ctx) {
|
|
91
|
+
if (existsSync(ctx.migrationsDir))
|
|
92
|
+
return true;
|
|
93
|
+
ctx.out.err(` ${ctx.migrationsDir} is missing — run schema generate <name> first.`);
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { createDbConnection, fetchAppliedMigrations, findCommentedDropTables, isRetiredFrameworkTable, loadMigrationsFromDir, readMigrationSqlTexts, tableExists, } from "../db/index.js";
|
|
4
|
+
import { describeThrown, loadPreviousSnapshot, readDatabaseUrlOrReport, SNAPSHOT_FILENAME, } from "./shared.js";
|
|
5
|
+
// A table that a migration only drops as a commented DESTRUCTIVE marker and
|
|
6
|
+
// that the snapshot no longer has is gone on paper but still in the live DB.
|
|
7
|
+
async function reportLeftoverDroppedTables(db, migrationsDir, out) {
|
|
8
|
+
const snapshot = loadPreviousSnapshot(join(migrationsDir, SNAPSHOT_FILENAME));
|
|
9
|
+
const leftovers = [];
|
|
10
|
+
for (const tableName of findCommentedDropTables(readMigrationSqlTexts(migrationsDir), snapshot)) {
|
|
11
|
+
if (await tableExists(db, tableName))
|
|
12
|
+
leftovers.push(tableName);
|
|
13
|
+
}
|
|
14
|
+
// skip: nothing left over, nothing to report
|
|
15
|
+
if (leftovers.length === 0)
|
|
16
|
+
return;
|
|
17
|
+
out.log(" Tables dropped only as a commented DESTRUCTIVE marker but still in the database:");
|
|
18
|
+
for (const tableName of leftovers) {
|
|
19
|
+
out.log(isRetiredFrameworkTable(tableName)
|
|
20
|
+
? ` ! ${tableName} — retired framework table: the next \`schema generate <name>\` creates the drop`
|
|
21
|
+
: ` ! ${tableName} — app table: after a backup, write a new migration with the drop`);
|
|
22
|
+
}
|
|
23
|
+
out.log("");
|
|
24
|
+
}
|
|
25
|
+
async function fetchAppliedMigrationIds(db) {
|
|
26
|
+
// On a fresh database where `kumiko schema apply` has never run, the tracking table is
|
|
27
|
+
// absent, which means "nothing applied". Connection and permission errors must not
|
|
28
|
+
// be swallowed (false pending results would hide the problem) — tableExists checks
|
|
29
|
+
// only for existence, while all other errors propagate.
|
|
30
|
+
const trackingExists = await tableExists(db, "_kumiko_migrations");
|
|
31
|
+
return trackingExists
|
|
32
|
+
? new Set((await fetchAppliedMigrations(db)).map((a) => a.id))
|
|
33
|
+
: new Set();
|
|
34
|
+
}
|
|
35
|
+
export async function runStatusCommand(ctx) {
|
|
36
|
+
const { out, migrationsDir } = ctx;
|
|
37
|
+
const dbUrl = readDatabaseUrlOrReport(out);
|
|
38
|
+
if (dbUrl === undefined)
|
|
39
|
+
return 1;
|
|
40
|
+
if (!existsSync(migrationsDir)) {
|
|
41
|
+
out.log(" No kumiko/migrations/ — the app is still on the legacy drizzle path.");
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
const local = loadMigrationsFromDir(migrationsDir);
|
|
45
|
+
const { db, close } = createDbConnection({ url: dbUrl });
|
|
46
|
+
try {
|
|
47
|
+
const applied = await fetchAppliedMigrationIds(db);
|
|
48
|
+
out.log("");
|
|
49
|
+
out.log(` ${local.length} migrations in ${migrationsDir}:`);
|
|
50
|
+
for (const m of local)
|
|
51
|
+
out.log(` ${applied.has(m.id) ? "✓" : " "} ${m.id}`);
|
|
52
|
+
const pending = local.filter((m) => !applied.has(m.id)).length;
|
|
53
|
+
out.log("");
|
|
54
|
+
out.log(` ${applied.size} applied, ${pending} pending.`);
|
|
55
|
+
out.log("");
|
|
56
|
+
await reportLeftoverDroppedTables(db, migrationsDir, out);
|
|
57
|
+
return pending === 0 ? 0 : 1;
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
// Message only, never dbUrl: it can carry the password.
|
|
61
|
+
out.err(` ✗ schema status failed: ${describeThrown(e)}`);
|
|
62
|
+
out.err(' Check that DATABASE_URL is reachable and the database exists (the scaffold\'s docker-compose creates "app").');
|
|
63
|
+
return 1;
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
try {
|
|
67
|
+
await close();
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// The connection already failed and was reported above; a failing close must not throw again.
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ValidateBootOptions } from "../engine/boot-validator.ts";
|
|
2
|
+
import type { FeatureDefinition } from "../engine/types/feature.ts";
|
|
3
|
+
export declare function withFileStoragePresence(env: Readonly<Record<string, string | undefined>>): Record<string, string | undefined>;
|
|
4
|
+
export declare function parseValidateBootOptions(raw: unknown): ValidateBootOptions;
|
|
5
|
+
export declare function featureDefinitionsOf(value: unknown): readonly FeatureDefinition[] | undefined;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
2
|
+
import { FILE_STORAGE_PROVIDER_BOOT_SENTINEL, FILE_STORAGE_PROVIDER_ENV, } from "../engine/extension-names.js";
|
|
3
|
+
// `validate` only sees the composed FEATURES. The file provider normally comes from
|
|
4
|
+
// runProdApp's `fileStorage` option, which defaults FILE_STORAGE_PROVIDER at boot, so
|
|
5
|
+
// an unset variable here is no misconfiguration. The deploy env is checked by the real boot.
|
|
6
|
+
export function withFileStoragePresence(env) {
|
|
7
|
+
return env[FILE_STORAGE_PROVIDER_ENV] === undefined
|
|
8
|
+
? { ...env, [FILE_STORAGE_PROVIDER_ENV]: FILE_STORAGE_PROVIDER_BOOT_SENTINEL }
|
|
9
|
+
: { ...env };
|
|
10
|
+
}
|
|
11
|
+
function isStringSet(value) {
|
|
12
|
+
return value instanceof Set && [...value].every((entry) => typeof entry === "string");
|
|
13
|
+
}
|
|
14
|
+
function isNonEmptyStringArray(value) {
|
|
15
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string" && entry !== "");
|
|
16
|
+
}
|
|
17
|
+
function isBoolean(value) {
|
|
18
|
+
return typeof value === "boolean";
|
|
19
|
+
}
|
|
20
|
+
function validateBootOptionsError(detail) {
|
|
21
|
+
return new Error(`kumiko/schema.ts: \`VALIDATE_BOOT_OPTIONS\` ${detail}`);
|
|
22
|
+
}
|
|
23
|
+
function readOptionalOption(raw, key, isValid, expected) {
|
|
24
|
+
const value = Reflect.get(raw, key);
|
|
25
|
+
if (value === undefined)
|
|
26
|
+
return undefined;
|
|
27
|
+
if (!isValid(value))
|
|
28
|
+
throw validateBootOptionsError(`\`${key}\` must be ${expected}.`);
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
// Boundary check for `export const VALIDATE_BOOT_OPTIONS` in kumiko/schema.ts.
|
|
32
|
+
// Only the options that change what validateBoot reports are honoured; `env` and
|
|
33
|
+
// `entityFieldCipher` are runner-injected and deliberately ignored here.
|
|
34
|
+
export function parseValidateBootOptions(raw) {
|
|
35
|
+
if (raw === undefined)
|
|
36
|
+
return {};
|
|
37
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
38
|
+
throw validateBootOptionsError("must be an object (ValidateBootOptions).");
|
|
39
|
+
}
|
|
40
|
+
const navAllowlist = readOptionalOption(raw, "navAllowlist", isStringSet, "a Set<string>");
|
|
41
|
+
const navAllowlistExempt = readOptionalOption(raw, "navAllowlistExempt", isStringSet, "a Set<string>");
|
|
42
|
+
const warnOnUniqueAccessRoles = readOptionalOption(raw, "warnOnUniqueAccessRoles", isBoolean, "a boolean");
|
|
43
|
+
const requiredLocales = readOptionalOption(raw, "requiredLocales", isNonEmptyStringArray, "an array of non-empty strings");
|
|
44
|
+
return {
|
|
45
|
+
...(requiredLocales !== undefined && { requiredLocales }),
|
|
46
|
+
...(navAllowlist !== undefined && { navAllowlist }),
|
|
47
|
+
...(navAllowlistExempt !== undefined && { navAllowlistExempt }),
|
|
48
|
+
...(warnOnUniqueAccessRoles !== undefined && { warnOnUniqueAccessRoles }),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// Every non-optional top-level property of FeatureDefinition, grouped by the runtime shape the
|
|
52
|
+
// boot validators rely on; a hand-built entry missing one would crash far from the cause.
|
|
53
|
+
const FEATURE_ARRAY_KEYS = [
|
|
54
|
+
"requires",
|
|
55
|
+
"optionalRequires",
|
|
56
|
+
"configSeeds",
|
|
57
|
+
"extensionUsages",
|
|
58
|
+
"extensionSelectors",
|
|
59
|
+
"bootChecks",
|
|
60
|
+
"referenceData",
|
|
61
|
+
"configReads",
|
|
62
|
+
"authClaimsHooks",
|
|
63
|
+
];
|
|
64
|
+
const FEATURE_SET_KEYS = [
|
|
65
|
+
"requiredProjections",
|
|
66
|
+
"requiredSteps",
|
|
67
|
+
"exposedApis",
|
|
68
|
+
"usedApis",
|
|
69
|
+
];
|
|
70
|
+
const FEATURE_RECORD_KEYS = [
|
|
71
|
+
"relations",
|
|
72
|
+
"writeHandlers",
|
|
73
|
+
"queryHandlers",
|
|
74
|
+
"streamHandlers",
|
|
75
|
+
"translations",
|
|
76
|
+
"configKeys",
|
|
77
|
+
"jobs",
|
|
78
|
+
"registrarExtensions",
|
|
79
|
+
"notifications",
|
|
80
|
+
"events",
|
|
81
|
+
"eventMigrations",
|
|
82
|
+
"handlerEntityMappings",
|
|
83
|
+
"metrics",
|
|
84
|
+
"secretKeys",
|
|
85
|
+
"secretNamespaces",
|
|
86
|
+
"projections",
|
|
87
|
+
"multiStreamProjections",
|
|
88
|
+
"claimKeys",
|
|
89
|
+
"screens",
|
|
90
|
+
"navs",
|
|
91
|
+
"workspaces",
|
|
92
|
+
"httpRoutes",
|
|
93
|
+
"webSocketRoutes",
|
|
94
|
+
"storeTables",
|
|
95
|
+
];
|
|
96
|
+
function firstFeatureShapeViolation(entry) {
|
|
97
|
+
if (!isPlainObject(entry))
|
|
98
|
+
return "entry must be an object";
|
|
99
|
+
if (typeof entry["name"] !== "string")
|
|
100
|
+
return "`name` must be a string";
|
|
101
|
+
if (typeof entry["systemScope"] !== "boolean")
|
|
102
|
+
return "`systemScope` must be a boolean";
|
|
103
|
+
const arrayKey = FEATURE_ARRAY_KEYS.find((key) => !Array.isArray(entry[key]));
|
|
104
|
+
if (arrayKey)
|
|
105
|
+
return `\`${arrayKey}\` must be an array`;
|
|
106
|
+
const setKey = FEATURE_SET_KEYS.find((key) => !(entry[key] instanceof Set));
|
|
107
|
+
if (setKey)
|
|
108
|
+
return `\`${setKey}\` must be a Set`;
|
|
109
|
+
const recordKey = FEATURE_RECORD_KEYS.find((key) => !isPlainObject(entry[key]));
|
|
110
|
+
if (recordKey)
|
|
111
|
+
return `\`${recordKey}\` must be an object`;
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
function isFeatureDefinitionShape(entry) {
|
|
115
|
+
return firstFeatureShapeViolation(entry) === undefined;
|
|
116
|
+
}
|
|
117
|
+
// FEATURES comes from the app's schema module; one malformed entry would make
|
|
118
|
+
// validateBoot fail with an unrelated error far from the cause, so name the index here.
|
|
119
|
+
export function featureDefinitionsOf(value) {
|
|
120
|
+
if (!Array.isArray(value))
|
|
121
|
+
return undefined;
|
|
122
|
+
const definitions = [];
|
|
123
|
+
for (const [index, entry] of value.entries()) {
|
|
124
|
+
if (!isFeatureDefinitionShape(entry)) {
|
|
125
|
+
throw new Error(`FEATURES[${index}] is not a FeatureDefinition: ${firstFeatureShapeViolation(entry)}`);
|
|
126
|
+
}
|
|
127
|
+
definitions.push(entry);
|
|
128
|
+
}
|
|
129
|
+
return definitions;
|
|
130
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { diffReplayAgainstSnapshot, generateMigration, readMigrationSqlTexts, replayMigrationsDir, } from "../db/index.js";
|
|
4
|
+
import { validateBoot } from "../engine/boot-validator.js";
|
|
5
|
+
import { describeThrown, loadPreviousSnapshot, loadSchemaModOrReport, nextSequenceNumber, SNAPSHOT_FILENAME, } from "./shared.js";
|
|
6
|
+
import { featureDefinitionsOf, parseValidateBootOptions, withFileStoragePresence, } from "./validate-boot-input.js";
|
|
7
|
+
// 1. Schema drift — compute the diff, never write.
|
|
8
|
+
function checkSchemaDrift(ctx, schemaMod, prevSnapshot) {
|
|
9
|
+
const { out, migrationsDir } = ctx;
|
|
10
|
+
const drift = generateMigration({
|
|
11
|
+
metas: schemaMod.entityMetas,
|
|
12
|
+
prevSnapshot,
|
|
13
|
+
name: "validate",
|
|
14
|
+
sequenceNumber: nextSequenceNumber(migrationsDir),
|
|
15
|
+
priorMigrationsSql: readMigrationSqlTexts(migrationsDir),
|
|
16
|
+
});
|
|
17
|
+
const pendingTables = [
|
|
18
|
+
...drift.diff.newTables.map((t) => t.tableName),
|
|
19
|
+
...drift.diff.changedTables.map((t) => t.tableName),
|
|
20
|
+
...drift.diff.droppedTables,
|
|
21
|
+
...drift.retiredDrops.map((t) => t.tableName),
|
|
22
|
+
];
|
|
23
|
+
if (pendingTables.length === 0) {
|
|
24
|
+
out.log(" ✓ schema: migrations match the entity definitions");
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
out.err(" ✗ schema drift: entity definitions are ahead of kumiko/migrations.");
|
|
28
|
+
out.err(` pending — new: ${drift.diff.newTables.length}, changed: ${drift.diff.changedTables.length}, dropped: ${drift.diff.droppedTables.length}`);
|
|
29
|
+
out.err(` tables: ${pendingTables.join(", ")}`);
|
|
30
|
+
out.err(" Fix: `kumiko-schema generate <name>`, then commit the migration.");
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
// 2. Boot validity — needs the composed feature set.
|
|
34
|
+
function checkBootValidity(out, schemaMod) {
|
|
35
|
+
let bootFeatures;
|
|
36
|
+
try {
|
|
37
|
+
bootFeatures = featureDefinitionsOf(schemaMod.features);
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
out.err(` ✗ boot: ${describeThrown(e)}`);
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (bootFeatures === undefined) {
|
|
44
|
+
out.log(" · boot: skipped — add `export const FEATURES = composeFeatures(APP_FEATURES, { includeBundled: true })` to kumiko/schema.ts to enable validateBoot.");
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
validateBoot(bootFeatures, {
|
|
49
|
+
...parseValidateBootOptions(schemaMod.validateBootOptions),
|
|
50
|
+
env: withFileStoragePresence(process.env),
|
|
51
|
+
});
|
|
52
|
+
out.log(" ✓ boot: feature configuration is valid");
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
out.err(` ✗ boot: ${describeThrown(e)}`);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function reportMigrationContentMismatches(out, mismatches) {
|
|
61
|
+
out.err(" ✗ migration-content drift: committed *.sql files don't produce .snapshot.json.");
|
|
62
|
+
for (const m of mismatches) {
|
|
63
|
+
out.err(` ${m.tableName} (${m.kind}): ${m.detail}`);
|
|
64
|
+
}
|
|
65
|
+
if (mismatches.some((m) => m.kind === "unexpected-table")) {
|
|
66
|
+
out.err(" Fix (unexpected-table): build a meta via `defineUnmanagedTable()` " +
|
|
67
|
+
"from `@cosmicdrift/kumiko-framework/db`, then `r.storeTable(meta, { reason: ... })` " +
|
|
68
|
+
"inside a feature — this adds it to ENTITY_METAS immediately; the .snapshot.json " +
|
|
69
|
+
"only picks it up on the NEXT `kumiko-schema generate` run, which you still need " +
|
|
70
|
+
"to run and commit. `table()` returns a query handle, not a storeTable()-compatible " +
|
|
71
|
+
"meta — don't pass its result to storeTable(). See the bundled `jobs` feature's " +
|
|
72
|
+
"job-run-log store table for the pattern.");
|
|
73
|
+
}
|
|
74
|
+
if (mismatches.some((m) => m.kind !== "unexpected-table")) {
|
|
75
|
+
out.err(" Fix (missing-table/column-drift): a migration file's body doesn't match " +
|
|
76
|
+
"what it (or the snapshot) claims — hand-fix the file, or ship a corrective " +
|
|
77
|
+
"migration if it's already applied in prod.");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// 3. Migration-content drift — replay the committed *.sql files and
|
|
81
|
+
// diff the reconstructed schema against .snapshot.json.
|
|
82
|
+
function checkMigrationContentDrift(ctx, prevSnapshot) {
|
|
83
|
+
const { out, migrationsDir } = ctx;
|
|
84
|
+
if (!existsSync(migrationsDir) || prevSnapshot === null)
|
|
85
|
+
return true;
|
|
86
|
+
try {
|
|
87
|
+
const replayed = replayMigrationsDir(migrationsDir);
|
|
88
|
+
const mismatches = diffReplayAgainstSnapshot(replayed, prevSnapshot);
|
|
89
|
+
if (mismatches.length === 0) {
|
|
90
|
+
out.log(" ✓ migrations: table/column names match .snapshot.json");
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
reportMigrationContentMismatches(out, mismatches);
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
// replayMigrationsDir fail-loud's on a table-DDL statement it can't
|
|
98
|
+
// parse — that must surface as a normal ✗ line like every other
|
|
99
|
+
// check here, not a raw stack trace that kills the process before
|
|
100
|
+
// any later check (or the Fix: help) ever runs.
|
|
101
|
+
out.err(` ✗ migration-content: ${describeThrown(e)}`);
|
|
102
|
+
out.err(" Fix: a statement in a migration file isn't recognizable to the replay parser — extend the parser's recognized patterns, or reword the migration to one it understands.");
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Static, DB-free boot-blocking checks for CI — catches "this won't boot"
|
|
107
|
+
// before deploy. Three layers, no database:
|
|
108
|
+
// 1. schema drift: would `generate` write a migration? (= an entity was
|
|
109
|
+
// added/changed but never generated → missing table → prod 500)
|
|
110
|
+
// 2. boot validity: validateBoot over the composed FEATURES (QN/screen/
|
|
111
|
+
// nav/role refs). Runs only if kumiko/schema.ts exports FEATURES;
|
|
112
|
+
// honours its optional VALIDATE_BOOT_OPTIONS export.
|
|
113
|
+
// 3. migration-content drift: replay every committed *.sql file and
|
|
114
|
+
// diff the result against .snapshot.json — catches a migration
|
|
115
|
+
// file whose SQL body doesn't match what its own snapshot entry
|
|
116
|
+
// claims (e.g. an accidental copy-paste from an earlier file).
|
|
117
|
+
// Layer 1 alone misses this: it only compares ENTITY_METAS to the
|
|
118
|
+
// snapshot, never the snapshot to the SQL that's supposed to have
|
|
119
|
+
// produced it.
|
|
120
|
+
// The DB-level gate (assertKumikoSchemaCurrent) stays at boot/deploy.
|
|
121
|
+
export async function runValidateCommand(ctx) {
|
|
122
|
+
const schemaMod = await loadSchemaModOrReport(ctx);
|
|
123
|
+
if (schemaMod === undefined)
|
|
124
|
+
return 1;
|
|
125
|
+
const prevSnapshot = loadPreviousSnapshot(join(ctx.migrationsDir, SNAPSHOT_FILENAME));
|
|
126
|
+
const schemaOk = checkSchemaDrift(ctx, schemaMod, prevSnapshot);
|
|
127
|
+
const bootOk = checkBootValidity(ctx.out, schemaMod);
|
|
128
|
+
const migrationsOk = checkMigrationContentDrift(ctx, prevSnapshot);
|
|
129
|
+
return schemaOk && bootOk && migrationsOk ? 0 : 1;
|
|
130
|
+
}
|
package/dist/schema-cli.d.ts
CHANGED
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export type SchemaCliOut
|
|
3
|
-
readonly log: (line: string) => void;
|
|
4
|
-
readonly err: (line: string) => void;
|
|
5
|
-
};
|
|
6
|
-
export type RunSchemaCliOptions = {
|
|
7
|
-
readonly features?: undefined;
|
|
8
|
-
readonly kmsSlots?: undefined;
|
|
9
|
-
} | {
|
|
10
|
-
/** Composed app features. When given, `apply` rebuilds the projections whose
|
|
11
|
-
* tables a freshly applied migration changed (via its `.rebuild.json`
|
|
12
|
-
* marker). Omitted (dev `kumiko schema`) → no rebuild, migrations only. */
|
|
13
|
-
readonly features: readonly FeatureDefinition[];
|
|
14
|
-
/** Schema-declared Key Manager slots, `kmsSlotsOf(composedEnv.schema)`, or
|
|
15
|
-
* `[]` when the app wires none. This CLI parses no env schema, so the
|
|
16
|
-
* rebuild's KMS wiring cannot derive them itself. */
|
|
17
|
-
readonly kmsSlots: readonly string[];
|
|
18
|
-
};
|
|
1
|
+
import type { RunSchemaCliOptions, SchemaCliOut } from "./schema-cli/shared.ts";
|
|
2
|
+
export type { RunSchemaCliOptions, SchemaCliOut } from "./schema-cli/shared.ts";
|
|
19
3
|
/**
|
|
20
4
|
* Runs a schema-CLI subcommand. `appCwd` is the app workspace root (where
|
|
21
5
|
* `kumiko/schema.ts` + `kumiko/migrations/` live). Returns a process exit code.
|