@cosmicdrift/kumiko-framework 1.0.0 → 2.0.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 +9 -38
- package/package.json +21 -2
- package/src/__tests__/anonymous-access.integration.test.ts +185 -0
- package/src/__tests__/consumer-cli.integration.test.ts +172 -0
- package/src/__tests__/entity-permalink-open.integration.test.ts +94 -0
- package/src/__tests__/full-stack.integration.test.ts +1 -1
- package/src/__tests__/schema-cli-temporal-polyfill.test.ts +48 -0
- package/src/__tests__/schema-cli.integration.test.ts +50 -0
- package/src/__tests__/store-table.integration.test.ts +93 -0
- package/src/api/__tests__/api.test.ts +373 -0
- package/src/api/__tests__/auth-middleware-anonymous-access-boot.test.ts +40 -0
- package/src/api/__tests__/auth-routes-cookie.test.ts +17 -1
- package/src/api/__tests__/auth-routes-invalid-body-invite.test.ts +253 -0
- package/src/api/__tests__/auth-routes-mfa-preauth-confirm.test.ts +222 -0
- package/src/api/__tests__/auth-routes-mfa-preauth-enable-start.test.ts +249 -0
- package/src/api/__tests__/auth-routes-mfa-verify.test.ts +202 -0
- package/src/api/__tests__/auth-routes-trusted-proxy.test.ts +135 -0
- package/src/api/__tests__/batch.integration.test.ts +101 -11
- package/src/api/__tests__/csrf-constants-sync.test.ts +20 -0
- package/src/api/__tests__/dispatcher-live.integration.test.ts +74 -0
- package/src/api/__tests__/jwt.test.ts +200 -1
- package/src/api/__tests__/login-rate-limiter-sweep.test.ts +50 -0
- package/src/api/__tests__/pat-scope.test.ts +36 -0
- package/src/api/__tests__/pii-leak-guard.integration.test.ts +103 -0
- package/src/api/__tests__/redis-login-rate-limiter.integration.test.ts +138 -0
- package/src/api/__tests__/request-id-middleware.test.ts +51 -0
- package/src/api/__tests__/server-boot-guards.test.ts +71 -0
- package/src/api/__tests__/server-jwt-ttl.test.ts +58 -0
- package/src/api/__tests__/sse-broker.test.ts +57 -0
- package/src/api/__tests__/sse-route.test.ts +4 -0
- package/src/api/api-constants.ts +11 -0
- package/src/api/auth-middleware.ts +248 -40
- package/src/api/auth-routes.ts +576 -95
- package/src/api/index.ts +13 -4
- package/src/api/jwt.ts +170 -11
- package/src/api/pat-scope.ts +14 -0
- package/src/api/pii-leak-guard.ts +47 -0
- package/src/api/request-context.ts +3 -0
- package/src/api/request-id-middleware.ts +2 -0
- package/src/api/routes.ts +169 -2
- package/src/api/server.ts +112 -16
- package/src/api/sse-broker.ts +39 -0
- package/src/bun-db/__tests__/PATTERN.md +0 -1
- package/src/bun-db/__tests__/coerce-row-temporal.test.ts +41 -0
- package/src/bun-db/__tests__/select-many-retry.test.ts +79 -0
- package/src/bun-db/__tests__/write-brand.test.ts +21 -0
- package/src/bun-db/connection.ts +3 -3
- package/src/bun-db/index.ts +2 -0
- package/src/bun-db/query.ts +105 -32
- package/src/consumer-cli.ts +134 -0
- package/src/crypto/__tests__/blind-index.test.ts +130 -0
- package/src/crypto/__tests__/event-pii.test.ts +161 -0
- package/src/crypto/__tests__/kek-rotation.integration.test.ts +180 -0
- package/src/crypto/__tests__/kms-adapter-contract.ts +134 -0
- package/src/crypto/__tests__/kms-adapter.contract.test.ts +4 -0
- package/src/crypto/__tests__/pg-kms-adapter.integration.test.ts +117 -0
- package/src/crypto/__tests__/pii-field-encryption.test.ts +376 -0
- package/src/crypto/__tests__/request-kms-cache.test.ts +74 -0
- package/src/crypto/__tests__/subject-resolver.test.ts +108 -0
- package/src/crypto/blind-index.ts +118 -0
- package/src/crypto/event-pii.ts +70 -0
- package/src/crypto/in-memory-kms-adapter.ts +50 -0
- package/src/crypto/index.ts +67 -0
- package/src/crypto/kms-adapter.ts +2 -0
- package/src/crypto/pg-kms-adapter.ts +295 -0
- package/src/crypto/pii-field-encryption.ts +248 -0
- package/src/crypto/request-kms-cache.ts +38 -0
- package/src/crypto/subject-resolver.ts +91 -0
- package/src/db/__tests__/assert-no-unreachable-live-rows.integration.test.ts +191 -0
- package/src/db/__tests__/blind-index.integration.test.ts +248 -0
- package/src/db/__tests__/build-filter-where.test.ts +34 -0
- package/src/db/__tests__/collect-table-metas.test.ts +10 -10
- package/src/db/__tests__/config-seed.integration.test.ts +13 -5
- package/src/db/__tests__/dialect-instant.test.ts +1 -4
- package/src/db/__tests__/entity-field-encryption.test.ts +7 -7
- package/src/db/__tests__/entity-table-meta-source.test.ts +50 -0
- package/src/db/__tests__/event-store-executor-context.pii-roundtrip.test.ts +67 -0
- package/src/db/__tests__/event-store-executor-write-verbs.integration.test.ts +402 -0
- package/src/db/__tests__/event-store-executor.integration.test.ts +299 -29
- package/src/db/__tests__/feature-table-sources.test.ts +34 -0
- package/src/db/__tests__/implicit-projection-equivalence.integration.test.ts +118 -39
- package/src/db/__tests__/instant-to-driver-temporal.test.ts +21 -0
- package/src/db/__tests__/located-timestamp.test.ts +19 -0
- package/src/db/__tests__/migrate-generator.test.ts +21 -0
- package/src/db/__tests__/migrate-runner.test.ts +61 -0
- package/src/db/__tests__/number-field-fractional.integration.test.ts +58 -0
- package/src/db/__tests__/rebuild-marker.test.ts +13 -3
- package/src/db/__tests__/replay-migration-sql.test.ts +384 -0
- package/src/db/__tests__/schema-inspection.test.ts +7 -0
- package/src/db/__tests__/schema-migration.integration.test.ts +1 -1
- package/src/db/__tests__/table-builder-meta-lockstep.test.ts +39 -0
- package/src/db/__tests__/tenant-db-where-merge.test.ts +49 -3
- package/src/db/__tests__/tenant-db.integration.test.ts +6 -2
- package/src/db/api.ts +2 -2
- package/src/db/apply-entity-event.ts +18 -14
- package/src/db/blind-index-cleanup.ts +55 -0
- package/src/db/bun-provider.ts +2 -2
- package/src/db/collect-table-metas.ts +6 -7
- package/src/db/config-seed.ts +9 -9
- package/src/db/connection.ts +7 -12
- package/src/db/cursor.ts +1 -18
- package/src/db/dialect.ts +20 -26
- package/src/db/entity-field-encryption.ts +81 -24
- package/src/db/entity-table-meta-types.ts +2 -0
- package/src/db/entity-table-meta.ts +63 -90
- package/src/db/event-store-executor-context.ts +404 -0
- package/src/db/event-store-executor-read.ts +275 -0
- package/src/db/event-store-executor-write.ts +644 -0
- package/src/db/event-store-executor.ts +28 -1155
- package/src/db/feature-table-sources.ts +6 -5
- package/src/db/index.ts +20 -3
- package/src/db/located-timestamp.ts +4 -0
- package/src/db/migrate-generator.ts +39 -6
- package/src/db/migrate-runner.ts +107 -11
- package/src/db/pg-error.ts +9 -1
- package/src/db/postgres-provider.ts +2 -2
- package/src/db/queries/__tests__/event-store-idempotency-index.integration.test.ts +80 -0
- package/src/db/queries/backfill-pii.ts +277 -0
- package/src/db/queries/ddl.ts +45 -0
- package/src/db/queries/event-consumer.ts +35 -2
- package/src/db/queries/event-store.ts +126 -19
- package/src/db/queries/projection-rebuild.ts +22 -8
- package/src/db/queries/shadow-swap.ts +183 -0
- package/src/db/queries/test-stack.ts +4 -30
- package/src/db/query-api.ts +1 -0
- package/src/db/query.ts +1 -0
- package/src/db/rebuild-marker.ts +18 -2
- package/src/db/reference-data.ts +2 -3
- package/src/db/replay-migration-sql.ts +257 -0
- package/src/db/schema-inspection.ts +1 -1
- package/src/db/table-builder.ts +102 -71
- package/src/db/tenant-db.ts +15 -53
- package/src/engine/__tests__/boot-validator-action-wiring.test.ts +242 -0
- package/src/engine/__tests__/boot-validator-boot-check.test.ts +99 -0
- package/src/engine/__tests__/boot-validator-dashboard.test.ts +237 -0
- package/src/engine/__tests__/boot-validator-entity-list.test.ts +104 -0
- package/src/engine/__tests__/boot-validator-gdpr-storage.test.ts +7 -69
- package/src/engine/__tests__/boot-validator-i18n-keys.test.ts +26 -5
- package/src/engine/__tests__/boot-validator-located-timestamps.test.ts +3 -19
- package/src/engine/__tests__/boot-validator-pii-retention.test.ts +246 -4
- package/src/engine/__tests__/boot-validator.test.ts +219 -15
- package/src/engine/__tests__/build-app-schema.test.ts +82 -0
- package/src/engine/__tests__/build-config-feature-schema.test.ts +125 -0
- package/src/engine/__tests__/build-target.test.ts +3 -11
- package/src/engine/__tests__/codemod-pipeline.test.ts +152 -21
- package/src/engine/__tests__/config-helpers.test.ts +16 -0
- package/src/engine/__tests__/define-feature-entity-mapping.test.ts +6 -0
- package/src/engine/__tests__/define-roles.test.ts +21 -0
- package/src/engine/__tests__/engine.test.ts +163 -1
- package/src/engine/__tests__/entity-handlers.test.ts +80 -0
- package/src/engine/__tests__/event-migration-declarative.test.ts +65 -0
- package/src/engine/__tests__/event-type-map-augmentation.test.ts +24 -0
- package/src/engine/__tests__/extend-entity-projection.test.ts +123 -0
- package/src/engine/__tests__/factories-time.test.ts +2 -66
- package/src/engine/__tests__/feature-crud-shorthand.test.ts +28 -0
- package/src/engine/__tests__/feature-manifest.test.ts +31 -1
- package/src/engine/__tests__/field-access.test.ts +23 -1
- package/src/engine/__tests__/hook-phases.test.ts +5 -5
- package/src/engine/__tests__/membership-roles.test.ts +4 -10
- package/src/engine/__tests__/nav.test.ts +86 -1
- package/src/engine/__tests__/pipeline-engine.test.ts +9 -9
- package/src/engine/__tests__/pipeline-handler.integration.test.ts +18 -18
- package/src/engine/__tests__/pipeline-observability.integration.test.ts +2 -2
- package/src/engine/__tests__/pipeline-performance.integration.test.ts +3 -3
- package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +9 -9
- package/src/engine/__tests__/post-query-hook.test.ts +7 -7
- package/src/engine/__tests__/registrar-object-form.test.ts +141 -0
- package/src/engine/__tests__/registry.test.ts +144 -0
- package/src/engine/__tests__/schema-builder.test.ts +18 -0
- package/src/engine/__tests__/screen.test.ts +147 -1
- package/src/engine/__tests__/soft-delete-cleanup.test.ts +3 -0
- package/src/engine/__tests__/store-table.test.ts +229 -0
- package/src/engine/__tests__/tier-resolver-extension.test.ts +19 -1
- package/src/engine/__tests__/{visual-tree-patterns.test.ts → tree-actions-patterns.test.ts} +7 -95
- package/src/engine/__tests__/validate-projection-allowlist.test.ts +15 -15
- package/src/engine/boot-validator/__tests__/config-deps.test.ts +92 -0
- package/src/engine/boot-validator/action-wiring.ts +149 -0
- package/src/engine/boot-validator/boot-check.ts +21 -0
- package/src/engine/boot-validator/config-deps.ts +40 -4
- package/src/engine/boot-validator/entity-handler.ts +20 -21
- package/src/engine/boot-validator/entity-list-screens.ts +88 -0
- package/src/engine/boot-validator/gdpr-storage.ts +0 -20
- package/src/engine/boot-validator/i18n-keys.ts +58 -4
- package/src/engine/boot-validator/index.ts +33 -12
- package/src/engine/boot-validator/nav.ts +125 -0
- package/src/engine/boot-validator/pii-retention.ts +95 -10
- package/src/engine/boot-validator/{screens-nav.ts → screens.ts} +214 -191
- package/src/engine/boot-validator/workspaces.ts +68 -0
- package/src/engine/build-app-schema.ts +16 -0
- package/src/engine/build-config-feature-schema.ts +44 -7
- package/src/engine/codemod/pipeline-codemod.ts +5 -5
- package/src/engine/config-helpers.ts +15 -0
- package/src/engine/constants.ts +32 -6
- package/src/engine/create-app.ts +11 -0
- package/src/engine/define-feature.ts +95 -947
- package/src/engine/define-handler.ts +28 -94
- package/src/engine/define-workflow.ts +1 -1
- package/src/engine/effective-features.ts +12 -2
- package/src/engine/entity-handlers.ts +76 -11
- package/src/engine/extensions/tenant-data.ts +19 -0
- package/src/engine/extensions/user-data.ts +29 -2
- package/src/engine/factories.ts +8 -49
- package/src/engine/feature-ast/__tests__/canonical-form.test.ts +26 -29
- package/src/engine/feature-ast/__tests__/fixtures/cross-file-registrar/feature.ts +9 -0
- package/src/engine/feature-ast/__tests__/fixtures/cross-file-registrar/screens.ts +4 -0
- package/src/engine/feature-ast/__tests__/parse-happy-path.test.ts +1 -2
- package/src/engine/feature-ast/__tests__/parse-real-features.test.ts +18 -8
- package/src/engine/feature-ast/__tests__/parse.test.ts +1640 -160
- package/src/engine/feature-ast/__tests__/patch.test.ts +206 -12
- package/src/engine/feature-ast/__tests__/patcher.test.ts +20 -23
- package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +380 -5
- package/src/engine/feature-ast/extractors/events.ts +322 -0
- package/src/engine/feature-ast/extractors/handlers.ts +234 -0
- package/src/engine/feature-ast/extractors/hooks.ts +243 -0
- package/src/engine/feature-ast/extractors/index.ts +34 -31
- package/src/engine/feature-ast/extractors/jobs-routes.ts +221 -0
- package/src/engine/feature-ast/extractors/projections-screens.ts +269 -0
- package/src/engine/feature-ast/extractors/round1.ts +3 -3
- package/src/engine/feature-ast/extractors/round5.ts +3 -3
- package/src/engine/feature-ast/extractors/round6.ts +2 -36
- package/src/engine/feature-ast/extractors/shared.ts +64 -6
- package/src/engine/feature-ast/index.ts +2 -4
- package/src/engine/feature-ast/parse.ts +130 -23
- package/src/engine/feature-ast/patch.ts +39 -50
- package/src/engine/feature-ast/patcher.ts +37 -38
- package/src/engine/feature-ast/patterns.ts +50 -62
- package/src/engine/feature-ast/render.ts +67 -44
- package/src/engine/feature-builder-state.ts +168 -0
- package/src/engine/feature-config-events-jobs.ts +403 -0
- package/src/engine/feature-entity-handlers.ts +208 -0
- package/src/engine/feature-manifest.ts +2 -1
- package/src/engine/feature-ui-extensions.ts +500 -0
- package/src/engine/field-access.ts +13 -2
- package/src/engine/field-helpers.ts +31 -0
- package/src/engine/handler-helpers.ts +26 -0
- package/src/engine/hook-helpers.ts +16 -0
- package/src/engine/index.ts +23 -7
- package/src/engine/membership-roles.ts +13 -0
- package/src/engine/object-form.ts +27 -0
- package/src/engine/ownership.ts +26 -79
- package/src/engine/pattern-library/__tests__/library.test.ts +7 -20
- package/src/engine/pattern-library/library.ts +44 -1152
- package/src/engine/pattern-library/mixed-schemas.ts +450 -0
- package/src/engine/pattern-library/opaque-schemas.ts +124 -0
- package/src/engine/pattern-library/shared-fields.ts +75 -0
- package/src/engine/pattern-library/static-schemas.ts +456 -0
- package/src/engine/pipeline.ts +6 -11
- package/src/engine/registry-facade.ts +362 -0
- package/src/engine/registry-ingest.ts +478 -0
- package/src/engine/registry-state.ts +388 -0
- package/src/engine/registry-validate.ts +642 -0
- package/src/engine/registry.ts +78 -1658
- package/src/engine/run-pipeline.ts +1 -1
- package/src/engine/schema-builder.ts +1 -0
- package/src/engine/screen-helpers.ts +54 -0
- package/src/engine/soft-delete-cleanup.ts +6 -2
- package/src/engine/steps/__tests__/duration-utils.test.ts +20 -0
- package/src/engine/steps/_duration-utils.ts +2 -0
- package/src/engine/steps/unsafe-projection-upsert.ts +1 -4
- package/src/engine/tier-resolver-extension.ts +3 -2
- package/src/engine/types/config.ts +2 -482
- package/src/engine/types/define-handler.ts +2 -0
- package/src/engine/types/entity-handlers.ts +2 -0
- package/src/engine/types/event-type-map.ts +1 -37
- package/src/engine/types/feature.ts +2 -972
- package/src/engine/types/fields.ts +2 -675
- package/src/engine/types/handlers.ts +2 -774
- package/src/engine/types/hooks.ts +2 -184
- package/src/engine/types/http-route.ts +1 -54
- package/src/engine/types/identifiers.ts +1 -47
- package/src/engine/types/index.ts +86 -40
- package/src/engine/types/nav.ts +2 -63
- package/src/engine/types/ownership.ts +2 -0
- package/src/engine/types/projection.ts +2 -138
- package/src/engine/types/relations.ts +1 -51
- package/src/engine/types/screen.ts +2 -574
- package/src/engine/types/step.ts +2 -334
- package/src/engine/types/target-ref.ts +1 -21
- package/src/engine/types/tree-node.ts +1 -132
- package/src/engine/types/workspace.ts +2 -49
- package/src/engine/validate-projection-allowlist.ts +6 -6
- package/src/entrypoint/__tests__/entrypoint-job-wiring.integration.test.ts +120 -1
- package/src/entrypoint/index.ts +49 -6
- package/src/errors/classes.ts +22 -1
- package/src/errors/field-issue.ts +0 -3
- package/src/errors/index.ts +1 -1
- package/src/errors/write-error-info.ts +10 -22
- package/src/es-ops/README.md +1 -1
- package/src/es-ops/__tests__/runner.integration.test.ts +74 -0
- package/src/es-ops/context.ts +4 -2
- package/src/es-ops/types.ts +8 -1
- package/src/event-store/__tests__/admin-api.integration.test.ts +27 -1
- package/src/event-store/__tests__/backfill-pii.integration.test.ts +279 -0
- package/src/event-store/__tests__/event-store.integration.test.ts +201 -0
- package/src/event-store/__tests__/row-to-stored-event.test.ts +2 -2
- package/src/event-store/__tests__/snapshot.integration.test.ts +86 -0
- package/src/event-store/__tests__/unscoped-stream-primitives.guard.test.ts +58 -0
- package/src/event-store/__tests__/upcaster.integration.test.ts +77 -45
- package/src/event-store/admin-api.ts +11 -4
- package/src/event-store/errors.ts +2 -35
- package/src/event-store/event-store.ts +64 -78
- package/src/event-store/events-schema.ts +11 -13
- package/src/event-store/index.ts +18 -3
- package/src/event-store/rebuild-dead-letter.ts +111 -0
- package/src/event-store/snapshot.ts +63 -40
- package/src/event-store/types.ts +2 -0
- package/src/files/__tests__/build-storage-key.test.ts +28 -0
- package/src/files/__tests__/file-ref-entity.test.ts +8 -0
- package/src/files/__tests__/files.integration.test.ts +24 -0
- package/src/files/__tests__/in-memory-provider.contract.test.ts +4 -0
- package/src/files/__tests__/local-provider.test.ts +31 -0
- package/src/files/__tests__/provider-resolver.test.ts +70 -0
- package/src/files/__tests__/write-stream.test.ts +13 -0
- package/src/files/file-handle.ts +2 -19
- package/src/files/file-ref-entity.ts +2 -2
- package/src/files/file-routes.ts +13 -0
- package/src/files/index.ts +1 -1
- package/src/files/local-provider.ts +27 -8
- package/src/files/provider-resolver.ts +31 -20
- package/src/files/types.ts +13 -55
- package/src/i18n/__tests__/required-surface-keys.test.ts +17 -0
- package/src/i18n/required-surface-keys.ts +120 -1
- package/src/jobs/__tests__/job-queue-depth.integration.test.ts +82 -0
- package/src/jobs/__tests__/jobs.integration.test.ts +329 -3
- package/src/jobs/job-runner.ts +82 -12
- package/src/logging/types.ts +1 -7
- package/src/migrations/pending-rebuilds.ts +1 -1
- package/src/observability/__tests__/observability.integration.test.ts +4 -1
- package/src/observability/__tests__/recording-tracer.test.ts +6 -4
- package/src/observability/index.ts +2 -0
- package/src/observability/noop-provider.ts +0 -9
- package/src/observability/recording-tracer.ts +0 -12
- package/src/observability/standard-metrics.ts +64 -2
- package/src/observability/types/index.ts +1 -29
- package/src/observability/types/metric.ts +1 -56
- package/src/observability/types/provider.ts +1 -32
- package/src/observability/types/span.ts +1 -64
- package/src/pipeline/__tests__/ctx-bridge.integration.test.ts +2 -2
- package/src/pipeline/__tests__/dispatcher.test.ts +366 -1
- package/src/pipeline/__tests__/event-consumer-state.integration.test.ts +31 -0
- package/src/pipeline/__tests__/event-dispatcher-delivery-max-attempts.test.ts +126 -0
- package/src/pipeline/__tests__/event-dispatcher-rearm.integration.test.ts +263 -0
- package/src/pipeline/__tests__/event-dispatcher.integration.test.ts +10 -0
- package/src/pipeline/__tests__/job-trigger-consumer.integration.test.ts +106 -0
- package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +308 -77
- package/src/pipeline/__tests__/load-aggregate-query.integration.test.ts +16 -8
- package/src/pipeline/__tests__/post-query-hook.integration.test.ts +3 -3
- package/src/pipeline/__tests__/projection-rebuild.integration.test.ts +80 -2
- package/src/pipeline/__tests__/rebuild-poison-quarantine.integration.test.ts +274 -0
- package/src/pipeline/__tests__/try-append-event.integration.test.ts +166 -0
- package/src/pipeline/dispatch-batch.ts +187 -0
- package/src/pipeline/dispatch-query.ts +165 -0
- package/src/pipeline/dispatch-shared.ts +826 -0
- package/src/pipeline/dispatch-stream.ts +90 -0
- package/src/pipeline/dispatch-write.ts +451 -0
- package/src/pipeline/dispatcher-utils.ts +1 -1
- package/src/pipeline/dispatcher.ts +44 -1372
- package/src/pipeline/entity-cache.ts +2 -33
- package/src/pipeline/event-consumer-state.ts +30 -2
- package/src/pipeline/event-dispatcher-admin.ts +293 -0
- package/src/pipeline/event-dispatcher-delivery.ts +305 -0
- package/src/pipeline/event-dispatcher.ts +85 -542
- package/src/pipeline/index.ts +2 -0
- package/src/pipeline/msp-rebuild.ts +42 -3
- package/src/pipeline/multi-stream-apply-context.ts +4 -42
- package/src/pipeline/projection-rebuild.ts +105 -3
- package/src/pipeline/system-hooks.ts +144 -1
- package/src/rate-limit/__tests__/resolver.integration.test.ts +18 -0
- package/src/rate-limit/resolver.ts +16 -32
- package/src/schema-cli.ts +76 -16
- package/src/search/__tests__/meilisearch-adapter.integration.test.ts +207 -185
- package/src/search/__tests__/meilisearch-ids.test.ts +30 -0
- package/src/search/__tests__/reindex-entity.integration.test.ts +144 -0
- package/src/search/index.ts +6 -0
- package/src/search/meilisearch-adapter.ts +13 -12
- package/src/search/reindex-entity.ts +177 -0
- package/src/search/types.ts +1 -39
- package/src/secrets/__tests__/contains-secret.test.ts +34 -0
- package/src/secrets/__tests__/envelope-cipher.test.ts +59 -0
- package/src/secrets/__tests__/envelope.test.ts +1 -1
- package/src/secrets/dek-cache.ts +26 -5
- package/src/secrets/envelope-cipher.ts +53 -0
- package/src/secrets/envelope.ts +5 -3
- package/src/secrets/index.ts +13 -1
- package/src/secrets/stored-envelope.ts +46 -0
- package/src/secrets/types.ts +2 -162
- package/src/stack/__tests__/event-collector.test.ts +42 -0
- package/src/stack/__tests__/setup-test-stack-jobs.integration.test.ts +125 -0
- package/src/stack/db.ts +2 -1
- package/src/stack/push-entity-projection-tables.ts +4 -3
- package/src/stack/redis.ts +8 -0
- package/src/stack/request-helper.ts +38 -2
- package/src/stack/table-helpers.ts +6 -4
- package/src/stack/test-stack.ts +249 -131
- package/src/testing/__tests__/late-bound.test.ts +32 -0
- package/src/testing/__tests__/wait-for.test.ts +59 -0
- package/src/testing/boot-validator-fixture.ts +121 -0
- package/src/testing/e2e-generator.ts +8 -0
- package/src/testing/file-provider-contract.ts +104 -0
- package/src/testing/handler-context.ts +3 -1
- package/src/testing/index.ts +5 -0
- package/src/testing/late-bound.ts +5 -3
- package/src/testing/mutable-master-key-provider.ts +29 -3
- package/src/testing/wait-for.ts +3 -0
- package/src/testing/without-ambient-temporal.ts +14 -0
- package/src/time/__tests__/tz-dateline.test.ts +8 -10
- package/src/time/geo-tz.ts +1 -32
- package/src/time/index.ts +1 -0
- package/src/time/legacy-date.ts +18 -0
- package/src/time/polyfill.ts +21 -38
- package/src/time/tz-context.ts +44 -85
- package/src/ui-types/app-schema.ts +12 -0
- package/src/ui-types/index.ts +20 -7
- package/src/utils/__tests__/safe-json-temporal.test.ts +18 -0
- package/src/utils/safe-json.ts +13 -1
- package/src/__tests__/raw-table.integration.test.ts +0 -116
- package/src/bun-db/__tests__/bun-test-stack.ts +0 -6
- package/src/db/__tests__/encryption.test.ts +0 -39
- package/src/db/encryption.ts +0 -39
- package/src/db/row-helpers.ts +0 -4
- package/src/engine/__tests__/raw-table.test.ts +0 -150
- package/src/engine/__tests__/unmanaged-table.test.ts +0 -127
- package/src/engine/feature-ast/__tests__/visual-tree-parse.test.ts +0 -184
- package/src/engine/feature-ast/extractors/round4.ts +0 -1366
|
@@ -1,972 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// PgTable historically came from drizzle-orm/pg-core; the native dialect
|
|
5
|
-
// no longer carries drizzle internal class types. Every caller really
|
|
6
|
-
// needs "an opaque table-object with Symbol-based introspection".
|
|
7
|
-
type PgTable = unknown;
|
|
8
|
-
|
|
9
|
-
import type { QueryHandlerDefinition, WriteHandlerDefinition } from "../define-handler";
|
|
10
|
-
import type {
|
|
11
|
-
ConfigKeyDefinition,
|
|
12
|
-
ConfigKeyHandle,
|
|
13
|
-
ConfigKeyType,
|
|
14
|
-
ConfigSeedDef,
|
|
15
|
-
ExtensionSelectorDef,
|
|
16
|
-
JobDefinition,
|
|
17
|
-
JobHandlerFn,
|
|
18
|
-
NotificationDataFn,
|
|
19
|
-
NotificationDefinition,
|
|
20
|
-
NotificationRecipientFn,
|
|
21
|
-
NotificationTemplateFn,
|
|
22
|
-
ReferenceDataDef,
|
|
23
|
-
RegistrarExtensionDef,
|
|
24
|
-
RegistrarExtensionRegistration,
|
|
25
|
-
TranslationKeys,
|
|
26
|
-
TranslationsDef,
|
|
27
|
-
} from "./config";
|
|
28
|
-
import type { EntityDefinition } from "./fields";
|
|
29
|
-
import type {
|
|
30
|
-
AccessRule,
|
|
31
|
-
AuthClaimsFn,
|
|
32
|
-
AuthClaimsHookDef,
|
|
33
|
-
ClaimKeyDefinition,
|
|
34
|
-
ClaimKeyHandle,
|
|
35
|
-
ClaimKeyType,
|
|
36
|
-
EntityRef,
|
|
37
|
-
EventDef,
|
|
38
|
-
EventMigrationDef,
|
|
39
|
-
EventUpcastFn,
|
|
40
|
-
HandlerRef,
|
|
41
|
-
NameOrRef,
|
|
42
|
-
QualifiedEventName,
|
|
43
|
-
QueryHandlerDef,
|
|
44
|
-
QueryHandlerFn,
|
|
45
|
-
RateLimitOption,
|
|
46
|
-
WriteHandlerDef,
|
|
47
|
-
WriteHandlerFn,
|
|
48
|
-
} from "./handlers";
|
|
49
|
-
import type {
|
|
50
|
-
EntityHookMap,
|
|
51
|
-
HookMap,
|
|
52
|
-
HookPhase,
|
|
53
|
-
OwnedFn,
|
|
54
|
-
PostDeleteHookFn,
|
|
55
|
-
PostQueryHookFn,
|
|
56
|
-
PostSaveHookFn,
|
|
57
|
-
PreDeleteHookFn,
|
|
58
|
-
PreQueryHookFn,
|
|
59
|
-
PreSaveHookFn,
|
|
60
|
-
SearchPayloadContributorFn,
|
|
61
|
-
ValidationHookFn,
|
|
62
|
-
} from "./hooks";
|
|
63
|
-
import type { HttpRouteDefinition } from "./http-route";
|
|
64
|
-
import type { NavDefinition } from "./nav";
|
|
65
|
-
import type { MultiStreamProjectionDefinition, ProjectionDefinition } from "./projection";
|
|
66
|
-
import type { EntityRelations, RelationDefinition } from "./relations";
|
|
67
|
-
import type { ScreenDefinition } from "./screen";
|
|
68
|
-
import type { TreeActionDef, TreeActionsHandle, TreeChildrenSubscribe } from "./tree-node";
|
|
69
|
-
import type { WorkspaceDefinition } from "./workspace";
|
|
70
|
-
|
|
71
|
-
// --- Metrics (declared by features via r.metric()) ---
|
|
72
|
-
|
|
73
|
-
export type FeatureMetricType = "counter" | "histogram" | "gauge";
|
|
74
|
-
|
|
75
|
-
// The user-facing short form written in a feature. The Framework prefixes it
|
|
76
|
-
// with `kumiko_<featureName>_` to produce the fully-qualified Prometheus name.
|
|
77
|
-
export type FeatureMetricDef = {
|
|
78
|
-
readonly shortName: string;
|
|
79
|
-
readonly type: FeatureMetricType;
|
|
80
|
-
readonly description?: string;
|
|
81
|
-
readonly labels?: readonly string[];
|
|
82
|
-
readonly buckets?: readonly number[];
|
|
83
|
-
readonly unit?: string;
|
|
84
|
-
// When true, Framework auto-adds tenant_id to labels (ctx-driven). Default
|
|
85
|
-
// false — cardinality multiplies by tenant count, so opt-in.
|
|
86
|
-
readonly tenantLabel?: boolean;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
export type MetricOptions = Omit<FeatureMetricDef, "shortName">;
|
|
90
|
-
|
|
91
|
-
// --- Secret Keys (declared by features via r.secret()) ---
|
|
92
|
-
|
|
93
|
-
// A feature-declared secret. The fully-qualified name is
|
|
94
|
-
// `<featureName>:<shortName>` — the Framework prefixes. Ops see the
|
|
95
|
-
// qualified name in list / audit; feature code reads it via
|
|
96
|
-
// ctx.secrets.get(tenantId, SecretKeys.stripeKey) with the typed handle.
|
|
97
|
-
export type SecretKeyDefinition = {
|
|
98
|
-
// Short name inside the feature (e.g. "stripe.apiKey"). Qualified to
|
|
99
|
-
// `<feature>:<shortName>` at registry-build time.
|
|
100
|
-
readonly shortName: string;
|
|
101
|
-
// Qualified name — `<feature>:<shortName>`. Set during registry build.
|
|
102
|
-
readonly qualifiedName: string;
|
|
103
|
-
// i18n label for TenantAdmin UI.
|
|
104
|
-
readonly label: { readonly [locale: string]: string };
|
|
105
|
-
// Optional redaction function. Takes the plaintext, returns the preview
|
|
106
|
-
// shown in list handlers. Default is first-3-chars + bullets.
|
|
107
|
-
readonly redact?: (plaintext: string) => string;
|
|
108
|
-
// Short human hint shown in UI ("Find this in your Stripe dashboard ...").
|
|
109
|
-
readonly hint?: { readonly [locale: string]: string };
|
|
110
|
-
// Per-secret scope. v1 only "tenant" — user / system scopes ship in v2.
|
|
111
|
-
readonly scope: "tenant";
|
|
112
|
-
// Tenant must set this secret before the owning feature works. Surfaced
|
|
113
|
-
// by readiness:query:status; keep in sync with the missing-secret throw
|
|
114
|
-
// in the feature's build-fn.
|
|
115
|
-
readonly required?: boolean;
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
export type SecretOptions = Omit<SecretKeyDefinition, "shortName" | "qualifiedName">;
|
|
119
|
-
|
|
120
|
-
// Typed reference returned by r.secret(). Lets feature code pass a
|
|
121
|
-
// strongly-named handle to ctx.secrets.get instead of retyping the
|
|
122
|
-
// qualified string. Parallels ConfigKeyHandle from the config system.
|
|
123
|
-
export type SecretKeyHandle = {
|
|
124
|
-
readonly name: string;
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
// --- Raw tables (declared by features via r.rawTable()) ---
|
|
128
|
-
|
|
129
|
-
/** Options accepted by `r.rawTable()`. The `reason` is required so the
|
|
130
|
-
* bypass leaves an audit trail at the registration site — reviewers can
|
|
131
|
-
* judge legitimacy without spelunking into history, and a future cleanup
|
|
132
|
-
* pass can find candidates for migration to `r.entity()`. */
|
|
133
|
-
export type RawTableOptions = {
|
|
134
|
-
/** Why this table needs to bypass the event-sourcing system. Examples:
|
|
135
|
-
* "imported from pre-ES system, read-only", "external Stripe webhook
|
|
136
|
-
* payload cache, write-only by webhook handler", "denormalised
|
|
137
|
-
* projection of a non-Kumiko data source". */
|
|
138
|
-
readonly reason: string;
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
/** Per-feature raw-table registration. Carries the bypass-justification
|
|
142
|
-
* reason but knows nothing about the owning feature — that's added when
|
|
143
|
-
* the registry aggregates entries cross-feature into `RawTableDef`. */
|
|
144
|
-
export type RawTableEntry = {
|
|
145
|
-
readonly name: string;
|
|
146
|
-
readonly table: PgTable;
|
|
147
|
-
readonly reason: string;
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
/** Registry-aggregated raw-table — the per-feature `RawTableEntry` plus
|
|
151
|
-
* the owning feature name. This is what `Registry.getAllRawTables()`
|
|
152
|
-
* exposes to readers (dev-server, ops UIs). */
|
|
153
|
-
export type RawTableDef = RawTableEntry & {
|
|
154
|
-
readonly featureName: string;
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// --- Unmanaged tables (declared by features via r.unmanagedTable()) ---
|
|
158
|
-
|
|
159
|
-
/** Per-feature unmanaged-table registration. `meta` is the
|
|
160
|
-
* `EntityTableMeta` (framework-native shape used by `migrate-runner`).
|
|
161
|
-
* The `reason` justifies the bypass at the registration site — same
|
|
162
|
-
* contract as `r.rawTable`. */
|
|
163
|
-
export type UnmanagedTableEntry = {
|
|
164
|
-
readonly name: string;
|
|
165
|
-
readonly meta: EntityTableMeta;
|
|
166
|
-
readonly reason: string;
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
/** Registry-aggregated unmanaged-table — adds the owning feature name. */
|
|
170
|
-
export type UnmanagedTableDef = UnmanagedTableEntry & {
|
|
171
|
-
readonly featureName: string;
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
// --- UI-Hints (manifest-only, picker/scaffolder metadata) ---
|
|
175
|
-
|
|
176
|
-
// Optional, declarative UI metadata declared via `r.uiHints({...})`. Surfaces
|
|
177
|
-
// in feature-manifest.json under `feature.uiHints`. Consumers (the picker in
|
|
178
|
-
// `create-kumiko-app`, the docs feature-reference) treat absent hints as
|
|
179
|
-
// "no special treatment" — bare feature.name + feature.description still work.
|
|
180
|
-
//
|
|
181
|
-
// Keep this list lean. Anything that already has a home on the feature
|
|
182
|
-
// (configKeys.scope/default/encrypted, secretKeys, requires, etc.) lives there.
|
|
183
|
-
// Only add fields here that are genuinely UI-only.
|
|
184
|
-
export type UiHintOption =
|
|
185
|
-
| {
|
|
186
|
-
readonly key: string;
|
|
187
|
-
readonly label: string;
|
|
188
|
-
readonly type: "boolean";
|
|
189
|
-
readonly default: boolean;
|
|
190
|
-
}
|
|
191
|
-
| {
|
|
192
|
-
readonly key: string;
|
|
193
|
-
readonly label: string;
|
|
194
|
-
readonly type: "select";
|
|
195
|
-
readonly options: readonly string[];
|
|
196
|
-
readonly default: string;
|
|
197
|
-
}
|
|
198
|
-
| {
|
|
199
|
-
readonly key: string;
|
|
200
|
-
readonly label: string;
|
|
201
|
-
readonly type: "text";
|
|
202
|
-
readonly default?: string;
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
export type UiHints = {
|
|
206
|
-
// Picker-facing label ("Auth · Email + Password" instead of the bare
|
|
207
|
-
// feature-name "auth-email-password").
|
|
208
|
-
readonly displayLabel?: string;
|
|
209
|
-
// Grouping for the picker. Free-form string; the picker sorts/groups by it.
|
|
210
|
-
readonly category?: string;
|
|
211
|
-
// Pre-checked in the picker when the user runs `bun create kumiko-app`.
|
|
212
|
-
readonly recommended?: boolean;
|
|
213
|
-
// Sub-options the picker asks about per-feature (e.g. "Password-Reset-Flow
|
|
214
|
-
// on/off"). The scaffolder maps each key to a generator decision; the
|
|
215
|
-
// framework doesn't act on them at runtime.
|
|
216
|
-
readonly configurableOptions?: readonly UiHintOption[];
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
// --- Feature Definition (output of defineFeature) ---
|
|
220
|
-
|
|
221
|
-
export type FeatureDefinition = {
|
|
222
|
-
readonly name: string;
|
|
223
|
-
// Docs-lead paragraph declared via r.describe(). Flows through the
|
|
224
|
-
// manifest introspection into the generated feature-reference pages.
|
|
225
|
-
readonly description?: string;
|
|
226
|
-
readonly systemScope: boolean;
|
|
227
|
-
// Set from the setup-callback return — typed via `defineFeature<TExports>`.
|
|
228
|
-
// `undefined` for setups that return nothing.
|
|
229
|
-
readonly exports?: unknown;
|
|
230
|
-
readonly requires: readonly string[];
|
|
231
|
-
readonly optionalRequires: readonly string[];
|
|
232
|
-
// Read-side projection-tables this feature is allowed to write via
|
|
233
|
-
// r.step.unsafeProjectionUpsert / unsafeProjectionDelete. Declared via
|
|
234
|
-
// r.requires.projection("table_name"). Hard requirement — boot-error
|
|
235
|
-
// if a step targets a non-listed table or one that's already an
|
|
236
|
-
// r.entity-registered aggregate-table. See step-vocabulary.md Q10.
|
|
237
|
-
readonly requiredProjections: ReadonlySet<string>;
|
|
238
|
-
// Tier-2 step kinds opted-in via r.requires.step("webhook.send"). Q9.
|
|
239
|
-
readonly requiredSteps: ReadonlySet<string>;
|
|
240
|
-
// Declared via r.toggleable({ default }). Presence makes the feature
|
|
241
|
-
// operator-switchable via the feature-toggles bundled feature; absence
|
|
242
|
-
// means the feature is always-on (e.g. auth, tenant, user — core infra
|
|
243
|
-
// that would brick the system if switchable).
|
|
244
|
-
readonly toggleableDefault?: boolean;
|
|
245
|
-
// Declarative UI metadata for picker/scaffolder tooling. Set via r.uiHints().
|
|
246
|
-
// Pure manifest-side info — the framework runtime doesn't read it.
|
|
247
|
-
readonly uiHints?: UiHints;
|
|
248
|
-
// entities/hooks/entityHooks are optional: defineFeature always
|
|
249
|
-
// materializes them, but hand-built definitions at system boundaries
|
|
250
|
-
// (test fixtures, partial boots — see registry.test.ts "slot robustness")
|
|
251
|
-
// omit them and the registry guards against that. Type follows runtime.
|
|
252
|
-
readonly entities?: Readonly<Record<string, EntityDefinition>>;
|
|
253
|
-
// Optional backing Drizzle table per entity, declared via the third arg of
|
|
254
|
-
// `r.entity(name, def, { table })`. Source of truth for the physical DDL
|
|
255
|
-
// when the table carries columns/indexes the field-DSL can't express
|
|
256
|
-
// (e.g. secrets' envelope jsonb without default). `collectTableMetas` and
|
|
257
|
-
// the registry's implicit-projection use this object instead of the
|
|
258
|
-
// field-derived table, so generate + test-push + executor share ONE table.
|
|
259
|
-
readonly entityTables?: Readonly<Record<string, unknown>>;
|
|
260
|
-
readonly relations: Readonly<Record<string, EntityRelations>>;
|
|
261
|
-
readonly writeHandlers: Readonly<Record<string, WriteHandlerDef>>;
|
|
262
|
-
readonly queryHandlers: Readonly<Record<string, QueryHandlerDef>>;
|
|
263
|
-
readonly translations: TranslationKeys;
|
|
264
|
-
readonly hooks?: HookMap;
|
|
265
|
-
readonly entityHooks?: EntityHookMap;
|
|
266
|
-
// F3 search-payload-extension — per-entity contributors that add flat fields
|
|
267
|
-
// to the search-index payload during indexing. Keyed by entityName. Wrapped
|
|
268
|
-
// in OwnedFn for feature-toggle filtering (consistent with postQuery-Hooks).
|
|
269
|
-
readonly searchPayloadExtensions?: Readonly<
|
|
270
|
-
Record<string, readonly OwnedFn<SearchPayloadContributorFn>[]>
|
|
271
|
-
>;
|
|
272
|
-
readonly configKeys: Readonly<Record<string, ConfigKeyDefinition>>;
|
|
273
|
-
readonly configSeeds: readonly ConfigSeedDef[];
|
|
274
|
-
readonly jobs: Readonly<Record<string, JobDefinition>>;
|
|
275
|
-
readonly registrarExtensions: Readonly<Record<string, RegistrarExtensionDef>>;
|
|
276
|
-
readonly extensionUsages: readonly RegistrarExtensionRegistration[];
|
|
277
|
-
readonly extensionSelectors: readonly ExtensionSelectorDef[];
|
|
278
|
-
/**
|
|
279
|
-
* Cross-feature API names this feature exposes via `r.exposesApi(name)`.
|
|
280
|
-
* Pure Marker-Deklaration — die echte Implementation wird als
|
|
281
|
-
* Query-/Write-Handler unter dem QN-Pattern registriert (z.B.
|
|
282
|
-
* `compliance-profiles:query:effective-profile`). Boot-Validator prüft
|
|
283
|
-
* dass jedes `r.usesApi(name)` einen passenden Exposer hier findet —
|
|
284
|
-
* Tippfehler oder Drop-Refactorings werden zu Boot-Fail statt Runtime-Crash.
|
|
285
|
-
*/
|
|
286
|
-
readonly exposedApis: ReadonlySet<string>;
|
|
287
|
-
/**
|
|
288
|
-
* Cross-feature API names this feature calls. Pflicht-Boot-Check:
|
|
289
|
-
* jeder Eintrag muss in `exposedApis` irgendeines Features auftauchen
|
|
290
|
-
* UND das Provider-Feature muss in requires/optionalRequires sein.
|
|
291
|
-
*/
|
|
292
|
-
readonly usedApis: ReadonlySet<string>;
|
|
293
|
-
readonly referenceData: readonly ReferenceDataDef[];
|
|
294
|
-
readonly notifications: Readonly<Record<string, NotificationDefinition>>;
|
|
295
|
-
readonly events: Readonly<Record<string, EventDef>>;
|
|
296
|
-
// Event schema migrations declared via r.eventMigration(). Keyed by event
|
|
297
|
-
// short-name; each entry carries the step transforms (fromVersion →
|
|
298
|
-
// toVersion). The registry stitches these with the defineEvent-declared
|
|
299
|
-
// current version and exposes a per-qualified-name upcaster chain.
|
|
300
|
-
readonly eventMigrations: Readonly<Record<string, readonly EventMigrationDef[]>>;
|
|
301
|
-
readonly configReads: readonly string[];
|
|
302
|
-
// Handler → entity mapping inferred from the colon convention
|
|
303
|
-
// ("entityName:verb") via tryMapEntity in defineFeature.
|
|
304
|
-
readonly handlerEntityMappings: Readonly<Record<string, string>>;
|
|
305
|
-
// Metrics declared via r.metric(). Short names — Framework prefixes on boot.
|
|
306
|
-
readonly metrics: Readonly<Record<string, FeatureMetricDef>>;
|
|
307
|
-
// Secret keys declared via r.secret(). Short names — Framework prefixes to
|
|
308
|
-
// "<feature>:<short>" during registry build.
|
|
309
|
-
readonly secretKeys: Readonly<Record<string, SecretKeyDefinition>>;
|
|
310
|
-
// Projections declared via r.projection(). Keyed by projection name; executor
|
|
311
|
-
// looks them up by source-entity at write-time.
|
|
312
|
-
readonly projections: Readonly<Record<string, ProjectionDefinition>>;
|
|
313
|
-
// Multi-stream projections — cross-aggregate async read-models. Keyed by
|
|
314
|
-
// short name; the dispatcher wraps each into an EventConsumer with its
|
|
315
|
-
// own cursor.
|
|
316
|
-
readonly multiStreamProjections: Readonly<Record<string, MultiStreamProjectionDefinition>>;
|
|
317
|
-
// Auth-claims hooks declared via r.authClaims(). Executed at login (and
|
|
318
|
-
// switch-tenant) time; their returned records are merged into
|
|
319
|
-
// SessionUser.claims under the auto-prefix "<featureName>:<key>".
|
|
320
|
-
readonly authClaimsHooks: readonly AuthClaimsFn[];
|
|
321
|
-
// Declared claim keys via r.claimKey(). Shorts keyed by their JS-side
|
|
322
|
-
// short name, qualified name qualified at registration time.
|
|
323
|
-
readonly claimKeys: Readonly<Record<string, ClaimKeyDefinition>>;
|
|
324
|
-
// Screen definitions declared via r.screen(). Keyed by the feature-local
|
|
325
|
-
// short id; the registry qualifies to "<feature>:screen:<id>" on boot.
|
|
326
|
-
// Pure data — ui-core + renderer packages interpret; engine only stores
|
|
327
|
-
// and validates entity/field references against the feature's entities.
|
|
328
|
-
readonly screens: Readonly<Record<string, ScreenDefinition>>;
|
|
329
|
-
// Nav entries declared via r.nav(). Keyed by the feature-local short id;
|
|
330
|
-
// registry qualifies to "<feature>:nav:<id>". Flat list — the renderer's
|
|
331
|
-
// resolveNavigation assembles the tree from parent refs at mount time.
|
|
332
|
-
readonly navs: Readonly<Record<string, NavDefinition>>;
|
|
333
|
-
// Workspaces declared via r.workspace(). Keyed by feature-local short id;
|
|
334
|
-
// registry qualifies to "<feature>:workspace:<id>". Pure UI metadata —
|
|
335
|
-
// shellWorkspaces consumes the resolved per-workspace nav list at mount
|
|
336
|
-
// time; engine validates roles + nav refs at boot.
|
|
337
|
-
readonly workspaces: Readonly<Record<string, WorkspaceDefinition>>;
|
|
338
|
-
// Tree-Actions-Map declared via r.treeActions(). At-most-one per feature
|
|
339
|
-
// (only-once-guard at registration). Erased to `Record<string,
|
|
340
|
-
// TreeActionDef>` for runtime registry-lookup (Visual-Tree-Component
|
|
341
|
-
// dispatching, Pattern-AST consumers). The compile-time-typed surface
|
|
342
|
-
// is the registrar's return value (TreeActionsHandle) which the
|
|
343
|
-
// feature exports via setup-return — buildTarget consumes the handle,
|
|
344
|
-
// not this slot. See visual-tree.md A5 + A7.
|
|
345
|
-
readonly treeActions?: Readonly<Record<string, TreeActionDef>>;
|
|
346
|
-
// Tree-Provider declared via r.tree(). At-most-one per feature.
|
|
347
|
-
// Provider liefert die Top-Level-Knoten dieses Features im Visual-
|
|
348
|
-
// Workspace (navigation: "tree"). Subscribe-Form mit lazy-Eval: erst
|
|
349
|
-
// beim Mount des Workspaces aufgerufen, kann Updates emittieren.
|
|
350
|
-
// Feature ohne treeProvider ist im Visual-Workspace unsichtbar
|
|
351
|
-
// (Zero-Whitelist-Filter aus visual-tree.md A2).
|
|
352
|
-
readonly treeProvider?: TreeChildrenSubscribe;
|
|
353
|
-
// HTTP-Routes declared via r.httpRoute(). Index is "METHOD path"
|
|
354
|
-
// (z.B. "GET /feed.xml") — eindeutig pro Feature. Die App-Server-
|
|
355
|
-
// Boot-Stage iteriert getAllHttpRoutes() und mountet jede Route auf
|
|
356
|
-
// den Hono-app (außerhalb /api/*). Pattern symmetrisch zu queryHandlers/
|
|
357
|
-
// writeHandlers — Routes leben mit dem Feature, nicht im Bootstrap.
|
|
358
|
-
readonly httpRoutes: Readonly<Record<string, HttpRouteDefinition>>;
|
|
359
|
-
// Raw tables declared via r.rawTable() — bypass the event-sourcing
|
|
360
|
-
// system. Keyed by feature-local short name. The registry attaches
|
|
361
|
-
// featureName on aggregation, lifting RawTableEntry → RawTableDef.
|
|
362
|
-
readonly rawTables: Readonly<Record<string, RawTableEntry>>;
|
|
363
|
-
// Unmanaged tables declared via r.unmanagedTable() — `EntityTableMeta`
|
|
364
|
-
// shape (post-drizzle), keyed by feature-local table-name. Cousin of
|
|
365
|
-
// rawTables: same bypass-justification contract, different storage
|
|
366
|
-
// shape. `kumiko schema generate` aggregates these alongside
|
|
367
|
-
// r.entity()-derived metas to build the full schema.
|
|
368
|
-
readonly unmanagedTables: Readonly<Record<string, UnmanagedTableEntry>>;
|
|
369
|
-
// Optional Zod-schema for env-vars this feature reads at runtime.
|
|
370
|
-
// Declared via `r.envSchema(z.object({...}))`. `composeEnvSchema` reads
|
|
371
|
-
// this to build one app-wide schema for boot-validation + dry-run
|
|
372
|
-
// rendering. Absence means the feature reads no env-vars (or hasn't
|
|
373
|
-
// been migrated yet — Sprint-9 migration is add-only per phase).
|
|
374
|
-
readonly envSchema?: z.ZodObject<z.ZodRawShape>;
|
|
375
|
-
};
|
|
376
|
-
|
|
377
|
-
// --- Feature Registrar (the "r" object in defineFeature) ---
|
|
378
|
-
|
|
379
|
-
type RefOrRefs = NameOrRef | readonly NameOrRef[];
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* `TFeature` is the literal feature-name from `defineFeature("foo", ...)` —
|
|
383
|
-
* default-`string` keeps every existing usage zero-config. Strict-typed
|
|
384
|
-
* features (apps that opt into the literal-name flavour) get propagated
|
|
385
|
-
* through to `defineEvent` so the returned `EventDef.name` is a literal
|
|
386
|
-
* `${CamelToKebab<TFeature>}:event:${CamelToKebab<TInner>}`. That literal
|
|
387
|
-
* threads through `ctx.appendEvent({ type: eventDef.name, ... })`,
|
|
388
|
-
* keeping strict-mode alive even when handlers route via `eventDef.name`
|
|
389
|
-
* instead of hand-typed string literals.
|
|
390
|
-
*/
|
|
391
|
-
/**
|
|
392
|
-
* `r.requires` is a callable+namespace: existing call form takes feature
|
|
393
|
-
* names (`r.requires("auth", "tenant")`), the `.projection` extension
|
|
394
|
-
* declares read-side projection tables that this feature's pipeline
|
|
395
|
-
* steps are allowed to write via `r.step.unsafeProjectionUpsert`.
|
|
396
|
-
* Hard-required for any unsafeProjection-* step usage (see Q10).
|
|
397
|
-
*/
|
|
398
|
-
export type RequiresApi = ((...featureNames: string[]) => void) & {
|
|
399
|
-
readonly projection: (tableName: string) => void;
|
|
400
|
-
// Tier-2 step opt-in (Q9). Tier-1 implicit, Tier-2 must be declared.
|
|
401
|
-
readonly step: (stepKind: string) => void;
|
|
402
|
-
};
|
|
403
|
-
|
|
404
|
-
export type FeatureRegistrar<TFeature extends string = string> = {
|
|
405
|
-
systemScope(): void;
|
|
406
|
-
// One-to-three-sentence docs-lead for the feature ("what it does + when
|
|
407
|
-
// you need it"). At most once per feature; must be non-empty.
|
|
408
|
-
describe(text: string): void;
|
|
409
|
-
requires: RequiresApi;
|
|
410
|
-
optionalRequires(...featureNames: string[]): void;
|
|
411
|
-
// Declare the feature as operator-togglable. `default` is the effective
|
|
412
|
-
// state when no global-toggle row exists. Must be called at most once per
|
|
413
|
-
// feature; calling on an always-on feature (e.g. auth/tenant/user) is a
|
|
414
|
-
// bug — and one nothing catches at boot, so don't.
|
|
415
|
-
toggleable(options: { default: boolean }): void;
|
|
416
|
-
// Picker/scaffolder metadata — see UiHints. At most once per feature.
|
|
417
|
-
uiHints(hints: UiHints): void;
|
|
418
|
-
|
|
419
|
-
entity(
|
|
420
|
-
name: string,
|
|
421
|
-
definition: EntityDefinition,
|
|
422
|
-
options?: { readonly table?: unknown },
|
|
423
|
-
): EntityRef;
|
|
424
|
-
|
|
425
|
-
writeHandler<TName extends string, TSchema extends ZodType>(
|
|
426
|
-
def: WriteHandlerDefinition<TName, TSchema>,
|
|
427
|
-
): HandlerRef;
|
|
428
|
-
writeHandler<TSchema extends ZodType>(
|
|
429
|
-
name: string,
|
|
430
|
-
schema: TSchema,
|
|
431
|
-
handler: WriteHandlerFn<z.infer<TSchema>>,
|
|
432
|
-
options?: { access?: AccessRule; rateLimit?: RateLimitOption },
|
|
433
|
-
): HandlerRef;
|
|
434
|
-
|
|
435
|
-
queryHandler<TName extends string, TSchema extends ZodType>(
|
|
436
|
-
def: QueryHandlerDefinition<TName, TSchema>,
|
|
437
|
-
): HandlerRef;
|
|
438
|
-
queryHandler<TSchema extends ZodType>(
|
|
439
|
-
name: string,
|
|
440
|
-
schema: TSchema,
|
|
441
|
-
handler: QueryHandlerFn<z.infer<TSchema>>,
|
|
442
|
-
options?: { access?: AccessRule; rateLimit?: RateLimitOption },
|
|
443
|
-
): HandlerRef;
|
|
444
|
-
|
|
445
|
-
relation(entity: NameOrRef, relationName: string, definition: RelationDefinition): void;
|
|
446
|
-
|
|
447
|
-
hook(type: "validation", target: RefOrRefs, fn: ValidationHookFn): void;
|
|
448
|
-
hook(type: "preSave", target: RefOrRefs, fn: PreSaveHookFn): void;
|
|
449
|
-
hook(
|
|
450
|
-
type: "postSave",
|
|
451
|
-
target: RefOrRefs,
|
|
452
|
-
fn: PostSaveHookFn,
|
|
453
|
-
options?: { phase?: HookPhase },
|
|
454
|
-
): void;
|
|
455
|
-
// preDelete always runs in-transaction (it guards the delete — there is no
|
|
456
|
-
// meaningful "after" for a pre-hook). No phase option.
|
|
457
|
-
hook(type: "preDelete", target: RefOrRefs, fn: PreDeleteHookFn): void;
|
|
458
|
-
hook(
|
|
459
|
-
type: "postDelete",
|
|
460
|
-
target: RefOrRefs,
|
|
461
|
-
fn: PostDeleteHookFn,
|
|
462
|
-
options?: { phase?: HookPhase },
|
|
463
|
-
): void;
|
|
464
|
-
hook(type: "preQuery", target: RefOrRefs, fn: PreQueryHookFn): void;
|
|
465
|
-
hook(type: "postQuery", target: RefOrRefs, fn: PostQueryHookFn): void;
|
|
466
|
-
|
|
467
|
-
entityHook(
|
|
468
|
-
type: "postSave",
|
|
469
|
-
entity: NameOrRef,
|
|
470
|
-
fn: PostSaveHookFn,
|
|
471
|
-
options?: { phase?: HookPhase },
|
|
472
|
-
): void;
|
|
473
|
-
entityHook(type: "preDelete", entity: NameOrRef, fn: PreDeleteHookFn): void;
|
|
474
|
-
entityHook(
|
|
475
|
-
type: "postDelete",
|
|
476
|
-
entity: NameOrRef,
|
|
477
|
-
fn: PostDeleteHookFn,
|
|
478
|
-
options?: { phase?: HookPhase },
|
|
479
|
-
): void;
|
|
480
|
-
// postQuery-entityHook: fires for ALL query-handlers of this entity (e.g.,
|
|
481
|
-
// for customFields-bundle to merge custom-fields-jsonb into every read).
|
|
482
|
-
// No phase semantics (synchronous after handler-execute, before field-
|
|
483
|
-
// access-filter).
|
|
484
|
-
entityHook(type: "postQuery", entity: NameOrRef, fn: PostQueryHookFn): void;
|
|
485
|
-
|
|
486
|
-
// F3 — Search-Payload-Extension: contributor function adds flat fields to
|
|
487
|
-
// an entity's search-index document. Fires synchronously during
|
|
488
|
-
// buildSearchDocument indexing. Use-case: custom-fields-bundle merging
|
|
489
|
-
// customFields-jsonb-keys flat into search-doc; tags-bundle projecting
|
|
490
|
-
// tags-array as searchable. See `SearchPayloadContributorFn`.
|
|
491
|
-
searchPayloadExtension(entity: NameOrRef, fn: SearchPayloadContributorFn): void;
|
|
492
|
-
|
|
493
|
-
// Returns a handle map keyed exactly like the input. Pass any handle to
|
|
494
|
-
// `ctx.config(handle)` to get the value type narrowed by the key's `type`.
|
|
495
|
-
// Optional `seeds` declare boot-time system-rows that are written via the
|
|
496
|
-
// event-store executor — idempotent, skipped when the stream already exists.
|
|
497
|
-
config<TKeys extends Readonly<Record<string, ConfigKeyDefinition<ConfigKeyType>>>>(definition: {
|
|
498
|
-
readonly keys: TKeys;
|
|
499
|
-
readonly seeds?: Readonly<Record<string, ConfigSeedDef>>;
|
|
500
|
-
}): { readonly [K in keyof TKeys]: ConfigKeyHandle<TKeys[K]["type"]> };
|
|
501
|
-
|
|
502
|
-
job(name: string, options: Omit<JobDefinition, "name" | "handler">, handler: JobHandlerFn): void;
|
|
503
|
-
|
|
504
|
-
notification(
|
|
505
|
-
name: string,
|
|
506
|
-
definition: {
|
|
507
|
-
readonly trigger: { readonly on: NameOrRef };
|
|
508
|
-
readonly recipient: NotificationRecipientFn;
|
|
509
|
-
readonly data: NotificationDataFn;
|
|
510
|
-
readonly templates?: Readonly<Record<string, NotificationTemplateFn>>;
|
|
511
|
-
},
|
|
512
|
-
): void;
|
|
513
|
-
|
|
514
|
-
translations(def: TranslationsDef): void;
|
|
515
|
-
|
|
516
|
-
// Register an event payload shape. Returns the qualified def so callers
|
|
517
|
-
// can pass `.name` to ctx.appendEvent without hand-building the
|
|
518
|
-
// "<feature>:event:<short>" string.
|
|
519
|
-
//
|
|
520
|
-
// `options.version` declares the CURRENT schema generation. Defaults to 1
|
|
521
|
-
// on first registration. When you bump the payload shape, raise version
|
|
522
|
-
// AND register r.eventMigration(shortName, N, N+1, transform) — the
|
|
523
|
-
// framework refuses to boot if the chain from 1 → version has gaps.
|
|
524
|
-
defineEvent<const TInner extends string, TPayload>(
|
|
525
|
-
name: TInner,
|
|
526
|
-
schema: ZodType<TPayload>,
|
|
527
|
-
options?: { readonly version?: number },
|
|
528
|
-
): EventDef<TPayload, QualifiedEventName<TFeature, TInner>>;
|
|
529
|
-
|
|
530
|
-
// Register a step-wise payload transform for event-schema evolution.
|
|
531
|
-
// `eventName` is the SHORT name (same as defineEvent). `toVersion` must
|
|
532
|
-
// be `fromVersion + 1` — chain larger jumps by registering each step.
|
|
533
|
-
// Transforms are pure functions: old payload in, new payload out. They
|
|
534
|
-
// run once per read (not once per event persisted), so keep them cheap.
|
|
535
|
-
eventMigration(
|
|
536
|
-
eventName: string,
|
|
537
|
-
fromVersion: number,
|
|
538
|
-
toVersion: number,
|
|
539
|
-
transform: EventUpcastFn,
|
|
540
|
-
): void;
|
|
541
|
-
|
|
542
|
-
readsConfig(...qualifiedKeys: string[]): void;
|
|
543
|
-
|
|
544
|
-
referenceData(
|
|
545
|
-
entity: NameOrRef,
|
|
546
|
-
data: readonly Record<string, unknown>[],
|
|
547
|
-
options?: { upsertKey?: string },
|
|
548
|
-
): void;
|
|
549
|
-
|
|
550
|
-
extendsRegistrar(name: string, def: RegistrarExtensionDef): void;
|
|
551
|
-
|
|
552
|
-
useExtension(extensionName: string, entity: NameOrRef, options?: Record<string, unknown>): void;
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* Declares which config key selects the active provider under an
|
|
556
|
-
* extension point — called by the point-owning foundation (e.g.
|
|
557
|
-
* `r.extensionSelector("mailTransport", configKeys.provider)`).
|
|
558
|
-
* Readiness gating counts a provider-feature's `required` keys and
|
|
559
|
-
* secrets only while that provider is the selected one. Registry-build
|
|
560
|
-
* fails on duplicate declarations per extension and on selector keys
|
|
561
|
-
* that no mounted feature declares.
|
|
562
|
-
*/
|
|
563
|
-
extensionSelector(extensionName: string, key: { readonly name: string } | string): void;
|
|
564
|
-
|
|
565
|
-
/**
|
|
566
|
-
* Marker-Deklaration: dieses Feature stellt eine Cross-Feature-API
|
|
567
|
-
* unter dem genannten Namen bereit. Die eigentliche Implementation
|
|
568
|
-
* wird separat als Query- oder Write-Handler unter dem QN-Pattern
|
|
569
|
-
* registriert; `r.exposesApi` ist reine Boot-Check-Surface.
|
|
570
|
-
*
|
|
571
|
-
* Boot-Validator prüft, dass jedes `r.usesApi(name)` einen passenden
|
|
572
|
-
* Exposer findet, dass das Exposer-Feature in requires/optionalRequires
|
|
573
|
-
* gelisted ist und dass kein API-Name doppelt exposed wird.
|
|
574
|
-
*
|
|
575
|
-
* ```ts
|
|
576
|
-
* defineFeature("compliance-profiles", (r) => {
|
|
577
|
-
* r.exposesApi("compliance.forTenant");
|
|
578
|
-
* r.queryHandler({
|
|
579
|
-
* name: "compliance:query:for-tenant",
|
|
580
|
-
* // ... echte Implementation
|
|
581
|
-
* });
|
|
582
|
-
* });
|
|
583
|
-
* ```
|
|
584
|
-
*/
|
|
585
|
-
exposesApi(apiName: string): void;
|
|
586
|
-
|
|
587
|
-
/**
|
|
588
|
-
* Declares that this feature calls a cross-feature API. Boot-Validator
|
|
589
|
-
* checkt dass irgendein anderes Feature `r.exposesApi(apiName)` macht
|
|
590
|
-
* und dass dieses Feature `r.requires/optionalRequires` darauf hat.
|
|
591
|
-
*
|
|
592
|
-
* ```ts
|
|
593
|
-
* defineFeature("user-data-rights", (r) => {
|
|
594
|
-
* r.requires("compliance-profiles");
|
|
595
|
-
* r.usesApi("compliance.forTenant");
|
|
596
|
-
* });
|
|
597
|
-
* ```
|
|
598
|
-
*/
|
|
599
|
-
usesApi(apiName: string): void;
|
|
600
|
-
|
|
601
|
-
// Declare a metric. Short name (without kumiko_<feature>_ prefix) — Framework
|
|
602
|
-
// qualifies it on boot. Validation (snake_case + typ-suffix) runs at boot.
|
|
603
|
-
// Usage at runtime: ctx.metrics.inc("created_total", { status: "new" }).
|
|
604
|
-
metric(shortName: string, options: MetricOptions): void;
|
|
605
|
-
|
|
606
|
-
// Declare a secret key. Qualified name follows "<feature>:secret:<kebab>"
|
|
607
|
-
// via the QN helper. Returns a typed handle so feature code can pass it
|
|
608
|
-
// to ctx.secrets.get without retyping the qualified string — same
|
|
609
|
-
// ergonomics as r.config's handle.
|
|
610
|
-
secret(shortName: string, options: SecretOptions): SecretKeyHandle;
|
|
611
|
-
|
|
612
|
-
// Register a projection driven by events of one or more source entities.
|
|
613
|
-
// The runtime fires projection.apply[event.type] inside the event-store's
|
|
614
|
-
// transaction, so projections stay consistent with the events that feed them.
|
|
615
|
-
projection(definition: ProjectionDefinition): void;
|
|
616
|
-
|
|
617
|
-
// Register a cross-aggregate async projection. The event-dispatcher owns
|
|
618
|
-
// delivery via a dedicated cursor — at-least-once, strictly-ordered by
|
|
619
|
-
// events.id. Handlers must be idempotent. Marten's MultiStreamProjection
|
|
620
|
-
// equivalent: customer billing summaries, cross-feature audit views,
|
|
621
|
-
// saga state machines where a single view spans many aggregate types.
|
|
622
|
-
// Omit `table` for pure side-effect handlers (notifications, webhooks,
|
|
623
|
-
// external-system sync) — the dispatcher still delivers at-least-once with
|
|
624
|
-
// per-consumer ordering and dead-letter behaviour.
|
|
625
|
-
multiStreamProjection(definition: MultiStreamProjectionDefinition): void;
|
|
626
|
-
|
|
627
|
-
// Register a function that contributes claims into SessionUser.claims at
|
|
628
|
-
// login time. Multiple features (and multiple calls within one feature)
|
|
629
|
-
// are allowed; returns are merged. Keys are auto-prefixed with the feature
|
|
630
|
-
// name ("<feature>:<key>") — cross-feature collisions are impossible by
|
|
631
|
-
// construction. Same-feature duplicate keys follow last-wins.
|
|
632
|
-
//
|
|
633
|
-
// Hooks run in parallel. If one throws, the error is logged and that
|
|
634
|
-
// feature's claims are simply missing from the merged record — login
|
|
635
|
-
// still succeeds. This is a deliberate best-effort policy: identity-facts
|
|
636
|
-
// are convenience, not access-gates (that's what `roles` + field-access
|
|
637
|
-
// rules are for).
|
|
638
|
-
authClaims(fn: AuthClaimsFn): void;
|
|
639
|
-
|
|
640
|
-
// Declare a claim key. Qualified name follows "<feature>:<shortName>" —
|
|
641
|
-
// NO kebab conversion (it would break the claim round-trip, unlike
|
|
642
|
-
// r.secret / r.config). Returns a
|
|
643
|
-
// typed handle so feature code can pass it to `readClaim(user, handle)`
|
|
644
|
-
// without retyping the qualified string and with the right narrowed
|
|
645
|
-
// return type.
|
|
646
|
-
//
|
|
647
|
-
// Declaring claim keys also turns on a runtime check: when the feature's
|
|
648
|
-
// r.authClaims hooks return an inner-key not in the declared list, the
|
|
649
|
-
// resolver logs a warning (the claim still lands in the JWT — declared
|
|
650
|
-
// or not — so strict-mode isn't on; this is typo-drift protection).
|
|
651
|
-
claimKey<T extends ClaimKeyType>(
|
|
652
|
-
shortName: string,
|
|
653
|
-
options: { readonly type: T },
|
|
654
|
-
): ClaimKeyHandle<T>;
|
|
655
|
-
|
|
656
|
-
// Register a screen. The id is the feature-local short name (kebab-case);
|
|
657
|
-
// the registry qualifies to "<feature>:screen:<id>". Boot-validation checks
|
|
658
|
-
// that entity-bound screens reference a registered entity and that the
|
|
659
|
-
// columns / form-field refs name real fields — cross-feature component-QN
|
|
660
|
-
// validation (r.uiComponent) comes in M4/M5.
|
|
661
|
-
screen(definition: ScreenDefinition): void;
|
|
662
|
-
|
|
663
|
-
// Register a nav entry. The id is the feature-local short name (kebab-case);
|
|
664
|
-
// the registry qualifies to "<feature>:nav:<id>". Boot-validation checks
|
|
665
|
-
// that `screen` and `parent` refs exist (cross-feature QNs allowed) and
|
|
666
|
-
// that parent chains don't contain cycles.
|
|
667
|
-
nav(definition: NavDefinition): void;
|
|
668
|
-
|
|
669
|
-
// Register a workspace — a persona-/role-scoped UI surface. Pure UI
|
|
670
|
-
// composition; the registry qualifies the short id to
|
|
671
|
-
// "<feature>:workspace:<id>". Boot-validation checks that any nav refs
|
|
672
|
-
// exist, that workspace ids referenced from r.nav() are real, and that
|
|
673
|
-
// at most one workspace per app declares `default: true`.
|
|
674
|
-
workspace(definition: WorkspaceDefinition): void;
|
|
675
|
-
|
|
676
|
-
// Register an HTTP-route owned by this feature. The route is mounted
|
|
677
|
-
// outside the dispatcher pipeline (= außerhalb /api/write|query|batch),
|
|
678
|
-
// direkt an die app — Use-Case: RSS/Atom-Feeds, OG-Images, OpenAPI-Specs.
|
|
679
|
-
// Duplicate "method path"-Combinations are rejected per feature at setup
|
|
680
|
-
// time; there is no cross-feature check.
|
|
681
|
-
// Symmetric to queryHandler/writeHandler — Routes leben mit dem Feature,
|
|
682
|
-
// nicht im Bootstrap. Escape-hatch für nicht-feature-bound Routes
|
|
683
|
-
// bleibt runProdApp.extraRoutes.
|
|
684
|
-
httpRoute(definition: HttpRouteDefinition): void;
|
|
685
|
-
|
|
686
|
-
// Declare a raw Drizzle table that bypasses the event-sourcing system.
|
|
687
|
-
// Reserved for legacy-import, read-only caches, write-only webhook
|
|
688
|
-
// payload buffers, or any other case where the event-sourced flow
|
|
689
|
-
// doesn't fit. The dev-server iterates these alongside r.entity()
|
|
690
|
-
// projections at boot so the table exists before the first query.
|
|
691
|
-
// Apps still declare the table in `drizzle/schema.ts` so drizzle-kit
|
|
692
|
-
// tracks migrations and schema-drift detection works automatically.
|
|
693
|
-
//
|
|
694
|
-
// The required `reason` string is the marker that justifies the bypass —
|
|
695
|
-
// a non-empty string is the contract. If you can't write a reason,
|
|
696
|
-
// declare data via `r.entity()` instead.
|
|
697
|
-
rawTable(name: string, table: PgTable, options: RawTableOptions): void;
|
|
698
|
-
|
|
699
|
-
// Declare an "unmanaged" framework-native table (post-drizzle).
|
|
700
|
-
// EntityTableMeta carries the same column-shape that r.entity() builds,
|
|
701
|
-
// minus the audit-trail + base-columns scaffolding — used for read-side
|
|
702
|
-
// projections of event-streams (delivery-attempts, job-run-logs) where
|
|
703
|
-
// r.entity()'s aggregate-lifecycle assumptions don't fit.
|
|
704
|
-
//
|
|
705
|
-
// The `meta` argument is the result of `defineUnmanagedTable(...)` from
|
|
706
|
-
// `@cosmicdrift/kumiko-framework/db`. Reason-justification + audit-trail
|
|
707
|
-
// contract identical to `r.rawTable`.
|
|
708
|
-
//
|
|
709
|
-
// Why this exists separate from `r.rawTable`: rawTable carries a Drizzle
|
|
710
|
-
// `PgTable` (legacy), unmanagedTable carries the new `EntityTableMeta`
|
|
711
|
-
// shape that `migrate-runner` consumes. After the full drizzle-cut they
|
|
712
|
-
// will likely merge; for now they coexist.
|
|
713
|
-
unmanagedTable(meta: EntityTableMeta, options: RawTableOptions): void;
|
|
714
|
-
|
|
715
|
-
// Register the tree-actions schema for this feature — a map of
|
|
716
|
-
// action-name → action-definition (with optional typed args). At-most-
|
|
717
|
-
// one call per feature.
|
|
718
|
-
//
|
|
719
|
-
// Returns a TreeActionsHandle that the feature exports via setup-return
|
|
720
|
-
// (Memory `[EventDef-Exports-Pattern]`). The handle carries the
|
|
721
|
-
// literal-typed action-map that `buildTarget` consumes for compile-
|
|
722
|
-
// time validation:
|
|
723
|
-
//
|
|
724
|
-
// const handle = r.treeActions({
|
|
725
|
-
// edit: { args: { slug: "" as string } },
|
|
726
|
-
// list: {},
|
|
727
|
-
// });
|
|
728
|
-
// return { handle };
|
|
729
|
-
//
|
|
730
|
-
// Without this typed return, the action-map collapses to
|
|
731
|
-
// `Record<string, TreeActionDef>` at the buildTarget call-site and
|
|
732
|
-
// every action becomes accept-anything-string. See visual-tree.md A5.
|
|
733
|
-
//
|
|
734
|
-
// The runtime FeatureDefinition.treeActions slot stores the same map
|
|
735
|
-
// as erased Record (registry lookup, Pattern-AST consumers).
|
|
736
|
-
treeActions<const TActions extends Record<string, TreeActionDef>>(
|
|
737
|
-
actions: TActions,
|
|
738
|
-
): TreeActionsHandle<TFeature, TActions>;
|
|
739
|
-
|
|
740
|
-
// Declare the Zod-schema for env-vars this feature reads at runtime.
|
|
741
|
-
// At-most-one call per feature. composeEnvSchema reads it across all
|
|
742
|
-
// features to build one app-wide schema, which runProdApp parses
|
|
743
|
-
// process.env against at boot. App-Authors can also call
|
|
744
|
-
// `KUMIKO_DRY_RUN_ENV=human|json|pulumi|k8s` to introspect the
|
|
745
|
-
// required env-vars without booting.
|
|
746
|
-
//
|
|
747
|
-
// Convention: keys are SHOUTING_SNAKE_CASE env-var names. Per-var
|
|
748
|
-
// metadata (Pulumi-config-key override, openssl-generator suggestion,
|
|
749
|
-
// k8s-secret hints) goes into `.meta({ kumiko: { pulumi: {...} } })`
|
|
750
|
-
// — see framework/env/index.ts for the meta-shape.
|
|
751
|
-
envSchema(schema: z.ZodObject<z.ZodRawShape>): void;
|
|
752
|
-
|
|
753
|
-
// Register the tree-provider for this feature — the Subscribe-Function
|
|
754
|
-
// that emits the top-level Tree-Knoten when the Visual-Workspace
|
|
755
|
-
// (navigation: "tree") mounts. At-most-one call per feature.
|
|
756
|
-
//
|
|
757
|
-
// Provider returns a Subscribe-Function (emit-fn → unsubscribe-fn).
|
|
758
|
-
// Initial-emit synchron oder async, weitere Emits beliebig oft (e.g.
|
|
759
|
-
// on entity-update SSE). Provider sind session-bound; tenantId fließt
|
|
760
|
-
// über die Backend-Session bei fetch/dispatch, nicht über ein ctx-Arg.
|
|
761
|
-
//
|
|
762
|
-
// A feature without r.tree() is invisible in `navigation: "tree"`-
|
|
763
|
-
// workspaces — that's the Zero-Whitelist-Filter from visual-tree.md A2:
|
|
764
|
-
// provider-Vorhandensein ist der Filter, kein Workspace-Mapping.
|
|
765
|
-
tree(provider: TreeChildrenSubscribe): void;
|
|
766
|
-
};
|
|
767
|
-
|
|
768
|
-
// --- Registry (created from features) ---
|
|
769
|
-
|
|
770
|
-
export type Registry = {
|
|
771
|
-
readonly features: ReadonlyMap<string, FeatureDefinition>;
|
|
772
|
-
|
|
773
|
-
getFeature(name: string): FeatureDefinition | undefined;
|
|
774
|
-
getEntity(name: string): EntityDefinition | undefined;
|
|
775
|
-
getWriteHandler(name: string): WriteHandlerDef | undefined;
|
|
776
|
-
getQueryHandler(name: string): QueryHandlerDef | undefined;
|
|
777
|
-
getSearchableFields(entityName: string): readonly string[];
|
|
778
|
-
getSortableFields(entityName: string): readonly string[];
|
|
779
|
-
getRelations(entityName: string): EntityRelations;
|
|
780
|
-
getSearchIncludes(entityName: string): ReadonlyMap<string, readonly string[]>;
|
|
781
|
-
getIncomingRelations(entityName: string): ReadonlyArray<{
|
|
782
|
-
sourceEntity: string;
|
|
783
|
-
relationName: string;
|
|
784
|
-
relation: RelationDefinition;
|
|
785
|
-
}>;
|
|
786
|
-
// Hook getters — pass `effectiveFeatures` to drop hooks registered by
|
|
787
|
-
// globally-disabled features. Omit the arg to get all hooks (legacy
|
|
788
|
-
// callers + places where the feature-toggles feature isn't wired).
|
|
789
|
-
getPreSaveHooks(name: string, effectiveFeatures?: ReadonlySet<string>): readonly PreSaveHookFn[];
|
|
790
|
-
getPostSaveHooks(
|
|
791
|
-
name: string,
|
|
792
|
-
phase?: HookPhase,
|
|
793
|
-
effectiveFeatures?: ReadonlySet<string>,
|
|
794
|
-
): readonly PostSaveHookFn[];
|
|
795
|
-
getPreDeleteHooks(
|
|
796
|
-
name: string,
|
|
797
|
-
phase?: HookPhase,
|
|
798
|
-
effectiveFeatures?: ReadonlySet<string>,
|
|
799
|
-
): readonly PreDeleteHookFn[];
|
|
800
|
-
getPostDeleteHooks(
|
|
801
|
-
name: string,
|
|
802
|
-
phase?: HookPhase,
|
|
803
|
-
effectiveFeatures?: ReadonlySet<string>,
|
|
804
|
-
): readonly PostDeleteHookFn[];
|
|
805
|
-
getPreQueryHooks(
|
|
806
|
-
name: string,
|
|
807
|
-
effectiveFeatures?: ReadonlySet<string>,
|
|
808
|
-
): readonly PreQueryHookFn[];
|
|
809
|
-
getPostQueryHooks(
|
|
810
|
-
name: string,
|
|
811
|
-
effectiveFeatures?: ReadonlySet<string>,
|
|
812
|
-
): readonly PostQueryHookFn[];
|
|
813
|
-
getEntityPostSaveHooks(
|
|
814
|
-
entityName: string,
|
|
815
|
-
phase?: HookPhase,
|
|
816
|
-
effectiveFeatures?: ReadonlySet<string>,
|
|
817
|
-
): readonly PostSaveHookFn[];
|
|
818
|
-
getEntityPreDeleteHooks(
|
|
819
|
-
entityName: string,
|
|
820
|
-
phase?: HookPhase,
|
|
821
|
-
effectiveFeatures?: ReadonlySet<string>,
|
|
822
|
-
): readonly PreDeleteHookFn[];
|
|
823
|
-
getEntityPostDeleteHooks(
|
|
824
|
-
entityName: string,
|
|
825
|
-
phase?: HookPhase,
|
|
826
|
-
effectiveFeatures?: ReadonlySet<string>,
|
|
827
|
-
): readonly PostDeleteHookFn[];
|
|
828
|
-
getEntityPostQueryHooks(
|
|
829
|
-
entityName: string,
|
|
830
|
-
effectiveFeatures?: ReadonlySet<string>,
|
|
831
|
-
): readonly PostQueryHookFn[];
|
|
832
|
-
// F3 — contributors for an entity's search-doc-payload, fired during
|
|
833
|
-
// buildSearchDocument indexing. See `SearchPayloadContributorFn`.
|
|
834
|
-
// `effectiveFeatures` filters out contributors owned by feature-toggle-
|
|
835
|
-
// disabled features (parallel to other getters' filtering semantic).
|
|
836
|
-
getSearchPayloadExtensions(
|
|
837
|
-
entityName: string,
|
|
838
|
-
effectiveFeatures?: ReadonlySet<string>,
|
|
839
|
-
): readonly SearchPayloadContributorFn[];
|
|
840
|
-
getHandlerEntity(qualifiedHandler: string): string | undefined;
|
|
841
|
-
isHandlerSystemScoped(qualifiedHandler: string): boolean;
|
|
842
|
-
getHandlerFeature(qualifiedHandler: string): string | undefined;
|
|
843
|
-
// True iff at least one registered handler declares a `rateLimit`
|
|
844
|
-
// option. Pre-computed at registry-build so the boot path can skip
|
|
845
|
-
// wiring the RateLimitResolver (and its Lua-script registration on
|
|
846
|
-
// Redis) entirely when nobody opted in. Per-request cost stays zero
|
|
847
|
-
// for apps that don't use the feature.
|
|
848
|
-
hasRateLimitedHandler(): boolean;
|
|
849
|
-
// All metrics from all features, keyed by fully-qualified name
|
|
850
|
-
// (kumiko_<feature>_<shortName>). Consumed at boot to register them on the
|
|
851
|
-
// active Meter.
|
|
852
|
-
getAllMetrics(): ReadonlyMap<string, FeatureMetricDef & { readonly featureName: string }>;
|
|
853
|
-
getAllTranslations(): TranslationKeys;
|
|
854
|
-
getConfigKey(qualifiedKey: string): ConfigKeyDefinition | undefined;
|
|
855
|
-
getAllConfigKeys(): ReadonlyMap<string, ConfigKeyDefinition>;
|
|
856
|
-
getAllConfigSeeds(): readonly ConfigSeedDef[];
|
|
857
|
-
// Feature-declared secrets, aggregated across all registered features.
|
|
858
|
-
// Keyed by qualified name ("<feature>:<shortName>"). Used by the rotation
|
|
859
|
-
// job (to iterate "known" secrets) and admin-UIs to list available keys.
|
|
860
|
-
getAllSecretKeys(): ReadonlyMap<string, SecretKeyDefinition>;
|
|
861
|
-
getSecretKey(qualifiedName: string): SecretKeyDefinition | undefined;
|
|
862
|
-
getJob(qualifiedName: string): JobDefinition | undefined;
|
|
863
|
-
getAllJobs(): ReadonlyMap<string, JobDefinition>;
|
|
864
|
-
getEvent(qualifiedName: string): EventDef | undefined;
|
|
865
|
-
|
|
866
|
-
// Upcaster chain per qualified event name. Entries describe the current
|
|
867
|
-
// schema version and the step-wise transforms that upgrade older stored
|
|
868
|
-
// payloads. Empty chain when an event has never been migrated (version=1).
|
|
869
|
-
getEventUpcasters(): ReadonlyMap<
|
|
870
|
-
string,
|
|
871
|
-
{ readonly currentVersion: number; readonly chain: ReadonlyMap<number, EventUpcastFn> }
|
|
872
|
-
>;
|
|
873
|
-
getExtension(name: string): RegistrarExtensionDef | undefined;
|
|
874
|
-
getExtensionUsages(extensionName: string): readonly RegistrarExtensionRegistration[];
|
|
875
|
-
// Extension point → selector config key, from r.extensionSelector calls.
|
|
876
|
-
getAllExtensionSelectors(): ReadonlyMap<string, string>;
|
|
877
|
-
getAllNotifications(): ReadonlyMap<string, NotificationDefinition>;
|
|
878
|
-
getAllReferenceData(): readonly ReferenceDataDef[];
|
|
879
|
-
// Look up projections by source-entity name. Empty list when no projection
|
|
880
|
-
// feeds off the entity — event-store-executor uses this as the hot-path.
|
|
881
|
-
getProjectionsForSource(entityName: string): readonly ProjectionDefinition[];
|
|
882
|
-
getAllProjections(): ReadonlyMap<string, ProjectionDefinition>;
|
|
883
|
-
|
|
884
|
-
// All r.rawTable() registrations across all features, keyed by
|
|
885
|
-
// feature-local short name. The dev-server iterates this alongside
|
|
886
|
-
// implicit projections at boot. Cross-feature uniqueness is enforced
|
|
887
|
-
// at registry-build — duplicate names from different features fail
|
|
888
|
-
// the boot, so callers can rely on a flat keyspace.
|
|
889
|
-
getAllRawTables(): ReadonlyMap<string, RawTableDef>;
|
|
890
|
-
|
|
891
|
-
// Multi-stream projections registered via r.multiStreamProjection().
|
|
892
|
-
// Keyed by qualified name. The server wires each into the event-dispatcher
|
|
893
|
-
// as its own EventConsumer with a dedicated cursor.
|
|
894
|
-
getAllMultiStreamProjections(): ReadonlyMap<string, MultiStreamProjectionDefinition>;
|
|
895
|
-
// The feature that registered the given MSP. Used by the event-dispatcher
|
|
896
|
-
// to pause MSP-consumers whose owning feature is globally disabled.
|
|
897
|
-
getMultiStreamProjectionFeature(qualifiedName: string): string | undefined;
|
|
898
|
-
|
|
899
|
-
// All r.authClaims() hooks across all features, tagged with the declaring
|
|
900
|
-
// feature name so the resolver can apply the auto-prefix. Pre-aggregated
|
|
901
|
-
// at registry-build so the login hot path is a single Map read.
|
|
902
|
-
getAuthClaimsHooks(): readonly AuthClaimsHookDef[];
|
|
903
|
-
|
|
904
|
-
// Feature-declared claim keys, aggregated across all features. Keyed by
|
|
905
|
-
// qualified name ("<feature>:<short>"). Ops-UI + Boot-Validator use this
|
|
906
|
-
// to introspect what claims the app can produce.
|
|
907
|
-
getAllClaimKeys(): ReadonlyMap<string, ClaimKeyDefinition>;
|
|
908
|
-
getClaimKey(qualifiedName: string): ClaimKeyDefinition | undefined;
|
|
909
|
-
|
|
910
|
-
// Screens declared via r.screen() across all features. Keyed by qualified
|
|
911
|
-
// name ("<feature>:screen:<id>"). ui-core / renderer consume this to build
|
|
912
|
-
// navigation + screen-tree at mount time.
|
|
913
|
-
getAllScreens(): ReadonlyMap<string, ScreenDefinition>;
|
|
914
|
-
getScreen(qualifiedName: string): ScreenDefinition | undefined;
|
|
915
|
-
// The feature that registered the given screen. Consumed by the nav
|
|
916
|
-
// resolver to gate a nav-entry whose screen belongs to a disabled feature.
|
|
917
|
-
getScreenFeature(qualifiedName: string): string | undefined;
|
|
918
|
-
// All entity-bound screens (entityList / entityEdit) that target the given
|
|
919
|
-
// entity. Pre-grouped so ui-core's view-model builders don't re-filter
|
|
920
|
-
// getAllScreens() on every render. Custom screens have no entity and are
|
|
921
|
-
// never returned here — walk getAllScreens() for those.
|
|
922
|
-
getScreensByEntity(entityName: string): readonly ScreenDefinition[];
|
|
923
|
-
|
|
924
|
-
// Nav entries declared via r.nav() across all features. Keyed by qualified
|
|
925
|
-
// name ("<feature>:nav:<id>"). Flat list — the renderer's resolveNavigation
|
|
926
|
-
// assembles the tree from parent refs and gates by effective-features.
|
|
927
|
-
getAllNavs(): ReadonlyMap<string, NavDefinition>;
|
|
928
|
-
getNav(qualifiedName: string): NavDefinition | undefined;
|
|
929
|
-
// The feature that registered the given nav entry. Used by the nav
|
|
930
|
-
// resolver to drop entries whose owning feature is globally disabled.
|
|
931
|
-
getNavFeature(qualifiedName: string): string | undefined;
|
|
932
|
-
// Direct children of the given parent nav entry. Empty array when the
|
|
933
|
-
// parent has no children. Pre-grouped for O(1) tree-walk — resolveNavigation
|
|
934
|
-
// recurses with getNavsByParent(child.qn) instead of filtering getAllNavs().
|
|
935
|
-
getNavsByParent(parentQualifiedName: string): readonly NavDefinition[];
|
|
936
|
-
// Nav entries that declare no parent — the roots of the navigation tree.
|
|
937
|
-
// resolveNavigation starts its walk here and descends via getNavsByParent.
|
|
938
|
-
getTopLevelNavs(): readonly NavDefinition[];
|
|
939
|
-
|
|
940
|
-
// Workspaces declared via r.workspace() across all features. Keyed by
|
|
941
|
-
// qualified name ("<feature>:workspace:<id>"). The active web shell
|
|
942
|
-
// (shellWorkspaces) consumes this to render the switcher.
|
|
943
|
-
getAllWorkspaces(): ReadonlyMap<string, WorkspaceDefinition>;
|
|
944
|
-
getWorkspace(qualifiedName: string): WorkspaceDefinition | undefined;
|
|
945
|
-
// The feature that registered the workspace. Mirrors getNavFeature —
|
|
946
|
-
// lets the resolver drop workspaces whose owning feature is disabled.
|
|
947
|
-
getWorkspaceFeature(qualifiedName: string): string | undefined;
|
|
948
|
-
// Resolved nav QNs that belong to the given workspace. Pre-computed at
|
|
949
|
-
// boot from BOTH r.workspace.nav AND r.nav.workspaces — the shell
|
|
950
|
-
// doesn't have to merge sources at render time.
|
|
951
|
-
getWorkspaceNavs(workspaceQualifiedName: string): readonly string[];
|
|
952
|
-
// The single workspace whose `default: true` is set, if any. Boot
|
|
953
|
-
// validator rejects more than one. Apps without a default fall back to
|
|
954
|
-
// the first workspace the user has access to.
|
|
955
|
-
getDefaultWorkspace(): WorkspaceDefinition | undefined;
|
|
956
|
-
|
|
957
|
-
// Tree-Providers declared via r.tree() across all features. Keyed by
|
|
958
|
-
// declaring feature name (NOT qualified — Provider sind feature-bound,
|
|
959
|
-
// ein Feature liefert genau eine Provider-Function). The Visual-Tree
|
|
960
|
-
// component (renderer-web) iteriert getTreeProviders() beim Mount des
|
|
961
|
-
// navigation: "tree"-Workspaces, ruft jeden Provider mit ctx auf,
|
|
962
|
-
// sammelt die emitted TreeNode[] und merged sie zur Top-Level-Liste.
|
|
963
|
-
// See visual-tree.md A2 (Zero-Whitelist) + A4 (Subscribe-Form).
|
|
964
|
-
getTreeProviders(): ReadonlyMap<string, TreeChildrenSubscribe>;
|
|
965
|
-
|
|
966
|
-
// Tree-Actions-Map des Features. Returns the erased Record (compile-
|
|
967
|
-
// time-typed handle wandert über setup-export, nicht hier). Visual-
|
|
968
|
-
// Tree-Component nutzt das für Runtime-Action-Lookup beim Klick auf
|
|
969
|
-
// einen TreeNode.target — der Resolver findet das Feature via
|
|
970
|
-
// TargetRef.featureId und holt sich die zugehörige Action-Definition.
|
|
971
|
-
getTreeActions(featureName: string): Readonly<Record<string, TreeActionDef>> | undefined;
|
|
972
|
-
};
|
|
1
|
+
// Legacy path — re-exported for callers still importing this module directly.
|
|
2
|
+
export type * from "@cosmicdrift/kumiko-types/feature";
|