@cosmicdrift/kumiko-framework 0.374.0 → 0.378.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 +2 -2
- package/agents/examples/apex-landing.md +1 -1
- package/agents/examples/apex-surface-auth.md +3 -3
- package/agents/examples/compose-stacks.md +15 -8
- package/agents/examples/delivery-notifications.md +25 -2
- package/agents/examples/embedded-entity-form.md +1 -1
- package/agents/examples/embedded.md +191 -0
- package/agents/examples/notes-history-basic.md +1 -1
- package/agents/examples/tags-basic.md +1 -1
- package/agents/examples/user-profile.md +1 -1
- package/agents/examples/wizard-form.md +4 -4
- package/agents/features.md +2 -2
- package/agents/widgets.md +4 -3
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.js +1 -1
- package/dist/api/routes.js +2 -2
- package/dist/api/server.js +2 -1
- package/dist/config/index.d.ts +4 -0
- package/dist/db/index.d.ts +2 -2
- package/dist/db/index.js +1 -1
- package/dist/db/migrate-runner.js +12 -4
- package/dist/db/money.d.ts +2 -1
- package/dist/db/money.js +3 -8
- package/dist/db/query.js +14 -14
- package/dist/db/replay-migration-sql.d.ts +1 -0
- package/dist/db/replay-migration-sql.js +115 -11
- package/dist/db/tenant-db.d.ts +7 -0
- package/dist/db/tenant-db.js +21 -5
- package/dist/engine/boot-validator/entity-handler.js +3 -0
- package/dist/engine/boot-validator/form-derivations.d.ts +4 -0
- package/dist/engine/boot-validator/form-derivations.js +82 -0
- package/dist/engine/boot-validator/screens.js +23 -4
- package/dist/engine/build-config-feature-schema.js +33 -10
- package/dist/engine/embedded-derived.d.ts +1 -1
- package/dist/engine/embedded-derived.js +7 -1
- package/dist/engine/form-derivations.d.ts +21 -0
- package/dist/engine/form-derivations.js +175 -0
- package/dist/engine/index.d.ts +5 -1
- package/dist/engine/index.js +3 -0
- package/dist/engine/minor-units.d.ts +2 -0
- package/dist/engine/minor-units.js +8 -0
- package/dist/engine/parse-ref-target.js +1 -1
- package/dist/engine/provider-plugin-context.d.ts +3 -0
- package/dist/engine/provider-plugin-context.js +28 -0
- package/dist/engine/schema-builder.js +7 -0
- package/dist/engine/template-variables.d.ts +2 -1
- package/dist/engine/template-variables.js +4 -0
- package/dist/engine/types/index.d.ts +3 -3
- package/dist/engine/with-form-derivations.d.ts +9 -0
- package/dist/engine/with-form-derivations.js +16 -0
- package/dist/env/index.js +1 -1
- package/dist/errors/zod-bridge.js +1 -1
- package/dist/files/file-ref-entity.d.ts +6 -6
- package/dist/files/file-ref-table.d.ts +6 -6
- package/dist/files/index.d.ts +1 -1
- package/dist/files/index.js +1 -1
- package/dist/files/provider-resolver.d.ts +17 -17
- package/dist/files/provider-resolver.js +1 -1
- package/dist/http/egress.d.ts +14 -0
- package/dist/http/egress.js +144 -0
- package/dist/http/index.d.ts +3 -2
- package/dist/http/index.js +2 -1
- package/dist/http/policy.d.ts +30 -0
- package/dist/http/policy.js +299 -0
- package/dist/i18n/required-surface-keys.js +2 -0
- package/dist/i18n/settings-hub-keys.js +1 -0
- package/dist/jobs/job-runner.js +4 -2
- package/dist/pipeline/config-accessor-factory.d.ts +3 -0
- package/dist/pipeline/config-accessor-factory.js +3 -0
- package/dist/pipeline/dispatch-shared.js +7 -5
- package/dist/pipeline/index.d.ts +1 -0
- package/dist/pipeline/index.js +1 -0
- package/dist/search/configure-tenant-index.d.ts +4 -0
- package/dist/search/configure-tenant-index.js +8 -0
- package/dist/search/index.d.ts +1 -0
- package/dist/search/index.js +1 -0
- package/dist/ui-types/index.d.ts +3 -1
- package/dist/ui-types/index.js +1 -0
- package/dist/upgrade-changelog.d.ts +25 -0
- package/dist/upgrade-changelog.js +48 -0
- package/dist/upgrade-cli.js +78 -13
- package/package.json +3 -5
- package/presets/biome-app.json +8 -0
- package/src/changes.json +103 -0
- package/src/scripts/codemod/README.md +25 -0
- package/src/scripts/codemod/codemod-support.ts +64 -6
- package/src/scripts/codemod/package-merge.ts +260 -0
- package/src/scripts/codemod/s3-imports.ts +4 -42
- package/src/scripts/codemod/testing-injection.ts +135 -0
|
@@ -46,6 +46,10 @@ const PROVIDER_PANEL_TITLE_ENTRY = {
|
|
|
46
46
|
es: "Proveedor",
|
|
47
47
|
};
|
|
48
48
|
const PROVIDER_SECTION_TITLE_KEY = "config.settings.provider";
|
|
49
|
+
// The empty option of a provider select means "no own provider, the platform handles it". A selector
|
|
50
|
+
// names it through `<field label key>.platformDefault`, else this generic text applies.
|
|
51
|
+
const PLATFORM_DEFAULT_LABEL_SUFFIX = ".platformDefault";
|
|
52
|
+
const PLATFORM_DEFAULT_FALLBACK_KEY = "config.settings.provider.platformDefault";
|
|
49
53
|
const SECRETS_SECTION_DESCRIPTION_KEY = "config.secrets.description";
|
|
50
54
|
const audienceNavShortId = (scope) => `audience-${scope}`;
|
|
51
55
|
// Generated post-boot, never via r.nav() — the boot validator exempts exactly these QNs (an app references one to place the settings group inline).
|
|
@@ -214,8 +218,8 @@ function buildFeatureNav(scope, feature, ordered, access, declaredTranslationKey
|
|
|
214
218
|
access,
|
|
215
219
|
};
|
|
216
220
|
}
|
|
217
|
-
// One configEdit screen + child-nav per feature; a tenant-scope selector owner
|
|
218
|
-
//
|
|
221
|
+
// One configEdit screen + child-nav per feature; a tenant-scope selector owner folds its
|
|
222
|
+
// gated plugins into its own screen (a dashboard when it carries extra panels).
|
|
219
223
|
function buildFeatureScreensAndNavs(scope, visible, hub, gating, secretsAccess) {
|
|
220
224
|
const out = { screens: [], navs: [], translations: {} };
|
|
221
225
|
const ungated = visible.filter((v) => !gating.pluginFeatures.has(v.key.ownerFeature));
|
|
@@ -230,7 +234,7 @@ function buildFeatureScreensAndNavs(scope, visible, hub, gating, secretsAccess)
|
|
|
230
234
|
out.navs.push(buildFeatureNav(scope, feature, ordered, access, hub.declaredTranslationKeys));
|
|
231
235
|
continue;
|
|
232
236
|
}
|
|
233
|
-
const ownerHub =
|
|
237
|
+
const ownerHub = buildSelectorOwnerScreens(feature, ordered, access, plugins, gating.ownerPanelsByGroup.get(feature) ?? [], hub, secretsAccess);
|
|
234
238
|
out.screens.push(...ownerHub.screens);
|
|
235
239
|
out.navs.push(buildFeatureNav(scope, feature, ordered, ownerHub.access, hub.declaredTranslationKeys));
|
|
236
240
|
Object.assign(out.translations, ownerHub.translations);
|
|
@@ -251,15 +255,19 @@ function assertUniquePanelIds(ownerGroup, panels) {
|
|
|
251
255
|
// One form for the provider and the settings of every provider: a single save writes
|
|
252
256
|
// all of it, so switching sections can't drop typed input. Each plugin's fields are
|
|
253
257
|
// shown only while its provider is picked in the form; hidden fields are not saved.
|
|
254
|
-
|
|
255
|
-
|
|
258
|
+
// Without extra panels the form is the owner's screen itself; with panels it sits
|
|
259
|
+
// chromeless in a dashboard.
|
|
260
|
+
function buildSelectorOwnerScreens(ownerGroup, ownerKeys, ownerAccess, plugins, ownerPanels, hub, secretsAccess) {
|
|
261
|
+
const hasPanels = ownerPanels.length > 0;
|
|
262
|
+
const selectionScreenId = hasPanels ? `${ownerGroup}-tenant-selection` : `${ownerGroup}-tenant`;
|
|
256
263
|
const form = new ProviderFormBuilder(selectionScreenId, buildScreen(selectionScreenId, "tenant", ownerGroup, ownerKeys, ownerAccess, hub, {
|
|
257
264
|
description: EXTENSION_SELECTOR_HINT_KEY,
|
|
258
|
-
sectionTitle: PROVIDER_SECTION_TITLE_KEY,
|
|
265
|
+
...(hasPanels && { sectionTitle: PROVIDER_SECTION_TITLE_KEY }),
|
|
259
266
|
}));
|
|
260
267
|
const accessRules = [ownerAccess];
|
|
261
268
|
const translations = {};
|
|
262
|
-
|
|
269
|
+
if (hasPanels)
|
|
270
|
+
translations[`screen:${selectionScreenId}.title`] = PROVIDER_PANEL_TITLE_ENTRY;
|
|
263
271
|
Object.assign(translations, providerOptionTranslations(form.base, hub));
|
|
264
272
|
for (const plugin of plugins) {
|
|
265
273
|
const providerFieldId = form.fieldIdOfConfigKey(plugin.selectorQn);
|
|
@@ -279,7 +287,10 @@ function buildSelectorOwnerDashboard(ownerGroup, ownerKeys, ownerAccess, plugins
|
|
|
279
287
|
}
|
|
280
288
|
}
|
|
281
289
|
const access = unionAccessRules(accessRules);
|
|
282
|
-
const
|
|
290
|
+
const providerForm = form.build(access);
|
|
291
|
+
if (!hasPanels)
|
|
292
|
+
return { screens: [providerForm], access, translations };
|
|
293
|
+
const selectionScreen = { ...providerForm, dormant: true };
|
|
283
294
|
const dashboard = {
|
|
284
295
|
id: `${ownerGroup}-tenant`,
|
|
285
296
|
type: "dashboard",
|
|
@@ -726,6 +737,14 @@ function providerOptionTranslations(selectionScreen, hub) {
|
|
|
726
737
|
const featureByPluginId = hub.selectorOptionFeatures.get(qn);
|
|
727
738
|
if (featureByPluginId === undefined)
|
|
728
739
|
continue;
|
|
740
|
+
const fieldLabelTranslationKey = selectionScreen.fieldLabels?.[fieldId];
|
|
741
|
+
const platformDefaultLabel = (fieldLabelTranslationKey !== undefined
|
|
742
|
+
? hub.translationsByKey.get(`${fieldLabelTranslationKey}${PLATFORM_DEFAULT_LABEL_SUFFIX}`)
|
|
743
|
+
: undefined) ?? hub.translationsByKey.get(PLATFORM_DEFAULT_FALLBACK_KEY);
|
|
744
|
+
if (platformDefaultLabel !== undefined) {
|
|
745
|
+
out[`${fieldLabelKey(SETTINGS_HUB_FEATURE, CONFIG_EDIT_ENTITY, fieldId)}:option:`] =
|
|
746
|
+
platformDefaultLabel;
|
|
747
|
+
}
|
|
729
748
|
for (const [pluginId, feature] of featureByPluginId) {
|
|
730
749
|
const label = hub.translationsByKey.get(`${feature}.settings`);
|
|
731
750
|
if (label === undefined)
|
|
@@ -738,8 +757,12 @@ function providerOptionTranslations(selectionScreen, hub) {
|
|
|
738
757
|
}
|
|
739
758
|
function deriveField(key, selectorPluginIds, screenKeys, fieldIdOf) {
|
|
740
759
|
const def = key.def;
|
|
741
|
-
if (selectorPluginIds !== undefined)
|
|
742
|
-
return createSelectField({
|
|
760
|
+
if (selectorPluginIds !== undefined) {
|
|
761
|
+
return createSelectField({
|
|
762
|
+
options: selectorPluginIds.length > 0 ? ["", ...selectorPluginIds] : selectorPluginIds,
|
|
763
|
+
display: "radio",
|
|
764
|
+
});
|
|
765
|
+
}
|
|
743
766
|
switch (def.type) {
|
|
744
767
|
case "number":
|
|
745
768
|
return createNumberField({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EmbeddedDerivedCellDef, EmbeddedSubFieldDef } from "./types/index.ts";
|
|
2
2
|
/** Computes a derived cell from its source values. Missing/non-numeric
|
|
3
|
-
* sources: "multiply" with any missing source → undefined; "sum"/"subtract"
|
|
3
|
+
* sources: "multiply"/"percent" with any missing source → undefined; "sum"/"subtract"
|
|
4
4
|
* treat missing as 0 only when at least one source is present — if every
|
|
5
5
|
* source is missing, return undefined (do not invent 0). Money cells are
|
|
6
6
|
* minor-unit integers — unit-agnostic arithmetic; `withDerivedCells` rounds
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
2
2
|
/** Computes a derived cell from its source values. Missing/non-numeric
|
|
3
|
-
* sources: "multiply" with any missing source → undefined; "sum"/"subtract"
|
|
3
|
+
* sources: "multiply"/"percent" with any missing source → undefined; "sum"/"subtract"
|
|
4
4
|
* treat missing as 0 only when at least one source is present — if every
|
|
5
5
|
* source is missing, return undefined (do not invent 0). Money cells are
|
|
6
6
|
* minor-unit integers — unit-agnostic arithmetic; `withDerivedCells` rounds
|
|
@@ -15,6 +15,12 @@ export function computeDerivedCellValue(op, values) {
|
|
|
15
15
|
}
|
|
16
16
|
return product;
|
|
17
17
|
}
|
|
18
|
+
if (op === "percent") {
|
|
19
|
+
const [base, rate] = values;
|
|
20
|
+
if (values.length !== 2 || base === undefined || rate === undefined)
|
|
21
|
+
return undefined;
|
|
22
|
+
return (base * rate) / 100;
|
|
23
|
+
}
|
|
18
24
|
if (values.every((value) => value === undefined))
|
|
19
25
|
return undefined;
|
|
20
26
|
const numeric = values.map((value) => value ?? 0);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { FormFieldDefinition } from "@cosmicdrift/kumiko-types/fields";
|
|
2
|
+
import type { FormFieldDerivation } from "./types/screen.ts";
|
|
3
|
+
export type FormDerivationScreen = {
|
|
4
|
+
readonly fields: Readonly<Record<string, FormFieldDefinition>>;
|
|
5
|
+
readonly derive?: Readonly<Record<string, FormFieldDerivation>>;
|
|
6
|
+
};
|
|
7
|
+
/** Targets ordered so every derived source is computed before its reader. */
|
|
8
|
+
export declare function orderFormDerivations(derive: Readonly<Record<string, FormFieldDerivation>>): readonly string[];
|
|
9
|
+
/** True when `field` is a `listSum` without `whenEmpty` whose list has no
|
|
10
|
+
* rows: the sum has nothing to add up, so the user types the value. */
|
|
11
|
+
export declare function isFormDerivationInputMode(screen: FormDerivationScreen, values: unknown, field: string): boolean;
|
|
12
|
+
/** True when `field` is only read as a `whenEmpty` operand of `listSum`
|
|
13
|
+
* derivations whose lists all have rows: nothing reads it, so it is locked
|
|
14
|
+
* rather than inviting an input that is silently dropped. */
|
|
15
|
+
export declare function isUnusedFallbackOperand(screen: FormDerivationScreen, values: unknown, field: string): boolean;
|
|
16
|
+
/** Recomputes every derived head field of an action form from its inputs.
|
|
17
|
+
* Pure and client-safe: the renderer runs it live, the server runs it
|
|
18
|
+
* again (`withFormDerivations`) and overwrites whatever the client sent.
|
|
19
|
+
* Money operands are converted to minor units for the arithmetic and money
|
|
20
|
+
* targets are written back as `{ amount, currency }` in major units. */
|
|
21
|
+
export declare function applyFormDerivations(values: unknown, screen: FormDerivationScreen): unknown;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
2
|
+
import { computeDerivedCellValue, roundDerivedCellValue, withDerivedCells, } from "./embedded-derived.js";
|
|
3
|
+
import { MINOR_UNIT_SCALE, toMinorUnits } from "./minor-units.js";
|
|
4
|
+
function readMoneyOperand(raw) {
|
|
5
|
+
if (typeof raw === "number")
|
|
6
|
+
return { minor: toMinorUnits(raw), currency: undefined };
|
|
7
|
+
if (!isPlainObject(raw) || typeof raw["amount"] !== "number")
|
|
8
|
+
return undefined;
|
|
9
|
+
const currency = raw["currency"];
|
|
10
|
+
return {
|
|
11
|
+
minor: toMinorUnits(raw["amount"]),
|
|
12
|
+
currency: typeof currency === "string" && currency !== "" ? currency : undefined,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function readOperand(values, fields, name) {
|
|
16
|
+
const raw = values[name];
|
|
17
|
+
if (fields[name]?.type === "money") {
|
|
18
|
+
const money = readMoneyOperand(raw);
|
|
19
|
+
return { value: money?.minor, currency: money?.currency };
|
|
20
|
+
}
|
|
21
|
+
return { value: typeof raw === "number" ? raw : undefined, currency: undefined };
|
|
22
|
+
}
|
|
23
|
+
function dependenciesOf(derivation) {
|
|
24
|
+
if (derivation.op === "listSum") {
|
|
25
|
+
return derivation.whenEmpty === undefined ? [] : derivation.whenEmpty.from;
|
|
26
|
+
}
|
|
27
|
+
return derivation.from;
|
|
28
|
+
}
|
|
29
|
+
/** Targets ordered so every derived source is computed before its reader. */
|
|
30
|
+
export function orderFormDerivations(derive) {
|
|
31
|
+
const ordered = [];
|
|
32
|
+
const state = new Map();
|
|
33
|
+
const visit = (name, path) => {
|
|
34
|
+
const derivation = derive[name];
|
|
35
|
+
// skip: plain input field, nothing to order
|
|
36
|
+
if (derivation === undefined)
|
|
37
|
+
return;
|
|
38
|
+
const status = state.get(name);
|
|
39
|
+
// skip: already ordered via another path
|
|
40
|
+
if (status === "done")
|
|
41
|
+
return;
|
|
42
|
+
if (status === "visiting") {
|
|
43
|
+
throw new Error(`Form derivation cycle: ${[...path, name].join(" -> ")}`);
|
|
44
|
+
}
|
|
45
|
+
state.set(name, "visiting");
|
|
46
|
+
for (const dependency of dependenciesOf(derivation))
|
|
47
|
+
visit(dependency, [...path, name]);
|
|
48
|
+
state.set(name, "done");
|
|
49
|
+
ordered.push(name);
|
|
50
|
+
};
|
|
51
|
+
for (const name of Object.keys(derive))
|
|
52
|
+
visit(name, []);
|
|
53
|
+
return ordered;
|
|
54
|
+
}
|
|
55
|
+
function isNonEmptyList(value) {
|
|
56
|
+
return Array.isArray(value) && value.length > 0;
|
|
57
|
+
}
|
|
58
|
+
/** True when `field` is a `listSum` without `whenEmpty` whose list has no
|
|
59
|
+
* rows: the sum has nothing to add up, so the user types the value. */
|
|
60
|
+
export function isFormDerivationInputMode(screen, values, field) {
|
|
61
|
+
const derivation = screen.derive?.[field];
|
|
62
|
+
if (derivation === undefined || derivation.op !== "listSum")
|
|
63
|
+
return false;
|
|
64
|
+
if (derivation.whenEmpty !== undefined)
|
|
65
|
+
return false;
|
|
66
|
+
return !isPlainObject(values) || !isNonEmptyList(values[derivation.list]);
|
|
67
|
+
}
|
|
68
|
+
/** True when `field` is only read as a `whenEmpty` operand of `listSum`
|
|
69
|
+
* derivations whose lists all have rows: nothing reads it, so it is locked
|
|
70
|
+
* rather than inviting an input that is silently dropped. */
|
|
71
|
+
export function isUnusedFallbackOperand(screen, values, field) {
|
|
72
|
+
const derive = screen.derive;
|
|
73
|
+
if (derive === undefined || derive[field] !== undefined)
|
|
74
|
+
return false;
|
|
75
|
+
let usedAsFallback = false;
|
|
76
|
+
for (const derivation of Object.values(derive)) {
|
|
77
|
+
if (derivation.op !== "listSum") {
|
|
78
|
+
if (derivation.from.includes(field))
|
|
79
|
+
return false;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (derivation.whenEmpty?.from.includes(field) !== true)
|
|
83
|
+
continue;
|
|
84
|
+
if (!isPlainObject(values) || !isNonEmptyList(values[derivation.list]))
|
|
85
|
+
return false;
|
|
86
|
+
usedAsFallback = true;
|
|
87
|
+
}
|
|
88
|
+
return usedAsFallback;
|
|
89
|
+
}
|
|
90
|
+
function sumListCell(rows, listField, cell) {
|
|
91
|
+
const embedded = listField?.type === "embedded" ? listField : undefined;
|
|
92
|
+
let sum = 0;
|
|
93
|
+
for (const rawRow of rows) {
|
|
94
|
+
// Recompute the row's derived cells so a manipulated row value can't leak into the head.
|
|
95
|
+
const row = embedded?.derived === undefined
|
|
96
|
+
? rawRow
|
|
97
|
+
: withDerivedCells(rawRow, embedded.derived, embedded.schema);
|
|
98
|
+
if (isPlainObject(row) && typeof row[cell] === "number")
|
|
99
|
+
sum += row[cell];
|
|
100
|
+
}
|
|
101
|
+
return sum;
|
|
102
|
+
}
|
|
103
|
+
function targetCurrency(field, current, sourceCurrency) {
|
|
104
|
+
if (isPlainObject(current) &&
|
|
105
|
+
typeof current["currency"] === "string" &&
|
|
106
|
+
current["currency"] !== "") {
|
|
107
|
+
return current["currency"];
|
|
108
|
+
}
|
|
109
|
+
if (sourceCurrency !== undefined)
|
|
110
|
+
return sourceCurrency;
|
|
111
|
+
if (field.type === "money" && field.currency.kind === "literal")
|
|
112
|
+
return field.currency.code;
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
/** Recomputes every derived head field of an action form from its inputs.
|
|
116
|
+
* Pure and client-safe: the renderer runs it live, the server runs it
|
|
117
|
+
* again (`withFormDerivations`) and overwrites whatever the client sent.
|
|
118
|
+
* Money operands are converted to minor units for the arithmetic and money
|
|
119
|
+
* targets are written back as `{ amount, currency }` in major units. */
|
|
120
|
+
export function applyFormDerivations(values, screen) {
|
|
121
|
+
if (!isPlainObject(values))
|
|
122
|
+
return values;
|
|
123
|
+
const derive = screen.derive;
|
|
124
|
+
if (derive === undefined)
|
|
125
|
+
return values;
|
|
126
|
+
const result = { ...values };
|
|
127
|
+
for (const target of orderFormDerivations(derive)) {
|
|
128
|
+
const derivation = derive[target];
|
|
129
|
+
const targetField = screen.fields[target];
|
|
130
|
+
if (derivation === undefined || targetField === undefined)
|
|
131
|
+
continue;
|
|
132
|
+
let computed;
|
|
133
|
+
let sourceCurrency;
|
|
134
|
+
let ordinary;
|
|
135
|
+
if (derivation.op === "listSum") {
|
|
136
|
+
const rows = result[derivation.list];
|
|
137
|
+
if (isNonEmptyList(rows)) {
|
|
138
|
+
computed = sumListCell(rows, screen.fields[derivation.list], derivation.cell);
|
|
139
|
+
}
|
|
140
|
+
else if (derivation.whenEmpty === undefined) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
ordinary = derivation.whenEmpty;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
ordinary = derivation;
|
|
149
|
+
}
|
|
150
|
+
if (ordinary !== undefined) {
|
|
151
|
+
const operands = ordinary.from.map((name) => readOperand(result, screen.fields, name));
|
|
152
|
+
sourceCurrency = operands.find((operand) => operand.currency !== undefined)?.currency;
|
|
153
|
+
computed = computeDerivedCellValue(ordinary.op, operands.map((operand) => operand.value));
|
|
154
|
+
}
|
|
155
|
+
if (computed === undefined) {
|
|
156
|
+
delete result[target];
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (targetField.type === "money") {
|
|
160
|
+
const minor = roundDerivedCellValue(computed, { type: "money" });
|
|
161
|
+
const currency = targetCurrency(targetField, result[target], sourceCurrency);
|
|
162
|
+
result[target] =
|
|
163
|
+
currency === undefined
|
|
164
|
+
? { amount: minor / MINOR_UNIT_SCALE }
|
|
165
|
+
: { amount: minor / MINOR_UNIT_SCALE, currency };
|
|
166
|
+
}
|
|
167
|
+
else if (targetField.type === "decimal") {
|
|
168
|
+
result[target] = roundDerivedCellValue(computed, targetField);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
result[target] = computed;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return result;
|
|
175
|
+
}
|
package/dist/engine/index.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export { addPattern, applyChanges, createFeaturePatcher, getPatternSchema, group
|
|
|
45
45
|
export { type ChangelogEntry, type ChangelogType, compareVersions, type FeatureChangelog, filterEntriesAfter, parseFeatureChangelog, sortEntries, validateChangelog, } from "./feature-changelog.ts";
|
|
46
46
|
export { type BuildManifestOptions, buildManifestFromRegistry, type FeatureManifest, type ManifestConfigKey, type ManifestExtension, type ManifestFeature, type ManifestSecret, type ManifestSystemEvent, serializeManifest, } from "./feature-manifest.ts";
|
|
47
47
|
export { checkWriteFieldOwnership, checkWriteFieldRoles, filterReadFields, maskWriteOnlyFields, } from "./field-access.ts";
|
|
48
|
+
export type { FormDerivationScreen } from "./form-derivations.ts";
|
|
49
|
+
export { applyFormDerivations, isFormDerivationInputMode } from "./form-derivations.ts";
|
|
48
50
|
export { HANDLER_FIELD_REFERENCES_META_KEY, type HandlerFieldPathSegment, type HandlerFieldReference, type HandlerFieldReferenceAtPath, type HandlerFieldReferenceMeta, handlerFieldReference, handlerFieldReferences, readHandlerFieldReferences, } from "./handler-field-references.ts";
|
|
49
51
|
export { resolveName, withResponseData } from "./handler-helpers.ts";
|
|
50
52
|
export { i18nKey } from "./i18n-key.ts";
|
|
@@ -57,6 +59,7 @@ export { parseRefTarget, parseRefTargetEntityName } from "./parse-ref-target.ts"
|
|
|
57
59
|
export { buildPipelineSteps, stepsPipeline } from "./pipeline.ts";
|
|
58
60
|
export { projectAppSchemaForRoles } from "./project-app-schema-for-roles.ts";
|
|
59
61
|
export { defineApply, defineMspApply, setFields } from "./projection-helpers.ts";
|
|
62
|
+
export { providerPluginContextFromHandler, providerPluginContextFromJob, } from "./provider-plugin-context.ts";
|
|
60
63
|
export type { BuiltinQnType, ParsedQn, QnType } from "./qualified-name.ts";
|
|
61
64
|
export { isValidQn, parseQn, QnTypes, qn, toKebab } from "./qualified-name.ts";
|
|
62
65
|
export { readClaim } from "./read-claim.ts";
|
|
@@ -84,7 +87,8 @@ export { assertNotificationVariables, templateVariable, templateVariablesToZod,
|
|
|
84
87
|
export { type EffectiveFeaturesResolver, findTierResolverUsage, isTierResolverPlugin, requireTierResolverPlugin, TENANT_TIER_RESOLVER_EXT, type TierResolverPlugin, type TrialGate, } from "./tier-resolver-extension.ts";
|
|
85
88
|
export { findOverDeepTransferChain, resolveTransferAdjacency as resolveEntityTransferAdjacency, type TransferAdjacency, type TransferEdge, type TransferEdgeLink, } from "./transfer-adjacency.ts";
|
|
86
89
|
export { isSystemTenant, isUuid, parseTenantId, SYSTEM_TENANT_ID } from "./types/identifiers.ts";
|
|
87
|
-
export type { AccessRule, ActionFormScreenDefinition, ActiveMembership, ActiveMembershipRejection, ActiveMembershipResult, AgentExposure, AgentHandlerHints, AgentRisk, AppContext, AppendEventArgs, AppendEventFn, AuthClaimsContext, AuthClaimsFn, AuthClaimsHookDef, BeforeTransactionPhase, BeforeTransactionResult, BelongsToRelation, BigIntFieldDef, BooleanFieldDef, BootOptionName, 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, DashboardCurrencyFormat, DashboardCustomPanel, DashboardDateParam, DashboardFeedPanel, DashboardFilterDefinition, DashboardI18nText, DashboardListPanel, DashboardMoneyParam, DashboardPanelDefinition, DashboardPanelVisibility, DashboardProgressListPanel, DashboardProgressTone, DashboardRecordBinding, DashboardRefresh, DashboardRelativeTimeFormat, DashboardRelativeTimeParam, DashboardScreenDefinition, DashboardScreenPanel, DashboardStatGroupPanel, DashboardStatPanel, DashboardText, DashboardTextBlock, DashboardTextPanel, 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, EntityObjectDefinition, 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, HookDefinition, HookMap, ImageFieldDef, ImagesFieldDef, JobBackoff, JobBackoffStrategy, JobContext, JobDefinition, JobHandlerFn, JobTrigger, JobTriggerEvent, JsonbFieldDef, KumikoEntityTypeMap, KumikoEventTypeMap, KumikoExtensionOptionsMap, KumikoHandlerPayloadMap, KumikoHandlerResultMap, LifecycleHookType, ListColumnSpec, ListFacetSpec, ListPaginationMode, ListSortDir, ListSortSpec, LongTextFindability, ManyToManyRelation, MaxPerTenantOptions, 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, StackBootRequirement, 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.ts";
|
|
90
|
+
export type { AccessRule, ActionFormScreenDefinition, ActiveMembership, ActiveMembershipRejection, ActiveMembershipResult, AgentExposure, AgentHandlerHints, AgentRisk, AppContext, AppendEventArgs, AppendEventFn, AuthClaimsContext, AuthClaimsFn, AuthClaimsHookDef, BeforeTransactionPhase, BeforeTransactionResult, BelongsToRelation, BigIntFieldDef, BooleanFieldDef, BootOptionName, 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, DashboardCurrencyFormat, DashboardCustomPanel, DashboardDateParam, DashboardFeedPanel, DashboardFilterDefinition, DashboardI18nText, DashboardListPanel, DashboardMoneyParam, DashboardPanelDefinition, DashboardPanelVisibility, DashboardProgressListPanel, DashboardProgressTone, DashboardRecordBinding, DashboardRefresh, DashboardRelativeTimeFormat, DashboardRelativeTimeParam, DashboardScreenDefinition, DashboardScreenPanel, DashboardStatGroupPanel, DashboardStatPanel, DashboardText, DashboardTextBlock, DashboardTextPanel, 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, EntityObjectDefinition, 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, HookDefinition, HookMap, ImageFieldDef, ImagesFieldDef, JobBackoff, JobBackoffStrategy, JobContext, JobDefinition, JobHandlerFn, JobTrigger, JobTriggerEvent, JsonbFieldDef, KumikoEntityTypeMap, KumikoEventTypeMap, KumikoExtensionOptionsMap, KumikoHandlerPayloadMap, KumikoHandlerResultMap, LifecycleHookType, ListColumnSpec, ListFacetSpec, ListPaginationMode, ListSortDir, ListSortSpec, LongTextFindability, ManyToManyRelation, MaxPerTenantOptions, 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, ProviderPluginContext, 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, StackBootRequirement, StreamHandlerDef, StreamHandlerFn, Subscribe, TargetRef, TemplateImagesVariableDef, 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.ts";
|
|
88
91
|
export { DEFAULT_CURRENCIES, DEFAULT_LOCALES, DELIVERY_FAILURE_CODES, DELIVERY_SKIP_REASONS, HookPhases, HTTP_ERROR_CODE_PATTERN, isAgentVisibleScreen, isDeliveryErrorCode, isOpenToAllGranted, resolveAgentExposure, } from "./types/index.ts";
|
|
89
92
|
export type { AwaitedEventType, PipelineBuildCtx, PipelineCtx, PipelineDef, StepBuilder, StepDef, StepFailureStrategy, StepInstance, StepKind, StepNamespace, StepResolver, } from "./types/step.ts";
|
|
90
93
|
export { runValidation } from "./validation.ts";
|
|
94
|
+
export { formDerivationsOfSchema, withFormDerivations } from "./with-form-derivations.ts";
|
package/dist/engine/index.js
CHANGED
|
@@ -40,6 +40,7 @@ export { addPattern, applyChanges, createFeaturePatcher, getPatternSchema, group
|
|
|
40
40
|
export { compareVersions, filterEntriesAfter, parseFeatureChangelog, sortEntries, validateChangelog, } from "./feature-changelog.js";
|
|
41
41
|
export { buildManifestFromRegistry, serializeManifest, } from "./feature-manifest.js";
|
|
42
42
|
export { checkWriteFieldOwnership, checkWriteFieldRoles, filterReadFields, maskWriteOnlyFields, } from "./field-access.js";
|
|
43
|
+
export { applyFormDerivations, isFormDerivationInputMode } from "./form-derivations.js";
|
|
43
44
|
export { HANDLER_FIELD_REFERENCES_META_KEY, handlerFieldReference, handlerFieldReferences, readHandlerFieldReferences, } from "./handler-field-references.js";
|
|
44
45
|
export { resolveName, withResponseData } from "./handler-helpers.js";
|
|
45
46
|
export { i18nKey } from "./i18n-key.js";
|
|
@@ -56,6 +57,7 @@ export { parseRefTarget, parseRefTargetEntityName } from "./parse-ref-target.js"
|
|
|
56
57
|
export { buildPipelineSteps, stepsPipeline } from "./pipeline.js";
|
|
57
58
|
export { projectAppSchemaForRoles } from "./project-app-schema-for-roles.js";
|
|
58
59
|
export { defineApply, defineMspApply, setFields } from "./projection-helpers.js";
|
|
60
|
+
export { providerPluginContextFromHandler, providerPluginContextFromJob, } from "./provider-plugin-context.js";
|
|
59
61
|
export { isValidQn, parseQn, QnTypes, qn, toKebab } from "./qualified-name.js";
|
|
60
62
|
export { readClaim } from "./read-claim.js";
|
|
61
63
|
export { createRegistry } from "./registry.js";
|
|
@@ -79,3 +81,4 @@ export { findOverDeepTransferChain, resolveTransferAdjacency as resolveEntityTra
|
|
|
79
81
|
export { isSystemTenant, isUuid, parseTenantId, SYSTEM_TENANT_ID } from "./types/identifiers.js";
|
|
80
82
|
export { DEFAULT_CURRENCIES, DEFAULT_LOCALES, DELIVERY_FAILURE_CODES, DELIVERY_SKIP_REASONS, HookPhases, HTTP_ERROR_CODE_PATTERN, isAgentVisibleScreen, isDeliveryErrorCode, isOpenToAllGranted, resolveAgentExposure, } from "./types/index.js";
|
|
81
83
|
export { runValidation } from "./validation.js";
|
|
84
|
+
export { formDerivationsOfSchema, withFormDerivations } from "./with-form-derivations.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// 2 decimal places (cents) — covers every currently-supported currency
|
|
2
|
+
// (EUR/USD/GBP/...). No ISO-4217 minor-unit table yet (JPY=0, BHD=3) —
|
|
3
|
+
// upgrade path once a currency needing a different scale actually lands.
|
|
4
|
+
// Client-safe on purpose: db/money.ts pulls in engine runtime values.
|
|
5
|
+
export const MINOR_UNIT_SCALE = 100;
|
|
6
|
+
export function toMinorUnits(amount) {
|
|
7
|
+
return Math.round(amount * MINOR_UNIT_SCALE);
|
|
8
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// - "users:user" → cross-feature ref (qualifiziert)
|
|
6
6
|
//
|
|
7
7
|
// Lebt im framework-Package damit Server-Validator + Renderer (über
|
|
8
|
-
// Re-Export aus @cosmicdrift/kumiko-headless) denselben Parser nutzen — die
|
|
8
|
+
// Re-Export aus @cosmicdrift/kumiko-renderer/headless) denselben Parser nutzen — die
|
|
9
9
|
// Convention darf nicht zweimal implementiert werden.
|
|
10
10
|
export function parseRefTarget(raw, currentFeature) {
|
|
11
11
|
const idx = raw.indexOf(":");
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { HandlerContext, JobContext, ProviderPluginContext } from "@cosmicdrift/kumiko-types/handlers";
|
|
2
|
+
export declare function providerPluginContextFromHandler(ctx: HandlerContext): ProviderPluginContext;
|
|
3
|
+
export declare function providerPluginContextFromJob(ctx: JobContext): ProviderPluginContext;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function providerPluginContextFromHandler(ctx) {
|
|
2
|
+
return {
|
|
3
|
+
db: ctx.db,
|
|
4
|
+
dbOutsideTransaction: ctx.dbOutsideTransaction,
|
|
5
|
+
systemDb: ctx.systemDb,
|
|
6
|
+
registry: ctx.registry,
|
|
7
|
+
user: ctx.user,
|
|
8
|
+
log: ctx.log,
|
|
9
|
+
triggeredBy: ctx.triggeredBy,
|
|
10
|
+
config: ctx.config,
|
|
11
|
+
secrets: ctx.secrets,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
// Jobs have no request user: the job's system user is the acting identity, and no
|
|
15
|
+
// outside-transaction handle exists because jobs never run inside the handler's tx.
|
|
16
|
+
export function providerPluginContextFromJob(ctx) {
|
|
17
|
+
return {
|
|
18
|
+
db: ctx.db,
|
|
19
|
+
dbOutsideTransaction: undefined,
|
|
20
|
+
systemDb: ctx.systemDb,
|
|
21
|
+
registry: ctx.registry,
|
|
22
|
+
user: ctx.systemUser,
|
|
23
|
+
log: ctx.log,
|
|
24
|
+
triggeredBy: ctx.triggeredBy,
|
|
25
|
+
config: ctx.config,
|
|
26
|
+
secrets: ctx.secrets,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -4,6 +4,7 @@ import { isValidIanaTimeZone } from "../time/index.js";
|
|
|
4
4
|
import { assertUnreachable } from "../utils/index.js";
|
|
5
5
|
import { withDerivedCells } from "./embedded-derived.js";
|
|
6
6
|
import { DEFAULT_CURRENCIES } from "./types/index.js";
|
|
7
|
+
const MONTH_PATTERN = /^\d{4}-(0[1-9]|1[0-2])$/;
|
|
7
8
|
// True if `n` carries at most `scale` decimal places. A relative epsilon
|
|
8
9
|
// tolerates float artifacts (`0.1 + 0.2 = 0.30000000000000004` is accepted at
|
|
9
10
|
// scale 2) — the exact `toFixed`-roundtrip-equality it replaces rejected such
|
|
@@ -106,6 +107,12 @@ export function fieldToZod(field, currencies, opts = {}) {
|
|
|
106
107
|
schema = schema.email();
|
|
107
108
|
if (field.format === "url")
|
|
108
109
|
schema = schema.url();
|
|
110
|
+
if (field.format === "month") {
|
|
111
|
+
// "" stays valid for an optional field; `required` rejects it via min(1) below.
|
|
112
|
+
schema = schema.refine((value) => value === "" || MONTH_PATTERN.test(value), {
|
|
113
|
+
message: "expected a month as YYYY-MM",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
109
116
|
if (field.required)
|
|
110
117
|
schema = schema.min(1);
|
|
111
118
|
return field.default !== undefined && applyDefaults ? schema.default(field.default) : schema;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
|
-
import type { Registry, TemplateListVariableDef, TemplateScalarVariableDef, TemplateVariables } from "./types/index.ts";
|
|
2
|
+
import type { Registry, TemplateImagesVariableDef, TemplateListVariableDef, TemplateScalarVariableDef, TemplateVariables } from "./types/index.ts";
|
|
3
3
|
type CommonOptions = {
|
|
4
4
|
readonly label?: string;
|
|
5
5
|
readonly required?: boolean;
|
|
@@ -26,6 +26,7 @@ export declare const templateVariable: {
|
|
|
26
26
|
readonly percent: (opts?: CommonOptions & {
|
|
27
27
|
readonly example?: number;
|
|
28
28
|
}) => TemplateScalarVariableDef;
|
|
29
|
+
readonly images: (opts?: CommonOptions) => TemplateImagesVariableDef;
|
|
29
30
|
readonly list: (item: Readonly<Record<string, TemplateScalarVariableDef>>, opts?: CommonOptions & {
|
|
30
31
|
readonly example?: readonly Readonly<Record<string, unknown>>[];
|
|
31
32
|
}) => TemplateListVariableDef;
|
|
@@ -17,6 +17,7 @@ export const templateVariable = {
|
|
|
17
17
|
...opts,
|
|
18
18
|
}),
|
|
19
19
|
percent: (opts = {}) => ({ type: "percent", ...opts }),
|
|
20
|
+
images: (opts = {}) => ({ type: "images", ...opts }),
|
|
20
21
|
list: (item, opts = {}) => ({ type: "list", item, ...opts }),
|
|
21
22
|
};
|
|
22
23
|
const LINK_PROTOCOLS = new Set(["http:", "https:", "mailto:"]);
|
|
@@ -53,7 +54,10 @@ function scalarToZod(def) {
|
|
|
53
54
|
return z.number().finite();
|
|
54
55
|
}
|
|
55
56
|
}
|
|
57
|
+
const imagesSchema = z.union([z.string(), z.array(z.string())]);
|
|
56
58
|
function variableToZod(def) {
|
|
59
|
+
if (def.type === "images")
|
|
60
|
+
return imagesSchema;
|
|
57
61
|
if (def.type !== "list")
|
|
58
62
|
return scalarToZod(def);
|
|
59
63
|
return z.array(z.object(Object.fromEntries(Object.entries(def.item).map(([name, item]) => [name, withOptionality(item)]))));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { isAgentVisibleScreen, resolveAgentExposure, } from "@cosmicdrift/kumiko-types/agent-exposure";
|
|
2
|
-
export type { ConfigAccessor, ConfigAccessorFactory, ConfigBacking, ConfigBounds, ConfigCascade, ConfigCascadeLevel, ConfigComputedContext, ConfigComputedFn, ConfigDefinition, ConfigKeyAccess, ConfigKeyDefinition, ConfigKeyHandle, ConfigKeyType, ConfigMask, ConfigResolver, ConfigSecretsReader, ConfigSeedDef, ConfigStoredRow, ConfigStoredRowWithSource, ConfigValidateContext, ConfigValidateFn, ConfigValue, ConfigValueSource, ConfigValueWithSource, CreateSeedOptions, CreateTenantSeedOptions, CreateUserSeedOptions, ExtensionSelectorDef, ExtensionSelectorPanel, JobBackoff, JobBackoffStrategy, JobDefinition, JobHandlerFn, JobRunIn, JobTrigger, MaxPerTenantOptions, NotificationDataFn, NotificationDefinition, NotificationLocalizedTemplate, NotificationRecipientFn, NotificationRecordMapping, NotificationTemplateBody, NotificationTemplateFn, NotificationTemplateMeta, PluralForms, ReferenceDataDef, RegistrarExtensionDef, RegistrarExtensionHooks, RegistrarExtensionRegistration, RunIn, TemplateListVariableDef, TemplateScalarVariableDef, TemplateVariableDef, TemplateVariables, TranslationEntry, TranslationKeys, TranslationsDef, TranslationValue, UiExtensionDef, } from "@cosmicdrift/kumiko-types/config";
|
|
2
|
+
export type { ConfigAccessor, ConfigAccessorFactory, ConfigBacking, ConfigBounds, ConfigCascade, ConfigCascadeLevel, ConfigComputedContext, ConfigComputedFn, ConfigDefinition, ConfigKeyAccess, ConfigKeyDefinition, ConfigKeyHandle, ConfigKeyType, ConfigMask, ConfigResolver, ConfigSecretsReader, ConfigSeedDef, ConfigStoredRow, ConfigStoredRowWithSource, ConfigValidateContext, ConfigValidateFn, ConfigValue, ConfigValueSource, ConfigValueWithSource, CreateSeedOptions, CreateTenantSeedOptions, CreateUserSeedOptions, ExtensionSelectorDef, ExtensionSelectorPanel, JobBackoff, JobBackoffStrategy, JobDefinition, JobHandlerFn, JobRunIn, JobTrigger, MaxPerTenantOptions, NotificationDataFn, NotificationDefinition, NotificationLocalizedTemplate, NotificationRecipientFn, NotificationRecordMapping, NotificationTemplateBody, NotificationTemplateFn, NotificationTemplateMeta, PluralForms, ReferenceDataDef, RegistrarExtensionDef, RegistrarExtensionHooks, RegistrarExtensionRegistration, RunIn, TemplateImagesVariableDef, TemplateListVariableDef, TemplateScalarVariableDef, TemplateVariableDef, TemplateVariables, TranslationEntry, TranslationKeys, TranslationsDef, TranslationValue, UiExtensionDef, } from "@cosmicdrift/kumiko-types/config";
|
|
3
3
|
export type { QueryHandlerDefinition, StreamHandlerDefinition, WriteHandlerDefinition, WriteHandlerInput, } from "@cosmicdrift/kumiko-types/define-handler";
|
|
4
4
|
export type { EntityCrudRegistrar, EntityCrudVerb, EntityHandlerOptions, EntityQueryHandlerOptions, EntityWriteHandlerOptions, RegisterEntityCrudOptions, } from "@cosmicdrift/kumiko-types/entity-handlers";
|
|
5
5
|
export type { KumikoEntityTypeMap, KumikoEventTypeMap, KumikoHandlerPayloadMap, KumikoHandlerResultMap, } from "@cosmicdrift/kumiko-types/event-type-map";
|
|
@@ -7,7 +7,7 @@ export type { ExtensionOptionsArgs, ExtensionOptionsFor, KumikoExtensionOptionsM
|
|
|
7
7
|
export type { BootCheckContext, BootCheckFn, BootOptionName, EntityObjectDefinition, FeatureDefinition, FeatureMetricDef, FeatureMetricType, FeatureRegistrar, HookDefinition, MetricOptions, Registry, SecretKeyDefinition, SecretKeyHandle, SecretNamespaceDefinition, SecretNamespaceHandle, SecretNamespaceOptions, SecretOptions, StackBootRequirement, 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, 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, RateLimitOptions, 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, ProviderPluginContext, QualifiedEventName, QueryEvent, QueryHandlerDef, QueryHandlerFn, RateLimitDeclaration, RateLimitDisabled, RateLimitOption, RateLimitOptions, 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, declaredRateLimitOptions, HTTP_ERROR_CODE_PATTERN, isDeliveryErrorCode, isOpenToAllGranted, isRateLimitDisabled, isRateLimitOption, 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";
|
|
@@ -17,7 +17,7 @@ export type { ContentCollectionDefinition, NavDefinition, NavIconKey, } from "@c
|
|
|
17
17
|
export type { FromRule, FromRuleKind, OwnershipClause, OwnershipMap, OwnershipRef, OwnershipRule, SqlFragment, WhereRule, WhereRuleContext, } from "@cosmicdrift/kumiko-types/ownership";
|
|
18
18
|
export type { EntityProjectionExtension, MspErrorMode, MspErrorPolicy, MultiStreamApplyFn, MultiStreamProjectionDefinition, ProjectionDefinition, ProjectionRowIdOf, ProjectionTable, SingleStreamApplyFn, } from "@cosmicdrift/kumiko-types/projection";
|
|
19
19
|
export type { BelongsToRelation, EntityRelations, HasManyRelation, ManyToManyRelation, RelationDefinition, } from "@cosmicdrift/kumiko-types/relations";
|
|
20
|
-
export type { ActionFormScreenDefinition, ConfigEditScreenDefinition, CustomScreenDefinition, CustomScreenRoute, DashboardChartMarkerKind, DashboardChartPanel, DashboardChartRangeOption, DashboardChartRanges, DashboardCurrencyFormat, DashboardCustomPanel, DashboardDateParam, DashboardFeedPanel, DashboardFilterDefinition, DashboardI18nText, DashboardListPanel, DashboardMoneyParam, DashboardPanelDefinition, DashboardPanelGate, DashboardPanelVisibility, DashboardProgressListPanel, DashboardProgressTone, DashboardRecordBinding, DashboardRefresh, DashboardRelativeTimeFormat, DashboardRelativeTimeParam, DashboardScreenDefinition, DashboardScreenPanel, DashboardStatGroupPanel, DashboardStatPanel, DashboardText, DashboardTextBlock, DashboardTextPanel, DashboardTextParam, DashboardValueFormat, EditExtensionSection, EditFieldPrefill, EditFieldSpec, EditFieldsSection, EditLayout, EditRelatedListSection, EditSectionSpec, EditWriteFormSection, EmbeddedListEditSpec, EntityEditAction, EntityEditActionPlacement, EntityEditScreenDefinition, EntityListExpandableRow, EntityListScreenDefinition, FieldCondition, FieldFormatRegistry, FieldRenderer, FormatSpec, FormWidth, ListColumnSpec, ListEmptyStateSpec, ListFacetSpec, ListPaginationMode, ListSortDir, ListSortSpec, MetricNavigate, MetricSpec, PlatformComponent, ProjectionDetailScreenDefinition, ProjectionListScreenDefinition, RecordHeaderSpec, RecordHeaderSubtitlePart, RowAction, RowActionNavigate, RowActionNavigateBase, RowActionWriteHandler, RowFieldExtractor, ScreenDefinition, ScreenFilter, ScreenFilterOp, ScreenNavSugar, ScreenSlots, SecretMintConfirmStep, SecretMintScreenDefinition, SecretReveal, SecretRevealField, SecretsEditScreenDefinition, SecretsEditSection, ToolbarAction, UnitKey, } from "@cosmicdrift/kumiko-types/screen";
|
|
20
|
+
export type { ActionFormScreenDefinition, ConfigEditScreenDefinition, CustomScreenDefinition, CustomScreenRoute, DashboardChartMarkerKind, DashboardChartPanel, DashboardChartRangeOption, DashboardChartRanges, DashboardCurrencyFormat, DashboardCustomPanel, DashboardDateParam, DashboardFeedPanel, DashboardFilterDefinition, DashboardI18nText, DashboardListPanel, DashboardMoneyParam, DashboardPanelDefinition, DashboardPanelGate, DashboardPanelVisibility, DashboardProgressListPanel, DashboardProgressTone, DashboardRecordBinding, DashboardRefresh, DashboardRelativeTimeFormat, DashboardRelativeTimeParam, DashboardScreenDefinition, DashboardScreenPanel, DashboardStatGroupPanel, DashboardStatPanel, DashboardText, DashboardTextBlock, DashboardTextPanel, DashboardTextParam, DashboardValueFormat, EditExtensionSection, EditFieldPrefill, EditFieldSpec, EditFieldsSection, EditLayout, EditRelatedListSection, EditSectionSpec, EditWriteFormSection, EmbeddedListEditSpec, EntityEditAction, EntityEditActionPlacement, EntityEditScreenDefinition, EntityListExpandableRow, EntityListScreenDefinition, FieldCondition, FieldFormatRegistry, FieldRenderer, FormatSpec, FormFieldDerivation, FormWidth, ListColumnSpec, ListEmptyStateSpec, ListFacetSpec, ListPaginationMode, ListSortDir, ListSortSpec, MetricNavigate, MetricSpec, PlatformComponent, ProjectionDetailScreenDefinition, ProjectionListScreenDefinition, RecordHeaderSpec, RecordHeaderSubtitlePart, RowAction, RowActionNavigate, RowActionNavigateBase, RowActionWriteHandler, RowFieldExtractor, ScreenDefinition, ScreenFilter, ScreenFilterOp, ScreenNavSugar, ScreenSlots, SecretMintConfirmStep, SecretMintScreenDefinition, SecretReveal, SecretRevealField, SecretsEditScreenDefinition, SecretsEditSection, ToolbarAction, UnitKey, } from "@cosmicdrift/kumiko-types/screen";
|
|
21
21
|
export { metricField, relatedListGroupHeaderLabel, relatedListGroupKey, } from "@cosmicdrift/kumiko-types/screen";
|
|
22
22
|
export type { TargetRef } from "@cosmicdrift/kumiko-types/target-ref";
|
|
23
23
|
export type { Subscribe, TreeAction, TreeActionDef, TreeActionsHandle, TreeChildrenSubscribe, TreeNode, TreeNodeState, } from "@cosmicdrift/kumiko-types/tree-node";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { type FormDerivationScreen } from "./form-derivations.ts";
|
|
3
|
+
import type { FormFieldDerivation } from "./types/screen.ts";
|
|
4
|
+
/** The `derive` a schema was wrapped with via `withFormDerivations`, so the
|
|
5
|
+
* boot validator can prove an actionForm's handler recomputes its totals. */
|
|
6
|
+
export declare function formDerivationsOfSchema(schema: unknown): Readonly<Record<string, FormFieldDerivation>> | undefined;
|
|
7
|
+
/** Server counterpart of an actionForm's `derive`: recomputes the derived head
|
|
8
|
+
* fields before the schema validates them, so client-sent totals never count. */
|
|
9
|
+
export declare function withFormDerivations<Schema extends z.ZodType>(schema: Schema, screen: FormDerivationScreen): z.ZodPreprocess<Schema, unknown>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { applyFormDerivations } from "./form-derivations.js";
|
|
3
|
+
const derivationsBySchema = new WeakMap();
|
|
4
|
+
/** The `derive` a schema was wrapped with via `withFormDerivations`, so the
|
|
5
|
+
* boot validator can prove an actionForm's handler recomputes its totals. */
|
|
6
|
+
export function formDerivationsOfSchema(schema) {
|
|
7
|
+
return schema instanceof z.ZodType ? derivationsBySchema.get(schema) : undefined;
|
|
8
|
+
}
|
|
9
|
+
/** Server counterpart of an actionForm's `derive`: recomputes the derived head
|
|
10
|
+
* fields before the schema validates them, so client-sent totals never count. */
|
|
11
|
+
export function withFormDerivations(schema, screen) {
|
|
12
|
+
const wrapped = z.preprocess((value) => applyFormDerivations(value, screen), schema);
|
|
13
|
+
if (screen.derive !== undefined)
|
|
14
|
+
derivationsBySchema.set(wrapped, screen.derive);
|
|
15
|
+
return wrapped;
|
|
16
|
+
}
|
package/dist/env/index.js
CHANGED
|
@@ -277,7 +277,7 @@ function formatBootError(errors) {
|
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
279
|
lines.push("");
|
|
280
|
-
lines.push("See:
|
|
280
|
+
lines.push("See: https://github.com/CosmicDriftGameStudio/infra/blob/main/docs/runbooks/standard-deploy-app.md#schritt-1--env-vertrag-lokal-ausgeben");
|
|
281
281
|
return lines.join("\n");
|
|
282
282
|
}
|
|
283
283
|
export class KumikoBootError extends Error {
|
|
@@ -35,7 +35,7 @@ export function validationErrorFromZod(error) {
|
|
|
35
35
|
// `code: "custom"` is zod's catch-all for every superRefine/refine check;
|
|
36
36
|
// left mechanical it'd collapse onto one generic key, so a superRefine can
|
|
37
37
|
// set `params.i18nKey` to override it. Keep in sync with the client mirror
|
|
38
|
-
// (packages/
|
|
38
|
+
// (packages/renderer/src/headless/form/zod-bridge.ts).
|
|
39
39
|
function resolveI18nKey(issue) {
|
|
40
40
|
if (issue.code === "custom") {
|
|
41
41
|
const override = issue.params?.["i18nKey"];
|