@cosmicdrift/kumiko-framework 0.363.0 → 0.364.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +307 -0
- package/agents/examples/access-control.md +355 -0
- package/agents/examples/anonymous-access-multitenant.md +443 -0
- package/agents/examples/anonymous-access.md +300 -0
- package/agents/examples/apex-landing.md +528 -0
- package/agents/examples/apex-surface-auth.md +355 -0
- package/agents/examples/api-worker-split.md +434 -0
- package/agents/examples/auth-claims.md +351 -0
- package/agents/examples/auth-foundation-providers.md +155 -0
- package/agents/examples/auth-mfa.md +323 -0
- package/agents/examples/basic-entity.md +420 -0
- package/agents/examples/boot-check.md +122 -0
- package/agents/examples/chat-channels.md +254 -0
- package/agents/examples/compliance-profiles-demo.md +307 -0
- package/agents/examples/cross-feature-events.md +317 -0
- package/agents/examples/crypto-shredding-hr.md +314 -0
- package/agents/examples/currencies-global.md +609 -0
- package/agents/examples/currencies-per-tenant.md +453 -0
- package/agents/examples/custom-fields-basic.md +314 -0
- package/agents/examples/custom-handlers.md +397 -0
- package/agents/examples/delivery-notifications.md +432 -0
- package/agents/examples/designer-demo.md +86 -0
- package/agents/examples/e2e-generator.md +211 -0
- package/agents/examples/embedded-entity-form.md +598 -0
- package/agents/examples/embedded.md +649 -0
- package/agents/examples/encrypted-tenant-config.md +405 -0
- package/agents/examples/error-contract.md +539 -0
- package/agents/examples/event-sourcing.md +1287 -0
- package/agents/examples/feature-toggles.md +263 -0
- package/agents/examples/field-access.md +271 -0
- package/agents/examples/files-post-processing.md +433 -0
- package/agents/examples/i18n.md +166 -0
- package/agents/examples/idempotency.md +179 -0
- package/agents/examples/inbound-mail-triage.md +315 -0
- package/agents/examples/lane-routing.md +246 -0
- package/agents/examples/ledger-banking.md +214 -0
- package/agents/examples/ledger-invoicing.md +235 -0
- package/agents/examples/legal-pages.md +393 -0
- package/agents/examples/lifecycle-hooks.md +368 -0
- package/agents/examples/managed-config.md +428 -0
- package/agents/examples/managed-pages.md +353 -0
- package/agents/examples/notes-history-basic.md +345 -0
- package/agents/examples/ownership.md +396 -0
- package/agents/examples/pdf-audit-export.md +270 -0
- package/agents/examples/pipeline-basics.md +792 -0
- package/agents/examples/public-share-token.md +351 -0
- package/agents/examples/rate-limiting.md +211 -0
- package/agents/examples/realtime-sse.md +187 -0
- package/agents/examples/record-detail-layout.md +297 -0
- package/agents/examples/reference-data.md +151 -0
- package/agents/examples/relations.md +280 -0
- package/agents/examples/screens-nav.md +353 -0
- package/agents/examples/search.md +233 -0
- package/agents/examples/secrets-demo.md +417 -0
- package/agents/examples/security-baseline.md +187 -0
- package/agents/examples/seed-migration.md +99 -0
- package/agents/examples/session-revocation.md +259 -0
- package/agents/examples/state-machine.md +920 -0
- package/agents/examples/tags-basic.md +401 -0
- package/agents/examples/tenant-isolation.md +173 -0
- package/agents/examples/tenant-settings.md +275 -0
- package/agents/examples/tier-admin.md +347 -0
- package/agents/examples/timezones.md +366 -0
- package/agents/examples/user-data-rights.md +410 -0
- package/agents/examples/user-profile.md +226 -0
- package/agents/examples/webhook-step.md +597 -0
- package/agents/examples/wizard-form.md +379 -0
- package/agents/examples/workflow-engine.md +1731 -0
- package/agents/examples/writeform-section.md +300 -0
- package/agents/examples.md +74 -0
- package/agents/features.md +768 -0
- package/agents/widgets.md +181 -0
- package/dist/api/auth-middleware.d.ts +2 -0
- package/dist/api/auth-middleware.js +7 -0
- package/dist/api/auth-routes.js +5 -3
- package/dist/api/extra-route.d.ts +3 -0
- package/dist/api/index.d.ts +2 -2
- package/dist/api/index.js +1 -1
- package/dist/api/routes.js +46 -17
- package/dist/api/server.d.ts +2 -1
- package/dist/api/server.js +18 -6
- package/dist/api/session-cookies.d.ts +1 -0
- package/dist/api/session-cookies.js +20 -5
- package/dist/api/sse-route.js +16 -1
- package/dist/bun-db/index.d.ts +1 -1
- package/dist/bun-db/index.js +1 -1
- package/dist/bun-db/query.d.ts +8 -3
- package/dist/bun-db/query.js +27 -7
- package/dist/db/collect-table-metas.js +11 -7
- package/dist/db/eagerload.js +2 -2
- package/dist/db/event-store-executor-context.d.ts +5 -6
- package/dist/db/event-store-executor-context.js +24 -22
- package/dist/db/event-store-executor-read.js +31 -16
- package/dist/db/event-store-executor-write.js +18 -20
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/lock-guard.d.ts +4 -0
- package/dist/db/lock-guard.js +14 -0
- package/dist/db/migrate-generator.js +1 -1
- package/dist/db/migrate-runner.js +9 -8
- package/dist/db/queries/event-consumer.d.ts +6 -6
- package/dist/db/queries/event-store.d.ts +1 -1
- package/dist/db/queries/projection-rebuild.d.ts +1 -1
- package/dist/db/tenant-db-runner.d.ts +4 -0
- package/dist/db/tenant-db-runner.js +17 -0
- package/dist/db/tenant-db.d.ts +2 -0
- package/dist/db/tenant-db.js +30 -4
- package/dist/derivatives/derivatives-context.js +1 -1
- package/dist/engine/boot-validator/access-declarations.js +2 -2
- package/dist/engine/boot-validator/api-ext.js +3 -3
- package/dist/engine/boot-validator/boot-check.js +1 -1
- package/dist/engine/boot-validator/config-deps.js +13 -13
- package/dist/engine/boot-validator/detail-screens.js +1 -1
- package/dist/engine/boot-validator/entity-handler.d.ts +1 -0
- package/dist/engine/boot-validator/entity-handler.js +84 -35
- package/dist/engine/boot-validator/entity-list-screens.js +2 -2
- package/dist/engine/boot-validator/handler-field-references.js +19 -9
- package/dist/engine/boot-validator/i18n-keys.js +2 -2
- package/dist/engine/boot-validator/nav.js +1 -1
- package/dist/engine/boot-validator/pii-retention.js +8 -8
- package/dist/engine/boot-validator/projection-list-screens.js +5 -5
- package/dist/engine/boot-validator/query-output-columns.js +7 -7
- package/dist/engine/boot-validator/screens.d.ts +2 -1
- package/dist/engine/boot-validator/screens.js +153 -95
- package/dist/engine/boot-validator/url-prefill-fields.js +54 -35
- package/dist/engine/define-handler.d.ts +2 -2
- package/dist/engine/define-handler.js +8 -1
- package/dist/engine/feature-ast/extractors/handlers.js +2 -0
- package/dist/engine/feature-ast/pattern-change-schema.d.ts +2 -0
- package/dist/engine/feature-ast/pattern-change-schema.js +1 -0
- package/dist/engine/feature-ast/render.js +10 -2
- package/dist/engine/feature-entity-handlers.d.ts +2 -2
- package/dist/engine/feature-entity-handlers.js +12 -3
- package/dist/engine/handler-field-references.d.ts +21 -2
- package/dist/engine/handler-field-references.js +55 -17
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/schema-builder.js +2 -1
- package/dist/engine/soft-delete-cleanup.js +10 -3
- package/dist/engine/steps/retry.js +2 -1
- package/dist/engine/steps/unsafe-projection-delete.js +2 -2
- package/dist/engine/steps/unsafe-projection-upsert.js +2 -2
- package/dist/engine/template-variables.d.ts +3 -0
- package/dist/engine/template-variables.js +3 -0
- package/dist/engine/types/index.d.ts +1 -1
- package/dist/env/_zod-introspect.d.ts +1 -0
- package/dist/errors/classes.js +1 -0
- package/dist/errors/kumiko-error.d.ts +2 -0
- package/dist/errors/kumiko-error.js +2 -0
- package/dist/errors/write-error-info.js +2 -0
- package/dist/event-store/tenant-db-append.js +2 -2
- package/dist/files/assert-file-refs-belong-to-tenant.d.ts +4 -5
- package/dist/files/assert-file-refs-belong-to-tenant.js +7 -15
- package/dist/files/bind-pending-file-refs.d.ts +2 -0
- package/dist/files/bind-pending-file-refs.js +3 -3
- package/dist/files/bind-referenced-files.write.d.ts +1 -1
- package/dist/files/bind-referenced-files.write.js +22 -13
- package/dist/files/create-file-ref-from-bytes.d.ts +6 -3
- package/dist/files/create-file-ref-from-bytes.js +67 -4
- package/dist/files/create-file-ref.d.ts +22 -0
- package/dist/files/create-file-ref.js +53 -25
- package/dist/files/file-handle.d.ts +1 -1
- package/dist/files/file-handle.js +8 -0
- package/dist/files/file-routes.js +1 -1
- package/dist/files/index.d.ts +2 -2
- package/dist/files/index.js +1 -1
- package/dist/files/load-save-file-refs.d.ts +10 -0
- package/dist/files/load-save-file-refs.js +47 -0
- package/dist/files/purge-expired-file-ref-bytes.d.ts +4 -1
- package/dist/files/purge-expired-file-ref-bytes.js +45 -19
- package/dist/jobs/index.d.ts +1 -1
- package/dist/jobs/job-runner.d.ts +4 -1
- package/dist/jobs/job-runner.js +8 -4
- package/dist/observability/escape-hatch-report.d.ts +1 -1
- package/dist/observability/escape-hatch-report.js +17 -5
- package/dist/pipeline/cascade-handler.js +5 -2
- package/dist/pipeline/dispatch-batch.js +79 -31
- package/dist/pipeline/dispatch-shared.d.ts +7 -2
- package/dist/pipeline/dispatch-shared.js +99 -22
- package/dist/pipeline/dispatch-write.js +34 -22
- package/dist/pipeline/index.d.ts +1 -0
- package/dist/pipeline/index.js +1 -0
- package/dist/pipeline/pre-transaction-reservations.d.ts +3 -1
- package/dist/pipeline/pre-transaction-reservations.js +28 -9
- package/dist/rate-limit/bucket.d.ts +3 -3
- package/dist/rate-limit/bucket.js +14 -4
- package/dist/redis/cache-sync-bus.js +9 -3
- package/dist/search/meilisearch-adapter.js +36 -9
- package/package.json +7 -5
- package/src/changes.json +207 -8
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { qualifyEntityName } from "../qualified-name.js";
|
|
2
2
|
import { isFieldsEditSection, normalizeEditField, sectionFieldSpecs } from "../screen-helpers.js";
|
|
3
|
-
import { collectScreensByShortId, readsNavigateParamsAsFormPrefill, rowFieldExtractorKeys, } from "./screens.js";
|
|
3
|
+
import { collectScreensByShortId, readsNavigateParamsAsFormPrefill, rowFieldExtractorKeys, validateDrawerPrefillKeys, } from "./screens.js";
|
|
4
4
|
function isNavigateKind(action) {
|
|
5
5
|
return action.kind === undefined || action.kind === "navigate";
|
|
6
6
|
}
|
|
@@ -59,13 +59,14 @@ function navigateActionsOf(screen) {
|
|
|
59
59
|
return [];
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
function toNavigateParamsSource(action, params, sourceScreenEntity) {
|
|
62
|
+
function toNavigateParamsSource(action, params, sourceScreenEntity, sourceLabel) {
|
|
63
63
|
return {
|
|
64
64
|
...(action.screen !== undefined && { screen: action.screen }),
|
|
65
65
|
...(action.entity !== undefined && { entity: action.entity }),
|
|
66
66
|
...(action.entityId !== undefined && { entityId: action.entityId }),
|
|
67
67
|
params,
|
|
68
68
|
sourceScreenEntity,
|
|
69
|
+
sourceLabel,
|
|
69
70
|
};
|
|
70
71
|
}
|
|
71
72
|
// Every declarative navigate that writes params into the target's URL —
|
|
@@ -73,17 +74,19 @@ function toNavigateParamsSource(action, params, sourceScreenEntity) {
|
|
|
73
74
|
// plus projectionDetail metrics (runMetricNavigate), which reuse that shape —
|
|
74
75
|
// plus a relatedList toolbarAction with no `params` of its own, which still
|
|
75
76
|
// writes its implicit parent-id param at runtime (see `implicitParams`).
|
|
76
|
-
function navigateParamsSources(screen) {
|
|
77
|
+
function navigateParamsSources(screen, sourceLabel) {
|
|
77
78
|
return navigateActionsOf(screen).flatMap(({ action, sourceScreenEntity, implicitParams }) => {
|
|
78
79
|
if (!isNavigateKind(action))
|
|
79
80
|
return [];
|
|
80
81
|
const params = action.params ?? implicitParams;
|
|
81
|
-
return params === undefined
|
|
82
|
+
return params === undefined
|
|
83
|
+
? []
|
|
84
|
+
: [toNavigateParamsSource(action, params, sourceScreenEntity, sourceLabel)];
|
|
82
85
|
});
|
|
83
86
|
}
|
|
84
|
-
// Per target screen QN,
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
+
// Per target screen QN, every navigate `params` source that writes into the
|
|
88
|
+
// target's URL prefill. A form screen absent from the map accepts no URL prefill.
|
|
89
|
+
function collectUrlPrefillSourcesByScreenQn(features) {
|
|
87
90
|
const screensByShortId = collectScreensByShortId(features);
|
|
88
91
|
const detailForByEntity = new Map();
|
|
89
92
|
for (const feature of features) {
|
|
@@ -95,8 +98,9 @@ export function collectUrlPrefillFieldsByScreenQn(features) {
|
|
|
95
98
|
}
|
|
96
99
|
const result = new Map();
|
|
97
100
|
for (const feature of features) {
|
|
98
|
-
for (const screen of Object.
|
|
99
|
-
|
|
101
|
+
for (const [screenId, screen] of Object.entries(feature.screens)) {
|
|
102
|
+
const sourceLabel = `[Feature ${feature.name}] Screen "${screenId}" (${screen.type}) navigate action`;
|
|
103
|
+
for (const source of navigateParamsSources(screen, sourceLabel)) {
|
|
100
104
|
const target = resolveTarget(source, screensByShortId, detailForByEntity);
|
|
101
105
|
if (target === undefined)
|
|
102
106
|
continue;
|
|
@@ -105,15 +109,21 @@ export function collectUrlPrefillFieldsByScreenQn(features) {
|
|
|
105
109
|
continue;
|
|
106
110
|
}
|
|
107
111
|
const qn = qualifyEntityName(target.featureName, "screen", target.shortId);
|
|
108
|
-
|
|
109
|
-
for (const key of rowFieldExtractorKeys(source.params))
|
|
110
|
-
fields.add(key);
|
|
111
|
-
result.set(qn, fields);
|
|
112
|
+
result.set(qn, [...(result.get(qn) ?? []), source]);
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
return result;
|
|
116
117
|
}
|
|
118
|
+
// Per target screen QN, the union of field names any navigate `params` may
|
|
119
|
+
// prefill from the URL.
|
|
120
|
+
export function collectUrlPrefillFieldsByScreenQn(features) {
|
|
121
|
+
const result = new Map();
|
|
122
|
+
for (const [qn, sources] of collectUrlPrefillSourcesByScreenQn(features)) {
|
|
123
|
+
result.set(qn, new Set(sources.flatMap((source) => rowFieldExtractorKeys(source.params))));
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
117
127
|
// Runtime resolution: a bare screen id matches the first feature registering
|
|
118
128
|
// it (create-app's router); an entity target opens its detailFor screen with an id.
|
|
119
129
|
function resolveTarget(source, screensByShortId, detailForByEntity) {
|
|
@@ -124,37 +134,46 @@ function resolveTarget(source, screensByShortId, detailForByEntity) {
|
|
|
124
134
|
const match = screensByShortId.get(source.screen)?.[0];
|
|
125
135
|
return match === undefined ? undefined : { ...match, shortId: source.screen };
|
|
126
136
|
}
|
|
137
|
+
function assertHiddenRequiredFieldsSeeded(featureName, screenId, screen, seeded) {
|
|
138
|
+
for (const section of screen.layout.sections) {
|
|
139
|
+
if (!isFieldsEditSection(section))
|
|
140
|
+
continue;
|
|
141
|
+
for (const rawSpec of sectionFieldSpecs(section)) {
|
|
142
|
+
const spec = normalizeEditField(rawSpec);
|
|
143
|
+
if (spec.visible !== false)
|
|
144
|
+
continue;
|
|
145
|
+
const fieldDef = screen.fields[spec.field];
|
|
146
|
+
if (fieldDef === undefined || !("required" in fieldDef) || fieldDef.required !== true) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (seeded.has(spec.field))
|
|
150
|
+
continue;
|
|
151
|
+
throw new Error(`[Feature ${featureName}] Screen "${screenId}" (${screen.type}) sets submitPrefilled but ` +
|
|
152
|
+
`hides required field "${spec.field}" (visible: false) and no navigate action passes it in ` +
|
|
153
|
+
`params — the field could never get a value and the form could never submit. Add ` +
|
|
154
|
+
`"${spec.field}" to the params of a navigate action targeting this screen, make the field ` +
|
|
155
|
+
`visible, or drop required.`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
127
159
|
// submitPrefilled lets an unchanged form submit, so a required field the layout
|
|
128
|
-
// hides must be seeded by a navigate param — nothing else can ever fill it.
|
|
160
|
+
// hides must be seeded by a navigate param — nothing else can ever fill it. The
|
|
161
|
+
// param keys of every source (incl. metric/subtitle navigates and the implicit
|
|
162
|
+
// relatedList parent id) must also be fields the renderer would actually prefill.
|
|
129
163
|
export function validateSubmitPrefilledHiddenFields(features) {
|
|
130
|
-
const
|
|
164
|
+
const sourcesByScreenQn = collectUrlPrefillSourcesByScreenQn(features);
|
|
131
165
|
for (const feature of features) {
|
|
132
166
|
for (const [screenId, screen] of Object.entries(feature.screens)) {
|
|
133
167
|
if (screen.type !== "actionForm" && screen.type !== "secretMint")
|
|
134
168
|
continue;
|
|
135
169
|
if (screen.submitPrefilled !== true)
|
|
136
170
|
continue;
|
|
137
|
-
const
|
|
138
|
-
for (const
|
|
139
|
-
|
|
140
|
-
continue;
|
|
141
|
-
for (const rawSpec of sectionFieldSpecs(section)) {
|
|
142
|
-
const spec = normalizeEditField(rawSpec);
|
|
143
|
-
if (spec.visible !== false)
|
|
144
|
-
continue;
|
|
145
|
-
const fieldDef = screen.fields[spec.field];
|
|
146
|
-
if (fieldDef === undefined || !("required" in fieldDef) || fieldDef.required !== true) {
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
if (seeded?.has(spec.field) === true)
|
|
150
|
-
continue;
|
|
151
|
-
throw new Error(`[Feature ${feature.name}] Screen "${screenId}" (${screen.type}) sets submitPrefilled but ` +
|
|
152
|
-
`hides required field "${spec.field}" (visible: false) and no navigate action passes it in ` +
|
|
153
|
-
`params — the field could never get a value and the form could never submit. Add ` +
|
|
154
|
-
`"${spec.field}" to the params of a navigate action targeting this screen, make the field ` +
|
|
155
|
-
`visible, or drop required.`);
|
|
156
|
-
}
|
|
171
|
+
const sources = sourcesByScreenQn.get(qualifyEntityName(feature.name, "screen", screenId));
|
|
172
|
+
for (const source of sources ?? []) {
|
|
173
|
+
validateDrawerPrefillKeys(source.sourceLabel, "navigate-target", screenId, screen, source.params);
|
|
157
174
|
}
|
|
175
|
+
const seeded = new Set((sources ?? []).flatMap((source) => rowFieldExtractorKeys(source.params)));
|
|
176
|
+
assertHiddenRequiredFieldsSeeded(feature.name, screenId, screen, seeded);
|
|
158
177
|
}
|
|
159
178
|
}
|
|
160
179
|
}
|
|
@@ -4,9 +4,9 @@ import type { ContainsSecret } from "../secrets/types.js";
|
|
|
4
4
|
import type { QueryHandlerDefinition, WriteHandlerDefinition, WriteHandlerInput } from "./types/define-handler.js";
|
|
5
5
|
import type { KumikoEventTypeMap } from "./types/index.js";
|
|
6
6
|
export type { QueryHandlerDefinition, StreamHandlerDefinition, WriteHandlerDefinition, WriteHandlerInput, } from "./types/define-handler.js";
|
|
7
|
-
export declare function defineWriteHandler<const TName extends string, TSchema extends ZodType, TData = unknown, TMap extends object = KumikoEventTypeMap>(def: WriteHandlerInput<TName, TSchema, TData, TMap>, ..._noSecretInResponse: true extends ContainsSecret<TData> ? [
|
|
7
|
+
export declare function defineWriteHandler<const TName extends string, TSchema extends ZodType, TData = unknown, TMap extends object = KumikoEventTypeMap, TPrepared = undefined>(def: WriteHandlerInput<TName, TSchema, TData, TMap, TPrepared>, ..._noSecretInResponse: true extends ContainsSecret<TData> ? [
|
|
8
8
|
secretLeak: "A handler response must not contain a Secret<> — call .reveal() and return the plaintext, or drop the field."
|
|
9
|
-
] : []): WriteHandlerDefinition<TName, TSchema, TData, TMap>;
|
|
9
|
+
] : []): WriteHandlerDefinition<TName, TSchema, TData, TMap, TPrepared>;
|
|
10
10
|
export declare function defineQueryHandler<const TName extends string, TSchema extends ZodType, TResult = unknown, TMap extends object = KumikoEventTypeMap>(def: QueryHandlerDefinition<TName, TSchema, TResult, TMap>, ..._noSecretInResponse: true extends ContainsSecret<TResult> ? [
|
|
11
11
|
secretLeak: "A handler response must not contain a Secret<> — call .reveal() and return the plaintext, or drop the field."
|
|
12
12
|
] : []): QueryHandlerDefinition<TName, TSchema, TResult, TMap>;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { ENTITY_CONVENTION_QUERY_BRAND } from "@cosmicdrift/kumiko-types/handlers";
|
|
2
2
|
import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
3
3
|
import { runPipeline } from "./run-pipeline.js";
|
|
4
|
+
function pickPreTransactionPhases(def) {
|
|
5
|
+
return {
|
|
6
|
+
...(def.reserveBeforeTransaction && { reserveBeforeTransaction: def.reserveBeforeTransaction }),
|
|
7
|
+
...(def.beforeTransaction && { beforeTransaction: def.beforeTransaction }),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
4
10
|
export function defineWriteHandler(def,
|
|
5
11
|
// R6: a phantom rest-param. When the inferred response `TData` carries a
|
|
6
12
|
// Secret<> anywhere, ContainsSecret<TData> is `true` and this resolves to a
|
|
@@ -51,8 +57,9 @@ export function defineWriteHandler(def,
|
|
|
51
57
|
...(def.rateLimit && { rateLimit: def.rateLimit }),
|
|
52
58
|
...(def.additionalRateLimits && { additionalRateLimits: def.additionalRateLimits }),
|
|
53
59
|
...(def.escapeHatch && { escapeHatch: def.escapeHatch }),
|
|
54
|
-
...(def
|
|
60
|
+
...pickPreTransactionPhases(def),
|
|
55
61
|
...(def.tenantlessAnonymous && { tenantlessAnonymous: true }),
|
|
62
|
+
...(def.transaction && { transaction: def.transaction }),
|
|
56
63
|
};
|
|
57
64
|
if ("perform" in def && def.perform !== undefined) {
|
|
58
65
|
const performDef = def.perform;
|
|
@@ -202,6 +202,7 @@ export declare const PATTERN_SCHEMAS_BY_KIND: {
|
|
|
202
202
|
limit: z.ZodNumber;
|
|
203
203
|
windowSeconds: z.ZodNumber;
|
|
204
204
|
cost: z.ZodOptional<z.ZodNumber>;
|
|
205
|
+
normalize: z.ZodOptional<z.ZodLiteral<"email">>;
|
|
205
206
|
}, z.core.$strict>>, z.ZodObject<{
|
|
206
207
|
__raw: z.ZodString;
|
|
207
208
|
}, z.core.$strict>]>>;
|
|
@@ -724,6 +725,7 @@ export declare const patternSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
724
725
|
limit: z.ZodNumber;
|
|
725
726
|
windowSeconds: z.ZodNumber;
|
|
726
727
|
cost: z.ZodOptional<z.ZodNumber>;
|
|
728
|
+
normalize: z.ZodOptional<z.ZodLiteral<"email">>;
|
|
727
729
|
}, z.core.$strict>>, z.ZodObject<{
|
|
728
730
|
__raw: z.ZodString;
|
|
729
731
|
}, z.core.$strict>]>>;
|
|
@@ -240,18 +240,26 @@ function renderMetric(p) {
|
|
|
240
240
|
const merged = { name: p.shortName, ...p.options };
|
|
241
241
|
return `r.metric(${renderValue(merged)});`;
|
|
242
242
|
}
|
|
243
|
+
// r.secret / r.secretNamespace throw on an empty writeRoles list; the designer's
|
|
244
|
+
// string-list input emits [] when cleared, which means "rely on handler access alone".
|
|
245
|
+
function dropEmptyWriteRoles(options) {
|
|
246
|
+
if (options.writeRoles?.length !== 0)
|
|
247
|
+
return options;
|
|
248
|
+
const { writeRoles: _emptyWriteRoles, ...rest } = options;
|
|
249
|
+
return rest;
|
|
250
|
+
}
|
|
243
251
|
function renderSecret(p) {
|
|
244
252
|
if (isRawRefSentinel(p.options)) {
|
|
245
253
|
return `r.secret(${JSON.stringify(p.shortName)}, ${p.options.__raw});`;
|
|
246
254
|
}
|
|
247
|
-
const merged = { name: p.shortName, ...p.options };
|
|
255
|
+
const merged = { name: p.shortName, ...dropEmptyWriteRoles(p.options) };
|
|
248
256
|
return `r.secret(${renderValue(merged)});`;
|
|
249
257
|
}
|
|
250
258
|
function renderSecretNamespace(p) {
|
|
251
259
|
if (isRawRefSentinel(p.options)) {
|
|
252
260
|
return `r.secretNamespace(${JSON.stringify(p.shortName)}, ${p.options.__raw});`;
|
|
253
261
|
}
|
|
254
|
-
const merged = { name: p.shortName, ...p.options };
|
|
262
|
+
const merged = { name: p.shortName, ...dropEmptyWriteRoles(p.options) };
|
|
255
263
|
return `r.secretNamespace(${renderValue(merged)});`;
|
|
256
264
|
}
|
|
257
265
|
function renderClaimKey(p) {
|
|
@@ -3,7 +3,7 @@ import type { ZodType } from "zod";
|
|
|
3
3
|
import { type QueryHandlerDefinition, type StreamHandlerDefinition, type WriteHandlerDefinition } from "./define-handler.js";
|
|
4
4
|
import type { RegisterEntityCrudOptions } from "./entity-handlers.js";
|
|
5
5
|
import type { FeatureBuilderState } from "./feature-builder-state.js";
|
|
6
|
-
import type { AccessRule, AgentHandlerHints, EntityDefinition, EntityRef, EscapeHatchDeclaration, HandlerRef, NameOrRef, PayloadRateLimitOption, QueryHandlerFn, RateLimitDeclaration, RelationDefinition, StreamHandlerFn, WriteHandlerFn } from "./types/index.js";
|
|
6
|
+
import type { AccessRule, AgentHandlerHints, EntityDefinition, EntityRef, EscapeHatchDeclaration, HandlerRef, KumikoEventTypeMap, NameOrRef, PayloadRateLimitOption, QueryHandlerFn, RateLimitDeclaration, RelationDefinition, StreamHandlerFn, WriteHandlerFn } from "./types/index.js";
|
|
7
7
|
export declare function buildEntityHandlerMethods<TName extends string>(state: FeatureBuilderState, name: TName): {
|
|
8
8
|
entity(nameOrDefinition: string | ({
|
|
9
9
|
readonly name: string;
|
|
@@ -14,7 +14,7 @@ export declare function buildEntityHandlerMethods<TName extends string>(state: F
|
|
|
14
14
|
readonly entity: NameOrRef;
|
|
15
15
|
readonly name: string;
|
|
16
16
|
} & RelationDefinition), relationName?: string, definition?: RelationDefinition): void;
|
|
17
|
-
writeHandler<TName_1 extends string, TSchema extends ZodType>(nameOrDef: string | WriteHandlerDefinition<TName_1, TSchema>, schema?: TSchema, handler?: WriteHandlerFn<z.infer<TSchema>>, options?: {
|
|
17
|
+
writeHandler<TName_1 extends string, TSchema extends ZodType, TPrepared = undefined>(nameOrDef: string | WriteHandlerDefinition<TName_1, TSchema, unknown, KumikoEventTypeMap, TPrepared>, schema?: TSchema, handler?: WriteHandlerFn<z.infer<TSchema>>, options?: {
|
|
18
18
|
access: AccessRule;
|
|
19
19
|
rateLimit?: RateLimitDeclaration;
|
|
20
20
|
additionalRateLimits?: readonly PayloadRateLimitOption[];
|
|
@@ -54,6 +54,17 @@ function agentSlots(source) {
|
|
|
54
54
|
...(source.agent && { agent: source.agent }),
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
+
function transactionPhaseSlots(def) {
|
|
58
|
+
return {
|
|
59
|
+
...(def.reserveBeforeTransaction && {
|
|
60
|
+
reserveBeforeTransaction: def.reserveBeforeTransaction,
|
|
61
|
+
}),
|
|
62
|
+
...(def.beforeTransaction && {
|
|
63
|
+
beforeTransaction: def.beforeTransaction, // @cast-boundary engine-bridge
|
|
64
|
+
}),
|
|
65
|
+
...(def.transaction && { transaction: def.transaction }),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
57
68
|
function requireInlineHandlerArgs(kind, schema, handler, options) {
|
|
58
69
|
if (!schema || !handler || !options?.access) {
|
|
59
70
|
throw new Error(`${kind} inline form requires schema + handler + options.access`);
|
|
@@ -108,9 +119,7 @@ export function buildEntityHandlerMethods(state, name) {
|
|
|
108
119
|
...(def.rateLimit && { rateLimit: def.rateLimit }),
|
|
109
120
|
...(def.additionalRateLimits && { additionalRateLimits: def.additionalRateLimits }),
|
|
110
121
|
...(def.escapeHatch && { escapeHatch: def.escapeHatch }),
|
|
111
|
-
...(def
|
|
112
|
-
reserveBeforeTransaction: def.reserveBeforeTransaction,
|
|
113
|
-
}),
|
|
122
|
+
...transactionPhaseSlots(def),
|
|
114
123
|
...(def.tenantlessAnonymous && { tenantlessAnonymous: true }),
|
|
115
124
|
// Forward the pipeline-build closure so boot-validators and
|
|
116
125
|
// Designer/AI tooling can inspect the step list. Absent on
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as z from "zod";
|
|
2
2
|
export declare const HANDLER_FIELD_REFERENCES_META_KEY = "references";
|
|
3
|
-
export
|
|
3
|
+
export type HandlerFieldReferenceMeta = string | {
|
|
4
|
+
readonly entity: string;
|
|
5
|
+
readonly by?: string;
|
|
6
|
+
};
|
|
7
|
+
export type HandlerFieldReference = {
|
|
8
|
+
readonly entity: string;
|
|
9
|
+
readonly by: string | undefined;
|
|
10
|
+
readonly multiple: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type HandlerFieldReferenceReading = {
|
|
13
|
+
readonly kind: "none";
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: "invalid";
|
|
16
|
+
readonly raw: unknown;
|
|
17
|
+
} | {
|
|
18
|
+
readonly kind: "ok";
|
|
19
|
+
readonly reference: HandlerFieldReference;
|
|
20
|
+
};
|
|
21
|
+
export declare function readHandlerFieldReference(schema: z.ZodType, field: string): HandlerFieldReferenceReading;
|
|
22
|
+
export declare function handlerFieldReference(schema: z.ZodType, field: string): HandlerFieldReference | undefined;
|
|
@@ -1,31 +1,69 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as z from "zod";
|
|
2
2
|
import { zodDef, zodMeta, zodShapeField } from "../env/_zod-introspect.js";
|
|
3
3
|
export const HANDLER_FIELD_REFERENCES_META_KEY = "references";
|
|
4
4
|
const MAX_WRAPPER_DEPTH = 8;
|
|
5
|
-
|
|
5
|
+
const referenceMetaSchema = z.union([
|
|
6
|
+
z.string().min(1),
|
|
7
|
+
z.object({ entity: z.string().min(1), by: z.string().min(1).optional() }),
|
|
8
|
+
]);
|
|
9
|
+
function rawReferenceMeta(field) {
|
|
6
10
|
const meta = zodMeta(field);
|
|
7
11
|
if (typeof meta !== "object" || meta === null)
|
|
8
12
|
return undefined;
|
|
9
|
-
|
|
10
|
-
return typeof value === "string" ? value : undefined;
|
|
13
|
+
return Reflect.get(meta, HANDLER_FIELD_REFERENCES_META_KEY);
|
|
11
14
|
}
|
|
12
|
-
|
|
13
|
-
// from the outer type, so every wrapper layer is probed on the way in.
|
|
14
|
-
export function handlerFieldReference(schema, field) {
|
|
15
|
+
function unwrapObjectSchema(schema) {
|
|
15
16
|
let current = schema;
|
|
16
17
|
for (let depth = 0; depth < MAX_WRAPPER_DEPTH && current !== undefined; depth++) {
|
|
17
|
-
if (current instanceof ZodObject)
|
|
18
|
-
|
|
18
|
+
if (current instanceof z.ZodObject)
|
|
19
|
+
return current;
|
|
19
20
|
current = zodDef(current)?.innerType;
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
// `.optional()` / `.nullable()` / `.default()` after `.meta()` hide the meta from the outer
|
|
25
|
+
// type, so every wrapper layer is probed on the way in; array elements are probed too.
|
|
26
|
+
function findRawReferenceMeta(field) {
|
|
27
|
+
let current = field;
|
|
28
|
+
for (let depth = 0; depth < MAX_WRAPPER_DEPTH && current !== undefined; depth++) {
|
|
29
|
+
const raw = rawReferenceMeta(current);
|
|
30
|
+
if (raw !== undefined)
|
|
31
|
+
return raw;
|
|
32
|
+
current = current instanceof z.ZodArray ? zodDef(current)?.element : zodDef(current)?.innerType;
|
|
29
33
|
}
|
|
30
34
|
return undefined;
|
|
31
35
|
}
|
|
36
|
+
function isArrayField(field) {
|
|
37
|
+
let current = field;
|
|
38
|
+
for (let depth = 0; depth < MAX_WRAPPER_DEPTH && current !== undefined; depth++) {
|
|
39
|
+
if (current instanceof z.ZodArray)
|
|
40
|
+
return true;
|
|
41
|
+
current = zodDef(current)?.innerType;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
export function readHandlerFieldReference(schema, field) {
|
|
46
|
+
const objectSchema = unwrapObjectSchema(schema);
|
|
47
|
+
const fieldSchema = objectSchema && zodShapeField(objectSchema, field);
|
|
48
|
+
if (fieldSchema === undefined)
|
|
49
|
+
return { kind: "none" };
|
|
50
|
+
const raw = findRawReferenceMeta(fieldSchema);
|
|
51
|
+
if (raw === undefined)
|
|
52
|
+
return { kind: "none" };
|
|
53
|
+
const parsed = referenceMetaSchema.safeParse(raw);
|
|
54
|
+
if (!parsed.success)
|
|
55
|
+
return { kind: "invalid", raw };
|
|
56
|
+
const meta = parsed.data;
|
|
57
|
+
return {
|
|
58
|
+
kind: "ok",
|
|
59
|
+
reference: {
|
|
60
|
+
entity: typeof meta === "string" ? meta : meta.entity,
|
|
61
|
+
by: typeof meta === "string" ? undefined : meta.by,
|
|
62
|
+
multiple: isArrayField(fieldSchema),
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export function handlerFieldReference(schema, field) {
|
|
67
|
+
const reading = readHandlerFieldReference(schema, field);
|
|
68
|
+
return reading.kind === "ok" ? reading.reference : undefined;
|
|
69
|
+
}
|
package/dist/engine/index.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ export { addPattern, applyChanges, createFeaturePatcher, getPatternSchema, group
|
|
|
43
43
|
export { type ChangelogEntry, type ChangelogType, compareVersions, type FeatureChangelog, filterEntriesAfter, parseFeatureChangelog, sortEntries, validateChangelog, } from "./feature-changelog.js";
|
|
44
44
|
export { type BuildManifestOptions, buildManifestFromRegistry, type FeatureManifest, type ManifestConfigKey, type ManifestExtension, type ManifestFeature, type ManifestSecret, type ManifestSystemEvent, serializeManifest, } from "./feature-manifest.js";
|
|
45
45
|
export { checkWriteFieldOwnership, checkWriteFieldRoles, filterReadFields, maskWriteOnlyFields, } from "./field-access.js";
|
|
46
|
-
export { HANDLER_FIELD_REFERENCES_META_KEY, handlerFieldReference, } from "./handler-field-references.js";
|
|
46
|
+
export { HANDLER_FIELD_REFERENCES_META_KEY, type HandlerFieldReference, type HandlerFieldReferenceMeta, handlerFieldReference, } from "./handler-field-references.js";
|
|
47
47
|
export { resolveName, withResponseData } from "./handler-helpers.js";
|
|
48
48
|
export { i18nKey } from "./i18n-key.js";
|
|
49
49
|
export { buildSessionRoles, findForbiddenMembershipRole, isForbiddenMembershipRole, stripForbiddenMembershipRoles, } from "./membership-roles.js";
|
|
@@ -81,7 +81,7 @@ export { assertNotificationVariables, templateVariable, templateVariablesToZod,
|
|
|
81
81
|
export { type EffectiveFeaturesResolver, findTierResolverUsage, isTierResolverPlugin, TENANT_TIER_RESOLVER_EXT, type TierResolverPlugin, type TrialGate, } from "./tier-resolver-extension.js";
|
|
82
82
|
export { findOverDeepTransferChain, resolveTransferAdjacency as resolveEntityTransferAdjacency, type TransferAdjacency, type TransferEdge, type TransferEdgeLink, } from "./transfer-adjacency.js";
|
|
83
83
|
export { isSystemTenant, isUuid, parseTenantId, SYSTEM_TENANT_ID } from "./types/identifiers.js";
|
|
84
|
-
export type { AccessRule, ActionFormScreenDefinition, ActiveMembership, ActiveMembershipRejection, ActiveMembershipResult, AgentExposure, AgentHandlerHints, AgentRisk, AppContext, AppendEventArgs, AppendEventFn, AuthClaimsContext, AuthClaimsFn, AuthClaimsHookDef, BelongsToRelation, BigIntFieldDef, BooleanFieldDef, CamelToKebab, ChatSendFailureCode, 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, DashboardDateParam, DashboardFeedPanel, DashboardFilterDefinition, DashboardI18nText, DashboardListPanel, DashboardMoneyParam, DashboardPanelDefinition, DashboardPanelVisibility, DashboardProgressListPanel, DashboardScreenDefinition, DashboardScreenPanel, DashboardStatGroupPanel, DashboardStatPanel, DashboardText, DashboardTextParam, DashboardValueFormat, DateFieldDef, DecimalFieldDef, DeleteContext, DeliveryErrorCode, DeliveryFailureCode, DeliverySkipReason, DeriveContext, DerivedFieldDef, DerivedFieldsMap, DerivedValueType, EditExtensionSection, EditFieldPrefill, EditFieldSpec, EditFieldsSection, EditLayout, EditRelatedListSection, EditSectionSpec, EmbeddedListEditSpec, EntityDefinition, EntityEditAction, EntityEditActionPlacement, 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, NotificationLocalizedTemplate, NotificationRecipientFn, NotificationRecordMapping, NotificationTemplateBody, NotificationTemplateFn, NotificationTemplateMeta, NotifyDeferred, NotifyDelivery, NotifyDeliveryStatus, NotifyDispatched, NotifyFactory, NotifyFn, NotifyJobDispatcher, NotifyOptions, NotifyPriority, NotifyResult, 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, TemplateListVariableDef, TemplateScalarVariableDef, TemplateVariableDef, TemplateVariables, TenantId, TenantWriteTarget, 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";
|
|
84
|
+
export type { AccessRule, ActionFormScreenDefinition, ActiveMembership, ActiveMembershipRejection, ActiveMembershipResult, AgentExposure, AgentHandlerHints, AgentRisk, AppContext, AppendEventArgs, AppendEventFn, AuthClaimsContext, AuthClaimsFn, AuthClaimsHookDef, BeforeTransactionPhase, BeforeTransactionResult, BelongsToRelation, BigIntFieldDef, BooleanFieldDef, CamelToKebab, ChatSendFailureCode, 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, DashboardDateParam, DashboardFeedPanel, DashboardFilterDefinition, DashboardI18nText, DashboardListPanel, DashboardMoneyParam, DashboardPanelDefinition, DashboardPanelVisibility, DashboardProgressListPanel, DashboardScreenDefinition, DashboardScreenPanel, DashboardStatGroupPanel, DashboardStatPanel, DashboardText, DashboardTextParam, DashboardValueFormat, DateFieldDef, DecimalFieldDef, DeleteContext, DeliveryErrorCode, DeliveryFailureCode, DeliverySkipReason, DeriveContext, DerivedFieldDef, DerivedFieldsMap, DerivedValueType, EditExtensionSection, EditFieldPrefill, EditFieldSpec, EditFieldsSection, EditLayout, EditRelatedListSection, EditSectionSpec, EmbeddedListEditSpec, EntityDefinition, EntityEditAction, EntityEditActionPlacement, 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, NotificationLocalizedTemplate, NotificationRecipientFn, NotificationRecordMapping, NotificationTemplateBody, NotificationTemplateFn, NotificationTemplateMeta, NotifyDeferred, NotifyDelivery, NotifyDeliveryStatus, NotifyDispatched, NotifyFactory, NotifyFn, NotifyJobDispatcher, NotifyOptions, NotifyPriority, NotifyResult, NumberFieldDef, OnDeleteStrategy, OpenToAllAccessRule, OpenToAllDeclaration, OpenToAllPersonalData, ParentRefDef, PersonalAnnotations, PersonalAnnotationsLongText, PersonalAnnotationsNoFind, PersonalSubject, PlatformComponent, PluralForms, PostDeleteHookFn, PostSaveHookFn, PreDeleteHookFn, PreparedWriteEvent, 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, TemplateListVariableDef, TemplateScalarVariableDef, TemplateVariableDef, TemplateVariables, TenantId, TenantWriteTarget, 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";
|
|
85
85
|
export { DEFAULT_CURRENCIES, DEFAULT_LOCALES, DELIVERY_FAILURE_CODES, DELIVERY_SKIP_REASONS, HookPhases, HTTP_ERROR_CODE_PATTERN, isAgentVisibleScreen, isDeliveryErrorCode, isOpenToAllGranted, resolveAgentExposure, } from "./types/index.js";
|
|
86
86
|
export type { AwaitedEventType, PipelineBuildCtx, PipelineCtx, PipelineDef, StepBuilder, StepDef, StepFailureStrategy, StepInstance, StepKind, StepNamespace, StepResolver, } from "./types/step.js";
|
|
87
87
|
export { runValidation } from "./validation.js";
|
|
@@ -288,7 +288,8 @@ export function fieldToZod(field, currencies, opts = {}) {
|
|
|
288
288
|
// Multi file: array of fileRef UUIDs. Same story as the singular
|
|
289
289
|
// variant — the element type has to match the UUID column on
|
|
290
290
|
// fileRefsTable.id.
|
|
291
|
-
|
|
291
|
+
const ids = z.array(z.uuid());
|
|
292
|
+
return field.maxCount === undefined ? ids : ids.max(field.maxCount);
|
|
292
293
|
}
|
|
293
294
|
case "reference":
|
|
294
295
|
// Tier 2.7e-3: Validiert UUID-shape. Existenz-Check der Reference
|
|
@@ -80,13 +80,20 @@ export const softDeleteCleanupJob = async (_payload, ctx) => {
|
|
|
80
80
|
if (isSystemScopedSoftDeleteTable(proj.table, entity))
|
|
81
81
|
continue;
|
|
82
82
|
const where = { isDeleted: true, deletedAt: { lt: cutoff }, tenantId };
|
|
83
|
-
await purgeExpiredRows(db, proj.source, proj.table, where, ctx);
|
|
83
|
+
await purgeExpiredRows(db, proj.source, proj.table, where, cutoff, ctx);
|
|
84
84
|
}
|
|
85
85
|
};
|
|
86
|
-
async function purgeExpiredRows(db, source, table, where, ctx) {
|
|
86
|
+
async function purgeExpiredRows(db, source, table, where, cutoff, ctx) {
|
|
87
87
|
if (source === FILE_REF_ENTITY_NAME) {
|
|
88
88
|
// The row is the only record of the storageKey, so its bytes go first.
|
|
89
|
-
await purgeExpiredFileRefsWithBytes({
|
|
89
|
+
await purgeExpiredFileRefsWithBytes({
|
|
90
|
+
db,
|
|
91
|
+
table,
|
|
92
|
+
where,
|
|
93
|
+
expiredBefore: cutoff,
|
|
94
|
+
files: ctx.files,
|
|
95
|
+
log: ctx.log,
|
|
96
|
+
});
|
|
90
97
|
}
|
|
91
98
|
else {
|
|
92
99
|
await deleteMany(db, table, where);
|
|
@@ -11,6 +11,7 @@ import { defineStep } from "../define-step.js";
|
|
|
11
11
|
import { runStepList } from "../run-pipeline.js";
|
|
12
12
|
import { SUSPEND_SENTINEL, WORKFLOW_AGGREGATE_TYPE, WORKFLOW_RETRY_SCHEDULED_TYPE, } from "./_step-dispatch-constants.js";
|
|
13
13
|
import { describeWorkflowStepError } from "./describe-workflow-step-error.js";
|
|
14
|
+
import { unwrapWorkflowStepError } from "./workflow-step-error.js";
|
|
14
15
|
const log = createFallbackLogger("workflow-retry");
|
|
15
16
|
defineStep({
|
|
16
17
|
kind: "workflow.retry",
|
|
@@ -38,7 +39,7 @@ defineStep({
|
|
|
38
39
|
workflowName: ctx.workflow.workflowName,
|
|
39
40
|
stepIndex: ctx.workflow.stepIndex,
|
|
40
41
|
attempt,
|
|
41
|
-
error: String(error),
|
|
42
|
+
error: String(unwrapWorkflowStepError(error)),
|
|
42
43
|
});
|
|
43
44
|
const backoffMs = calculateBackoff(attempt, args.backoff);
|
|
44
45
|
const wakeAt = Temporal.Now.instant().add({ milliseconds: backoffMs }).toString();
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// must commit in the same TX as the aggregate-mutation that triggered
|
|
17
17
|
// it (stronger consistency than an async projection). Reviewer judges.
|
|
18
18
|
import { deleteMany } from "../../db/query.js";
|
|
19
|
-
import {
|
|
19
|
+
import { tenantDbWriteRunner } from "../../db/tenant-db-runner.js";
|
|
20
20
|
import { defineStep } from "../define-step.js";
|
|
21
21
|
import { resolveRequired } from "./_resolver-utils.js";
|
|
22
22
|
defineStep({
|
|
@@ -27,7 +27,7 @@ defineStep({
|
|
|
27
27
|
// The `unsafe*`-step IS the sanctioned direct projection write — it exists
|
|
28
28
|
// to bypass the executor deliberately (boot-validated allowlist + reviewer
|
|
29
29
|
// gate). The brand-strip cast makes that explicit at the one blessed seam.
|
|
30
|
-
await deleteMany(
|
|
30
|
+
await deleteMany(tenantDbWriteRunner(ctx.db, "unsafe projection delete"), args.table, where);
|
|
31
31
|
},
|
|
32
32
|
});
|
|
33
33
|
export function buildUnsafeProjectionDeleteStep(args) {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { extractTableName } from "../../db/index.js";
|
|
14
14
|
import { executeRawQuery } from "../../db/queries/raw-sql.js";
|
|
15
15
|
import { columnSqlNameOf } from "../../db/table-introspection.js";
|
|
16
|
-
import {
|
|
16
|
+
import { tenantDbWriteRunner } from "../../db/tenant-db-runner.js";
|
|
17
17
|
import { defineStep } from "../define-step.js";
|
|
18
18
|
import { resolveRequired } from "./_resolver-utils.js";
|
|
19
19
|
function resolveColumnName(table, field) {
|
|
@@ -54,7 +54,7 @@ defineStep({
|
|
|
54
54
|
}
|
|
55
55
|
const sqlText = `INSERT INTO ${quoteIdent(tableName)} (${colNames.join(", ")}) VALUES (${placeholders.join(", ")}) ` +
|
|
56
56
|
`ON CONFLICT (${conflictCols}) DO UPDATE SET ${setClauses.join(", ")}`;
|
|
57
|
-
await executeRawQuery(
|
|
57
|
+
await executeRawQuery(tenantDbWriteRunner(ctx.db, "unsafe projection upsert"), sqlText, params);
|
|
58
58
|
},
|
|
59
59
|
});
|
|
60
60
|
export function buildUnsafeProjectionUpsertStep(args) {
|
|
@@ -11,6 +11,9 @@ export declare const templateVariable: {
|
|
|
11
11
|
readonly link: (opts?: CommonOptions & {
|
|
12
12
|
readonly example?: string;
|
|
13
13
|
}) => TemplateScalarVariableDef;
|
|
14
|
+
readonly richText: (opts?: CommonOptions & {
|
|
15
|
+
readonly example?: string;
|
|
16
|
+
}) => TemplateScalarVariableDef;
|
|
14
17
|
readonly money: (opts?: CommonOptions & {
|
|
15
18
|
readonly example?: number | {
|
|
16
19
|
readonly amount: number;
|
|
@@ -10,6 +10,7 @@ export const templateVariable = {
|
|
|
10
10
|
type: "link",
|
|
11
11
|
...opts,
|
|
12
12
|
}),
|
|
13
|
+
richText: (opts = {}) => ({ type: "richText", ...opts }),
|
|
13
14
|
money: (opts = {}) => ({ type: "money", ...opts }),
|
|
14
15
|
date: (opts = {}) => ({
|
|
15
16
|
type: "date",
|
|
@@ -39,6 +40,8 @@ function scalarToZod(def) {
|
|
|
39
40
|
switch (def.type) {
|
|
40
41
|
case "text":
|
|
41
42
|
return z.string();
|
|
43
|
+
case "richText":
|
|
44
|
+
return z.string();
|
|
42
45
|
case "link":
|
|
43
46
|
return linkSchema;
|
|
44
47
|
case "money":
|
|
@@ -7,7 +7,7 @@ export type { ExtensionOptionsArgs, ExtensionOptionsFor, KumikoExtensionOptionsM
|
|
|
7
7
|
export type { BootCheckContext, BootCheckFn, FeatureDefinition, FeatureMetricDef, FeatureMetricType, FeatureRegistrar, MetricOptions, Registry, SecretKeyDefinition, SecretKeyHandle, SecretNamespaceDefinition, SecretNamespaceHandle, SecretNamespaceOptions, SecretOptions, StoreTableDef, StoreTableEntry, StoreTableOptions, UiHintOption, UiHints, } from "@cosmicdrift/kumiko-types/feature";
|
|
8
8
|
export type { FieldIconKey } from "@cosmicdrift/kumiko-types/field-icon";
|
|
9
9
|
export type { AnyFileFieldDef, BigIntFieldDef, BooleanFieldDef, ClientDerivedFieldDef, DateFieldDef, DecimalFieldDef, DefaultCurrency, DeriveContext, DerivedFieldDef, DerivedFieldsMap, DerivedValueType, EmbeddedDerivedCellDef, EmbeddedFieldDef, EmbeddedSubFieldDef, EntityDefinition, EntityEventMigration, EntityIndexDef, FieldAccess, FieldDefinition, FieldsMap, FileFieldDef, FilesFieldDef, Findability, ImageFieldDef, ImagesFieldDef, JsonbFieldDef, LocatedTimestampFieldDef, LongTextFieldDef, LongTextFindability, MoneyCurrencySource, MoneyFieldDef, MultiSelectFieldDef, NumberFieldDef, OptionsQueryPayload, OptionsQueryPayloadValue, ParentRefDef, PersonalAnnotations, PersonalAnnotationsLongText, PersonalAnnotationsNoFind, PersonalSubject, ReferenceFieldDef, ResolvedPiiFlags, RetentionDef, SelectFieldDef, SelectOptionTone, TextFieldDef, TimestampFieldDef, TransitionMap, TzFieldDef, } from "@cosmicdrift/kumiko-types/fields";
|
|
10
|
-
export type { AccessRule, ActiveMembership, ActiveMembershipRejection, ActiveMembershipResult, AgentExposure, AgentHandlerHints, AgentRisk, AggregateStreamHandle, AppContext, AppendEventArgs, AppendEventFn, AuthClaimsContext, AuthClaimsFn, AuthClaimsHookDef, CamelToKebab, ChatSendFailureCode, ClaimKeyDefinition, ClaimKeyHandle, ClaimKeyJsType, ClaimKeyType, DeclarativeEventMigration, DeliveryErrorCode, DeliveryFailureCode, DeliverySkipReason, DispatchWriteRef, EntityRef, EscapeHatchAuditSink, EscapeHatchDeclaration, EscapeHatchKind, EscapeHatchReporter, EscapeHatchTarget, EscapeHatchUseEvent, EventDef, EventMigrationDef, EventPiiFields, EventPiiStance, EventUpcastCtx, EventUpcastFn, FetchForWritingArgs, HandlerContext, HandlerRef, JobContext, JobRunnerRef, JobTriggerEvent, MemberReader, NameOrRef, NotifyDeferred, NotifyDelivery, NotifyDeliveryStatus, NotifyDispatched, NotifyFactory, NotifyFn, NotifyJobDispatcher, NotifyOptions, NotifyPriority, NotifyResult, OpenToAllAccessRule, OpenToAllDeclaration, OpenToAllPersonalData, PayloadRateLimitOption, QualifiedEventName, QueryEvent, QueryHandlerDef, QueryHandlerFn, RateLimitDeclaration, RateLimitDisabled, RateLimitOption, RateLimitPer, RoleAccessPersonalData, RoleAccessRule, SessionUser, SessionUserOrigin, StreamHandlerDef, StreamHandlerFn, TenantWriteTarget, TryAppendEventFn, TryAppendEventResult, UnsafeAppendEventFn, WriteEvent, WriteHandlerDef, WriteHandlerFn, WriteResult, } from "@cosmicdrift/kumiko-types/handlers";
|
|
10
|
+
export type { AccessRule, ActiveMembership, ActiveMembershipRejection, ActiveMembershipResult, AgentExposure, AgentHandlerHints, AgentRisk, AggregateStreamHandle, AppContext, AppendEventArgs, AppendEventFn, AuthClaimsContext, AuthClaimsFn, AuthClaimsHookDef, BeforeTransactionPhase, BeforeTransactionResult, CamelToKebab, ChatSendFailureCode, ClaimKeyDefinition, ClaimKeyHandle, ClaimKeyJsType, ClaimKeyType, DeclarativeEventMigration, DeliveryErrorCode, DeliveryFailureCode, DeliverySkipReason, DispatchWriteRef, EntityRef, EscapeHatchAuditSink, EscapeHatchDeclaration, EscapeHatchKind, EscapeHatchReporter, EscapeHatchTarget, EscapeHatchUseEvent, EventDef, EventMigrationDef, EventPiiFields, EventPiiStance, EventUpcastCtx, EventUpcastFn, FetchForWritingArgs, HandlerContext, HandlerRef, JobContext, JobRunnerRef, JobTriggerEvent, MemberReader, NameOrRef, NotifyDeferred, NotifyDelivery, NotifyDeliveryStatus, NotifyDispatched, NotifyFactory, NotifyFn, NotifyJobDispatcher, NotifyOptions, NotifyPriority, NotifyResult, OpenToAllAccessRule, OpenToAllDeclaration, OpenToAllPersonalData, PayloadBucketNormalization, PayloadRateLimitOption, PreparedWriteEvent, QualifiedEventName, QueryEvent, QueryHandlerDef, QueryHandlerFn, RateLimitDeclaration, RateLimitDisabled, RateLimitOption, RateLimitPer, RoleAccessPersonalData, RoleAccessRule, SessionUser, SessionUserOrigin, StreamHandlerDef, StreamHandlerFn, TenantWriteTarget, TryAppendEventFn, TryAppendEventResult, UnsafeAppendEventFn, WriteEvent, WriteHandlerDef, WriteHandlerFn, WriteResult, } from "@cosmicdrift/kumiko-types/handlers";
|
|
11
11
|
export { DELIVERY_FAILURE_CODES, DELIVERY_SKIP_REASONS, HTTP_ERROR_CODE_PATTERN, isDeliveryErrorCode, isOpenToAllGranted, isRateLimitDisabled, isUiAccessGranted, } from "@cosmicdrift/kumiko-types/handlers";
|
|
12
12
|
export type { DeleteContext, EntityHookMap, HookMap, HookOwner, HookPhase, LifecycleHookFn, LifecycleResult, OwnedFn, PhasedHook, PostDeleteBatchHookFn, PostDeleteHookFn, PostQueryHookFn, PostSaveBatchHookFn, PostSaveHookFn, PreDeleteHookFn, PreQueryHookFn, PreSaveHookFn, SaveContext, SearchPayloadContributorFn, ValidationError, ValidationHookFn, } from "@cosmicdrift/kumiko-types/hooks";
|
|
13
13
|
export type { HttpRouteDefinition, HttpRouteHandler, HttpRouteHandlerDeps, HttpRouteMethod, } from "@cosmicdrift/kumiko-types/http-route";
|
package/dist/errors/classes.js
CHANGED
|
@@ -223,6 +223,7 @@ export class RateLimitError extends KumikoError {
|
|
|
223
223
|
message: `rate limited: ${details.bucket} (limit ${details.limit} per ${details.windowSeconds}s)`,
|
|
224
224
|
i18nKey: opts?.i18nKey ?? "errors.rate_limited",
|
|
225
225
|
details,
|
|
226
|
+
retryAfterSeconds: details.retryAfterSeconds,
|
|
226
227
|
...(opts?.cause && { cause: opts.cause }),
|
|
227
228
|
});
|
|
228
229
|
this.details = details;
|