@cosmicdrift/kumiko-framework 0.336.1 → 0.338.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/dist/api/api-constants.d.ts +1 -0
- package/dist/api/api-constants.js +9 -0
- package/dist/api/auth-middleware.js +45 -21
- package/dist/api/auth-routes.d.ts +1 -0
- package/dist/api/auth-routes.js +20 -0
- package/dist/api/redis-sse-broker.d.ts +1 -0
- package/dist/api/redis-sse-broker.js +4 -5
- package/dist/api/server.js +14 -4
- package/dist/api/sse-route.js +14 -1
- package/dist/bun-db/query.js +6 -4
- package/dist/compliance/sub-processors.js +10 -1
- package/dist/crypto/kek-source.js +1 -0
- package/dist/db/blind-index-cleanup.d.ts +1 -0
- package/dist/db/blind-index-cleanup.js +12 -1
- package/dist/db/event-store-executor-read.js +30 -4
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/queries/event-consumer.d.ts +2 -2
- package/dist/db/queries/event-consumer.js +5 -4
- package/dist/db/queries/event-store.js +6 -6
- package/dist/db/queries/projection-rebuild.js +4 -4
- package/dist/db/queries/seed-context.js +6 -6
- package/dist/db/tenant-db.js +16 -8
- package/dist/derivatives/derivatives-context.js +5 -2
- package/dist/engine/boot-validator/access-declarations.js +13 -7
- package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
- package/dist/engine/boot-validator/entity-handler.js +26 -0
- package/dist/engine/boot-validator/index.js +2 -1
- package/dist/engine/boot-validator/pii-retention.js +11 -1
- package/dist/engine/boot-validator/projection-list-screens.d.ts +1 -1
- package/dist/engine/boot-validator/projection-list-screens.js +13 -0
- package/dist/engine/boot-validator/screens.js +12 -0
- package/dist/engine/build-config-feature-schema.js +4 -0
- package/dist/engine/config-helpers.d.ts +2 -1
- package/dist/engine/config-helpers.js +1 -0
- package/dist/engine/define-feature.js +1 -0
- package/dist/engine/extensions/escape-hatch-usage.js +3 -1
- package/dist/engine/feature-ast/extractors/hooks.js +7 -1
- package/dist/engine/feature-ast/extractors/index.d.ts +1 -1
- package/dist/engine/feature-ast/extractors/index.js +1 -1
- package/dist/engine/feature-ast/extractors/jobs-routes.js +6 -2
- package/dist/engine/feature-ast/extractors/round3.d.ts +2 -1
- package/dist/engine/feature-ast/extractors/round3.js +11 -0
- package/dist/engine/feature-ast/extractors/shared.js +1 -3
- package/dist/engine/feature-ast/index.d.ts +1 -1
- package/dist/engine/feature-ast/parse.js +3 -1
- package/dist/engine/feature-ast/patch.d.ts +3 -0
- package/dist/engine/feature-ast/patch.js +8 -11
- package/dist/engine/feature-ast/patcher.d.ts +6 -1
- package/dist/engine/feature-ast/patcher.js +3 -0
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +630 -1
- package/dist/engine/feature-ast/pattern-change-schema.js +19 -2
- package/dist/engine/feature-ast/patterns.d.ts +8 -2
- package/dist/engine/feature-ast/patterns.js +1 -0
- package/dist/engine/feature-ast/render.js +9 -0
- package/dist/engine/feature-builder-state.d.ts +2 -1
- package/dist/engine/feature-builder-state.js +1 -0
- package/dist/engine/feature-config-events-jobs.d.ts +4 -1
- package/dist/engine/feature-config-events-jobs.js +25 -1
- package/dist/engine/feature-manifest.d.ts +1 -0
- package/dist/engine/feature-manifest.js +1 -0
- package/dist/engine/feature-ui-extensions.js +24 -21
- package/dist/engine/index.d.ts +1 -1
- package/dist/engine/pattern-library/library.js +2 -1
- package/dist/engine/pattern-library/static-schemas.d.ts +1 -0
- package/dist/engine/pattern-library/static-schemas.js +47 -4
- package/dist/engine/registry-facade.js +7 -0
- package/dist/engine/registry-ingest.js +7 -0
- package/dist/engine/registry-state.d.ts +2 -1
- package/dist/engine/registry-state.js +1 -0
- package/dist/engine/registry-validate.d.ts +1 -1
- package/dist/engine/registry-validate.js +39 -17
- package/dist/engine/registry.js +2 -2
- package/dist/engine/types/index.d.ts +3 -3
- package/dist/engine/types/index.js +1 -1
- package/dist/engine/types/screen.d.ts +1 -1
- package/dist/engine/types/screen.js +1 -1
- package/dist/errors/classes.d.ts +5 -0
- package/dist/errors/classes.js +15 -0
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/i18n/required-surface-keys.js +3 -5
- package/dist/jobs/job-runner.js +2 -1
- package/dist/migrations/pending-rebuilds.d.ts +2 -0
- package/dist/migrations/pending-rebuilds.js +3 -0
- package/dist/pipeline/dispatch-query.js +3 -5
- package/dist/pipeline/dispatch-shared.js +32 -5
- package/dist/pipeline/dispatch-stream.js +2 -4
- package/dist/pipeline/dispatch-write.js +2 -4
- package/dist/pipeline/event-dispatcher-delivery.d.ts +0 -1
- package/dist/pipeline/event-dispatcher-delivery.js +1 -8
- package/dist/pipeline/event-dispatcher.js +16 -25
- package/dist/pipeline/handler-access-error.d.ts +3 -0
- package/dist/pipeline/handler-access-error.js +17 -0
- package/dist/pipeline/irreversible-operation-gate.js +9 -3
- package/dist/pipeline/member-read-only-transaction.js +8 -3
- package/dist/pipeline/system-hooks.js +7 -1
- package/dist/pipeline/system-identity-switch.js +11 -8
- package/dist/search/meilisearch-adapter.js +0 -2
- package/dist/stack/push-entity-projection-tables.js +5 -3
- package/dist/stack/redis.d.ts +1 -0
- package/dist/stack/redis.js +11 -2
- package/dist/stack/table-helpers.d.ts +3 -1
- package/dist/stack/table-helpers.js +9 -7
- package/dist/stack/test-stack.js +1 -7
- package/dist/testing/handler-context.js +3 -6
- package/dist/ui-types/index.d.ts +1 -1
- package/dist/ui-types/index.js +1 -1
- package/dist/upgrade-cli.js +29 -2
- package/package.json +4 -4
- package/src/changes.json +153 -0
- package/src/scripts/codemod/collect-source-files.ts +48 -0
- package/src/scripts/codemod/migrate-cross-tenant.ts +3 -23
- package/src/scripts/codemod/migrate-db-raw.ts +6 -23
- package/src/scripts/codemod/migrate-open-to-all.ts +4 -24
- package/src/scripts/codemod/unprocessable-error-details-reason.ts +47 -3
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
// resolve this statically" markers) and, for EntityDefinition only,
|
|
22
22
|
// additionally checked for field-type membership via the shared
|
|
23
23
|
// `findUnknownEntityFieldTypes` helper (kept in sync with `extractEntity`).
|
|
24
|
+
import { ESCAPE_HATCH_GRANTS } from "@cosmicdrift/kumiko-types/handlers";
|
|
24
25
|
import * as z from "zod";
|
|
25
26
|
import { LifecycleHookTypes } from "../constants.js";
|
|
26
27
|
import { describeUnknownFieldType, findUnknownEntityFieldTypes } from "./entity-field-types.js";
|
|
@@ -131,6 +132,7 @@ const escapeHatchSchema = z
|
|
|
131
132
|
reason: z.string().refine((s) => s.trim().length > 0, {
|
|
132
133
|
message: "reason must be non-empty",
|
|
133
134
|
}),
|
|
135
|
+
grants: z.array(z.enum(ESCAPE_HATCH_GRANTS)).min(1).optional(),
|
|
134
136
|
})
|
|
135
137
|
.strict();
|
|
136
138
|
const rawRefSentinelSchema = z.object({ __raw: z.string() }).strict();
|
|
@@ -413,6 +415,14 @@ const secretSchema = z
|
|
|
413
415
|
options: passthroughSchema(),
|
|
414
416
|
})
|
|
415
417
|
.strict();
|
|
418
|
+
const secretNamespaceSchema = z
|
|
419
|
+
.object({
|
|
420
|
+
kind: z.literal("secretNamespace"),
|
|
421
|
+
source: sourceFieldSchema,
|
|
422
|
+
shortName: z.string(),
|
|
423
|
+
options: passthroughSchema(),
|
|
424
|
+
})
|
|
425
|
+
.strict();
|
|
416
426
|
const claimKeySchema = z
|
|
417
427
|
.object({
|
|
418
428
|
kind: z.literal("claimKey"),
|
|
@@ -707,6 +717,7 @@ export const PATTERN_SCHEMAS_BY_KIND = {
|
|
|
707
717
|
uiHints: uiHintsSchema,
|
|
708
718
|
metric: metricSchema,
|
|
709
719
|
secret: secretSchema,
|
|
720
|
+
secretNamespace: secretNamespaceSchema,
|
|
710
721
|
claimKey: claimKeySchema,
|
|
711
722
|
referenceData: referenceDataSchema,
|
|
712
723
|
readsConfig: readsConfigSchema,
|
|
@@ -733,7 +744,7 @@ export const PATTERN_SCHEMAS_BY_KIND = {
|
|
|
733
744
|
"ai.classify": aiClassifySchema,
|
|
734
745
|
unknown: unknownPatternSchema,
|
|
735
746
|
};
|
|
736
|
-
const patternSchema = z.discriminatedUnion("kind", [
|
|
747
|
+
export const patternSchema = z.discriminatedUnion("kind", [
|
|
737
748
|
entitySchema,
|
|
738
749
|
relationSchema,
|
|
739
750
|
navSchema,
|
|
@@ -748,6 +759,7 @@ const patternSchema = z.discriminatedUnion("kind", [
|
|
|
748
759
|
uiHintsSchema,
|
|
749
760
|
metricSchema,
|
|
750
761
|
secretSchema,
|
|
762
|
+
secretNamespaceSchema,
|
|
751
763
|
claimKeySchema,
|
|
752
764
|
referenceDataSchema,
|
|
753
765
|
readsConfigSchema,
|
|
@@ -810,6 +822,9 @@ const patternIdMetricSchema = z
|
|
|
810
822
|
const patternIdSecretSchema = z
|
|
811
823
|
.object({ kind: z.literal("secret"), shortName: z.string() })
|
|
812
824
|
.strict();
|
|
825
|
+
const patternIdSecretNamespaceSchema = z
|
|
826
|
+
.object({ kind: z.literal("secretNamespace"), shortName: z.string() })
|
|
827
|
+
.strict();
|
|
813
828
|
const patternIdClaimKeySchema = z
|
|
814
829
|
.object({ kind: z.literal("claimKey"), shortName: z.string() })
|
|
815
830
|
.strict();
|
|
@@ -870,6 +885,7 @@ export const PATTERN_ID_SCHEMAS_BY_KIND = {
|
|
|
870
885
|
hook: patternIdHookSchema,
|
|
871
886
|
metric: patternIdMetricSchema,
|
|
872
887
|
secret: patternIdSecretSchema,
|
|
888
|
+
secretNamespace: patternIdSecretNamespaceSchema,
|
|
873
889
|
claimKey: patternIdClaimKeySchema,
|
|
874
890
|
referenceData: patternIdReferenceDataSchema,
|
|
875
891
|
useExtension: patternIdUseExtensionSchema,
|
|
@@ -895,7 +911,7 @@ export const PATTERN_ID_SCHEMAS_BY_KIND = {
|
|
|
895
911
|
authClaims: patternIdAuthClaimsSchema,
|
|
896
912
|
treeActions: patternIdTreeActionsSchema,
|
|
897
913
|
};
|
|
898
|
-
const patternIdSchema = z.discriminatedUnion("kind", [
|
|
914
|
+
export const patternIdSchema = z.discriminatedUnion("kind", [
|
|
899
915
|
patternIdEntitySchema,
|
|
900
916
|
patternIdRelationSchema,
|
|
901
917
|
patternIdNavSchema,
|
|
@@ -907,6 +923,7 @@ const patternIdSchema = z.discriminatedUnion("kind", [
|
|
|
907
923
|
patternIdHookSchema,
|
|
908
924
|
patternIdMetricSchema,
|
|
909
925
|
patternIdSecretSchema,
|
|
926
|
+
patternIdSecretNamespaceSchema,
|
|
910
927
|
patternIdClaimKeySchema,
|
|
911
928
|
patternIdReferenceDataSchema,
|
|
912
929
|
patternIdUseExtensionSchema,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { LifecycleHookType } from "../constants.js";
|
|
2
2
|
import type { ConfigKeyDefinition, ConfigKeyType, JobDefinition, ReferenceDataDef, RunIn, TranslationKeys } from "../types/config.js";
|
|
3
|
-
import type { MetricOptions, SecretOptions } from "../types/feature.js";
|
|
3
|
+
import type { MetricOptions, SecretNamespaceOptions, SecretOptions } from "../types/feature.js";
|
|
4
4
|
import type { EntityDefinition } from "../types/fields.js";
|
|
5
5
|
import type { AccessRule, AgentHandlerHints, ClaimKeyType, EscapeHatchDeclaration, RateLimitDeclaration } from "../types/handlers.js";
|
|
6
6
|
import type { HookPhase } from "../types/hooks.js";
|
|
@@ -86,6 +86,12 @@ export type SecretPattern = {
|
|
|
86
86
|
readonly shortName: string;
|
|
87
87
|
readonly options: SecretOptions;
|
|
88
88
|
};
|
|
89
|
+
export type SecretNamespacePattern = {
|
|
90
|
+
readonly kind: "secretNamespace";
|
|
91
|
+
readonly source: SourceLocation;
|
|
92
|
+
readonly shortName: string;
|
|
93
|
+
readonly options: SecretNamespaceOptions;
|
|
94
|
+
};
|
|
89
95
|
export type ClaimKeyPattern = {
|
|
90
96
|
readonly kind: "claimKey";
|
|
91
97
|
readonly source: SourceLocation;
|
|
@@ -295,7 +301,7 @@ export type UnknownPattern = {
|
|
|
295
301
|
readonly source: SourceLocation;
|
|
296
302
|
readonly methodName: string;
|
|
297
303
|
};
|
|
298
|
-
export type FeaturePattern = EntityPattern | RelationPattern | NavPattern | WorkspacePattern | ConfigPattern | TranslationsPattern | RequiresPattern | OptionalRequiresPattern | SystemScopePattern | ToggleablePattern | DescribePattern | UiHintsPattern | MetricPattern | SecretPattern | ClaimKeyPattern | ReferenceDataPattern | ReadsConfigPattern | UseExtensionPattern | UsesApiPattern | ExposesApiPattern | TreeActionsPattern | ScreenPattern | WriteHandlerPattern | QueryHandlerPattern | StreamHandlerPattern | HookPattern | JobPattern | NotificationPattern | AuthClaimsPattern | HttpRoutePattern | ProjectionPattern | MultiStreamProjectionPattern | DefineEventPattern | ExtendsRegistrarPattern | EnvSchemaPattern | AiGeneratePattern | AiExtractPattern | AiClassifyPattern | UnknownPattern;
|
|
304
|
+
export type FeaturePattern = EntityPattern | RelationPattern | NavPattern | WorkspacePattern | ConfigPattern | TranslationsPattern | RequiresPattern | OptionalRequiresPattern | SystemScopePattern | ToggleablePattern | DescribePattern | UiHintsPattern | MetricPattern | SecretPattern | SecretNamespacePattern | ClaimKeyPattern | ReferenceDataPattern | ReadsConfigPattern | UseExtensionPattern | UsesApiPattern | ExposesApiPattern | TreeActionsPattern | ScreenPattern | WriteHandlerPattern | QueryHandlerPattern | StreamHandlerPattern | HookPattern | JobPattern | NotificationPattern | AuthClaimsPattern | HttpRoutePattern | ProjectionPattern | MultiStreamProjectionPattern | DefineEventPattern | ExtendsRegistrarPattern | EnvSchemaPattern | AiGeneratePattern | AiExtractPattern | AiClassifyPattern | UnknownPattern;
|
|
299
305
|
export type FeaturePatternKind = FeaturePattern["kind"];
|
|
300
306
|
export type Editability = "static" | "mixed" | "opaque";
|
|
301
307
|
export declare function getEditability(pattern: FeaturePattern): Editability;
|
|
@@ -61,6 +61,8 @@ export function renderPattern(pattern) {
|
|
|
61
61
|
return renderMetric(pattern);
|
|
62
62
|
case "secret":
|
|
63
63
|
return renderSecret(pattern);
|
|
64
|
+
case "secretNamespace":
|
|
65
|
+
return renderSecretNamespace(pattern);
|
|
64
66
|
case "claimKey":
|
|
65
67
|
return renderClaimKey(pattern);
|
|
66
68
|
case "referenceData":
|
|
@@ -245,6 +247,13 @@ function renderSecret(p) {
|
|
|
245
247
|
const merged = { name: p.shortName, ...p.options };
|
|
246
248
|
return `r.secret(${renderValue(merged)});`;
|
|
247
249
|
}
|
|
250
|
+
function renderSecretNamespace(p) {
|
|
251
|
+
if (isRawRefSentinel(p.options)) {
|
|
252
|
+
return `r.secretNamespace(${JSON.stringify(p.shortName)}, ${p.options.__raw});`;
|
|
253
|
+
}
|
|
254
|
+
const merged = { name: p.shortName, ...p.options };
|
|
255
|
+
return `r.secretNamespace(${renderValue(merged)});`;
|
|
256
|
+
}
|
|
248
257
|
function renderClaimKey(p) {
|
|
249
258
|
return `r.claimKey(${renderValue({ name: p.shortName, type: p.claimType })});`;
|
|
250
259
|
}
|
|
@@ -2,7 +2,7 @@ import type { WebSocketRouteDefinition } from "@cosmicdrift/kumiko-types/websock
|
|
|
2
2
|
import type * as z from "zod";
|
|
3
3
|
import type { ZodType } from "zod";
|
|
4
4
|
import type { HttpRouteDefinition } from "./types/http-route.js";
|
|
5
|
-
import type { AuthClaimsFn, BootCheckFn, ClaimKeyDefinition, ConfigKeyDefinition, ConfigSeedDef, EntityDefinition, EntityProjectionExtension, EventMigrationDef, ExtensionSelectorDef, FeatureMetricDef, JobDefinition, LifecycleHookFn, MultiStreamProjectionDefinition, NotificationDefinition, OwnedFn, PhasedHook, PostDeleteHookFn, PostQueryHookFn, PostSaveHookFn, PreDeleteHookFn, ProjectionDefinition, QueryHandlerDef, ReferenceDataDef, RegistrarExtensionDef, RegistrarExtensionRegistration, RelationDefinition, SearchPayloadContributorFn, SecretKeyDefinition, StoreTableEntry, StreamHandlerDef, TranslationKeys, TreeActionDef, UiHints, ValidationHookFn, WriteHandlerDef } from "./types/index.js";
|
|
5
|
+
import type { AuthClaimsFn, BootCheckFn, ClaimKeyDefinition, ConfigKeyDefinition, ConfigSeedDef, EntityDefinition, EntityProjectionExtension, EventMigrationDef, ExtensionSelectorDef, FeatureMetricDef, JobDefinition, LifecycleHookFn, MultiStreamProjectionDefinition, NotificationDefinition, OwnedFn, PhasedHook, PostDeleteHookFn, PostQueryHookFn, PostSaveHookFn, PreDeleteHookFn, ProjectionDefinition, QueryHandlerDef, ReferenceDataDef, RegistrarExtensionDef, RegistrarExtensionRegistration, RelationDefinition, SearchPayloadContributorFn, SecretKeyDefinition, SecretNamespaceDefinition, StoreTableEntry, StreamHandlerDef, TranslationKeys, TreeActionDef, UiHints, ValidationHookFn, WriteHandlerDef } from "./types/index.js";
|
|
6
6
|
import type { ContentCollectionDefinition, NavDefinition } from "./types/nav.js";
|
|
7
7
|
import type { ScreenDefinition } from "./types/screen.js";
|
|
8
8
|
import type { WorkspaceDefinition } from "./types/workspace.js";
|
|
@@ -46,6 +46,7 @@ export type FeatureBuilderState = {
|
|
|
46
46
|
handlerEntityMappings: Record<string, string>;
|
|
47
47
|
metrics: Record<string, FeatureMetricDef>;
|
|
48
48
|
secretKeys: Record<string, SecretKeyDefinition>;
|
|
49
|
+
secretNamespaces: Record<string, SecretNamespaceDefinition>;
|
|
49
50
|
projections: Record<string, ProjectionDefinition>;
|
|
50
51
|
multiStreamProjections: Record<string, MultiStreamProjectionDefinition>;
|
|
51
52
|
entityProjectionExtensions: Record<string, EntityProjectionExtension[]>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type * as z from "zod";
|
|
2
2
|
import { type ZodType } from "zod";
|
|
3
3
|
import type { FeatureBuilderState } from "./feature-builder-state.js";
|
|
4
|
-
import type { AuthClaimsFn, ClaimKeyHandle, ClaimKeyType, ConfigKeyDefinition, ConfigKeyHandle, ConfigKeyType, ConfigSeedDef, DeclarativeEventMigration, EventDef, EventPiiStance, EventUpcastFn, JobDefinition, JobHandlerFn, MetricOptions, NameOrRef, NotificationDataFn, NotificationRecipientFn, NotificationTemplateFn, QualifiedEventName, SecretKeyHandle, SecretOptions, TranslationsDef } from "./types/index.js";
|
|
4
|
+
import type { AuthClaimsFn, ClaimKeyHandle, ClaimKeyType, ConfigKeyDefinition, ConfigKeyHandle, ConfigKeyType, ConfigSeedDef, DeclarativeEventMigration, EventDef, EventPiiStance, EventUpcastFn, JobDefinition, JobHandlerFn, MetricOptions, NameOrRef, NotificationDataFn, NotificationRecipientFn, NotificationTemplateFn, QualifiedEventName, SecretKeyHandle, SecretNamespaceHandle, SecretNamespaceOptions, SecretOptions, TranslationsDef } from "./types/index.js";
|
|
5
5
|
export declare function buildConfigEventsJobsMethods<TName extends string>(state: FeatureBuilderState, name: TName): {
|
|
6
6
|
config: {
|
|
7
7
|
<T extends ConfigKeyType>(keyName: string, def: ConfigKeyDefinition<T>): ConfigKeyHandle<T>;
|
|
@@ -48,6 +48,9 @@ export declare function buildConfigEventsJobsMethods<TName extends string>(state
|
|
|
48
48
|
secret(shortNameOrDefinition: string | ({
|
|
49
49
|
readonly name: string;
|
|
50
50
|
} & SecretOptions), options?: SecretOptions): SecretKeyHandle;
|
|
51
|
+
secretNamespace(shortNameOrDefinition: string | ({
|
|
52
|
+
readonly name: string;
|
|
53
|
+
} & SecretNamespaceOptions), options?: SecretNamespaceOptions): SecretNamespaceHandle;
|
|
51
54
|
claimKey<T extends ClaimKeyType>(shortNameOrDefinition: string | {
|
|
52
55
|
readonly name: string;
|
|
53
56
|
readonly type: T;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { normalizeEventPiiSubject } from "@cosmicdrift/kumiko-types/handlers";
|
|
1
|
+
import { escapeHatchGrantsProblem, normalizeEventPiiSubject, } from "@cosmicdrift/kumiko-types/handlers";
|
|
2
2
|
import { ZodObject } from "zod";
|
|
3
3
|
import { resolveName } from "./handler-helpers.js";
|
|
4
4
|
import { splitNamedDefinition, unwrapArrayForm } from "./object-form.js";
|
|
@@ -124,6 +124,10 @@ export function buildConfigEventsJobsMethods(state, name) {
|
|
|
124
124
|
throw new Error(`[Feature ${name}] r.job("${jobName}") declares { escapeHatch: { reason: "" } } — ` +
|
|
125
125
|
"the reason must be a non-empty string explaining why this job needs unfiltered database access.");
|
|
126
126
|
}
|
|
127
|
+
const jobGrantsProblem = jobOptions.escapeHatch && escapeHatchGrantsProblem(jobOptions.escapeHatch);
|
|
128
|
+
if (jobGrantsProblem) {
|
|
129
|
+
throw new Error(`[Feature ${name}] r.job("${jobName}") declares an invalid escapeHatch — ${jobGrantsProblem}`);
|
|
130
|
+
}
|
|
127
131
|
// Resolve NameOrRef(s) in trigger.on. Multi-Trigger-Form: Array
|
|
128
132
|
// wird zu Array von resolved strings, Single bleibt single string —
|
|
129
133
|
// job-runner unterscheidet anhand Array.isArray.
|
|
@@ -235,6 +239,10 @@ export function buildConfigEventsJobsMethods(state, name) {
|
|
|
235
239
|
throw new Error(`[Feature ${name}] Secret "${shortName}" already registered. ` +
|
|
236
240
|
`Secret key names must be unique per feature.`);
|
|
237
241
|
}
|
|
242
|
+
if (secretOptions.writeRoles !== undefined && secretOptions.writeRoles.length === 0) {
|
|
243
|
+
throw new Error(`[Feature ${name}] Secret "${shortName}" declares an empty writeRoles list, ` +
|
|
244
|
+
`which nobody could ever satisfy. Omit writeRoles to fall back to the secrets handler access.`);
|
|
245
|
+
}
|
|
238
246
|
// Qualified name follows the framework's "<feature>:<type>:<name>"
|
|
239
247
|
// QN convention — same pattern config / jobs / events use. toKebab
|
|
240
248
|
// handles the common input shapes ("stripe.apiKey" → "stripe-api-key")
|
|
@@ -248,6 +256,22 @@ export function buildConfigEventsJobsMethods(state, name) {
|
|
|
248
256
|
};
|
|
249
257
|
return { name: qualifiedName };
|
|
250
258
|
},
|
|
259
|
+
secretNamespace(shortNameOrDefinition, options) {
|
|
260
|
+
const [shortName, namespaceOptions] = typeof shortNameOrDefinition === "string"
|
|
261
|
+
? [shortNameOrDefinition, options]
|
|
262
|
+
: splitNamedDefinition(shortNameOrDefinition);
|
|
263
|
+
if (state.secretNamespaces[shortName]) {
|
|
264
|
+
throw new Error(`[Feature ${name}] Secret namespace "${shortName}" already registered. ` +
|
|
265
|
+
`Secret namespace names must be unique per feature.`);
|
|
266
|
+
}
|
|
267
|
+
if (namespaceOptions.writeRoles !== undefined && namespaceOptions.writeRoles.length === 0) {
|
|
268
|
+
throw new Error(`[Feature ${name}] Secret namespace "${shortName}" declares an empty writeRoles list, ` +
|
|
269
|
+
`which nobody could ever satisfy. Omit writeRoles to fall back to the secrets handler access.`);
|
|
270
|
+
}
|
|
271
|
+
const qualifiedPrefix = `${toKebab(name)}:${toKebab(shortName)}.`;
|
|
272
|
+
state.secretNamespaces[shortName] = { shortName, qualifiedPrefix, ...namespaceOptions };
|
|
273
|
+
return { prefix: qualifiedPrefix, keyFor: (suffix) => `${qualifiedPrefix}${suffix}` };
|
|
274
|
+
},
|
|
251
275
|
claimKey(shortNameOrDefinition, options) {
|
|
252
276
|
const shortName = typeof shortNameOrDefinition === "string"
|
|
253
277
|
? shortNameOrDefinition
|
|
@@ -7,6 +7,7 @@ export type ManifestConfigKey = {
|
|
|
7
7
|
readonly default: string | number | boolean | null;
|
|
8
8
|
readonly encrypted: boolean;
|
|
9
9
|
readonly computed: boolean;
|
|
10
|
+
readonly validated: boolean;
|
|
10
11
|
readonly options: readonly string[] | null;
|
|
11
12
|
readonly bounds: {
|
|
12
13
|
readonly min?: number;
|
|
@@ -29,6 +29,7 @@ export function buildManifestFromRegistry(registry, options) {
|
|
|
29
29
|
default: def.default ?? null,
|
|
30
30
|
encrypted: isEncryptedAtRest(def),
|
|
31
31
|
computed: def.computed !== undefined,
|
|
32
|
+
validated: def.validate !== undefined,
|
|
32
33
|
options: def.options ?? null,
|
|
33
34
|
bounds: def.bounds ?? null,
|
|
34
35
|
pattern: def.pattern ?? null,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { escapeHatchGrantsProblem } from "@cosmicdrift/kumiko-types/handlers";
|
|
1
2
|
import { WEBSOCKET_MAX_CONNECTIONS_PER_USER_LIMIT, WEBSOCKET_MAX_PAYLOAD_BYTES, WEBSOCKET_ROUTE_PATH_PREFIX, } from "@cosmicdrift/kumiko-types/websocket-route";
|
|
2
3
|
import { bindHookEscapeHatchGrant } from "../pipeline/system-identity-switch.js";
|
|
3
4
|
import { LifecycleHookTypes } from "./constants.js";
|
|
@@ -99,7 +100,10 @@ export function buildUiExtensionsMethods(state, name) {
|
|
|
99
100
|
`Nav ids must be unique per feature — remove the standalone ` +
|
|
100
101
|
`r.nav("${navDefinition.id}", ...) call or the screen's inline nav.`);
|
|
101
102
|
}
|
|
102
|
-
|
|
103
|
+
// Explicit `undefined` values (e.g. `icon: cond ? "x" : undefined`) would trip
|
|
104
|
+
// buildAppSchema's JSON-safety check, so strip them for every nav path.
|
|
105
|
+
// @cast-boundary engine-bridge — filtering undefined keeps the NavDefinition shape.
|
|
106
|
+
state.navs[navDefinition.id] = Object.fromEntries(Object.entries(navDefinition).filter(([, value]) => value !== undefined));
|
|
103
107
|
}
|
|
104
108
|
return {
|
|
105
109
|
hook(type, target, fn, options) {
|
|
@@ -114,6 +118,10 @@ export function buildUiExtensionsMethods(state, name) {
|
|
|
114
118
|
throw new Error(`[Feature ${name}] r.hook("${type}", ...) declares { escapeHatch: { reason: "" } } — ` +
|
|
115
119
|
"the reason must be a non-empty string explaining why this hook switches identity to SYSTEM.");
|
|
116
120
|
}
|
|
121
|
+
const hookGrantsProblem = options?.escapeHatch && escapeHatchGrantsProblem(options.escapeHatch);
|
|
122
|
+
if (hookGrantsProblem) {
|
|
123
|
+
throw new Error(`[Feature ${name}] r.hook("${type}", ...) declares an invalid escapeHatch — ${hookGrantsProblem}`);
|
|
124
|
+
}
|
|
117
125
|
// Wrapped once for both branches below; validation hooks stay unwrapped (no context).
|
|
118
126
|
const hookLabel = `${type} hook of feature "${name}"`;
|
|
119
127
|
const wrapped = type === "validation"
|
|
@@ -208,6 +216,10 @@ export function buildUiExtensionsMethods(state, name) {
|
|
|
208
216
|
throw new Error(`[Feature ${name}] r.useExtension("${extensionName}", "${resolvedEntityName}", ...) declares an invalid { escapeHatch } — ` +
|
|
209
217
|
`must be { reason: "<non-empty string>" } explaining why this usage needs unfiltered db access.`);
|
|
210
218
|
}
|
|
219
|
+
const extGrantsProblem = escapeHatchGrantsProblem(escapeHatch);
|
|
220
|
+
if (extGrantsProblem) {
|
|
221
|
+
throw new Error(`[Feature ${name}] r.useExtension("${extensionName}", "${resolvedEntityName}", ...) declares an invalid { escapeHatch } — ${extGrantsProblem}`);
|
|
222
|
+
}
|
|
211
223
|
}
|
|
212
224
|
state.extensionUsages.push({
|
|
213
225
|
extensionName,
|
|
@@ -337,15 +349,12 @@ export function buildUiExtensionsMethods(state, name) {
|
|
|
337
349
|
// Sugar for the common "one nav entry pointing at this screen"
|
|
338
350
|
// case — synthesizes id/screen from the screen's own id. Beyond
|
|
339
351
|
// label/icon/parent/order, declare a standalone r.nav() instead.
|
|
340
|
-
// Optional fields stay absent rather than explicitly undefined —
|
|
341
|
-
// buildAppSchema's JSON-safety check flags undefined values. Same
|
|
342
|
-
// pattern as r.contentCollection() below.
|
|
343
352
|
registerNav({
|
|
344
353
|
id: definition.id,
|
|
345
354
|
label: definition.nav.label,
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
355
|
+
icon: definition.nav.icon,
|
|
356
|
+
parent: definition.nav.parent,
|
|
357
|
+
order: definition.nav.order,
|
|
349
358
|
screen: `${name}:screen:${definition.id}`,
|
|
350
359
|
});
|
|
351
360
|
}
|
|
@@ -359,26 +368,20 @@ export function buildUiExtensionsMethods(state, name) {
|
|
|
359
368
|
`Collection ids must be unique per feature.`);
|
|
360
369
|
}
|
|
361
370
|
// registerNav owns the kebab + collision checks, including collisions
|
|
362
|
-
// with a plain r.nav() of the same id.
|
|
363
|
-
// rather than explicitly undefined — buildAppSchema's JSON-safety check
|
|
364
|
-
// flags undefined values.
|
|
371
|
+
// with a plain r.nav() of the same id.
|
|
365
372
|
registerNav({
|
|
366
373
|
id: definition.id,
|
|
367
374
|
label: definition.nav.label,
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
375
|
+
icon: definition.nav.icon,
|
|
376
|
+
parent: definition.nav.parent,
|
|
377
|
+
order: definition.nav.order,
|
|
371
378
|
// Nav visibility follows the collection's access unless the caller
|
|
372
379
|
// overrode it — a node the handler would refuse has no business in
|
|
373
380
|
// the sidebar.
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
...(definition.nav.createAction !== undefined && {
|
|
379
|
-
createAction: definition.nav.createAction,
|
|
380
|
-
}),
|
|
381
|
-
...(definition.nav.actions !== undefined && { actions: definition.nav.actions }),
|
|
381
|
+
access: definition.nav.access ?? definition.access,
|
|
382
|
+
workspaces: definition.nav.workspaces,
|
|
383
|
+
createAction: definition.nav.createAction,
|
|
384
|
+
actions: definition.nav.actions,
|
|
382
385
|
// The tree children come from a runtime provider keyed on this QN —
|
|
383
386
|
// a collection without it would render as an empty leaf.
|
|
384
387
|
provider: true,
|
package/dist/engine/index.d.ts
CHANGED
|
@@ -73,7 +73,7 @@ export { describeWorkflowStepError } from "./steps/describe-workflow-step-error.
|
|
|
73
73
|
export { ANONYMOUS_ROLE, ANONYMOUS_USER_ID, createAnonymousUser, createSystemUser, SYSTEM_ROLE, SYSTEM_USER_ID, } from "./system-user.js";
|
|
74
74
|
export { type EffectiveFeaturesResolver, findTierResolverUsage, isTierResolverPlugin, TENANT_TIER_RESOLVER_EXT, type TierResolverPlugin, type TrialGate, } from "./tier-resolver-extension.js";
|
|
75
75
|
export { isSystemTenant, isUuid, parseTenantId, SYSTEM_TENANT_ID } from "./types/identifiers.js";
|
|
76
|
-
export type { AccessRule, ActionFormScreenDefinition, ActiveMembership, ActiveMembershipRejection, ActiveMembershipResult, AgentExposure, AgentHandlerHints, AgentRisk, AppContext, AppendEventArgs, AppendEventFn, AuthClaimsContext, AuthClaimsFn, AuthClaimsHookDef, BelongsToRelation, BigIntFieldDef, BooleanFieldDef, CamelToKebab, ClaimKeyDefinition, ClaimKeyHandle, ClaimKeyJsType, ClaimKeyType, ConcurrencyMode, ConfigAccessor, ConfigAccessorFactory, ConfigBacking, ConfigCascade, ConfigCascadeLevel, ConfigDefinition, ConfigEditScreenDefinition, ConfigKeyAccess, ConfigKeyDefinition, ConfigKeyHandle, ConfigKeyType, ConfigMask, ConfigResolver, ConfigScope, ConfigSecretsReader, ConfigSeedDef, ConfigStoredRow, ConfigStoredRowWithSource, ConfigValue, ConfigValueSource, ConfigValueWithSource, ContentCollectionDefinition, CreateSeedOptions, CreateTenantSeedOptions, CreateUserSeedOptions, CustomScreenDefinition, CustomScreenRoute, DashboardChartPanel, DashboardCustomPanel, DashboardFeedPanel, DashboardFilterDefinition, DashboardListPanel, DashboardPanelDefinition, DashboardPanelVisibility, DashboardProgressListPanel, DashboardScreenDefinition, DashboardScreenPanel, DashboardStatGroupPanel, DashboardStatPanel, DateFieldDef, DecimalFieldDef, DeleteContext, DeriveContext, DerivedFieldDef, DerivedFieldsMap, DerivedValueType, EditExtensionSection, EditFieldSpec, EditFieldsSection, EditLayout, EditRelatedListSection, EditSectionSpec, EntityDefinition, EntityEditScreenDefinition, EntityId, EntityListExpandableRow, EntityListScreenDefinition, EntityRef, EntityRelations, EscapeHatchAuditSink, EscapeHatchDeclaration, EscapeHatchKind, EscapeHatchReporter, EscapeHatchTarget, EscapeHatchUseEvent, EventDef, ExtensionOptionsArgs, ExtensionOptionsFor, FeatureDefinition, FeatureRegistrar, FieldAccess, FieldCondition, FieldDefinition, FieldFormatRegistry, FieldIconKey, FieldRenderer, FileFieldDef, FilesFieldDef, Findability, FormatSpec, FormWidth, HandlerContext, HasManyRelation, HookMap, ImageFieldDef, ImagesFieldDef, JobBackoff, JobBackoffStrategy, JobContext, JobDefinition, JobHandlerFn, JobTrigger, JobTriggerEvent, JsonbFieldDef, KumikoEntityTypeMap, KumikoEventTypeMap, KumikoExtensionOptionsMap, KumikoHandlerPayloadMap, KumikoHandlerResultMap, LifecycleHookType, ListColumnSpec, ListFacetSpec, ListPaginationMode, ListSortDir, ListSortSpec, LongTextFindability, ManyToManyRelation, MemberReader, MetricNavigate, MetricSpec, MspErrorMode, MspErrorPolicy, MultiSelectFieldDef, MultiStreamApplyFn, MultiStreamProjectionDefinition, NameOrRef, NavDefinition, NavIconKey, NotificationDataFn, NotificationDefinition, NotificationRecipientFn, NotificationTemplateFn, NotifyFactory, NotifyFn, NotifyOptions, NotifyPriority, NumberFieldDef, OnDeleteStrategy, OpenToAllAccessRule, OpenToAllDeclaration, OpenToAllPersonalData, ParentRefDef, PersonalAnnotations, PersonalAnnotationsLongText, PersonalAnnotationsNoFind, PersonalSubject, PlatformComponent, PluralForms, PostDeleteHookFn, PostSaveHookFn, PreDeleteHookFn, PreQueryHookFn, PreSaveHookFn, ProjectionDefinition, ProjectionDetailScreenDefinition, ProjectionListScreenDefinition, ProjectionTable, QualifiedEventName, QueryEvent, QueryHandlerDef, QueryHandlerFn, RateLimitPer, RecordHeaderSpec, RecordHeaderSubtitlePart, ReferenceDataDef, Registry, RelationDefinition, ResolvedPiiFlags, RetentionDef, RoleAccessPersonalData, RoleAccessRule, RowAction, RowActionNavigate, RowActionNavigateBase, RowActionWriteHandler, RowFieldExtractor, SaveContext, ScreenDefinition, ScreenFilter, ScreenFilterOp, ScreenNavSugar, ScreenSlots, SecretKeyHandle, SecretMintConfirmStep, SecretMintScreenDefinition, SecretReveal, SecretRevealField, SecretsEditScreenDefinition, SecretsEditSection, SelectFieldDef, SessionUser, SessionUserOrigin, StreamHandlerDef, StreamHandlerFn, Subscribe, TargetRef, TenantId, TextFieldDef, ToolbarAction, TranslationEntry, TranslationKeys, TranslationsDef, TranslationValue, TreeAction, TreeActionDef, TreeActionsHandle, TreeChildrenSubscribe, TreeNode, TreeNodeState, UnitKey, UnsafeAppendEventFn, ValidationError, ValidationHookFn, WorkspaceDefinition, WriteEvent, WriteHandlerDef, WriteHandlerFn, WriteResult, } from "./types/index.js";
|
|
76
|
+
export type { AccessRule, ActionFormScreenDefinition, ActiveMembership, ActiveMembershipRejection, ActiveMembershipResult, AgentExposure, AgentHandlerHints, AgentRisk, AppContext, AppendEventArgs, AppendEventFn, AuthClaimsContext, AuthClaimsFn, AuthClaimsHookDef, BelongsToRelation, BigIntFieldDef, BooleanFieldDef, CamelToKebab, ClaimKeyDefinition, ClaimKeyHandle, ClaimKeyJsType, ClaimKeyType, ConcurrencyMode, ConfigAccessor, ConfigAccessorFactory, ConfigBacking, ConfigCascade, ConfigCascadeLevel, ConfigDefinition, ConfigEditScreenDefinition, ConfigKeyAccess, ConfigKeyDefinition, ConfigKeyHandle, ConfigKeyType, ConfigMask, ConfigResolver, ConfigScope, ConfigSecretsReader, ConfigSeedDef, ConfigStoredRow, ConfigStoredRowWithSource, ConfigValidateContext, ConfigValidateFn, ConfigValue, ConfigValueSource, ConfigValueWithSource, ContentCollectionDefinition, CreateSeedOptions, CreateTenantSeedOptions, CreateUserSeedOptions, CustomScreenDefinition, CustomScreenRoute, DashboardChartPanel, DashboardCustomPanel, DashboardFeedPanel, DashboardFilterDefinition, DashboardListPanel, DashboardPanelDefinition, DashboardPanelVisibility, DashboardProgressListPanel, DashboardScreenDefinition, DashboardScreenPanel, DashboardStatGroupPanel, DashboardStatPanel, DateFieldDef, DecimalFieldDef, DeleteContext, DeriveContext, DerivedFieldDef, DerivedFieldsMap, DerivedValueType, EditExtensionSection, EditFieldSpec, EditFieldsSection, EditLayout, EditRelatedListSection, EditSectionSpec, EntityDefinition, EntityEditScreenDefinition, EntityId, EntityListExpandableRow, EntityListScreenDefinition, EntityRef, EntityRelations, EscapeHatchAuditSink, EscapeHatchDeclaration, EscapeHatchKind, EscapeHatchReporter, EscapeHatchTarget, EscapeHatchUseEvent, EventDef, ExtensionOptionsArgs, ExtensionOptionsFor, FeatureDefinition, FeatureRegistrar, FieldAccess, FieldCondition, FieldDefinition, FieldFormatRegistry, FieldIconKey, FieldRenderer, FileFieldDef, FilesFieldDef, Findability, FormatSpec, FormWidth, HandlerContext, HasManyRelation, HookMap, ImageFieldDef, ImagesFieldDef, JobBackoff, JobBackoffStrategy, JobContext, JobDefinition, JobHandlerFn, JobTrigger, JobTriggerEvent, JsonbFieldDef, KumikoEntityTypeMap, KumikoEventTypeMap, KumikoExtensionOptionsMap, KumikoHandlerPayloadMap, KumikoHandlerResultMap, LifecycleHookType, ListColumnSpec, ListFacetSpec, ListPaginationMode, ListSortDir, ListSortSpec, LongTextFindability, ManyToManyRelation, MemberReader, MetricNavigate, MetricSpec, MspErrorMode, MspErrorPolicy, MultiSelectFieldDef, MultiStreamApplyFn, MultiStreamProjectionDefinition, NameOrRef, NavDefinition, NavIconKey, NotificationDataFn, NotificationDefinition, NotificationRecipientFn, NotificationTemplateFn, NotifyFactory, NotifyFn, NotifyOptions, NotifyPriority, NumberFieldDef, OnDeleteStrategy, OpenToAllAccessRule, OpenToAllDeclaration, OpenToAllPersonalData, ParentRefDef, PersonalAnnotations, PersonalAnnotationsLongText, PersonalAnnotationsNoFind, PersonalSubject, PlatformComponent, PluralForms, PostDeleteHookFn, PostSaveHookFn, PreDeleteHookFn, PreQueryHookFn, PreSaveHookFn, ProjectionDefinition, ProjectionDetailScreenDefinition, ProjectionListScreenDefinition, ProjectionTable, QualifiedEventName, QueryEvent, QueryHandlerDef, QueryHandlerFn, RateLimitPer, RecordHeaderSpec, RecordHeaderSubtitlePart, ReferenceDataDef, Registry, RelationDefinition, ResolvedPiiFlags, RetentionDef, RoleAccessPersonalData, RoleAccessRule, RowAction, RowActionNavigate, RowActionNavigateBase, RowActionWriteHandler, RowFieldExtractor, SaveContext, ScreenDefinition, ScreenFilter, ScreenFilterOp, ScreenNavSugar, ScreenSlots, SecretKeyDefinition, SecretKeyHandle, SecretMintConfirmStep, SecretMintScreenDefinition, SecretNamespaceDefinition, SecretNamespaceHandle, SecretNamespaceOptions, SecretReveal, SecretRevealField, SecretsEditScreenDefinition, SecretsEditSection, SelectFieldDef, SessionUser, SessionUserOrigin, StreamHandlerDef, StreamHandlerFn, Subscribe, TargetRef, TenantId, TextFieldDef, ToolbarAction, TranslationEntry, TranslationKeys, TranslationsDef, TranslationValue, TreeAction, TreeActionDef, TreeActionsHandle, TreeChildrenSubscribe, TreeNode, TreeNodeState, UnitKey, UnsafeAppendEventFn, ValidationError, ValidationHookFn, WorkspaceDefinition, WriteEvent, WriteHandlerDef, WriteHandlerFn, WriteResult, } from "./types/index.js";
|
|
77
77
|
export { DEFAULT_CURRENCIES, DEFAULT_LOCALES, HookPhases, isAgentVisibleScreen, isOpenToAllGranted, resolveAgentExposure, } from "./types/index.js";
|
|
78
78
|
export type { AwaitedEventType, PipelineBuildCtx, PipelineCtx, PipelineDef, StepBuilder, StepDef, StepFailureStrategy, StepInstance, StepKind, StepNamespace, StepResolver, } from "./types/step.js";
|
|
79
79
|
export { runValidation } from "./validation.js";
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// the Designer/LLM relies on.
|
|
15
15
|
import { aiClassifySchema, aiExtractSchema, aiGenerateSchema, authClaimsSchema, defineEventSchema, hookSchema, httpRouteSchema, jobSchema, multiStreamProjectionSchema, notificationSchema, projectionSchema, queryHandlerSchema, screenSchema, streamHandlerSchema, writeHandlerSchema, } from "./mixed-schemas.js";
|
|
16
16
|
import { envSchemaSchema, exposesApiSchema, extendsRegistrarSchema, treeActionsSchema, unknownSchema, usesApiSchema, } from "./opaque-schemas.js";
|
|
17
|
-
import { claimKeySchema, configSchema, describeSchema, entitySchema, metricSchema, navSchema, optionalRequiresSchema, readsConfigSchema, referenceDataSchema, relationSchema, requiresSchema, secretSchema, systemScopeSchema, toggleableSchema, translationsSchema, uiHintsSchema, useExtensionSchema, workspaceSchema, } from "./static-schemas.js";
|
|
17
|
+
import { claimKeySchema, configSchema, describeSchema, entitySchema, metricSchema, navSchema, optionalRequiresSchema, readsConfigSchema, referenceDataSchema, relationSchema, requiresSchema, secretNamespaceSchema, secretSchema, systemScopeSchema, toggleableSchema, translationsSchema, uiHintsSchema, useExtensionSchema, workspaceSchema, } from "./static-schemas.js";
|
|
18
18
|
export const PATTERN_LIBRARY = {
|
|
19
19
|
requires: requiresSchema,
|
|
20
20
|
optionalRequires: optionalRequiresSchema,
|
|
@@ -31,6 +31,7 @@ export const PATTERN_LIBRARY = {
|
|
|
31
31
|
translations: translationsSchema,
|
|
32
32
|
metric: metricSchema,
|
|
33
33
|
secret: secretSchema,
|
|
34
|
+
secretNamespace: secretNamespaceSchema,
|
|
34
35
|
claimKey: claimKeySchema,
|
|
35
36
|
referenceData: referenceDataSchema,
|
|
36
37
|
useExtension: useExtensionSchema,
|
|
@@ -14,6 +14,7 @@ export declare const configSchema: PatternFormSchema;
|
|
|
14
14
|
export declare const translationsSchema: PatternFormSchema;
|
|
15
15
|
export declare const metricSchema: PatternFormSchema;
|
|
16
16
|
export declare const secretSchema: PatternFormSchema;
|
|
17
|
+
export declare const secretNamespaceSchema: PatternFormSchema;
|
|
17
18
|
export declare const claimKeySchema: PatternFormSchema;
|
|
18
19
|
export declare const referenceDataSchema: PatternFormSchema;
|
|
19
20
|
export declare const useExtensionSchema: PatternFormSchema;
|
|
@@ -351,10 +351,53 @@ export const secretSchema = {
|
|
|
351
351
|
path: "options.scope",
|
|
352
352
|
label: { en: "Scope", de: "Geltungsbereich" },
|
|
353
353
|
input: "select",
|
|
354
|
-
options: [
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
354
|
+
options: [{ value: "tenant", label: { en: "Tenant" } }],
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
path: "options.writeRoles",
|
|
358
|
+
label: { en: "Write roles", de: "Schreibrollen" },
|
|
359
|
+
hint: {
|
|
360
|
+
en: "Roles allowed to set or delete. Leave empty to rely on the secrets handler access alone.",
|
|
361
|
+
de: "Rollen, die setzen oder löschen dürfen. Leer lassen, dann entscheidet allein der Handler-Zugriff.",
|
|
362
|
+
},
|
|
363
|
+
input: "string-list",
|
|
364
|
+
},
|
|
365
|
+
],
|
|
366
|
+
};
|
|
367
|
+
export const secretNamespaceSchema = {
|
|
368
|
+
kind: "secretNamespace",
|
|
369
|
+
label: { en: "Secret namespace", de: "Secret-Namespace" },
|
|
370
|
+
summary: { en: "Family of tenant-scoped secrets with a runtime-chosen name." },
|
|
371
|
+
category: "meta",
|
|
372
|
+
editability: "static",
|
|
373
|
+
fields: [
|
|
374
|
+
{
|
|
375
|
+
path: "shortName",
|
|
376
|
+
label: { en: "Short name", de: "Kurzname" },
|
|
377
|
+
input: "text",
|
|
378
|
+
required: true,
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
path: "options.label",
|
|
382
|
+
label: { en: "UI label (i18n)", de: "UI-Label (i18n)" },
|
|
383
|
+
hint: { en: "{ en: 'Webhook token', de: 'Webhook-Token' }" },
|
|
384
|
+
input: "json-readonly",
|
|
385
|
+
required: true,
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
path: "options.scope",
|
|
389
|
+
label: { en: "Scope", de: "Geltungsbereich" },
|
|
390
|
+
input: "select",
|
|
391
|
+
options: [{ value: "tenant", label: { en: "Tenant" } }],
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
path: "options.writeRoles",
|
|
395
|
+
label: { en: "Write roles", de: "Schreibrollen" },
|
|
396
|
+
hint: {
|
|
397
|
+
en: "Roles allowed to set or delete. Leave empty to rely on the secrets handler access alone.",
|
|
398
|
+
de: "Rollen, die setzen oder löschen dürfen. Leer lassen, dann entscheidet allein der Handler-Zugriff.",
|
|
399
|
+
},
|
|
400
|
+
input: "string-list",
|
|
358
401
|
},
|
|
359
402
|
],
|
|
360
403
|
};
|
|
@@ -125,6 +125,13 @@ export function buildRegistryFacade(state) {
|
|
|
125
125
|
getSecretKey(qualifiedName) {
|
|
126
126
|
return state.secretKeyMap.get(qualifiedName);
|
|
127
127
|
},
|
|
128
|
+
findSecretNamespace(key) {
|
|
129
|
+
for (const [prefix, def] of state.secretNamespaceMap) {
|
|
130
|
+
if (key.startsWith(prefix))
|
|
131
|
+
return def;
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
},
|
|
128
135
|
getConfigKey(qualifiedKey) {
|
|
129
136
|
return state.configKeyMap.get(qualifiedKey);
|
|
130
137
|
},
|
|
@@ -221,6 +221,13 @@ export function populateMetricsAndSecrets(state, feature) {
|
|
|
221
221
|
}
|
|
222
222
|
state.secretKeyMap.set(def.qualifiedName, def);
|
|
223
223
|
}
|
|
224
|
+
for (const def of Object.values(feature.secretNamespaces ?? {})) {
|
|
225
|
+
if (state.secretNamespaceMap.has(def.qualifiedPrefix)) {
|
|
226
|
+
throw new Error(`[Kumiko Secrets] Secret namespace "${def.qualifiedPrefix}" registered multiple times. ` +
|
|
227
|
+
"Namespace prefixes must be globally unique across features.");
|
|
228
|
+
}
|
|
229
|
+
state.secretNamespaceMap.set(def.qualifiedPrefix, def);
|
|
230
|
+
}
|
|
224
231
|
}
|
|
225
232
|
// Explicit + multi-stream projections (source-entity indexed) + store tables +
|
|
226
233
|
// unmanaged tables (both cross-feature-uniqueness-by-physical-name guarded).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type QnType, qualifyEntityName } from "./qualified-name.js";
|
|
2
|
-
import type { AuthClaimsHookDef, ClaimKeyDefinition, ConfigKeyDefinition, ConfigSeedDef, EntityDefinition, EntityProjectionExtension, EventDef, EventUpcastFn, ExtensionSelectorPanel, FeatureDefinition, FeatureMetricDef, HookPhase, JobDefinition, MultiStreamProjectionDefinition, NavDefinition, NotificationDefinition, OwnedFn, PhasedHook, PostDeleteHookFn, PostQueryHookFn, PostSaveHookFn, PreDeleteHookFn, PreQueryHookFn, PreSaveHookFn, ProjectionDefinition, QueryHandlerDef, ReferenceDataDef, RegistrarExtensionDef, RegistrarExtensionRegistration, RelationDefinition, ScreenDefinition, SearchPayloadContributorFn, SecretKeyDefinition, StoreTableDef, StreamHandlerDef, TranslationValue, TreeActionDef, WorkspaceDefinition, WriteHandlerDef } from "./types/index.js";
|
|
2
|
+
import type { AuthClaimsHookDef, ClaimKeyDefinition, ConfigKeyDefinition, ConfigSeedDef, EntityDefinition, EntityProjectionExtension, EventDef, EventUpcastFn, ExtensionSelectorPanel, FeatureDefinition, FeatureMetricDef, HookPhase, JobDefinition, MultiStreamProjectionDefinition, NavDefinition, NotificationDefinition, OwnedFn, PhasedHook, PostDeleteHookFn, PostQueryHookFn, PostSaveHookFn, PreDeleteHookFn, PreQueryHookFn, PreSaveHookFn, ProjectionDefinition, QueryHandlerDef, ReferenceDataDef, RegistrarExtensionDef, RegistrarExtensionRegistration, RelationDefinition, ScreenDefinition, SearchPayloadContributorFn, SecretKeyDefinition, SecretNamespaceDefinition, StoreTableDef, StreamHandlerDef, TranslationValue, TreeActionDef, WorkspaceDefinition, WriteHandlerDef } from "./types/index.js";
|
|
3
3
|
export type IncomingRelation = {
|
|
4
4
|
sourceEntity: string;
|
|
5
5
|
relationName: string;
|
|
@@ -54,6 +54,7 @@ export type RegistryState = {
|
|
|
54
54
|
readonly featureName: string;
|
|
55
55
|
}>;
|
|
56
56
|
secretKeyMap: Map<string, SecretKeyDefinition>;
|
|
57
|
+
secretNamespaceMap: Map<string, SecretNamespaceDefinition>;
|
|
57
58
|
projectionMap: Map<string, ProjectionDefinition>;
|
|
58
59
|
projectionsBySource: Map<string, ProjectionDefinition[]>;
|
|
59
60
|
multiStreamProjectionMap: Map<string, MultiStreamProjectionDefinition>;
|
|
@@ -122,6 +122,7 @@ export function createInitialState() {
|
|
|
122
122
|
mergedTranslations: {},
|
|
123
123
|
metricMap: new Map(),
|
|
124
124
|
secretKeyMap: new Map(),
|
|
125
|
+
secretNamespaceMap: new Map(),
|
|
125
126
|
projectionMap: new Map(),
|
|
126
127
|
projectionsBySource: new Map(),
|
|
127
128
|
multiStreamProjectionMap: new Map(),
|
|
@@ -16,7 +16,7 @@ export declare function validateRequiredFeatures(state: RegistryState, features:
|
|
|
16
16
|
export declare function resolveNotificationTriggersAndRegisterHooks(state: RegistryState): void;
|
|
17
17
|
export declare function validateLifecycleHookTargets(state: RegistryState): void;
|
|
18
18
|
export declare function validateEntityHookTargets(state: RegistryState, features: readonly FeatureDefinition[]): void;
|
|
19
|
-
export declare function
|
|
19
|
+
export declare function resolveJobTriggers(state: RegistryState): void;
|
|
20
20
|
export declare function validateLiveEntities(state: RegistryState): void;
|
|
21
21
|
export declare function validateBootGates(state: RegistryState): void;
|
|
22
22
|
export declare function validateJobBackoff(state: RegistryState): void;
|
|
@@ -544,29 +544,51 @@ export function validateEntityHookTargets(state, features) {
|
|
|
544
544
|
}
|
|
545
545
|
}
|
|
546
546
|
}
|
|
547
|
-
export function
|
|
548
|
-
//
|
|
549
|
-
//
|
|
550
|
-
//
|
|
551
|
-
//
|
|
552
|
-
//
|
|
553
|
-
//
|
|
554
|
-
// auch wenn nur einer fehlt fail-fast.
|
|
547
|
+
export function resolveJobTriggers(state) {
|
|
548
|
+
// Job event triggers must reference an existing write/query handler OR an
|
|
549
|
+
// existing r.defineEvent registration. The latter is delivered async via the
|
|
550
|
+
// job-trigger event-consumer (server.ts), not the synchronous write-handler
|
|
551
|
+
// dispatch path — see createJobTriggerEventConsumer in
|
|
552
|
+
// pipeline/system-hooks.ts. Short names are qualified against the job's own
|
|
553
|
+
// feature and written back, because the job runner matches by exact QN.
|
|
555
554
|
const allHandlers = allHandlerQns(state);
|
|
556
555
|
for (const [jobName, jobDef] of state.jobMap) {
|
|
557
556
|
if (!("on" in jobDef.trigger))
|
|
558
557
|
continue;
|
|
558
|
+
const featureName = state.jobFeatureMap.get(jobName);
|
|
559
559
|
const triggerOn = jobDef.trigger.on;
|
|
560
|
-
const
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
560
|
+
const resolveTrigger = (trigger) => resolveJobTriggerName(state, allHandlers, jobName, featureName, resolveName(trigger));
|
|
561
|
+
const resolvedOn = isNameOrRefList(triggerOn)
|
|
562
|
+
? triggerOn.map(resolveTrigger)
|
|
563
|
+
: resolveTrigger(triggerOn);
|
|
564
|
+
state.jobMap.set(jobName, { ...jobDef, trigger: { ...jobDef.trigger, on: resolvedOn } });
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
// Array.isArray does not narrow a readonly array out of a union.
|
|
568
|
+
function isNameOrRefList(on) {
|
|
569
|
+
return Array.isArray(on);
|
|
570
|
+
}
|
|
571
|
+
// An already-qualified name (e.g. a misspelled own-feature QN) is not a
|
|
572
|
+
// short name; qualify() rejects it, and the "no handler" error below names it.
|
|
573
|
+
function qualifyShortTriggerName(featureName, type, trigger) {
|
|
574
|
+
try {
|
|
575
|
+
return [qualify(featureName, type, trigger)];
|
|
569
576
|
}
|
|
577
|
+
catch {
|
|
578
|
+
return [];
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
function resolveJobTriggerName(state, allHandlers, jobName, featureName, trigger) {
|
|
582
|
+
if (allHandlers.has(trigger) || state.eventMap.has(trigger))
|
|
583
|
+
return trigger;
|
|
584
|
+
const candidates = featureName === undefined
|
|
585
|
+
? []
|
|
586
|
+
: ["write", "query", "event"].flatMap((type) => qualifyShortTriggerName(featureName, type, trigger));
|
|
587
|
+
const match = candidates.find((qn) => allHandlers.has(qn) || state.eventMap.has(qn));
|
|
588
|
+
if (match !== undefined)
|
|
589
|
+
return match;
|
|
590
|
+
throw new Error(`Job "${jobName}" triggers on "${trigger}" but no handler or event with that name exists. ` +
|
|
591
|
+
`Tried: ${[trigger, ...candidates].map((name) => `"${name}"`).join(", ")}`);
|
|
570
592
|
}
|
|
571
593
|
export function validateLiveEntities(state) {
|
|
572
594
|
for (const [handlerQn, handler] of state.queryHandlerMap) {
|