@cosmicdrift/kumiko-framework 0.386.0 → 0.387.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/LICENSE +21 -57
- package/README.md +2 -2
- package/agents/widgets.md +39 -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-ip.js +3 -1
- package/dist/api/extra-route.js +1 -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/routes.js +2 -2
- package/dist/api/server.js +34 -33
- package/dist/api/sse-broker.js +1 -1
- package/dist/api/sse-route.d.ts +13 -13
- package/dist/api/sse-route.js +15 -15
- 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/pg-kms-adapter.js +2 -6
- package/dist/crypto/pii-field-encryption.js +4 -4
- package/dist/crypto/subject-resolver.d.ts +1 -1
- package/dist/crypto/subject-resolver.js +2 -2
- 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/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 +2 -2
- package/dist/db/eagerload.d.ts +3 -3
- package/dist/db/eagerload.js +21 -12
- package/dist/db/entity-table-meta.d.ts +2 -2
- package/dist/db/entity-table-meta.js +4 -4
- package/dist/db/event-store-executor-context.d.ts +1 -1
- package/dist/db/event-store-executor-context.js +13 -13
- package/dist/db/event-store-executor-read.js +27 -28
- package/dist/db/event-store-executor-write.js +65 -70
- 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 +26 -19
- package/dist/db/migrate-runner.js +25 -25
- 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 +1 -1
- package/dist/db/query.js +6 -6
- package/dist/db/rebuild-marker.js +20 -21
- 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 +82 -82
- package/dist/db/tenant-db.d.ts +2 -2
- package/dist/db/tenant-db.js +9 -2
- package/dist/derivatives/delete-stored-file.js +5 -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/config-deps.js +7 -6
- package/dist/engine/boot-validator/detail-screens.js +3 -3
- package/dist/engine/boot-validator/entity-handler.js +60 -61
- package/dist/engine/boot-validator/gdpr-storage.js +4 -4
- package/dist/engine/boot-validator/i18n-keys.js +1 -1
- package/dist/engine/boot-validator/index.d.ts +2 -2
- package/dist/engine/boot-validator/index.js +20 -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 +50 -57
- 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.js +15 -16
- package/dist/engine/boot-validator/screens-forms.js +10 -11
- package/dist/engine/boot-validator/screens-lists.js +14 -15
- package/dist/engine/boot-validator/screens.js +34 -40
- 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 +75 -75
- 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 +12 -11
- package/dist/engine/define-handler.js +12 -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.js +13 -14
- 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 +1 -1
- package/dist/engine/feature-ast/extractors/hooks.js +1 -1
- 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 +1 -1
- package/dist/engine/feature-ast/pattern-change-schema.js +1 -1
- package/dist/engine/feature-ast/patterns.d.ts +2 -2
- package/dist/engine/feature-ast/render.js +2 -2
- package/dist/engine/feature-config-events-jobs.js +15 -12
- package/dist/engine/feature-entity-handlers.js +9 -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/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.js +1 -1
- 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 +12 -12
- 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 +40 -38
- package/dist/engine/screen-filter-ops.js +17 -20
- package/dist/engine/screen-helpers.js +4 -3
- 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/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 +7 -27
- 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.js +3 -3
- package/dist/env/index.d.ts +7 -6
- package/dist/env/index.js +10 -9
- 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 +28 -28
- 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 +6 -7
- package/dist/event-store/errors.js +1 -1
- package/dist/event-store/event-store.js +2 -2
- package/dist/event-store/rebuild-dead-letter.js +1 -1
- package/dist/files/feature.js +9 -10
- package/dist/files/file-handle.js +5 -6
- 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 +11 -5
- 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/storage-tracking.js +2 -2
- 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 +8 -8
- package/dist/jobs/job-runner-helpers.js +8 -2
- package/dist/jobs/job-runner-schedulers.d.ts +1 -1
- package/dist/jobs/job-runner-schedulers.js +3 -4
- package/dist/jobs/job-runner.js +13 -15
- 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/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/dispatch-batch.js +2 -2
- package/dist/pipeline/dispatch-query.js +10 -10
- package/dist/pipeline/dispatch-shared.d.ts +1 -0
- package/dist/pipeline/dispatch-shared.js +15 -17
- package/dist/pipeline/dispatch-stream.js +1 -1
- package/dist/pipeline/dispatch-write.js +3 -3
- 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-delivery.js +1 -1
- package/dist/pipeline/event-dispatcher.js +17 -28
- 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/projection-rebuild.js +13 -12
- 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.js +116 -22
- package/dist/search/index.js +4 -4
- 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 +10 -9
- package/dist/utils/compare.js +1 -1
- package/package.json +4 -4
- package/src/changes.json +48 -0
- package/src/scripts/codemod/crypto-shredding-testing-move.ts +2 -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/pii-personal-migration.ts +17 -8
- package/src/scripts/codemod/unprocessable-error-details-reason.ts +2 -2
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
// - Top-level code outside `defineFeature(...)`. Designer/AI treat
|
|
18
18
|
// such files as "not a feature file".
|
|
19
19
|
//
|
|
20
|
-
// **Skeleton status
|
|
21
|
-
// Per-pattern extractors fill in iteratively
|
|
20
|
+
// **Skeleton status:** interface fixed, extractors are TODO.
|
|
21
|
+
// Per-pattern extractors fill in iteratively — each iteration adds
|
|
22
22
|
// one extractor + a focused test.
|
|
23
23
|
import { Project, SyntaxKind } from "ts-morph";
|
|
24
24
|
import { extractAiClassify, extractAiExtract, extractAiGenerate, extractAuthClaims, extractBootCheck, extractClaimKey, extractConfig, extractDefineEvent, extractDescribe, extractEntity, extractEnvSchema, extractExposesApi, extractExtendsRegistrar, extractHook, extractHttpRoute, extractJob, extractMetric, extractMultiStreamProjection, extractNav, extractNotification, extractOptionalRequires, extractProjection, extractQueryHandler, extractReadsConfig, extractReferenceData, extractRelation, extractRequires, extractScreen, extractSecret, extractSecretNamespace, extractStoreTable, extractStreamHandler, extractSystemScope, extractToggleable, extractTranslations, extractTreeActions, extractUiHints, extractUseExtension, extractUsesApi, extractWorkspace, extractWriteHandler, findFunctionLiteral, findImportBindingForLocalName, readObjectPropertyInitializer, resolveModuleFile, resolveSameFileObjectLiteral, } from "./extractors/index.js";
|
|
@@ -185,7 +185,7 @@ function walkSetupCallback(body, registrarParamName, sourceFile, patterns, error
|
|
|
185
185
|
// a registrar-wrapper call — a function that receives the registrar as
|
|
186
186
|
// a bare argument, e.g. `registerShowPonyScreens(r)`. Such wrappers are
|
|
187
187
|
// invisible to a receiver-shape match, so we resolve the callee (same
|
|
188
|
-
// file, or the declaring file of a named import
|
|
188
|
+
// file, or the declaring file of a named import) and recurse
|
|
189
189
|
// into its body with its own parameter name for the registrar slot.
|
|
190
190
|
const wrapper = resolveRegistrarWrapperCall(call, registrarParamName);
|
|
191
191
|
if (!wrapper)
|
|
@@ -368,7 +368,7 @@ function walkAiStepCalls(body, registrarParamName, sourceFile, patterns, errors,
|
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
370
|
// =============================================================================
|
|
371
|
-
// Internal — pattern extractors
|
|
371
|
+
// Internal — pattern extractors
|
|
372
372
|
// =============================================================================
|
|
373
373
|
/**
|
|
374
374
|
* Route an `<r>.<method>(...)` call to its concrete extractor. New
|
|
@@ -377,12 +377,12 @@ function walkAiStepCalls(body, registrarParamName, sourceFile, patterns, errors,
|
|
|
377
377
|
* commit to the new kind via compile errors.
|
|
378
378
|
*
|
|
379
379
|
* Skeleton: every recognised method currently returns an UnknownPattern
|
|
380
|
-
* with the right method name.
|
|
381
|
-
*
|
|
380
|
+
* with the right method name. Extractors replace the cases with concrete
|
|
381
|
+
* implementations one at a time.
|
|
382
382
|
*/
|
|
383
383
|
function dispatchExtractor(methodName, call, sourceFile) {
|
|
384
384
|
switch (methodName) {
|
|
385
|
-
//
|
|
385
|
+
// Simplest static patterns
|
|
386
386
|
case "requires":
|
|
387
387
|
return extractRequires(call, sourceFile);
|
|
388
388
|
case "optionalRequires":
|
|
@@ -397,7 +397,7 @@ function dispatchExtractor(methodName, call, sourceFile) {
|
|
|
397
397
|
return extractDescribe(call, sourceFile);
|
|
398
398
|
case "uiHints":
|
|
399
399
|
return extractUiHints(call, sourceFile);
|
|
400
|
-
//
|
|
400
|
+
// Object-literal-based static patterns
|
|
401
401
|
case "entity":
|
|
402
402
|
return extractEntity(call, sourceFile);
|
|
403
403
|
case "relation":
|
|
@@ -406,7 +406,7 @@ function dispatchExtractor(methodName, call, sourceFile) {
|
|
|
406
406
|
return extractNav(call, sourceFile);
|
|
407
407
|
case "workspace":
|
|
408
408
|
return extractWorkspace(call, sourceFile);
|
|
409
|
-
//
|
|
409
|
+
// Complex static patterns
|
|
410
410
|
case "config":
|
|
411
411
|
return extractConfig(call, sourceFile);
|
|
412
412
|
case "translations":
|
|
@@ -423,7 +423,7 @@ function dispatchExtractor(methodName, call, sourceFile) {
|
|
|
423
423
|
return extractReferenceData(call, sourceFile);
|
|
424
424
|
case "useExtension":
|
|
425
425
|
return extractUseExtension(call, sourceFile);
|
|
426
|
-
//
|
|
426
|
+
// Mixed (header + body) patterns
|
|
427
427
|
case "hook":
|
|
428
428
|
return extractHook(call, sourceFile);
|
|
429
429
|
case "authClaims":
|
|
@@ -448,7 +448,7 @@ function dispatchExtractor(methodName, call, sourceFile) {
|
|
|
448
448
|
return extractMultiStreamProjection(call, sourceFile);
|
|
449
449
|
case "screen":
|
|
450
450
|
return extractScreen(call, sourceFile);
|
|
451
|
-
//
|
|
451
|
+
// Opaque patterns
|
|
452
452
|
case "extendsRegistrar":
|
|
453
453
|
return extractExtendsRegistrar(call, sourceFile);
|
|
454
454
|
case "usesApi":
|
|
@@ -459,10 +459,10 @@ function dispatchExtractor(methodName, call, sourceFile) {
|
|
|
459
459
|
return extractStoreTable(call, sourceFile);
|
|
460
460
|
case "bootCheck":
|
|
461
461
|
return extractBootCheck(call, sourceFile);
|
|
462
|
-
//
|
|
462
|
+
// Tree-Actions pattern
|
|
463
463
|
case "treeActions":
|
|
464
464
|
return extractTreeActions(call, sourceFile);
|
|
465
|
-
//
|
|
465
|
+
// Env-schema contract (opaque, Zod-expression argument)
|
|
466
466
|
case "envSchema":
|
|
467
467
|
return extractEnvSchema(call, sourceFile);
|
|
468
468
|
// Unknown method — UnknownPattern signal so Designer/AI surface it
|
|
@@ -668,7 +668,7 @@ function callMatchesId(call, id) {
|
|
|
668
668
|
// a string literal directly, or a bare identifier following its
|
|
669
669
|
// declaration (same-file or imported) to a string-literal initializer —
|
|
670
670
|
// the dominant naming style in the framework's own bundled-features
|
|
671
|
-
// (`r.entity(ENTITY, ...)`, `r.useExtension(EXT_X, ...)
|
|
671
|
+
// (`r.entity(ENTITY, ...)`, `r.useExtension(EXT_X, ...)`).
|
|
672
672
|
//
|
|
673
673
|
// Narrow on purpose: every kind's arg-0 (and relation's arg-1) is parsed
|
|
674
674
|
// via readNameLiteral, never readNameOrRef (see round2.ts/round3.ts) —
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// tool (or any external caller) hands us JSON; before it ever reaches
|
|
3
3
|
// `applyChanges` it must pass through here. Every rejection carries an
|
|
4
4
|
// exact dotted/bracketed path so the caller (frequently an LLM) can
|
|
5
|
-
// self-correct without a human in the loop
|
|
5
|
+
// self-correct without a human in the loop.
|
|
6
6
|
//
|
|
7
7
|
// **Zod v4 constraint driving this file's shape:** every discriminated-
|
|
8
8
|
// union member below stays a plain `z.object(...)` (or `.superRefine`
|
|
@@ -258,14 +258,14 @@ export type EnvSchemaPattern = {
|
|
|
258
258
|
readonly source: SourceLocation;
|
|
259
259
|
readonly schemaBody: SourceLocation;
|
|
260
260
|
};
|
|
261
|
-
/** Live-editable policy defaults for an AI pipeline step instance
|
|
261
|
+
/** Live-editable policy defaults for an AI pipeline step instance. */
|
|
262
262
|
export type AiStepPolicy = {
|
|
263
263
|
readonly enabled: boolean;
|
|
264
264
|
readonly providerId?: string;
|
|
265
265
|
readonly model?: string;
|
|
266
266
|
readonly params: Record<string, unknown>;
|
|
267
267
|
};
|
|
268
|
-
/** Preserves an unresolvable argument reference across parse → render
|
|
268
|
+
/** Preserves an unresolvable argument reference across parse → render. */
|
|
269
269
|
export type AiStepOpaqueArgs = {
|
|
270
270
|
readonly __raw: string;
|
|
271
271
|
};
|
|
@@ -278,7 +278,7 @@ function renderUseExtension(p) {
|
|
|
278
278
|
if (p.extensionNameRaw !== undefined) {
|
|
279
279
|
// A raw-ref name can't be spread into the merged Object-Form without
|
|
280
280
|
// losing it to renderValue's plain-string serialization — fall back
|
|
281
|
-
// to positional form, which keeps the reference verbatim
|
|
281
|
+
// to positional form, which keeps the reference verbatim.
|
|
282
282
|
const optionsArg = p.options !== undefined ? `, ${renderValue(p.options)}` : "";
|
|
283
283
|
return `r.useExtension(${nameLiteral}, ${JSON.stringify(p.entityName)}${optionsArg});`;
|
|
284
284
|
}
|
|
@@ -568,7 +568,7 @@ function renderMultiStreamProjection(p) {
|
|
|
568
568
|
function renderDefineEvent(p) {
|
|
569
569
|
const nameLiteral = p.eventNameRaw ?? JSON.stringify(p.eventName);
|
|
570
570
|
const migrationEntries = p.migrations !== undefined ? Object.entries(p.migrations) : [];
|
|
571
|
-
// piiFields is a mandatory option
|
|
571
|
+
// piiFields is a mandatory option — the options object is
|
|
572
572
|
// always rendered, unlike the old version/migrations-only shortcut.
|
|
573
573
|
const lines = [`r.defineEvent(${nameLiteral}, ${p.schemaSource.raw}, {`];
|
|
574
574
|
lines.push(` piiFields: ${p.piiFields.raw},`);
|
|
@@ -4,7 +4,7 @@ import { isValidIanaTimeZone } from "../time/iana.js";
|
|
|
4
4
|
import { reasonProblem } from "./escape-hatch-reason.js";
|
|
5
5
|
import { createEventDef } from "./event-def.js";
|
|
6
6
|
import { resolveName } from "./handler-helpers.js";
|
|
7
|
-
import { splitNamedDefinition, unwrapArrayForm } from "./object-form.js";
|
|
7
|
+
import { requireArgument, splitNamedDefinition, unwrapArrayForm } from "./object-form.js";
|
|
8
8
|
import { QnTypes, qn, toKebab } from "./qualified-name.js";
|
|
9
9
|
// Builds config/secrets/claims/events/jobs/notifications registrar methods.
|
|
10
10
|
export function buildConfigEventsJobsMethods(state, name) {
|
|
@@ -49,7 +49,7 @@ export function buildConfigEventsJobsMethods(state, name) {
|
|
|
49
49
|
}
|
|
50
50
|
// Shared by defineEvent's `migrations` option — each entry registers a
|
|
51
51
|
// single upcast step, same validation/dedup as the old standalone
|
|
52
|
-
// r.eventMigration() call (folded into defineEvent
|
|
52
|
+
// r.eventMigration() call (folded into defineEvent).
|
|
53
53
|
function registerEventMigration(eventName, fromVersion, toVersion, transform) {
|
|
54
54
|
if (toVersion !== fromVersion + 1) {
|
|
55
55
|
throw new Error(`[Feature ${name}] defineEvent("${eventName}") migrations: only single-step migrations are allowed — toVersion must be fromVersion + 1 (got ${fromVersion} -> ${toVersion}). ` +
|
|
@@ -73,8 +73,8 @@ export function buildConfigEventsJobsMethods(state, name) {
|
|
|
73
73
|
const [jobName, jobOptions, jobHandler] = typeof jobNameOrDefinition === "string"
|
|
74
74
|
? [
|
|
75
75
|
jobNameOrDefinition,
|
|
76
|
-
options,
|
|
77
|
-
handler,
|
|
76
|
+
requireArgument(options, name, "job", "options"),
|
|
77
|
+
requireArgument(handler, name, "job", "handler"),
|
|
78
78
|
]
|
|
79
79
|
: (() => {
|
|
80
80
|
const { name: definedJobName, handler: h, ...rest } = jobNameOrDefinition;
|
|
@@ -97,9 +97,9 @@ export function buildConfigEventsJobsMethods(state, name) {
|
|
|
97
97
|
'tz must be an IANA time zone name such as "Europe/Berlin".');
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
-
// Resolve NameOrRef(s) in trigger.on.
|
|
101
|
-
//
|
|
102
|
-
// job
|
|
100
|
+
// Resolve NameOrRef(s) in trigger.on. For the multi-trigger form, arrays
|
|
101
|
+
// become arrays of resolved strings while a single value remains a string;
|
|
102
|
+
// the job runner distinguishes them with Array.isArray.
|
|
103
103
|
const trigger = "on" in jobOptions.trigger
|
|
104
104
|
? {
|
|
105
105
|
on: Array.isArray(jobOptions.trigger.on)
|
|
@@ -114,7 +114,10 @@ export function buildConfigEventsJobsMethods(state, name) {
|
|
|
114
114
|
},
|
|
115
115
|
notification(notificationNameOrDefinition, definition) {
|
|
116
116
|
const [notificationName, resolvedDefinition] = typeof notificationNameOrDefinition === "string"
|
|
117
|
-
? [
|
|
117
|
+
? [
|
|
118
|
+
notificationNameOrDefinition,
|
|
119
|
+
requireArgument(definition, name, "notification", "definition"),
|
|
120
|
+
]
|
|
118
121
|
: splitNamedDefinition(notificationNameOrDefinition);
|
|
119
122
|
state.notifications[notificationName] = {
|
|
120
123
|
name: notificationName,
|
|
@@ -167,7 +170,7 @@ export function buildConfigEventsJobsMethods(state, name) {
|
|
|
167
170
|
},
|
|
168
171
|
metric(shortNameOrDefinition, options) {
|
|
169
172
|
const [shortName, metricOptions] = typeof shortNameOrDefinition === "string"
|
|
170
|
-
? [shortNameOrDefinition, options]
|
|
173
|
+
? [shortNameOrDefinition, requireArgument(options, name, "metric", "options")]
|
|
171
174
|
: splitNamedDefinition(shortNameOrDefinition);
|
|
172
175
|
if (state.metrics[shortName]) {
|
|
173
176
|
throw new Error(`[Feature ${name}] Metric "${shortName}" already registered. ` +
|
|
@@ -183,7 +186,7 @@ export function buildConfigEventsJobsMethods(state, name) {
|
|
|
183
186
|
},
|
|
184
187
|
secret(shortNameOrDefinition, options) {
|
|
185
188
|
const [shortName, secretOptions] = typeof shortNameOrDefinition === "string"
|
|
186
|
-
? [shortNameOrDefinition, options]
|
|
189
|
+
? [shortNameOrDefinition, requireArgument(options, name, "secret", "options")]
|
|
187
190
|
: splitNamedDefinition(shortNameOrDefinition);
|
|
188
191
|
if (state.secretKeys[shortName]) {
|
|
189
192
|
throw new Error(`[Feature ${name}] Secret "${shortName}" already registered. ` +
|
|
@@ -208,7 +211,7 @@ export function buildConfigEventsJobsMethods(state, name) {
|
|
|
208
211
|
},
|
|
209
212
|
secretNamespace(shortNameOrDefinition, options) {
|
|
210
213
|
const [shortName, namespaceOptions] = typeof shortNameOrDefinition === "string"
|
|
211
|
-
? [shortNameOrDefinition, options]
|
|
214
|
+
? [shortNameOrDefinition, requireArgument(options, name, "secretNamespace", "options")]
|
|
212
215
|
: splitNamedDefinition(shortNameOrDefinition);
|
|
213
216
|
if (state.secretNamespaces[shortName]) {
|
|
214
217
|
throw new Error(`[Feature ${name}] Secret namespace "${shortName}" already registered. ` +
|
|
@@ -227,7 +230,7 @@ export function buildConfigEventsJobsMethods(state, name) {
|
|
|
227
230
|
? shortNameOrDefinition
|
|
228
231
|
: shortNameOrDefinition.name;
|
|
229
232
|
const claimType = typeof shortNameOrDefinition === "string"
|
|
230
|
-
? options.type
|
|
233
|
+
? requireArgument(options, name, "claimKey", "options").type
|
|
231
234
|
: shortNameOrDefinition.type;
|
|
232
235
|
if (state.claimKeys[shortName]) {
|
|
233
236
|
throw new Error(`[Feature ${name}] Claim key "${shortName}" already declared. ` +
|
|
@@ -3,7 +3,7 @@ import { toTableName } from "../db/table-builder.js";
|
|
|
3
3
|
import { isEntityConventionQueryHandler, isPagedQueryHandler, PAGED_QUERY_HANDLER_BRAND, } from "./define-handler.js";
|
|
4
4
|
import { assertEscapeHatchAccessIsSystemAdminOnly, registerCrudHandlers, } from "./entity-handlers.js";
|
|
5
5
|
import { resolveName } from "./handler-helpers.js";
|
|
6
|
-
import { isObjectArgument, throwObjectArgumentRequired } from "./object-form.js";
|
|
6
|
+
import { isObjectArgument, requireArgument, throwObjectArgumentRequired } from "./object-form.js";
|
|
7
7
|
const CRUD_VERBS = new Set(["create", "update", "delete"]);
|
|
8
8
|
const CRUD_REGISTERED_VERBS = new Set([...CRUD_VERBS, "restore", "list", "detail"]);
|
|
9
9
|
// A silent overwrite would drop framework behaviour that rides on the r.crud
|
|
@@ -42,9 +42,9 @@ function tryMapEntity(state, name, handlerName) {
|
|
|
42
42
|
// skip: feature-name entity match is the preferred mapping
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
|
-
const
|
|
46
|
-
if (
|
|
47
|
-
state.handlerEntityMappings[handlerName] =
|
|
45
|
+
const [onlyEntityName, ...otherEntityNames] = Object.keys(state.entities);
|
|
46
|
+
if (onlyEntityName !== undefined && otherEntityNames.length === 0) {
|
|
47
|
+
state.handlerEntityMappings[handlerName] = onlyEntityName;
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
}
|
|
@@ -97,7 +97,11 @@ export function buildEntityHandlerMethods(state, name) {
|
|
|
97
97
|
const { entity, name: innerName, ...rest } = entityRefOrDefinition;
|
|
98
98
|
return [entity, innerName, rest];
|
|
99
99
|
})()
|
|
100
|
-
: [
|
|
100
|
+
: [
|
|
101
|
+
entityRefOrDefinition,
|
|
102
|
+
requireArgument(relationName, name, "relation", "relation name"),
|
|
103
|
+
requireArgument(definition, name, "relation", "definition"),
|
|
104
|
+
];
|
|
101
105
|
const entityName = resolveName(entityRef);
|
|
102
106
|
if (!state.relations[entityName])
|
|
103
107
|
state.relations[entityName] = {};
|
|
@@ -48,10 +48,10 @@ export type ManifestFeature = {
|
|
|
48
48
|
readonly extensionsUsed: readonly ManifestExtension[];
|
|
49
49
|
readonly configKeys: readonly ManifestConfigKey[];
|
|
50
50
|
readonly secrets: readonly ManifestSecret[];
|
|
51
|
-
/**
|
|
52
|
-
* (
|
|
53
|
-
*
|
|
54
|
-
*
|
|
51
|
+
/** All registered write-handler QNs for this feature
|
|
52
|
+
* (e.g. "user:write:user:create", "auth-email-password:write:login").
|
|
53
|
+
* Derived from `collectWriteHandlerQns` and used as the source of truth
|
|
54
|
+
* for client-side type checking of `dispatcher.write` calls. */
|
|
55
55
|
readonly writeHandlers: readonly string[];
|
|
56
56
|
/** Picker/scaffolder UI metadata declared via r.uiHints(). Optional —
|
|
57
57
|
* absent for features that haven't been annotated yet (the picker falls
|
|
@@ -68,13 +68,13 @@ export type FeatureManifest = {
|
|
|
68
68
|
readonly tier?: string;
|
|
69
69
|
};
|
|
70
70
|
export type BuildManifestOptions = {
|
|
71
|
-
/**
|
|
71
|
+
/** Description of the manifest's origin, included unchanged in the JSON. */
|
|
72
72
|
readonly source: string;
|
|
73
|
-
/**
|
|
74
|
-
* Registry
|
|
75
|
-
*
|
|
73
|
+
/** Emit only these features (e.g. the 16 enterprise features from a
|
|
74
|
+
* Registry that also mounted their bundled requirements). Default:
|
|
75
|
+
* all features in the Registry. */
|
|
76
76
|
readonly featureNames?: ReadonlySet<string>;
|
|
77
|
-
/**
|
|
77
|
+
/** Tags each feature and the top-level manifest (e.g. "enterprise"). */
|
|
78
78
|
readonly tier?: string;
|
|
79
79
|
};
|
|
80
80
|
export declare function buildManifestFromRegistry(registry: Registry, options: BuildManifestOptions): FeatureManifest;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
// Runtime-introspected feature
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// lesen.
|
|
1
|
+
// Runtime-introspected feature manifest — shared extraction logic for
|
|
2
|
+
// `gen-feature-manifest.ts` (use-all-bundled) and the enterprise generator.
|
|
3
|
+
// These were previously two nearly identical forks with diverging schemas:
|
|
4
|
+
// when framework introspection was extended, the copy silently drifted. The
|
|
5
|
+
// booted Registry is authoritative because the AST parser cannot read the
|
|
6
|
+
// imperative factory helpers of bundled features.
|
|
8
7
|
import { SYSTEM_EVENT_PREFIX } from "../crypto/system-event-pii.js";
|
|
9
8
|
import { compareByCodepoint } from "../utils/index.js";
|
|
10
9
|
import { isEncryptedAtRest } from "./config-helpers.js";
|
|
@@ -38,9 +38,9 @@ export declare function buildUiExtensionsMethods<TName extends string>(state: Fe
|
|
|
38
38
|
*/
|
|
39
39
|
exposesApi(apiName: string): void;
|
|
40
40
|
/**
|
|
41
|
-
* Declares that this feature calls a cross-feature API.
|
|
42
|
-
*
|
|
43
|
-
*
|
|
41
|
+
* Declares that this feature calls a cross-feature API. The boot validator
|
|
42
|
+
* checks that another feature calls `r.exposesApi(name)` and that this
|
|
43
|
+
* feature declares a corresponding `r.requires` dependency.
|
|
44
44
|
*/
|
|
45
45
|
usesApi(apiName: string): void;
|
|
46
46
|
bootCheck(fn: BootCheckFn): void;
|
|
@@ -6,7 +6,7 @@ import { bindHookEscapeHatchGrant } from "../pipeline/system-identity-switch.js"
|
|
|
6
6
|
import { LifecycleHookTypes } from "./constants.js";
|
|
7
7
|
import { reasonProblem } from "./escape-hatch-reason.js";
|
|
8
8
|
import { resolveName } from "./handler-helpers.js";
|
|
9
|
-
import { isObjectArgument, throwObjectArgumentRequired } from "./object-form.js";
|
|
9
|
+
import { isObjectArgument, requireArgument, throwObjectArgumentRequired } from "./object-form.js";
|
|
10
10
|
import { isKebabSegment, isValidQn, qualifyEntityName, toKebab } from "./qualified-name.js";
|
|
11
11
|
import { HookPhases } from "./types/index.js";
|
|
12
12
|
// Builds hooks/extensions/projections/screens/nav/workspace/tables/tree-actions
|
|
@@ -159,7 +159,7 @@ export function buildUiExtensionsMethods(state, name) {
|
|
|
159
159
|
}
|
|
160
160
|
const targets = Array.isArray(target) ? target : [target];
|
|
161
161
|
const names = targets.map(resolveName);
|
|
162
|
-
//
|
|
162
|
+
// All hook-function casts below: @cast-boundary engine-bridge
|
|
163
163
|
// — typed Dev-API (LifecycleHookFn|ValidationHookFn) → erased Map<name, fn>.
|
|
164
164
|
if (type === "validation") {
|
|
165
165
|
for (const n of names) {
|
|
@@ -211,7 +211,11 @@ export function buildUiExtensionsMethods(state, name) {
|
|
|
211
211
|
},
|
|
212
212
|
useExtension(extensionNameOrDefinition, entityRef, options) {
|
|
213
213
|
const [extensionName, resolvedEntityRef, resolvedOptions] = typeof extensionNameOrDefinition === "string"
|
|
214
|
-
? [
|
|
214
|
+
? [
|
|
215
|
+
extensionNameOrDefinition,
|
|
216
|
+
requireArgument(entityRef, name, "useExtension", "entity"),
|
|
217
|
+
options,
|
|
218
|
+
]
|
|
215
219
|
: (() => {
|
|
216
220
|
const { name, entity, ...rest } = extensionNameOrDefinition;
|
|
217
221
|
return [name, entity, rest];
|
|
@@ -219,13 +223,14 @@ export function buildUiExtensionsMethods(state, name) {
|
|
|
219
223
|
const resolvedEntityName = resolveName(resolvedEntityRef);
|
|
220
224
|
// @cast-boundary engine-bridge — typed per-extension options → erased registration bag
|
|
221
225
|
const optionsBag = resolvedOptions;
|
|
222
|
-
//
|
|
226
|
+
// Cross-cutting escapeHatch convention for hook-context db
|
|
223
227
|
// access (mirrors r.hook's validation above). Validated the same way
|
|
224
228
|
// for every extension regardless of its typed hook shape.
|
|
225
229
|
const escapeHatch = optionsBag?.["escapeHatch"];
|
|
226
230
|
if (escapeHatch !== undefined) {
|
|
227
|
-
const
|
|
228
|
-
|
|
231
|
+
const escapeHatchObject = isPlainObject(escapeHatch) ? escapeHatch : undefined;
|
|
232
|
+
const reason = escapeHatchObject?.["reason"];
|
|
233
|
+
if (escapeHatchObject === undefined || typeof reason !== "string") {
|
|
229
234
|
throw new Error(`[Feature ${name}] r.useExtension("${extensionName}", "${resolvedEntityName}", ...) declares an invalid { escapeHatch } — ` +
|
|
230
235
|
`must be { reason: "<explanation>" } explaining why this usage needs unfiltered db access.`);
|
|
231
236
|
}
|
|
@@ -233,7 +238,7 @@ export function buildUiExtensionsMethods(state, name) {
|
|
|
233
238
|
if (extReasonProblem !== undefined) {
|
|
234
239
|
throw new Error(`[Feature ${name}] r.useExtension("${extensionName}", "${resolvedEntityName}", ...) declares an invalid { escapeHatch } — ${extReasonProblem}`);
|
|
235
240
|
}
|
|
236
|
-
const extGrantsProblem = escapeHatchGrantsProblem(
|
|
241
|
+
const extGrantsProblem = escapeHatchGrantsProblem(escapeHatchObject);
|
|
237
242
|
if (extGrantsProblem) {
|
|
238
243
|
throw new Error(`[Feature ${name}] r.useExtension("${extensionName}", "${resolvedEntityName}", ...) declares an invalid { escapeHatch } — ${extGrantsProblem}`);
|
|
239
244
|
}
|
|
@@ -282,9 +287,9 @@ export function buildUiExtensionsMethods(state, name) {
|
|
|
282
287
|
state.exposedApis.add(apiName);
|
|
283
288
|
},
|
|
284
289
|
/**
|
|
285
|
-
* Declares that this feature calls a cross-feature API.
|
|
286
|
-
*
|
|
287
|
-
*
|
|
290
|
+
* Declares that this feature calls a cross-feature API. The boot validator
|
|
291
|
+
* checks that another feature calls `r.exposesApi(name)` and that this
|
|
292
|
+
* feature declares a corresponding `r.requires` dependency.
|
|
288
293
|
*/
|
|
289
294
|
usesApi(apiName) {
|
|
290
295
|
state.usedApis.add(apiName);
|
|
@@ -344,7 +349,11 @@ export function buildUiExtensionsMethods(state, name) {
|
|
|
344
349
|
entityRefOrDefinition.data,
|
|
345
350
|
entityRefOrDefinition.upsertKey,
|
|
346
351
|
]
|
|
347
|
-
: [
|
|
352
|
+
: [
|
|
353
|
+
entityRefOrDefinition,
|
|
354
|
+
requireArgument(data, name, "referenceData", "data"),
|
|
355
|
+
options?.upsertKey,
|
|
356
|
+
];
|
|
348
357
|
state.referenceData.push({
|
|
349
358
|
entityName: resolveName(entityRef),
|
|
350
359
|
data: resolvedData,
|
|
@@ -424,10 +433,9 @@ export function buildUiExtensionsMethods(state, name) {
|
|
|
424
433
|
state.workspaces[definition.id] = definition;
|
|
425
434
|
},
|
|
426
435
|
httpRoute(definition) {
|
|
427
|
-
// Path
|
|
428
|
-
//
|
|
429
|
-
//
|
|
430
|
-
// das ausgesprochen hat — bewusster Block).
|
|
436
|
+
// Path validation: must start with "/"; /api/ routes are reserved for the
|
|
437
|
+
// dispatcher. An HTTP route occupying /api/foo could bypass the auth
|
|
438
|
+
// middleware without the author explicitly intending it, so block it.
|
|
431
439
|
if (!definition.path.startsWith("/")) {
|
|
432
440
|
throw new Error(`[Feature ${name}] httpRoute path "${definition.path}" must start with "/". ` +
|
|
433
441
|
`Got "${definition.path}".`);
|
|
@@ -505,7 +513,7 @@ export function buildUiExtensionsMethods(state, name) {
|
|
|
505
513
|
}
|
|
506
514
|
// `read_` is reserved for r.entity()/r.projection() (managed,
|
|
507
515
|
// event-sourced, rebuildable). storeTable is the unmanaged
|
|
508
|
-
// direct-write escape hatch — the prefix must say so
|
|
516
|
+
// direct-write escape hatch — the prefix must say so.
|
|
509
517
|
if (tableName.startsWith("read_")) {
|
|
510
518
|
throw new Error(`[Feature ${name}] r.storeTable("${tableName}"): the "read_" prefix is reserved ` +
|
|
511
519
|
`for managed r.entity()/r.projection() tables. Pick an unprefixed name or a ` +
|
|
@@ -514,7 +522,7 @@ export function buildUiExtensionsMethods(state, name) {
|
|
|
514
522
|
// meta.source must agree with the r.storeTable() escape hatch, or the
|
|
515
523
|
// migrate-generator treats schema drift on this table as safe to
|
|
516
524
|
// DROP+rebuild-from-events — wiping direct-write data with no events
|
|
517
|
-
// to replay it from
|
|
525
|
+
// to replay it from.
|
|
518
526
|
if (meta.source !== "unmanaged") {
|
|
519
527
|
throw new Error(`[Feature ${name}] r.storeTable("${tableName}") was given an EntityTableMeta with ` +
|
|
520
528
|
`source: "${meta.source}". r.storeTable() requires source: "unmanaged" (via ` +
|
|
@@ -538,16 +546,16 @@ export function buildUiExtensionsMethods(state, name) {
|
|
|
538
546
|
};
|
|
539
547
|
},
|
|
540
548
|
treeActions(actions) {
|
|
541
|
-
// Only-once
|
|
542
|
-
//
|
|
549
|
+
// Only-once guard: a second call is an authoring error that should surface in
|
|
550
|
+
// the feature file (matching the behavior of r.toggleable).
|
|
543
551
|
if (state.treeActions !== undefined) {
|
|
544
552
|
throw new Error(`[Feature ${name}] r.treeActions() already called. ` +
|
|
545
553
|
`Each feature may declare a single tree-actions schema.`);
|
|
546
554
|
}
|
|
547
555
|
state.treeActions = actions;
|
|
548
|
-
// Return typed handle
|
|
549
|
-
//
|
|
550
|
-
//
|
|
556
|
+
// Return a typed handle for the setup export. Freeze it so callers cannot
|
|
557
|
+
// mutate the map afterward, which would make the pattern AST and runtime
|
|
558
|
+
// lookup diverge.
|
|
551
559
|
return Object.freeze({
|
|
552
560
|
id: name,
|
|
553
561
|
treeActions: actions,
|
|
@@ -119,18 +119,17 @@ export function checkWriteFieldRoles(entity, changes, user) {
|
|
|
119
119
|
// row. For creates, pass oldRow = undefined; the check degenerates to a
|
|
120
120
|
// newRow-only evaluation.
|
|
121
121
|
//
|
|
122
|
-
// `submittedChanges` and `rowContext` are deliberately separate
|
|
122
|
+
// `submittedChanges` and `rowContext` are deliberately separate:
|
|
123
123
|
// - `submittedChanges` drives WHICH fields get checked — only what the user
|
|
124
124
|
// actually wrote in the request. A preSave-hook-derived field the user
|
|
125
125
|
// never touched (e.g. a system hook setting `assignedTo`) must not be
|
|
126
126
|
// field-ownership-checked against the user at all.
|
|
127
127
|
// - `rowContext` drives what a checked field's rule is evaluated AGAINST —
|
|
128
128
|
// this needs the full post-hook row, because an ownership rule can
|
|
129
|
-
// reference a DIFFERENT column that only a hook populates (
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
// `secretNote`). Defaults to `submittedChanges` when omitted.
|
|
129
|
+
// reference a DIFFERENT column that only a hook populates (a hook derives
|
|
130
|
+
// `authorId`, a user-submitted `secretNote` field's rule is `from("user:id", "authorId")`
|
|
131
|
+
// — the check needs the hook-derived `authorId` in scope even though the
|
|
132
|
+
// user only wrote `secretNote`). Defaults to `submittedChanges` when omitted.
|
|
134
133
|
//
|
|
135
134
|
// Returns the denied field name for the caller to wrap into an
|
|
136
135
|
// `ownership_denied` error with scope: "field", or null if all fields pass.
|
package/dist/engine/index.d.ts
CHANGED
|
@@ -91,4 +91,5 @@ export type { AccessRule, ActionFormScreenDefinition, ActiveMembership, ActiveMe
|
|
|
91
91
|
export { DEFAULT_CURRENCIES, DEFAULT_LOCALES, DELIVERY_FAILURE_CODES, DELIVERY_SKIP_REASONS, HookPhases, HTTP_ERROR_CODE_PATTERN, isAgentVisibleScreen, isDeliveryErrorCode, isOpenToAllGranted, resolveAgentExposure, } from "./types/index.ts";
|
|
92
92
|
export type { AwaitedEventType, PipelineBuildCtx, PipelineCtx, PipelineDef, StepBuilder, StepDef, StepFailureStrategy, StepInstance, StepKind, StepNamespace, StepResolver, } from "./types/step.ts";
|
|
93
93
|
export { runValidation } from "./validation.ts";
|
|
94
|
+
export { walkStepInstances } from "./walk-step-instances.ts";
|
|
94
95
|
export { formDerivationsOfSchema, withFormDerivations } from "./with-form-derivations.ts";
|
package/dist/engine/index.js
CHANGED
|
@@ -22,10 +22,9 @@ export { defineStep, getStep, listStepKinds } from "./define-step.js";
|
|
|
22
22
|
export { computeDefinitionFingerprint, defineWorkflow } from "./define-workflow.js";
|
|
23
23
|
export { computeEffectiveFeatures, isToggleableFeature } from "./effective-features.js";
|
|
24
24
|
export { createEntityExecutor, defineEntityCreateHandler, defineEntityDeleteHandler, defineEntityDetailHandler, defineEntityListHandler,
|
|
25
|
-
// Legacy single-fn-with-verb-string API. Backwards
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
// unverändert lauffähig.
|
|
25
|
+
// Legacy single-fn-with-verb-string API. Backwards compatibility — new
|
|
26
|
+
// apps use the verb-specific wrappers above. Existing callers (integration
|
|
27
|
+
// tests, old bundled-features) remain runnable without changes.
|
|
29
28
|
defineEntityQueryHandler, defineEntityRestoreHandler, defineEntityUpdateHandler, defineEntityWriteHandler, defineProjectionQueryHandler, entityListSchema, MAX_LIST_LIMIT, } from "./entity-handlers.js";
|
|
30
29
|
export { declareEscapeHatch } from "./escape-hatch-declaration.js";
|
|
31
30
|
export { createEventDef } from "./event-def.js";
|
|
@@ -81,4 +80,5 @@ export { findOverDeepTransferChain, resolveTransferAdjacency as resolveEntityTra
|
|
|
81
80
|
export { isSystemTenant, isUuid, parseTenantId, SYSTEM_TENANT_ID } from "./types/identifiers.js";
|
|
82
81
|
export { DEFAULT_CURRENCIES, DEFAULT_LOCALES, DELIVERY_FAILURE_CODES, DELIVERY_SKIP_REASONS, HookPhases, HTTP_ERROR_CODE_PATTERN, isAgentVisibleScreen, isDeliveryErrorCode, isOpenToAllGranted, resolveAgentExposure, } from "./types/index.js";
|
|
83
82
|
export { runValidation } from "./validation.js";
|
|
83
|
+
export { walkStepInstances } from "./walk-step-instances.js";
|
|
84
84
|
export { formDerivationsOfSchema, withFormDerivations } from "./with-form-derivations.js";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare function splitNamedDefinition<T extends {
|
|
2
2
|
readonly name: string;
|
|
3
3
|
}>(definition: T): [string, Omit<T, "name">];
|
|
4
|
+
export declare function requireArgument<T>(value: T | undefined, featureName: string, method: string, argument: string): T;
|
|
4
5
|
export declare function unwrapArrayForm<K extends string>(args: readonly [Record<K, readonly string[]>] | readonly string[], key: K): readonly string[];
|
|
5
6
|
export declare function isObjectArgument(value: unknown): boolean;
|
|
6
7
|
export declare function throwObjectArgumentRequired(featureName: string, method: string): never;
|
|
@@ -4,14 +4,24 @@ export function splitNamedDefinition(definition) {
|
|
|
4
4
|
const { name, ...rest } = definition;
|
|
5
5
|
return [name, rest];
|
|
6
6
|
}
|
|
7
|
+
// The `(name, options)` overloads make `options` required for typed callers;
|
|
8
|
+
// the implementation signature has to stay optional, so untyped callers are caught here.
|
|
9
|
+
export function requireArgument(value, featureName, method, argument) {
|
|
10
|
+
if (value === undefined) {
|
|
11
|
+
throw new Error(`[Feature ${featureName}] r.${method}() needs its ${argument} argument`);
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
function isStringList(args) {
|
|
16
|
+
return args.every((arg) => typeof arg === "string");
|
|
17
|
+
}
|
|
7
18
|
// `optionalRequires` and `readConfig` still accept variadic strings next to a
|
|
8
19
|
// single object wrapping the same string array under `key`.
|
|
9
20
|
export function unwrapArrayForm(args, key) {
|
|
21
|
+
if (isStringList(args))
|
|
22
|
+
return args;
|
|
10
23
|
const [first] = args;
|
|
11
|
-
|
|
12
|
-
return first[key];
|
|
13
|
-
}
|
|
14
|
-
return args;
|
|
24
|
+
return first[key];
|
|
15
25
|
}
|
|
16
26
|
export function isObjectArgument(value) {
|
|
17
27
|
return typeof value === "object" && value !== null;
|
package/dist/engine/ownership.js
CHANGED
|
@@ -72,7 +72,7 @@ export function matchesRule(rule, user, row) {
|
|
|
72
72
|
// `where` rules produce SQL for the DB-side filter and have no in-memory
|
|
73
73
|
// evaluator — the feature author owns the semantics. Only the read path
|
|
74
74
|
// reaches SQL (buildOwnershipClause); on write maps the boot-validator
|
|
75
|
-
// rejects them outright
|
|
75
|
+
// rejects them outright, and the in-memory write helpers below
|
|
76
76
|
// fail closed rather than calling in here.
|
|
77
77
|
throw new Error("where-rules can only be evaluated at the SQL layer (buildOwnershipClause); they are rejected on access.write at boot.");
|
|
78
78
|
}
|
|
@@ -219,7 +219,7 @@ export function shiftParams(fragment, shift) {
|
|
|
219
219
|
// is a boot-time misconfiguration; at request time we treat it as empty
|
|
220
220
|
// (safe default) rather than passing silently.
|
|
221
221
|
//
|
|
222
|
-
// Caller obligations
|
|
222
|
+
// Caller obligations — this function returns ONLY the ownership
|
|
223
223
|
// fragment, not the full row-access contract. A raw-SQL caller (not going
|
|
224
224
|
// through `ctx.db`, which already applies all three) must additionally:
|
|
225
225
|
// 1. Pass `paramStart` as `params.length + 1` for its own already-bound
|
|
@@ -309,7 +309,7 @@ function ruleToFragment(rule, user, table, paramStart) {
|
|
|
309
309
|
});
|
|
310
310
|
// Runtime backstop for the boot-time probe (boot-validator/ownership.ts):
|
|
311
311
|
// an unqualified column in a correlated subquery silently turns into a
|
|
312
|
-
// fail-open tautology
|
|
312
|
+
// fail-open tautology — throw instead of shipping it.
|
|
313
313
|
assertQualifiedWhereFragment(frag.sqlText, cachedTableColumnSqlNames(table), `ownership where-rule on "${tableNameOf(table)}"`);
|
|
314
314
|
return { kind: "sql", sqlText: frag.sqlText, params: frag.params };
|
|
315
315
|
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// Akzeptiert beide Formen:
|
|
1
|
+
// Parser for the ReferenceFieldDef.entity string. Accepts both forms:
|
|
4
2
|
// - "user" → same-feature ref (featureName = currentFeature)
|
|
5
|
-
// - "users:user" → cross-feature ref (
|
|
3
|
+
// - "users:user" → cross-feature ref (qualified)
|
|
6
4
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
5
|
+
// Kept in the framework package so the server validator and renderer (via
|
|
6
|
+
// re-export from @cosmicdrift/kumiko-renderer/headless) use the same parser;
|
|
7
|
+
// the convention must not be implemented twice.
|
|
10
8
|
export function parseRefTarget(raw, currentFeature) {
|
|
11
9
|
const idx = raw.indexOf(":");
|
|
12
10
|
if (idx < 0) {
|