@bsm-form/core 0.40.0 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +189 -31
- package/dist/index.d.cts +22 -3
- package/dist/index.d.ts +22 -3
- package/dist/index.js +183 -31
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -27,25 +27,86 @@ __export(index_exports, {
|
|
|
27
27
|
adaptDisplay: () => adaptDisplay,
|
|
28
28
|
adaptField: () => adaptField,
|
|
29
29
|
adaptLayout: () => adaptLayout,
|
|
30
|
+
collectFormEnvReferences: () => collectFormEnvReferences,
|
|
30
31
|
compileSchema: () => compileSchema,
|
|
31
32
|
conditions: () => conditions,
|
|
32
33
|
createFormEngineEnvironment: () => createFormEngineEnvironment,
|
|
33
34
|
createRuntimeNode: () => createRuntimeNode,
|
|
34
35
|
formatTagSourceValue: () => formatTagSourceValue,
|
|
35
36
|
isThenable: () => isThenable,
|
|
37
|
+
remapFormEnvReferences: () => remapFormEnvReferences,
|
|
38
|
+
resolveFormEnv: () => resolveFormEnv,
|
|
36
39
|
resolveLoadingProp: () => resolveLoadingProp,
|
|
37
40
|
resolveTagValueMapping: () => resolveTagValueMapping,
|
|
38
41
|
runConditionEngine: () => runConditionEngine,
|
|
39
42
|
tagMapKey: () => tagMapKey,
|
|
40
43
|
traverseNode: () => traverseNode,
|
|
41
44
|
validateApiUrlTemplate: () => validateApiUrlTemplate,
|
|
45
|
+
validateEnvBaseUrl: () => validateEnvBaseUrl,
|
|
46
|
+
validateEnvDefinitions: () => validateEnvDefinitions,
|
|
42
47
|
validateField: () => validateField,
|
|
48
|
+
validateFormEnvReferences: () => validateFormEnvReferences,
|
|
43
49
|
validateSchema: () => validateSchema,
|
|
44
50
|
validation: () => validation,
|
|
45
51
|
validationRegistry: () => validationRegistry
|
|
46
52
|
});
|
|
47
53
|
module.exports = __toCommonJS(index_exports);
|
|
48
54
|
|
|
55
|
+
// src/utils/form-env.ts
|
|
56
|
+
function validateEnvBaseUrl(value) {
|
|
57
|
+
if (!value || /[\\\s?#]/.test(value) || value.startsWith("//")) throw new Error("Base URL cannot contain whitespace, query, fragment, or backslashes");
|
|
58
|
+
if (value.startsWith("/")) return;
|
|
59
|
+
let url;
|
|
60
|
+
try {
|
|
61
|
+
url = new URL(value);
|
|
62
|
+
} catch {
|
|
63
|
+
throw new Error("Expected an http(s) URL or path starting with /");
|
|
64
|
+
}
|
|
65
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) throw new Error("Base URL must use http(s) without credentials");
|
|
66
|
+
}
|
|
67
|
+
function validateValue(name, type, value) {
|
|
68
|
+
if (typeof value !== (type === "url" ? "string" : type) || typeof value === "number" && !Number.isFinite(value)) throw new Error(`env.${name}: expected ${String(type)}`);
|
|
69
|
+
if (type === "url") {
|
|
70
|
+
try {
|
|
71
|
+
validateEnvBaseUrl(value);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
throw new Error(`env.${name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function validateEnvDefinitions(value) {
|
|
78
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Environment definitions must be an object");
|
|
79
|
+
for (const [name, entry] of Object.entries(value)) {
|
|
80
|
+
if (!/^[A-Za-z_]+$/.test(name) || ["__proto__", "prototype", "constructor"].includes(name)) throw new Error(`Invalid environment variable name: ${name}`);
|
|
81
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) throw new Error(`env.${name}: expected a definition`);
|
|
82
|
+
const definition = entry;
|
|
83
|
+
if (!["string", "number", "boolean", "url"].includes(String(definition.type))) throw new Error(`env.${name}: invalid type`);
|
|
84
|
+
for (const key of Object.keys(definition)) if (!["type", "label", "description", "default", "required", "allowOverride"].includes(key)) throw new Error(`env.${name}: unknown property ${key}`);
|
|
85
|
+
for (const key of ["required", "allowOverride"]) if (definition[key] !== void 0 && typeof definition[key] !== "boolean") throw new Error(`env.${name}.${key}: expected boolean`);
|
|
86
|
+
for (const key of ["label", "description"]) if (definition[key] !== void 0 && typeof definition[key] !== "string") throw new Error(`env.${name}.${key}: expected string`);
|
|
87
|
+
if (Object.hasOwn(definition, "default")) validateValue(name, definition.type, definition.default);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function resolveFormEnv(config = {}, requireValues = true) {
|
|
91
|
+
const definitions = config.definitions ?? {};
|
|
92
|
+
const overrides = config.overrides ?? {};
|
|
93
|
+
validateEnvDefinitions(definitions);
|
|
94
|
+
if (typeof overrides !== "object" || Array.isArray(overrides)) throw new Error("Environment overrides must be an object");
|
|
95
|
+
for (const name of Object.keys(overrides)) {
|
|
96
|
+
if (!Object.hasOwn(definitions, name)) throw new Error(`env.${name}: unknown override`);
|
|
97
|
+
if (definitions[name]?.allowOverride === false) throw new Error(`env.${name}: overrides are disabled`);
|
|
98
|
+
}
|
|
99
|
+
const values = {};
|
|
100
|
+
for (const [name, definition] of Object.entries(definitions)) {
|
|
101
|
+
const provided = Object.hasOwn(overrides, name);
|
|
102
|
+
const value = provided ? overrides[name] : definition.default;
|
|
103
|
+
if (provided || value !== void 0) validateValue(name, definition.type, value);
|
|
104
|
+
if (requireValues && definition.required && (value === void 0 || value === "")) throw new Error(`env.${name}: required value is missing`);
|
|
105
|
+
if (value !== void 0) values[name] = value;
|
|
106
|
+
}
|
|
107
|
+
return Object.freeze(values);
|
|
108
|
+
}
|
|
109
|
+
|
|
49
110
|
// src/utils/get-path.ts
|
|
50
111
|
function pathSegments(path) {
|
|
51
112
|
const segments = [];
|
|
@@ -93,6 +154,8 @@ function getPath(obj, path) {
|
|
|
93
154
|
|
|
94
155
|
// src/utils/scope-path.ts
|
|
95
156
|
function resolveScopedPath(ctx, path) {
|
|
157
|
+
if (path === "env") return ctx.env ?? {};
|
|
158
|
+
if (path.startsWith("env.")) return getPath(ctx.env ?? {}, path.slice(4));
|
|
96
159
|
if (path === "$values" || path === "values") {
|
|
97
160
|
return ctx.values;
|
|
98
161
|
}
|
|
@@ -120,7 +183,7 @@ function resolveScopedPath(ctx, path) {
|
|
|
120
183
|
return getPath(ctx.values, path);
|
|
121
184
|
}
|
|
122
185
|
function isExplicitScopedPath(path) {
|
|
123
|
-
return path === "$values" || path === "values" || path === "$resources" || path === "resources" || path === "$row" || path === "row" || path === "$item" || path === "item" || path.startsWith("values.") || path.startsWith("resources.") || path.startsWith("row.") || path.startsWith("item.");
|
|
186
|
+
return path === "env" || path.startsWith("env.") || path === "$values" || path === "values" || path === "$resources" || path === "resources" || path === "$row" || path === "row" || path === "$item" || path === "item" || path.startsWith("values.") || path.startsWith("resources.") || path.startsWith("row.") || path.startsWith("item.");
|
|
124
187
|
}
|
|
125
188
|
function resolvePathStringsInData(value, ctx) {
|
|
126
189
|
if (typeof value === "string") {
|
|
@@ -140,7 +203,7 @@ function resolvePathStringsInData(value, ctx) {
|
|
|
140
203
|
}
|
|
141
204
|
|
|
142
205
|
// src/utils/url-template.ts
|
|
143
|
-
var pathPattern = /^(values|resources)\.[A-Za-z_$][\w$]*(?:(?:\.(?:[A-Za-z_$][\w$]*|\d+))|(?:\[\d+\]))*$/;
|
|
206
|
+
var pathPattern = /^(values|resources|env)\.[A-Za-z_$][\w$]*(?:(?:\.(?:[A-Za-z_$][\w$]*|\d+))|(?:\[\d+\]))*$/;
|
|
144
207
|
function placeholders(url) {
|
|
145
208
|
const result = [];
|
|
146
209
|
let cursor = 0;
|
|
@@ -151,10 +214,10 @@ function placeholders(url) {
|
|
|
151
214
|
if (close < 0) throw new Error("API URL template has an unclosed placeholder");
|
|
152
215
|
const path = url.slice(start + 2, close).trim();
|
|
153
216
|
if (!pathPattern.test(path)) {
|
|
154
|
-
throw new Error(`Invalid API URL placeholder: ${path}. Use a values
|
|
217
|
+
throw new Error(`Invalid API URL placeholder: ${path}. Use a values.*, resources.*, or env.* data path`);
|
|
155
218
|
}
|
|
156
219
|
const origin = url.match(/^(?:[A-Za-z][A-Za-z0-9+.-]*:)?\/\/[^/?#]*/)?.[0];
|
|
157
|
-
if (start === 0 || origin && start < origin.length) {
|
|
220
|
+
if (start === 0 && !path.startsWith("env.") || origin && start < origin.length) {
|
|
158
221
|
throw new Error("API URL placeholders cannot replace the service origin; use a static base URL");
|
|
159
222
|
}
|
|
160
223
|
result.push({ start, end: close + 1, path });
|
|
@@ -166,7 +229,7 @@ function validateApiUrlTemplate(url) {
|
|
|
166
229
|
if (!url.trim()) throw new Error("API URL must not be empty");
|
|
167
230
|
placeholders(url);
|
|
168
231
|
}
|
|
169
|
-
function resolveApiUrlTemplate(url, context) {
|
|
232
|
+
function resolveApiUrlTemplate(url, context, definitions = {}) {
|
|
170
233
|
validateApiUrlTemplate(url);
|
|
171
234
|
let result = "";
|
|
172
235
|
let cursor = 0;
|
|
@@ -178,7 +241,17 @@ function resolveApiUrlTemplate(url, context) {
|
|
|
178
241
|
if (value === "." || value === "..") {
|
|
179
242
|
throw new Error(`API URL placeholder ${placeholder.path} cannot be a dot segment`);
|
|
180
243
|
}
|
|
181
|
-
|
|
244
|
+
if (placeholder.start === 0 && placeholder.path.startsWith("env.")) {
|
|
245
|
+
if (definitions[placeholder.path.slice(4)]?.type !== "url" || typeof value !== "string") {
|
|
246
|
+
throw new Error(`${placeholder.path}: only an env variable of type url can supply a base URL`);
|
|
247
|
+
}
|
|
248
|
+
validateEnvBaseUrl(value);
|
|
249
|
+
const suffix = url.slice(placeholder.end);
|
|
250
|
+
if (suffix && !suffix.startsWith("/")) throw new Error("Base URL placeholder must be followed by / or end of URL");
|
|
251
|
+
result += suffix.startsWith("/") ? value.replace(/\/+$/, "") : value;
|
|
252
|
+
} else {
|
|
253
|
+
result += url.slice(cursor, placeholder.start) + encodeURIComponent(String(value));
|
|
254
|
+
}
|
|
182
255
|
cursor = placeholder.end;
|
|
183
256
|
}
|
|
184
257
|
return result + url.slice(cursor);
|
|
@@ -1602,6 +1675,7 @@ function validateDrawerStepReferences(context) {
|
|
|
1602
1675
|
}
|
|
1603
1676
|
function validateStepConditionReferences(context) {
|
|
1604
1677
|
context.stepConditionReferences.forEach(({ path, field }) => {
|
|
1678
|
+
if (field.startsWith("env.")) return;
|
|
1605
1679
|
if ((0, import_schema.isReservedConditionField)(field)) {
|
|
1606
1680
|
if ((0, import_schema.isPendingActionConditionField)(field)) {
|
|
1607
1681
|
const actionId = (0, import_schema.pendingActionIdFromField)(field);
|
|
@@ -1762,6 +1836,7 @@ var validationRegistry = {
|
|
|
1762
1836
|
// src/engine/environment.ts
|
|
1763
1837
|
function createFormEngineEnvironment(overrides = {}) {
|
|
1764
1838
|
return {
|
|
1839
|
+
env: overrides.env ?? Object.freeze({}),
|
|
1765
1840
|
request: overrides.request ?? defaultRequest,
|
|
1766
1841
|
log: overrides.log ?? defaultLog,
|
|
1767
1842
|
navigate: overrides.navigate ?? defaultNavigate,
|
|
@@ -1831,6 +1906,7 @@ function runInvokeValidator(rule, options) {
|
|
|
1831
1906
|
const ctx = {
|
|
1832
1907
|
values: options?.values ?? {},
|
|
1833
1908
|
resources: options?.resources ?? {},
|
|
1909
|
+
env: options?.env ?? options?.environment?.env,
|
|
1834
1910
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
1835
1911
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
1836
1912
|
};
|
|
@@ -1926,6 +2002,7 @@ function evaluateCondition(condition, values, state, options) {
|
|
|
1926
2002
|
}
|
|
1927
2003
|
const target = resolveConditionTarget(condition.field, values, state, {
|
|
1928
2004
|
resources: options?.resources,
|
|
2005
|
+
env: options?.env ?? options?.environment?.env,
|
|
1929
2006
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
1930
2007
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
1931
2008
|
});
|
|
@@ -1972,6 +2049,7 @@ function evaluateInvokeCondition(name, values, options) {
|
|
|
1972
2049
|
const ctx = {
|
|
1973
2050
|
values,
|
|
1974
2051
|
resources: options?.resources ?? {},
|
|
2052
|
+
env: options?.env ?? options?.environment?.env,
|
|
1975
2053
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
1976
2054
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
1977
2055
|
};
|
|
@@ -2026,6 +2104,7 @@ function resolveConditionTarget(field, values, state, options) {
|
|
|
2026
2104
|
{
|
|
2027
2105
|
values,
|
|
2028
2106
|
resources: options?.resources ?? {},
|
|
2107
|
+
env: options?.env,
|
|
2029
2108
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
2030
2109
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
2031
2110
|
},
|
|
@@ -2319,7 +2398,7 @@ var DependencyGraph = class {
|
|
|
2319
2398
|
return ordered;
|
|
2320
2399
|
}
|
|
2321
2400
|
extractDependencies(conditions2) {
|
|
2322
|
-
return conditions2.filter(import_schema4.isFieldCondition).map((condition) => condition.field).filter((field) => !(0, import_schema4.isReservedConditionField)(field));
|
|
2401
|
+
return conditions2.filter(import_schema4.isFieldCondition).map((condition) => condition.field).filter((field) => !(0, import_schema4.isReservedConditionField)(field) && !field.startsWith("env."));
|
|
2323
2402
|
}
|
|
2324
2403
|
clear() {
|
|
2325
2404
|
this.forward.clear();
|
|
@@ -2341,10 +2420,52 @@ var EventBus = class {
|
|
|
2341
2420
|
}
|
|
2342
2421
|
};
|
|
2343
2422
|
|
|
2423
|
+
// src/utils/env-references.ts
|
|
2424
|
+
var pathKeys = /* @__PURE__ */ new Set(["from", "source", "field", "path", "bodyFrom", "queryFrom"]);
|
|
2425
|
+
function visit(value, path, key, data, transform) {
|
|
2426
|
+
if (typeof value === "string") {
|
|
2427
|
+
if (key === "url") return transform(value, path, true);
|
|
2428
|
+
if ((pathKeys.has(key) || path.endsWith(".bind.value") || data) && /^env\.[A-Za-z_]/.test(value)) return transform(value, path, false);
|
|
2429
|
+
return value;
|
|
2430
|
+
}
|
|
2431
|
+
if (Array.isArray(value)) return value.map((item, index) => visit(item, `${path}[${index}]`, key, data, transform));
|
|
2432
|
+
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [
|
|
2433
|
+
childKey,
|
|
2434
|
+
visit(child, `${path}.${childKey}`, childKey, data || childKey === "body" || childKey === "query", transform)
|
|
2435
|
+
]));
|
|
2436
|
+
return value;
|
|
2437
|
+
}
|
|
2438
|
+
function collectFormEnvReferences(schema) {
|
|
2439
|
+
const references = [];
|
|
2440
|
+
visit(schema, "schema", "", false, (value, path, template) => {
|
|
2441
|
+
if (template) {
|
|
2442
|
+
for (const match of value.matchAll(/\$\{\s*env\.([A-Za-z_][A-Za-z0-9_]*)\s*\}/g)) {
|
|
2443
|
+
references.push({ name: match[1], path });
|
|
2444
|
+
}
|
|
2445
|
+
} else references.push({ name: value.slice(4).split(/[.[]/)[0], path });
|
|
2446
|
+
return value;
|
|
2447
|
+
});
|
|
2448
|
+
return references;
|
|
2449
|
+
}
|
|
2450
|
+
function validateFormEnvReferences(schema, definitions = {}) {
|
|
2451
|
+
for (const reference of collectFormEnvReferences(schema)) {
|
|
2452
|
+
if (!Object.hasOwn(definitions, reference.name)) throw new Error(`${reference.path}: undefined environment variable env.${reference.name}`);
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
function remapFormEnvReferences(schema, previous, next) {
|
|
2456
|
+
return visit(schema, "schema", "", false, (value, _path, template) => {
|
|
2457
|
+
if (template) return value.replace(
|
|
2458
|
+
/\$\{\s*env\.([A-Za-z_][A-Za-z0-9_]*)\s*\}/g,
|
|
2459
|
+
(original, name) => name === previous ? "${env." + next + "}" : original
|
|
2460
|
+
);
|
|
2461
|
+
return value === `env.${previous}` ? `env.${next}` : value;
|
|
2462
|
+
});
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2344
2465
|
// src/pipeline/run-conditions.ts
|
|
2345
2466
|
function runConditions(ctx) {
|
|
2346
|
-
|
|
2347
|
-
function
|
|
2467
|
+
visit2(ctx.tree, true);
|
|
2468
|
+
function visit2(node, parentVisible) {
|
|
2348
2469
|
const schema = node.schema;
|
|
2349
2470
|
const hasVisibilityConditions = schema.kind === "field" || schema.kind === "display" || schema.kind === "action" || schema.kind === "layout" && schema.type === "step";
|
|
2350
2471
|
if (!parentVisible) {
|
|
@@ -2362,7 +2483,7 @@ function runConditions(ctx) {
|
|
|
2362
2483
|
}
|
|
2363
2484
|
node.state.disabled = evaluateDisabled(schema, ctx);
|
|
2364
2485
|
const childrenVisible = node.state.visible && !(schema.kind === "layout" && (schema.type === "dialog" || schema.type === "drawer") && node.state.open !== true);
|
|
2365
|
-
node.children.forEach((child) =>
|
|
2486
|
+
node.children.forEach((child) => visit2(child, childrenVisible));
|
|
2366
2487
|
}
|
|
2367
2488
|
}
|
|
2368
2489
|
function evaluateDisabled(schema, ctx) {
|
|
@@ -2481,6 +2602,7 @@ function executeInvokeComputer(name, values, options) {
|
|
|
2481
2602
|
const ctx = {
|
|
2482
2603
|
values,
|
|
2483
2604
|
resources: options?.resources ?? {},
|
|
2605
|
+
env: options?.env ?? options?.environment?.env,
|
|
2484
2606
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
2485
2607
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
2486
2608
|
};
|
|
@@ -2829,6 +2951,7 @@ var StepRunner = class {
|
|
|
2829
2951
|
(condition) => evaluateCondition(condition, values, conditionState, {
|
|
2830
2952
|
environment: this.environment,
|
|
2831
2953
|
resources: this.engine.getResources(),
|
|
2954
|
+
env: this.engine.getEnv(),
|
|
2832
2955
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
2833
2956
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
2834
2957
|
})
|
|
@@ -2913,6 +3036,7 @@ var StepRunner = class {
|
|
|
2913
3036
|
return {
|
|
2914
3037
|
values: this.engine.getValues(),
|
|
2915
3038
|
resources: this.engine.getResources(),
|
|
3039
|
+
env: this.engine.getEnv(),
|
|
2916
3040
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
2917
3041
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
2918
3042
|
};
|
|
@@ -2921,6 +3045,7 @@ var StepRunner = class {
|
|
|
2921
3045
|
return {
|
|
2922
3046
|
values: this.engine.getValues(),
|
|
2923
3047
|
resources: this.engine.getResources(),
|
|
3048
|
+
env: this.engine.getEnv(),
|
|
2924
3049
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
2925
3050
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
2926
3051
|
};
|
|
@@ -2930,6 +3055,7 @@ function resolveApiRequestBody(step, engine, options) {
|
|
|
2930
3055
|
const ctx = {
|
|
2931
3056
|
values: engine.getValues(),
|
|
2932
3057
|
resources: engine.getResources(),
|
|
3058
|
+
env: engine.getEnv(),
|
|
2933
3059
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
2934
3060
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
2935
3061
|
};
|
|
@@ -2949,10 +3075,11 @@ function resolveApiRequestUrl(step, engine, options) {
|
|
|
2949
3075
|
const ctx = {
|
|
2950
3076
|
values: engine.getValues(),
|
|
2951
3077
|
resources: engine.getResources(),
|
|
3078
|
+
env: engine.getEnv(),
|
|
2952
3079
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
2953
3080
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
2954
3081
|
};
|
|
2955
|
-
const url = resolveApiUrlTemplate(step.request.url, ctx);
|
|
3082
|
+
const url = resolveApiUrlTemplate(step.request.url, ctx, engine.getEnvDefinitions());
|
|
2956
3083
|
const hasQueryFrom = "queryFrom" in step.request && step.request.queryFrom !== void 0;
|
|
2957
3084
|
const hasQuery = "query" in step.request && step.request.query !== void 0;
|
|
2958
3085
|
if (hasQueryFrom && hasQuery) {
|
|
@@ -3245,11 +3372,11 @@ function collectTemplate(node, fields, nested) {
|
|
|
3245
3372
|
}
|
|
3246
3373
|
function collectValuesRepeatFieldIds(metas) {
|
|
3247
3374
|
const ids = /* @__PURE__ */ new Set();
|
|
3248
|
-
const
|
|
3375
|
+
const visit2 = (meta) => {
|
|
3249
3376
|
meta.fieldTemplates.forEach((node) => ids.add(node.id));
|
|
3250
|
-
meta.nested.forEach(
|
|
3377
|
+
meta.nested.forEach(visit2);
|
|
3251
3378
|
};
|
|
3252
|
-
metas.forEach(
|
|
3379
|
+
metas.forEach(visit2);
|
|
3253
3380
|
return ids;
|
|
3254
3381
|
}
|
|
3255
3382
|
function createDefaultRepeatItem(meta) {
|
|
@@ -3337,9 +3464,11 @@ function findValuesRepeatMeta(metas, repeatId) {
|
|
|
3337
3464
|
|
|
3338
3465
|
// src/engine/form-engine.ts
|
|
3339
3466
|
var FormEngine = class {
|
|
3340
|
-
constructor(runtimeTree, environment = {}) {
|
|
3467
|
+
constructor(runtimeTree, environment = {}, envConfig = {}) {
|
|
3341
3468
|
this.runtimeTree = runtimeTree;
|
|
3342
|
-
this.
|
|
3469
|
+
this.envConfig = structuredClone(envConfig);
|
|
3470
|
+
validateFormEnvReferences(this.runtimeTree.schema, this.envConfig.definitions);
|
|
3471
|
+
this.environment = createFormEngineEnvironment({ ...environment, env: resolveFormEnv(this.envConfig, false) });
|
|
3343
3472
|
this.stepRunner = new StepRunner(this, this.environment);
|
|
3344
3473
|
this.buildNodeIndexes();
|
|
3345
3474
|
this.buildDependencyGraph();
|
|
@@ -3365,6 +3494,13 @@ var FormEngine = class {
|
|
|
3365
3494
|
graph = new DependencyGraph();
|
|
3366
3495
|
stepRunner;
|
|
3367
3496
|
environment;
|
|
3497
|
+
envConfig;
|
|
3498
|
+
getEnvDefinitions() {
|
|
3499
|
+
return structuredClone(this.envConfig.definitions ?? {});
|
|
3500
|
+
}
|
|
3501
|
+
getEnv() {
|
|
3502
|
+
return this.environment.env ?? Object.freeze({});
|
|
3503
|
+
}
|
|
3368
3504
|
fieldNodes = /* @__PURE__ */ new Map();
|
|
3369
3505
|
actionNodes = /* @__PURE__ */ new Map();
|
|
3370
3506
|
displayNodes = /* @__PURE__ */ new Map();
|
|
@@ -3434,6 +3570,7 @@ var FormEngine = class {
|
|
|
3434
3570
|
this.notifyStateUpdated();
|
|
3435
3571
|
return this.batch(async () => {
|
|
3436
3572
|
try {
|
|
3573
|
+
this.environment.env = resolveFormEnv(this.envConfig, !this.skipLoadStepsOnInitialize);
|
|
3437
3574
|
this.hydrateValues(this.runtimeTree);
|
|
3438
3575
|
if (!this.skipLoadStepsOnInitialize) {
|
|
3439
3576
|
await this.runLoadSteps();
|
|
@@ -3881,7 +4018,8 @@ var FormEngine = class {
|
|
|
3881
4018
|
getPath(
|
|
3882
4019
|
{
|
|
3883
4020
|
values: this.values,
|
|
3884
|
-
resources: this.resources
|
|
4021
|
+
resources: this.resources,
|
|
4022
|
+
env: this.getEnv()
|
|
3885
4023
|
},
|
|
3886
4024
|
path
|
|
3887
4025
|
)
|
|
@@ -3972,7 +4110,8 @@ var FormEngine = class {
|
|
|
3972
4110
|
this.reevaluateConditions();
|
|
3973
4111
|
const result = runSubmitPipeline(this.getSnapshot(), {
|
|
3974
4112
|
environment: this.environment,
|
|
3975
|
-
resources: this.resources
|
|
4113
|
+
resources: this.resources,
|
|
4114
|
+
env: this.getEnv()
|
|
3976
4115
|
});
|
|
3977
4116
|
const itemErrors = {};
|
|
3978
4117
|
this.itemFieldState.forEach((state, name) => {
|
|
@@ -4363,7 +4502,8 @@ var FormEngine = class {
|
|
|
4363
4502
|
value: changedField === "__bulk__" ? void 0 : nextValues[changedField],
|
|
4364
4503
|
state: this.getConditionState(),
|
|
4365
4504
|
environment: this.environment,
|
|
4366
|
-
resources: this.resources
|
|
4505
|
+
resources: this.resources,
|
|
4506
|
+
env: this.getEnv()
|
|
4367
4507
|
});
|
|
4368
4508
|
this.validateValuesRepeatFields(
|
|
4369
4509
|
changedFields.filter((name) => name.includes("."))
|
|
@@ -4389,7 +4529,8 @@ var FormEngine = class {
|
|
|
4389
4529
|
value: void 0,
|
|
4390
4530
|
state: this.getConditionState(),
|
|
4391
4531
|
environment: this.environment,
|
|
4392
|
-
resources: this.resources
|
|
4532
|
+
resources: this.resources,
|
|
4533
|
+
env: this.getEnv()
|
|
4393
4534
|
};
|
|
4394
4535
|
}
|
|
4395
4536
|
getConditionState() {
|
|
@@ -4409,7 +4550,8 @@ var FormEngine = class {
|
|
|
4409
4550
|
value: void 0,
|
|
4410
4551
|
state: this.getConditionState(),
|
|
4411
4552
|
environment: this.environment,
|
|
4412
|
-
resources: this.resources
|
|
4553
|
+
resources: this.resources,
|
|
4554
|
+
env: this.getEnv()
|
|
4413
4555
|
});
|
|
4414
4556
|
}
|
|
4415
4557
|
syncPendingState() {
|
|
@@ -4611,7 +4753,8 @@ var FormEngine = class {
|
|
|
4611
4753
|
{
|
|
4612
4754
|
environment: this.environment,
|
|
4613
4755
|
values: this.values,
|
|
4614
|
-
resources: this.resources
|
|
4756
|
+
resources: this.resources,
|
|
4757
|
+
env: this.getEnv()
|
|
4615
4758
|
}
|
|
4616
4759
|
);
|
|
4617
4760
|
node.state.error = result.error;
|
|
@@ -4668,7 +4811,8 @@ var FormEngine = class {
|
|
|
4668
4811
|
const source = resolveScopedPath(
|
|
4669
4812
|
{
|
|
4670
4813
|
values: this.values,
|
|
4671
|
-
resources: this.resources
|
|
4814
|
+
resources: this.resources,
|
|
4815
|
+
env: this.getEnv()
|
|
4672
4816
|
},
|
|
4673
4817
|
options.source
|
|
4674
4818
|
);
|
|
@@ -4695,6 +4839,7 @@ var FormEngine = class {
|
|
|
4695
4839
|
{
|
|
4696
4840
|
values: this.values,
|
|
4697
4841
|
resources: this.resources,
|
|
4842
|
+
env: this.getEnv(),
|
|
4698
4843
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
4699
4844
|
},
|
|
4700
4845
|
data.source
|
|
@@ -4720,6 +4865,7 @@ var FormEngine = class {
|
|
|
4720
4865
|
executeComputed(schema.computed, this.values, {
|
|
4721
4866
|
environment: this.environment,
|
|
4722
4867
|
resources: this.resources,
|
|
4868
|
+
env: this.getEnv(),
|
|
4723
4869
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
4724
4870
|
})
|
|
4725
4871
|
);
|
|
@@ -4731,6 +4877,7 @@ var FormEngine = class {
|
|
|
4731
4877
|
{
|
|
4732
4878
|
values: this.values,
|
|
4733
4879
|
resources: this.resources,
|
|
4880
|
+
env: this.getEnv(),
|
|
4734
4881
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
4735
4882
|
},
|
|
4736
4883
|
props.from
|
|
@@ -4754,6 +4901,7 @@ var FormEngine = class {
|
|
|
4754
4901
|
{
|
|
4755
4902
|
values: this.values,
|
|
4756
4903
|
resources: this.resources,
|
|
4904
|
+
env: this.getEnv(),
|
|
4757
4905
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
4758
4906
|
},
|
|
4759
4907
|
props.from
|
|
@@ -4785,6 +4933,7 @@ var FormEngine = class {
|
|
|
4785
4933
|
{
|
|
4786
4934
|
values: this.values,
|
|
4787
4935
|
resources: this.resources,
|
|
4936
|
+
env: this.getEnv(),
|
|
4788
4937
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
4789
4938
|
},
|
|
4790
4939
|
props.titleFrom
|
|
@@ -4824,7 +4973,8 @@ var FormEngine = class {
|
|
|
4824
4973
|
const source = resolveScopedPath(
|
|
4825
4974
|
{
|
|
4826
4975
|
values: this.values,
|
|
4827
|
-
resources: this.resources
|
|
4976
|
+
resources: this.resources,
|
|
4977
|
+
env: this.getEnv()
|
|
4828
4978
|
},
|
|
4829
4979
|
props.source
|
|
4830
4980
|
);
|
|
@@ -5013,6 +5163,7 @@ var FormEngine = class {
|
|
|
5013
5163
|
{
|
|
5014
5164
|
values: this.values,
|
|
5015
5165
|
resources: this.resources,
|
|
5166
|
+
env: this.getEnv(),
|
|
5016
5167
|
...options.item !== void 0 ? { item: options.item } : {}
|
|
5017
5168
|
},
|
|
5018
5169
|
options.from.trim()
|
|
@@ -5031,7 +5182,7 @@ var FormEngine = class {
|
|
|
5031
5182
|
}
|
|
5032
5183
|
validateValuesRepeatFields(only) {
|
|
5033
5184
|
const targets = only ? new Set(only) : void 0;
|
|
5034
|
-
const
|
|
5185
|
+
const visit2 = (meta, absolutePath) => {
|
|
5035
5186
|
const items = readRepeatArray(this.values, absolutePath);
|
|
5036
5187
|
items.forEach((item, index) => {
|
|
5037
5188
|
meta.fieldTemplates.forEach((template, fieldName) => {
|
|
@@ -5049,20 +5200,21 @@ var FormEngine = class {
|
|
|
5049
5200
|
const result = validateField(template.schema, value, {
|
|
5050
5201
|
environment: this.environment,
|
|
5051
5202
|
values: this.values,
|
|
5052
|
-
resources: this.resources
|
|
5203
|
+
resources: this.resources,
|
|
5204
|
+
env: this.getEnv()
|
|
5053
5205
|
});
|
|
5054
5206
|
const state = this.ensureItemFieldState(scopedName);
|
|
5055
5207
|
state.error = result.error;
|
|
5056
5208
|
});
|
|
5057
5209
|
meta.nested.forEach((nested) => {
|
|
5058
|
-
|
|
5210
|
+
visit2(nested, `${absolutePath}.${index}.${nested.path}`);
|
|
5059
5211
|
});
|
|
5060
5212
|
});
|
|
5061
5213
|
};
|
|
5062
|
-
this.valuesRepeatMetas.forEach((meta) =>
|
|
5214
|
+
this.valuesRepeatMetas.forEach((meta) => visit2(meta, meta.path));
|
|
5063
5215
|
}
|
|
5064
5216
|
touchAllValuesRepeatFields() {
|
|
5065
|
-
const
|
|
5217
|
+
const visit2 = (meta, absolutePath) => {
|
|
5066
5218
|
const items = readRepeatArray(this.values, absolutePath);
|
|
5067
5219
|
items.forEach((_, index) => {
|
|
5068
5220
|
meta.fieldTemplates.forEach((_2, fieldName) => {
|
|
@@ -5070,11 +5222,11 @@ var FormEngine = class {
|
|
|
5070
5222
|
this.ensureItemFieldState(scopedName).touched = true;
|
|
5071
5223
|
});
|
|
5072
5224
|
meta.nested.forEach((nested) => {
|
|
5073
|
-
|
|
5225
|
+
visit2(nested, `${absolutePath}.${index}.${nested.path}`);
|
|
5074
5226
|
});
|
|
5075
5227
|
});
|
|
5076
5228
|
};
|
|
5077
|
-
this.valuesRepeatMetas.forEach((meta) =>
|
|
5229
|
+
this.valuesRepeatMetas.forEach((meta) => visit2(meta, meta.path));
|
|
5078
5230
|
}
|
|
5079
5231
|
pruneItemFieldState(absolutePath, removedIndex, previousLength) {
|
|
5080
5232
|
const next = /* @__PURE__ */ new Map();
|
|
@@ -5271,19 +5423,25 @@ function findNode(node, name) {
|
|
|
5271
5423
|
adaptDisplay,
|
|
5272
5424
|
adaptField,
|
|
5273
5425
|
adaptLayout,
|
|
5426
|
+
collectFormEnvReferences,
|
|
5274
5427
|
compileSchema,
|
|
5275
5428
|
conditions,
|
|
5276
5429
|
createFormEngineEnvironment,
|
|
5277
5430
|
createRuntimeNode,
|
|
5278
5431
|
formatTagSourceValue,
|
|
5279
5432
|
isThenable,
|
|
5433
|
+
remapFormEnvReferences,
|
|
5434
|
+
resolveFormEnv,
|
|
5280
5435
|
resolveLoadingProp,
|
|
5281
5436
|
resolveTagValueMapping,
|
|
5282
5437
|
runConditionEngine,
|
|
5283
5438
|
tagMapKey,
|
|
5284
5439
|
traverseNode,
|
|
5285
5440
|
validateApiUrlTemplate,
|
|
5441
|
+
validateEnvBaseUrl,
|
|
5442
|
+
validateEnvDefinitions,
|
|
5286
5443
|
validateField,
|
|
5444
|
+
validateFormEnvReferences,
|
|
5287
5445
|
validateSchema,
|
|
5288
5446
|
validation,
|
|
5289
5447
|
validationRegistry
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _bsm_form_schema from '@bsm-form/schema';
|
|
2
|
-
import { ToastVariant, ToastPosition, Condition, DisplayEventType, FieldEventType, TableInteraction, DialogEventType, DrawerEventType, RepeatOperation, Node, ActionNode, DisplayNode, FieldNode, LayoutNode, ValidationRule, FormSchema, TableTagValueMapping } from '@bsm-form/schema';
|
|
2
|
+
import { FormEnvValues, ToastVariant, ToastPosition, Condition, FormEnvConfig, DisplayEventType, FieldEventType, TableInteraction, DialogEventType, DrawerEventType, RepeatOperation, Node, ActionNode, DisplayNode, FieldNode, LayoutNode, ValidationRule, FormSchema, TableTagValueMapping, FormEnvDefinitions } from '@bsm-form/schema';
|
|
3
3
|
|
|
4
4
|
type FormEngineNotification = {
|
|
5
5
|
message: string;
|
|
@@ -9,6 +9,7 @@ type FormEngineNotification = {
|
|
|
9
9
|
duration?: number;
|
|
10
10
|
};
|
|
11
11
|
type InvokeContext = {
|
|
12
|
+
env?: FormEnvValues;
|
|
12
13
|
values: Record<string, unknown>;
|
|
13
14
|
resources: Record<string, unknown>;
|
|
14
15
|
row?: Record<string, unknown>;
|
|
@@ -25,6 +26,7 @@ type ValidatorHandler = (ctx: InvokeContext) => true | string;
|
|
|
25
26
|
/** Sync computed value producer. Must not return a Promise. */
|
|
26
27
|
type ComputerHandler = (ctx: InvokeContext) => unknown;
|
|
27
28
|
type FormEngineEnvironment = {
|
|
29
|
+
env?: FormEnvValues;
|
|
28
30
|
request: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
29
31
|
log: (message: string) => void;
|
|
30
32
|
navigate: (to: string) => void;
|
|
@@ -85,6 +87,9 @@ declare class FormEngine {
|
|
|
85
87
|
private graph;
|
|
86
88
|
private stepRunner;
|
|
87
89
|
private environment;
|
|
90
|
+
private envConfig;
|
|
91
|
+
getEnvDefinitions(): _bsm_form_schema.FormEnvDefinitions;
|
|
92
|
+
getEnv(): FormEnvValues;
|
|
88
93
|
private fieldNodes;
|
|
89
94
|
private actionNodes;
|
|
90
95
|
private displayNodes;
|
|
@@ -110,7 +115,7 @@ declare class FormEngine {
|
|
|
110
115
|
private hasPendingStateUpdate;
|
|
111
116
|
private initializationPromise?;
|
|
112
117
|
private skipLoadStepsOnInitialize;
|
|
113
|
-
constructor(runtimeTree: RuntimeNode, environment?: Partial<FormEngineEnvironment
|
|
118
|
+
constructor(runtimeTree: RuntimeNode, environment?: Partial<FormEngineEnvironment>, envConfig?: FormEnvConfig);
|
|
114
119
|
runLoadSteps(): Promise<void>;
|
|
115
120
|
initialize(options?: {
|
|
116
121
|
skipLoadSteps?: boolean;
|
|
@@ -335,6 +340,7 @@ type FormSnapshot = {
|
|
|
335
340
|
nodes: Map<string, RuntimeNode>;
|
|
336
341
|
};
|
|
337
342
|
type PipelineContext = {
|
|
343
|
+
env?: Readonly<Record<string, string | number | boolean>>;
|
|
338
344
|
tree: RuntimeNode;
|
|
339
345
|
values: FormValues;
|
|
340
346
|
changedField: string;
|
|
@@ -509,6 +515,7 @@ declare const validationRegistry: ValidationRegistry;
|
|
|
509
515
|
|
|
510
516
|
type ValidateFieldOptions = {
|
|
511
517
|
environment?: FormEngineEnvironment;
|
|
518
|
+
env?: Readonly<Record<string, string | number | boolean>>;
|
|
512
519
|
values?: Record<string, unknown>;
|
|
513
520
|
resources?: Record<string, unknown>;
|
|
514
521
|
row?: Record<string, unknown>;
|
|
@@ -591,4 +598,16 @@ declare function formatTagSourceValue(value: unknown): string;
|
|
|
591
598
|
/** Validates syntax without reading values or running author code. */
|
|
592
599
|
declare function validateApiUrlTemplate(url: string): void;
|
|
593
600
|
|
|
594
|
-
|
|
601
|
+
type FormEnvReference = {
|
|
602
|
+
name: string;
|
|
603
|
+
path: string;
|
|
604
|
+
};
|
|
605
|
+
declare function collectFormEnvReferences(schema: unknown): FormEnvReference[];
|
|
606
|
+
declare function validateFormEnvReferences(schema: unknown, definitions?: FormEnvDefinitions): void;
|
|
607
|
+
declare function remapFormEnvReferences<T>(schema: T, previous: string, next: string): T;
|
|
608
|
+
|
|
609
|
+
declare function validateEnvBaseUrl(value: string): void;
|
|
610
|
+
declare function validateEnvDefinitions(value: unknown): asserts value is FormEnvDefinitions;
|
|
611
|
+
declare function resolveFormEnv(config?: FormEnvConfig, requireValues?: boolean): FormEnvValues;
|
|
612
|
+
|
|
613
|
+
export { type AccordionChangeReason, type AccordionChangeResult, type AccordionState, type ActionAdapterInput, type ActionContext, type ActionHandler, type ComputerHandler, DependencyGraph, type DependencyMap, type DialogState, type DialogTransitionReason, type DialogTransitionResult, type DisplayAdapterInput, type DrawerState, type DrawerTransitionReason, type DrawerTransitionResult, EventBus, type FieldAdapterInput, FormEngine, type FormEngineConfig, type FormEngineEnvironment, type FormEngineNotification, type FormEngineState, type FormErrors, type FormEvent, type FormSnapshot, type FormState, type FormValues, type InitializationStatus, type InvokeContext, type InvokeHandler, type LayoutAdapterInput, type Listener, type OptionsConfig, type PaginationChangeReason, type PaginationChangeResult, type PaginationState, type PipelineContext, type PredicateHandler, type RepeatMutationReason, type RepeatMutationResult, type RuntimeNode, type RuntimeNodeState, type RuntimeTree, type StepRunOptions, type StepperNavigationReason, type StepperNavigationResult, type StepperState, type TabsChangeReason, type TabsChangeResult, type TabsState, type ValidateFieldOptions, type ValidationResult, type Validator, type ValidatorHandler, adaptAction, adaptDisplay, adaptField, adaptLayout, collectFormEnvReferences, compileSchema, conditions, createFormEngineEnvironment, createRuntimeNode, formatTagSourceValue, isThenable, remapFormEnvReferences, resolveFormEnv, resolveLoadingProp, resolveTagValueMapping, runConditionEngine, tagMapKey, traverseNode, validateApiUrlTemplate, validateEnvBaseUrl, validateEnvDefinitions, validateField, validateFormEnvReferences, validateSchema, validation, validationRegistry };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _bsm_form_schema from '@bsm-form/schema';
|
|
2
|
-
import { ToastVariant, ToastPosition, Condition, DisplayEventType, FieldEventType, TableInteraction, DialogEventType, DrawerEventType, RepeatOperation, Node, ActionNode, DisplayNode, FieldNode, LayoutNode, ValidationRule, FormSchema, TableTagValueMapping } from '@bsm-form/schema';
|
|
2
|
+
import { FormEnvValues, ToastVariant, ToastPosition, Condition, FormEnvConfig, DisplayEventType, FieldEventType, TableInteraction, DialogEventType, DrawerEventType, RepeatOperation, Node, ActionNode, DisplayNode, FieldNode, LayoutNode, ValidationRule, FormSchema, TableTagValueMapping, FormEnvDefinitions } from '@bsm-form/schema';
|
|
3
3
|
|
|
4
4
|
type FormEngineNotification = {
|
|
5
5
|
message: string;
|
|
@@ -9,6 +9,7 @@ type FormEngineNotification = {
|
|
|
9
9
|
duration?: number;
|
|
10
10
|
};
|
|
11
11
|
type InvokeContext = {
|
|
12
|
+
env?: FormEnvValues;
|
|
12
13
|
values: Record<string, unknown>;
|
|
13
14
|
resources: Record<string, unknown>;
|
|
14
15
|
row?: Record<string, unknown>;
|
|
@@ -25,6 +26,7 @@ type ValidatorHandler = (ctx: InvokeContext) => true | string;
|
|
|
25
26
|
/** Sync computed value producer. Must not return a Promise. */
|
|
26
27
|
type ComputerHandler = (ctx: InvokeContext) => unknown;
|
|
27
28
|
type FormEngineEnvironment = {
|
|
29
|
+
env?: FormEnvValues;
|
|
28
30
|
request: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
29
31
|
log: (message: string) => void;
|
|
30
32
|
navigate: (to: string) => void;
|
|
@@ -85,6 +87,9 @@ declare class FormEngine {
|
|
|
85
87
|
private graph;
|
|
86
88
|
private stepRunner;
|
|
87
89
|
private environment;
|
|
90
|
+
private envConfig;
|
|
91
|
+
getEnvDefinitions(): _bsm_form_schema.FormEnvDefinitions;
|
|
92
|
+
getEnv(): FormEnvValues;
|
|
88
93
|
private fieldNodes;
|
|
89
94
|
private actionNodes;
|
|
90
95
|
private displayNodes;
|
|
@@ -110,7 +115,7 @@ declare class FormEngine {
|
|
|
110
115
|
private hasPendingStateUpdate;
|
|
111
116
|
private initializationPromise?;
|
|
112
117
|
private skipLoadStepsOnInitialize;
|
|
113
|
-
constructor(runtimeTree: RuntimeNode, environment?: Partial<FormEngineEnvironment
|
|
118
|
+
constructor(runtimeTree: RuntimeNode, environment?: Partial<FormEngineEnvironment>, envConfig?: FormEnvConfig);
|
|
114
119
|
runLoadSteps(): Promise<void>;
|
|
115
120
|
initialize(options?: {
|
|
116
121
|
skipLoadSteps?: boolean;
|
|
@@ -335,6 +340,7 @@ type FormSnapshot = {
|
|
|
335
340
|
nodes: Map<string, RuntimeNode>;
|
|
336
341
|
};
|
|
337
342
|
type PipelineContext = {
|
|
343
|
+
env?: Readonly<Record<string, string | number | boolean>>;
|
|
338
344
|
tree: RuntimeNode;
|
|
339
345
|
values: FormValues;
|
|
340
346
|
changedField: string;
|
|
@@ -509,6 +515,7 @@ declare const validationRegistry: ValidationRegistry;
|
|
|
509
515
|
|
|
510
516
|
type ValidateFieldOptions = {
|
|
511
517
|
environment?: FormEngineEnvironment;
|
|
518
|
+
env?: Readonly<Record<string, string | number | boolean>>;
|
|
512
519
|
values?: Record<string, unknown>;
|
|
513
520
|
resources?: Record<string, unknown>;
|
|
514
521
|
row?: Record<string, unknown>;
|
|
@@ -591,4 +598,16 @@ declare function formatTagSourceValue(value: unknown): string;
|
|
|
591
598
|
/** Validates syntax without reading values or running author code. */
|
|
592
599
|
declare function validateApiUrlTemplate(url: string): void;
|
|
593
600
|
|
|
594
|
-
|
|
601
|
+
type FormEnvReference = {
|
|
602
|
+
name: string;
|
|
603
|
+
path: string;
|
|
604
|
+
};
|
|
605
|
+
declare function collectFormEnvReferences(schema: unknown): FormEnvReference[];
|
|
606
|
+
declare function validateFormEnvReferences(schema: unknown, definitions?: FormEnvDefinitions): void;
|
|
607
|
+
declare function remapFormEnvReferences<T>(schema: T, previous: string, next: string): T;
|
|
608
|
+
|
|
609
|
+
declare function validateEnvBaseUrl(value: string): void;
|
|
610
|
+
declare function validateEnvDefinitions(value: unknown): asserts value is FormEnvDefinitions;
|
|
611
|
+
declare function resolveFormEnv(config?: FormEnvConfig, requireValues?: boolean): FormEnvValues;
|
|
612
|
+
|
|
613
|
+
export { type AccordionChangeReason, type AccordionChangeResult, type AccordionState, type ActionAdapterInput, type ActionContext, type ActionHandler, type ComputerHandler, DependencyGraph, type DependencyMap, type DialogState, type DialogTransitionReason, type DialogTransitionResult, type DisplayAdapterInput, type DrawerState, type DrawerTransitionReason, type DrawerTransitionResult, EventBus, type FieldAdapterInput, FormEngine, type FormEngineConfig, type FormEngineEnvironment, type FormEngineNotification, type FormEngineState, type FormErrors, type FormEvent, type FormSnapshot, type FormState, type FormValues, type InitializationStatus, type InvokeContext, type InvokeHandler, type LayoutAdapterInput, type Listener, type OptionsConfig, type PaginationChangeReason, type PaginationChangeResult, type PaginationState, type PipelineContext, type PredicateHandler, type RepeatMutationReason, type RepeatMutationResult, type RuntimeNode, type RuntimeNodeState, type RuntimeTree, type StepRunOptions, type StepperNavigationReason, type StepperNavigationResult, type StepperState, type TabsChangeReason, type TabsChangeResult, type TabsState, type ValidateFieldOptions, type ValidationResult, type Validator, type ValidatorHandler, adaptAction, adaptDisplay, adaptField, adaptLayout, collectFormEnvReferences, compileSchema, conditions, createFormEngineEnvironment, createRuntimeNode, formatTagSourceValue, isThenable, remapFormEnvReferences, resolveFormEnv, resolveLoadingProp, resolveTagValueMapping, runConditionEngine, tagMapKey, traverseNode, validateApiUrlTemplate, validateEnvBaseUrl, validateEnvDefinitions, validateField, validateFormEnvReferences, validateSchema, validation, validationRegistry };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,58 @@
|
|
|
1
|
+
// src/utils/form-env.ts
|
|
2
|
+
function validateEnvBaseUrl(value) {
|
|
3
|
+
if (!value || /[\\\s?#]/.test(value) || value.startsWith("//")) throw new Error("Base URL cannot contain whitespace, query, fragment, or backslashes");
|
|
4
|
+
if (value.startsWith("/")) return;
|
|
5
|
+
let url;
|
|
6
|
+
try {
|
|
7
|
+
url = new URL(value);
|
|
8
|
+
} catch {
|
|
9
|
+
throw new Error("Expected an http(s) URL or path starting with /");
|
|
10
|
+
}
|
|
11
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password) throw new Error("Base URL must use http(s) without credentials");
|
|
12
|
+
}
|
|
13
|
+
function validateValue(name, type, value) {
|
|
14
|
+
if (typeof value !== (type === "url" ? "string" : type) || typeof value === "number" && !Number.isFinite(value)) throw new Error(`env.${name}: expected ${String(type)}`);
|
|
15
|
+
if (type === "url") {
|
|
16
|
+
try {
|
|
17
|
+
validateEnvBaseUrl(value);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
throw new Error(`env.${name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function validateEnvDefinitions(value) {
|
|
24
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Environment definitions must be an object");
|
|
25
|
+
for (const [name, entry] of Object.entries(value)) {
|
|
26
|
+
if (!/^[A-Za-z_]+$/.test(name) || ["__proto__", "prototype", "constructor"].includes(name)) throw new Error(`Invalid environment variable name: ${name}`);
|
|
27
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) throw new Error(`env.${name}: expected a definition`);
|
|
28
|
+
const definition = entry;
|
|
29
|
+
if (!["string", "number", "boolean", "url"].includes(String(definition.type))) throw new Error(`env.${name}: invalid type`);
|
|
30
|
+
for (const key of Object.keys(definition)) if (!["type", "label", "description", "default", "required", "allowOverride"].includes(key)) throw new Error(`env.${name}: unknown property ${key}`);
|
|
31
|
+
for (const key of ["required", "allowOverride"]) if (definition[key] !== void 0 && typeof definition[key] !== "boolean") throw new Error(`env.${name}.${key}: expected boolean`);
|
|
32
|
+
for (const key of ["label", "description"]) if (definition[key] !== void 0 && typeof definition[key] !== "string") throw new Error(`env.${name}.${key}: expected string`);
|
|
33
|
+
if (Object.hasOwn(definition, "default")) validateValue(name, definition.type, definition.default);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function resolveFormEnv(config = {}, requireValues = true) {
|
|
37
|
+
const definitions = config.definitions ?? {};
|
|
38
|
+
const overrides = config.overrides ?? {};
|
|
39
|
+
validateEnvDefinitions(definitions);
|
|
40
|
+
if (typeof overrides !== "object" || Array.isArray(overrides)) throw new Error("Environment overrides must be an object");
|
|
41
|
+
for (const name of Object.keys(overrides)) {
|
|
42
|
+
if (!Object.hasOwn(definitions, name)) throw new Error(`env.${name}: unknown override`);
|
|
43
|
+
if (definitions[name]?.allowOverride === false) throw new Error(`env.${name}: overrides are disabled`);
|
|
44
|
+
}
|
|
45
|
+
const values = {};
|
|
46
|
+
for (const [name, definition] of Object.entries(definitions)) {
|
|
47
|
+
const provided = Object.hasOwn(overrides, name);
|
|
48
|
+
const value = provided ? overrides[name] : definition.default;
|
|
49
|
+
if (provided || value !== void 0) validateValue(name, definition.type, value);
|
|
50
|
+
if (requireValues && definition.required && (value === void 0 || value === "")) throw new Error(`env.${name}: required value is missing`);
|
|
51
|
+
if (value !== void 0) values[name] = value;
|
|
52
|
+
}
|
|
53
|
+
return Object.freeze(values);
|
|
54
|
+
}
|
|
55
|
+
|
|
1
56
|
// src/utils/get-path.ts
|
|
2
57
|
function pathSegments(path) {
|
|
3
58
|
const segments = [];
|
|
@@ -45,6 +100,8 @@ function getPath(obj, path) {
|
|
|
45
100
|
|
|
46
101
|
// src/utils/scope-path.ts
|
|
47
102
|
function resolveScopedPath(ctx, path) {
|
|
103
|
+
if (path === "env") return ctx.env ?? {};
|
|
104
|
+
if (path.startsWith("env.")) return getPath(ctx.env ?? {}, path.slice(4));
|
|
48
105
|
if (path === "$values" || path === "values") {
|
|
49
106
|
return ctx.values;
|
|
50
107
|
}
|
|
@@ -72,7 +129,7 @@ function resolveScopedPath(ctx, path) {
|
|
|
72
129
|
return getPath(ctx.values, path);
|
|
73
130
|
}
|
|
74
131
|
function isExplicitScopedPath(path) {
|
|
75
|
-
return path === "$values" || path === "values" || path === "$resources" || path === "resources" || path === "$row" || path === "row" || path === "$item" || path === "item" || path.startsWith("values.") || path.startsWith("resources.") || path.startsWith("row.") || path.startsWith("item.");
|
|
132
|
+
return path === "env" || path.startsWith("env.") || path === "$values" || path === "values" || path === "$resources" || path === "resources" || path === "$row" || path === "row" || path === "$item" || path === "item" || path.startsWith("values.") || path.startsWith("resources.") || path.startsWith("row.") || path.startsWith("item.");
|
|
76
133
|
}
|
|
77
134
|
function resolvePathStringsInData(value, ctx) {
|
|
78
135
|
if (typeof value === "string") {
|
|
@@ -92,7 +149,7 @@ function resolvePathStringsInData(value, ctx) {
|
|
|
92
149
|
}
|
|
93
150
|
|
|
94
151
|
// src/utils/url-template.ts
|
|
95
|
-
var pathPattern = /^(values|resources)\.[A-Za-z_$][\w$]*(?:(?:\.(?:[A-Za-z_$][\w$]*|\d+))|(?:\[\d+\]))*$/;
|
|
152
|
+
var pathPattern = /^(values|resources|env)\.[A-Za-z_$][\w$]*(?:(?:\.(?:[A-Za-z_$][\w$]*|\d+))|(?:\[\d+\]))*$/;
|
|
96
153
|
function placeholders(url) {
|
|
97
154
|
const result = [];
|
|
98
155
|
let cursor = 0;
|
|
@@ -103,10 +160,10 @@ function placeholders(url) {
|
|
|
103
160
|
if (close < 0) throw new Error("API URL template has an unclosed placeholder");
|
|
104
161
|
const path = url.slice(start + 2, close).trim();
|
|
105
162
|
if (!pathPattern.test(path)) {
|
|
106
|
-
throw new Error(`Invalid API URL placeholder: ${path}. Use a values
|
|
163
|
+
throw new Error(`Invalid API URL placeholder: ${path}. Use a values.*, resources.*, or env.* data path`);
|
|
107
164
|
}
|
|
108
165
|
const origin = url.match(/^(?:[A-Za-z][A-Za-z0-9+.-]*:)?\/\/[^/?#]*/)?.[0];
|
|
109
|
-
if (start === 0 || origin && start < origin.length) {
|
|
166
|
+
if (start === 0 && !path.startsWith("env.") || origin && start < origin.length) {
|
|
110
167
|
throw new Error("API URL placeholders cannot replace the service origin; use a static base URL");
|
|
111
168
|
}
|
|
112
169
|
result.push({ start, end: close + 1, path });
|
|
@@ -118,7 +175,7 @@ function validateApiUrlTemplate(url) {
|
|
|
118
175
|
if (!url.trim()) throw new Error("API URL must not be empty");
|
|
119
176
|
placeholders(url);
|
|
120
177
|
}
|
|
121
|
-
function resolveApiUrlTemplate(url, context) {
|
|
178
|
+
function resolveApiUrlTemplate(url, context, definitions = {}) {
|
|
122
179
|
validateApiUrlTemplate(url);
|
|
123
180
|
let result = "";
|
|
124
181
|
let cursor = 0;
|
|
@@ -130,7 +187,17 @@ function resolveApiUrlTemplate(url, context) {
|
|
|
130
187
|
if (value === "." || value === "..") {
|
|
131
188
|
throw new Error(`API URL placeholder ${placeholder.path} cannot be a dot segment`);
|
|
132
189
|
}
|
|
133
|
-
|
|
190
|
+
if (placeholder.start === 0 && placeholder.path.startsWith("env.")) {
|
|
191
|
+
if (definitions[placeholder.path.slice(4)]?.type !== "url" || typeof value !== "string") {
|
|
192
|
+
throw new Error(`${placeholder.path}: only an env variable of type url can supply a base URL`);
|
|
193
|
+
}
|
|
194
|
+
validateEnvBaseUrl(value);
|
|
195
|
+
const suffix = url.slice(placeholder.end);
|
|
196
|
+
if (suffix && !suffix.startsWith("/")) throw new Error("Base URL placeholder must be followed by / or end of URL");
|
|
197
|
+
result += suffix.startsWith("/") ? value.replace(/\/+$/, "") : value;
|
|
198
|
+
} else {
|
|
199
|
+
result += url.slice(cursor, placeholder.start) + encodeURIComponent(String(value));
|
|
200
|
+
}
|
|
134
201
|
cursor = placeholder.end;
|
|
135
202
|
}
|
|
136
203
|
return result + url.slice(cursor);
|
|
@@ -1562,6 +1629,7 @@ function validateDrawerStepReferences(context) {
|
|
|
1562
1629
|
}
|
|
1563
1630
|
function validateStepConditionReferences(context) {
|
|
1564
1631
|
context.stepConditionReferences.forEach(({ path, field }) => {
|
|
1632
|
+
if (field.startsWith("env.")) return;
|
|
1565
1633
|
if (isReservedConditionField(field)) {
|
|
1566
1634
|
if (isPendingActionConditionField(field)) {
|
|
1567
1635
|
const actionId = pendingActionIdFromField(field);
|
|
@@ -1722,6 +1790,7 @@ var validationRegistry = {
|
|
|
1722
1790
|
// src/engine/environment.ts
|
|
1723
1791
|
function createFormEngineEnvironment(overrides = {}) {
|
|
1724
1792
|
return {
|
|
1793
|
+
env: overrides.env ?? Object.freeze({}),
|
|
1725
1794
|
request: overrides.request ?? defaultRequest,
|
|
1726
1795
|
log: overrides.log ?? defaultLog,
|
|
1727
1796
|
navigate: overrides.navigate ?? defaultNavigate,
|
|
@@ -1791,6 +1860,7 @@ function runInvokeValidator(rule, options) {
|
|
|
1791
1860
|
const ctx = {
|
|
1792
1861
|
values: options?.values ?? {},
|
|
1793
1862
|
resources: options?.resources ?? {},
|
|
1863
|
+
env: options?.env ?? options?.environment?.env,
|
|
1794
1864
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
1795
1865
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
1796
1866
|
};
|
|
@@ -1895,6 +1965,7 @@ function evaluateCondition(condition, values, state, options) {
|
|
|
1895
1965
|
}
|
|
1896
1966
|
const target = resolveConditionTarget(condition.field, values, state, {
|
|
1897
1967
|
resources: options?.resources,
|
|
1968
|
+
env: options?.env ?? options?.environment?.env,
|
|
1898
1969
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
1899
1970
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
1900
1971
|
});
|
|
@@ -1941,6 +2012,7 @@ function evaluateInvokeCondition(name, values, options) {
|
|
|
1941
2012
|
const ctx = {
|
|
1942
2013
|
values,
|
|
1943
2014
|
resources: options?.resources ?? {},
|
|
2015
|
+
env: options?.env ?? options?.environment?.env,
|
|
1944
2016
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
1945
2017
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
1946
2018
|
};
|
|
@@ -1995,6 +2067,7 @@ function resolveConditionTarget(field, values, state, options) {
|
|
|
1995
2067
|
{
|
|
1996
2068
|
values,
|
|
1997
2069
|
resources: options?.resources ?? {},
|
|
2070
|
+
env: options?.env,
|
|
1998
2071
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
1999
2072
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
2000
2073
|
},
|
|
@@ -2294,7 +2367,7 @@ var DependencyGraph = class {
|
|
|
2294
2367
|
return ordered;
|
|
2295
2368
|
}
|
|
2296
2369
|
extractDependencies(conditions2) {
|
|
2297
|
-
return conditions2.filter(isFieldCondition2).map((condition) => condition.field).filter((field) => !isReservedConditionField3(field));
|
|
2370
|
+
return conditions2.filter(isFieldCondition2).map((condition) => condition.field).filter((field) => !isReservedConditionField3(field) && !field.startsWith("env."));
|
|
2298
2371
|
}
|
|
2299
2372
|
clear() {
|
|
2300
2373
|
this.forward.clear();
|
|
@@ -2316,10 +2389,52 @@ var EventBus = class {
|
|
|
2316
2389
|
}
|
|
2317
2390
|
};
|
|
2318
2391
|
|
|
2392
|
+
// src/utils/env-references.ts
|
|
2393
|
+
var pathKeys = /* @__PURE__ */ new Set(["from", "source", "field", "path", "bodyFrom", "queryFrom"]);
|
|
2394
|
+
function visit(value, path, key, data, transform) {
|
|
2395
|
+
if (typeof value === "string") {
|
|
2396
|
+
if (key === "url") return transform(value, path, true);
|
|
2397
|
+
if ((pathKeys.has(key) || path.endsWith(".bind.value") || data) && /^env\.[A-Za-z_]/.test(value)) return transform(value, path, false);
|
|
2398
|
+
return value;
|
|
2399
|
+
}
|
|
2400
|
+
if (Array.isArray(value)) return value.map((item, index) => visit(item, `${path}[${index}]`, key, data, transform));
|
|
2401
|
+
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [
|
|
2402
|
+
childKey,
|
|
2403
|
+
visit(child, `${path}.${childKey}`, childKey, data || childKey === "body" || childKey === "query", transform)
|
|
2404
|
+
]));
|
|
2405
|
+
return value;
|
|
2406
|
+
}
|
|
2407
|
+
function collectFormEnvReferences(schema) {
|
|
2408
|
+
const references = [];
|
|
2409
|
+
visit(schema, "schema", "", false, (value, path, template) => {
|
|
2410
|
+
if (template) {
|
|
2411
|
+
for (const match of value.matchAll(/\$\{\s*env\.([A-Za-z_][A-Za-z0-9_]*)\s*\}/g)) {
|
|
2412
|
+
references.push({ name: match[1], path });
|
|
2413
|
+
}
|
|
2414
|
+
} else references.push({ name: value.slice(4).split(/[.[]/)[0], path });
|
|
2415
|
+
return value;
|
|
2416
|
+
});
|
|
2417
|
+
return references;
|
|
2418
|
+
}
|
|
2419
|
+
function validateFormEnvReferences(schema, definitions = {}) {
|
|
2420
|
+
for (const reference of collectFormEnvReferences(schema)) {
|
|
2421
|
+
if (!Object.hasOwn(definitions, reference.name)) throw new Error(`${reference.path}: undefined environment variable env.${reference.name}`);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
function remapFormEnvReferences(schema, previous, next) {
|
|
2425
|
+
return visit(schema, "schema", "", false, (value, _path, template) => {
|
|
2426
|
+
if (template) return value.replace(
|
|
2427
|
+
/\$\{\s*env\.([A-Za-z_][A-Za-z0-9_]*)\s*\}/g,
|
|
2428
|
+
(original, name) => name === previous ? "${env." + next + "}" : original
|
|
2429
|
+
);
|
|
2430
|
+
return value === `env.${previous}` ? `env.${next}` : value;
|
|
2431
|
+
});
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2319
2434
|
// src/pipeline/run-conditions.ts
|
|
2320
2435
|
function runConditions(ctx) {
|
|
2321
|
-
|
|
2322
|
-
function
|
|
2436
|
+
visit2(ctx.tree, true);
|
|
2437
|
+
function visit2(node, parentVisible) {
|
|
2323
2438
|
const schema = node.schema;
|
|
2324
2439
|
const hasVisibilityConditions = schema.kind === "field" || schema.kind === "display" || schema.kind === "action" || schema.kind === "layout" && schema.type === "step";
|
|
2325
2440
|
if (!parentVisible) {
|
|
@@ -2337,7 +2452,7 @@ function runConditions(ctx) {
|
|
|
2337
2452
|
}
|
|
2338
2453
|
node.state.disabled = evaluateDisabled(schema, ctx);
|
|
2339
2454
|
const childrenVisible = node.state.visible && !(schema.kind === "layout" && (schema.type === "dialog" || schema.type === "drawer") && node.state.open !== true);
|
|
2340
|
-
node.children.forEach((child) =>
|
|
2455
|
+
node.children.forEach((child) => visit2(child, childrenVisible));
|
|
2341
2456
|
}
|
|
2342
2457
|
}
|
|
2343
2458
|
function evaluateDisabled(schema, ctx) {
|
|
@@ -2456,6 +2571,7 @@ function executeInvokeComputer(name, values, options) {
|
|
|
2456
2571
|
const ctx = {
|
|
2457
2572
|
values,
|
|
2458
2573
|
resources: options?.resources ?? {},
|
|
2574
|
+
env: options?.env ?? options?.environment?.env,
|
|
2459
2575
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
2460
2576
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
2461
2577
|
};
|
|
@@ -2804,6 +2920,7 @@ var StepRunner = class {
|
|
|
2804
2920
|
(condition) => evaluateCondition(condition, values, conditionState, {
|
|
2805
2921
|
environment: this.environment,
|
|
2806
2922
|
resources: this.engine.getResources(),
|
|
2923
|
+
env: this.engine.getEnv(),
|
|
2807
2924
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
2808
2925
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
2809
2926
|
})
|
|
@@ -2888,6 +3005,7 @@ var StepRunner = class {
|
|
|
2888
3005
|
return {
|
|
2889
3006
|
values: this.engine.getValues(),
|
|
2890
3007
|
resources: this.engine.getResources(),
|
|
3008
|
+
env: this.engine.getEnv(),
|
|
2891
3009
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
2892
3010
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
2893
3011
|
};
|
|
@@ -2896,6 +3014,7 @@ var StepRunner = class {
|
|
|
2896
3014
|
return {
|
|
2897
3015
|
values: this.engine.getValues(),
|
|
2898
3016
|
resources: this.engine.getResources(),
|
|
3017
|
+
env: this.engine.getEnv(),
|
|
2899
3018
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
2900
3019
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
2901
3020
|
};
|
|
@@ -2905,6 +3024,7 @@ function resolveApiRequestBody(step, engine, options) {
|
|
|
2905
3024
|
const ctx = {
|
|
2906
3025
|
values: engine.getValues(),
|
|
2907
3026
|
resources: engine.getResources(),
|
|
3027
|
+
env: engine.getEnv(),
|
|
2908
3028
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
2909
3029
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
2910
3030
|
};
|
|
@@ -2924,10 +3044,11 @@ function resolveApiRequestUrl(step, engine, options) {
|
|
|
2924
3044
|
const ctx = {
|
|
2925
3045
|
values: engine.getValues(),
|
|
2926
3046
|
resources: engine.getResources(),
|
|
3047
|
+
env: engine.getEnv(),
|
|
2927
3048
|
...options?.row !== void 0 ? { row: options.row } : {},
|
|
2928
3049
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
2929
3050
|
};
|
|
2930
|
-
const url = resolveApiUrlTemplate(step.request.url, ctx);
|
|
3051
|
+
const url = resolveApiUrlTemplate(step.request.url, ctx, engine.getEnvDefinitions());
|
|
2931
3052
|
const hasQueryFrom = "queryFrom" in step.request && step.request.queryFrom !== void 0;
|
|
2932
3053
|
const hasQuery = "query" in step.request && step.request.query !== void 0;
|
|
2933
3054
|
if (hasQueryFrom && hasQuery) {
|
|
@@ -3220,11 +3341,11 @@ function collectTemplate(node, fields, nested) {
|
|
|
3220
3341
|
}
|
|
3221
3342
|
function collectValuesRepeatFieldIds(metas) {
|
|
3222
3343
|
const ids = /* @__PURE__ */ new Set();
|
|
3223
|
-
const
|
|
3344
|
+
const visit2 = (meta) => {
|
|
3224
3345
|
meta.fieldTemplates.forEach((node) => ids.add(node.id));
|
|
3225
|
-
meta.nested.forEach(
|
|
3346
|
+
meta.nested.forEach(visit2);
|
|
3226
3347
|
};
|
|
3227
|
-
metas.forEach(
|
|
3348
|
+
metas.forEach(visit2);
|
|
3228
3349
|
return ids;
|
|
3229
3350
|
}
|
|
3230
3351
|
function createDefaultRepeatItem(meta) {
|
|
@@ -3312,9 +3433,11 @@ function findValuesRepeatMeta(metas, repeatId) {
|
|
|
3312
3433
|
|
|
3313
3434
|
// src/engine/form-engine.ts
|
|
3314
3435
|
var FormEngine = class {
|
|
3315
|
-
constructor(runtimeTree, environment = {}) {
|
|
3436
|
+
constructor(runtimeTree, environment = {}, envConfig = {}) {
|
|
3316
3437
|
this.runtimeTree = runtimeTree;
|
|
3317
|
-
this.
|
|
3438
|
+
this.envConfig = structuredClone(envConfig);
|
|
3439
|
+
validateFormEnvReferences(this.runtimeTree.schema, this.envConfig.definitions);
|
|
3440
|
+
this.environment = createFormEngineEnvironment({ ...environment, env: resolveFormEnv(this.envConfig, false) });
|
|
3318
3441
|
this.stepRunner = new StepRunner(this, this.environment);
|
|
3319
3442
|
this.buildNodeIndexes();
|
|
3320
3443
|
this.buildDependencyGraph();
|
|
@@ -3340,6 +3463,13 @@ var FormEngine = class {
|
|
|
3340
3463
|
graph = new DependencyGraph();
|
|
3341
3464
|
stepRunner;
|
|
3342
3465
|
environment;
|
|
3466
|
+
envConfig;
|
|
3467
|
+
getEnvDefinitions() {
|
|
3468
|
+
return structuredClone(this.envConfig.definitions ?? {});
|
|
3469
|
+
}
|
|
3470
|
+
getEnv() {
|
|
3471
|
+
return this.environment.env ?? Object.freeze({});
|
|
3472
|
+
}
|
|
3343
3473
|
fieldNodes = /* @__PURE__ */ new Map();
|
|
3344
3474
|
actionNodes = /* @__PURE__ */ new Map();
|
|
3345
3475
|
displayNodes = /* @__PURE__ */ new Map();
|
|
@@ -3409,6 +3539,7 @@ var FormEngine = class {
|
|
|
3409
3539
|
this.notifyStateUpdated();
|
|
3410
3540
|
return this.batch(async () => {
|
|
3411
3541
|
try {
|
|
3542
|
+
this.environment.env = resolveFormEnv(this.envConfig, !this.skipLoadStepsOnInitialize);
|
|
3412
3543
|
this.hydrateValues(this.runtimeTree);
|
|
3413
3544
|
if (!this.skipLoadStepsOnInitialize) {
|
|
3414
3545
|
await this.runLoadSteps();
|
|
@@ -3856,7 +3987,8 @@ var FormEngine = class {
|
|
|
3856
3987
|
getPath(
|
|
3857
3988
|
{
|
|
3858
3989
|
values: this.values,
|
|
3859
|
-
resources: this.resources
|
|
3990
|
+
resources: this.resources,
|
|
3991
|
+
env: this.getEnv()
|
|
3860
3992
|
},
|
|
3861
3993
|
path
|
|
3862
3994
|
)
|
|
@@ -3947,7 +4079,8 @@ var FormEngine = class {
|
|
|
3947
4079
|
this.reevaluateConditions();
|
|
3948
4080
|
const result = runSubmitPipeline(this.getSnapshot(), {
|
|
3949
4081
|
environment: this.environment,
|
|
3950
|
-
resources: this.resources
|
|
4082
|
+
resources: this.resources,
|
|
4083
|
+
env: this.getEnv()
|
|
3951
4084
|
});
|
|
3952
4085
|
const itemErrors = {};
|
|
3953
4086
|
this.itemFieldState.forEach((state, name) => {
|
|
@@ -4338,7 +4471,8 @@ var FormEngine = class {
|
|
|
4338
4471
|
value: changedField === "__bulk__" ? void 0 : nextValues[changedField],
|
|
4339
4472
|
state: this.getConditionState(),
|
|
4340
4473
|
environment: this.environment,
|
|
4341
|
-
resources: this.resources
|
|
4474
|
+
resources: this.resources,
|
|
4475
|
+
env: this.getEnv()
|
|
4342
4476
|
});
|
|
4343
4477
|
this.validateValuesRepeatFields(
|
|
4344
4478
|
changedFields.filter((name) => name.includes("."))
|
|
@@ -4364,7 +4498,8 @@ var FormEngine = class {
|
|
|
4364
4498
|
value: void 0,
|
|
4365
4499
|
state: this.getConditionState(),
|
|
4366
4500
|
environment: this.environment,
|
|
4367
|
-
resources: this.resources
|
|
4501
|
+
resources: this.resources,
|
|
4502
|
+
env: this.getEnv()
|
|
4368
4503
|
};
|
|
4369
4504
|
}
|
|
4370
4505
|
getConditionState() {
|
|
@@ -4384,7 +4519,8 @@ var FormEngine = class {
|
|
|
4384
4519
|
value: void 0,
|
|
4385
4520
|
state: this.getConditionState(),
|
|
4386
4521
|
environment: this.environment,
|
|
4387
|
-
resources: this.resources
|
|
4522
|
+
resources: this.resources,
|
|
4523
|
+
env: this.getEnv()
|
|
4388
4524
|
});
|
|
4389
4525
|
}
|
|
4390
4526
|
syncPendingState() {
|
|
@@ -4586,7 +4722,8 @@ var FormEngine = class {
|
|
|
4586
4722
|
{
|
|
4587
4723
|
environment: this.environment,
|
|
4588
4724
|
values: this.values,
|
|
4589
|
-
resources: this.resources
|
|
4725
|
+
resources: this.resources,
|
|
4726
|
+
env: this.getEnv()
|
|
4590
4727
|
}
|
|
4591
4728
|
);
|
|
4592
4729
|
node.state.error = result.error;
|
|
@@ -4643,7 +4780,8 @@ var FormEngine = class {
|
|
|
4643
4780
|
const source = resolveScopedPath(
|
|
4644
4781
|
{
|
|
4645
4782
|
values: this.values,
|
|
4646
|
-
resources: this.resources
|
|
4783
|
+
resources: this.resources,
|
|
4784
|
+
env: this.getEnv()
|
|
4647
4785
|
},
|
|
4648
4786
|
options.source
|
|
4649
4787
|
);
|
|
@@ -4670,6 +4808,7 @@ var FormEngine = class {
|
|
|
4670
4808
|
{
|
|
4671
4809
|
values: this.values,
|
|
4672
4810
|
resources: this.resources,
|
|
4811
|
+
env: this.getEnv(),
|
|
4673
4812
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
4674
4813
|
},
|
|
4675
4814
|
data.source
|
|
@@ -4695,6 +4834,7 @@ var FormEngine = class {
|
|
|
4695
4834
|
executeComputed(schema.computed, this.values, {
|
|
4696
4835
|
environment: this.environment,
|
|
4697
4836
|
resources: this.resources,
|
|
4837
|
+
env: this.getEnv(),
|
|
4698
4838
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
4699
4839
|
})
|
|
4700
4840
|
);
|
|
@@ -4706,6 +4846,7 @@ var FormEngine = class {
|
|
|
4706
4846
|
{
|
|
4707
4847
|
values: this.values,
|
|
4708
4848
|
resources: this.resources,
|
|
4849
|
+
env: this.getEnv(),
|
|
4709
4850
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
4710
4851
|
},
|
|
4711
4852
|
props.from
|
|
@@ -4729,6 +4870,7 @@ var FormEngine = class {
|
|
|
4729
4870
|
{
|
|
4730
4871
|
values: this.values,
|
|
4731
4872
|
resources: this.resources,
|
|
4873
|
+
env: this.getEnv(),
|
|
4732
4874
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
4733
4875
|
},
|
|
4734
4876
|
props.from
|
|
@@ -4760,6 +4902,7 @@ var FormEngine = class {
|
|
|
4760
4902
|
{
|
|
4761
4903
|
values: this.values,
|
|
4762
4904
|
resources: this.resources,
|
|
4905
|
+
env: this.getEnv(),
|
|
4763
4906
|
...options?.item !== void 0 ? { item: options.item } : {}
|
|
4764
4907
|
},
|
|
4765
4908
|
props.titleFrom
|
|
@@ -4799,7 +4942,8 @@ var FormEngine = class {
|
|
|
4799
4942
|
const source = resolveScopedPath(
|
|
4800
4943
|
{
|
|
4801
4944
|
values: this.values,
|
|
4802
|
-
resources: this.resources
|
|
4945
|
+
resources: this.resources,
|
|
4946
|
+
env: this.getEnv()
|
|
4803
4947
|
},
|
|
4804
4948
|
props.source
|
|
4805
4949
|
);
|
|
@@ -4988,6 +5132,7 @@ var FormEngine = class {
|
|
|
4988
5132
|
{
|
|
4989
5133
|
values: this.values,
|
|
4990
5134
|
resources: this.resources,
|
|
5135
|
+
env: this.getEnv(),
|
|
4991
5136
|
...options.item !== void 0 ? { item: options.item } : {}
|
|
4992
5137
|
},
|
|
4993
5138
|
options.from.trim()
|
|
@@ -5006,7 +5151,7 @@ var FormEngine = class {
|
|
|
5006
5151
|
}
|
|
5007
5152
|
validateValuesRepeatFields(only) {
|
|
5008
5153
|
const targets = only ? new Set(only) : void 0;
|
|
5009
|
-
const
|
|
5154
|
+
const visit2 = (meta, absolutePath) => {
|
|
5010
5155
|
const items = readRepeatArray(this.values, absolutePath);
|
|
5011
5156
|
items.forEach((item, index) => {
|
|
5012
5157
|
meta.fieldTemplates.forEach((template, fieldName) => {
|
|
@@ -5024,20 +5169,21 @@ var FormEngine = class {
|
|
|
5024
5169
|
const result = validateField(template.schema, value, {
|
|
5025
5170
|
environment: this.environment,
|
|
5026
5171
|
values: this.values,
|
|
5027
|
-
resources: this.resources
|
|
5172
|
+
resources: this.resources,
|
|
5173
|
+
env: this.getEnv()
|
|
5028
5174
|
});
|
|
5029
5175
|
const state = this.ensureItemFieldState(scopedName);
|
|
5030
5176
|
state.error = result.error;
|
|
5031
5177
|
});
|
|
5032
5178
|
meta.nested.forEach((nested) => {
|
|
5033
|
-
|
|
5179
|
+
visit2(nested, `${absolutePath}.${index}.${nested.path}`);
|
|
5034
5180
|
});
|
|
5035
5181
|
});
|
|
5036
5182
|
};
|
|
5037
|
-
this.valuesRepeatMetas.forEach((meta) =>
|
|
5183
|
+
this.valuesRepeatMetas.forEach((meta) => visit2(meta, meta.path));
|
|
5038
5184
|
}
|
|
5039
5185
|
touchAllValuesRepeatFields() {
|
|
5040
|
-
const
|
|
5186
|
+
const visit2 = (meta, absolutePath) => {
|
|
5041
5187
|
const items = readRepeatArray(this.values, absolutePath);
|
|
5042
5188
|
items.forEach((_, index) => {
|
|
5043
5189
|
meta.fieldTemplates.forEach((_2, fieldName) => {
|
|
@@ -5045,11 +5191,11 @@ var FormEngine = class {
|
|
|
5045
5191
|
this.ensureItemFieldState(scopedName).touched = true;
|
|
5046
5192
|
});
|
|
5047
5193
|
meta.nested.forEach((nested) => {
|
|
5048
|
-
|
|
5194
|
+
visit2(nested, `${absolutePath}.${index}.${nested.path}`);
|
|
5049
5195
|
});
|
|
5050
5196
|
});
|
|
5051
5197
|
};
|
|
5052
|
-
this.valuesRepeatMetas.forEach((meta) =>
|
|
5198
|
+
this.valuesRepeatMetas.forEach((meta) => visit2(meta, meta.path));
|
|
5053
5199
|
}
|
|
5054
5200
|
pruneItemFieldState(absolutePath, removedIndex, previousLength) {
|
|
5055
5201
|
const next = /* @__PURE__ */ new Map();
|
|
@@ -5245,19 +5391,25 @@ export {
|
|
|
5245
5391
|
adaptDisplay,
|
|
5246
5392
|
adaptField,
|
|
5247
5393
|
adaptLayout,
|
|
5394
|
+
collectFormEnvReferences,
|
|
5248
5395
|
compileSchema,
|
|
5249
5396
|
conditions,
|
|
5250
5397
|
createFormEngineEnvironment,
|
|
5251
5398
|
createRuntimeNode,
|
|
5252
5399
|
formatTagSourceValue,
|
|
5253
5400
|
isThenable,
|
|
5401
|
+
remapFormEnvReferences,
|
|
5402
|
+
resolveFormEnv,
|
|
5254
5403
|
resolveLoadingProp,
|
|
5255
5404
|
resolveTagValueMapping,
|
|
5256
5405
|
runConditionEngine,
|
|
5257
5406
|
tagMapKey,
|
|
5258
5407
|
traverseNode,
|
|
5259
5408
|
validateApiUrlTemplate,
|
|
5409
|
+
validateEnvBaseUrl,
|
|
5410
|
+
validateEnvDefinitions,
|
|
5260
5411
|
validateField,
|
|
5412
|
+
validateFormEnvReferences,
|
|
5261
5413
|
validateSchema,
|
|
5262
5414
|
validation,
|
|
5263
5415
|
validationRegistry
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bsm-form/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "Framework-independent form engine for BSM FormSchema",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
],
|
|
20
20
|
"sideEffects": false,
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@bsm-form/schema": "0.
|
|
22
|
+
"@bsm-form/schema": "0.41.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"vitest": "^4.1.10"
|