@remoteoss/json-schema-form 0.11.11-dev.20250220174843 → 1.0.0-alpha.1

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 DELETED
@@ -1,2171 +0,0 @@
1
-
2
- /*!
3
- Copyright (c) 2025 Remote Technology, Inc.
4
- NPM Package: @remoteoss/json-schema-form@0.11.11-dev.20250220174843
5
- Generated: Thu, 20 Feb 2025 17:48:57 GMT
6
-
7
- MIT License
8
-
9
- Copyright (c) 2023 Remote Technology, Inc.
10
-
11
- Permission is hereby granted, free of charge, to any person obtaining a copy
12
- of this software and associated documentation files (the "Software"), to deal
13
- in the Software without restriction, including without limitation the rights
14
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
- copies of the Software, and to permit persons to whom the Software is
16
- furnished to do so, subject to the following conditions:
17
-
18
- The above copyright notice and this permission notice shall be included in all
19
- copies or substantial portions of the Software.
20
-
21
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
- SOFTWARE.
28
-
29
- */
30
- var __create = Object.create;
31
- var __defProp = Object.defineProperty;
32
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
33
- var __getOwnPropNames = Object.getOwnPropertyNames;
34
- var __getProtoOf = Object.getPrototypeOf;
35
- var __hasOwnProp = Object.prototype.hasOwnProperty;
36
- var __export = (target, all) => {
37
- for (var name in all)
38
- __defProp(target, name, { get: all[name], enumerable: true });
39
- };
40
- var __copyProps = (to, from, except, desc) => {
41
- if (from && typeof from === "object" || typeof from === "function") {
42
- for (let key of __getOwnPropNames(from))
43
- if (!__hasOwnProp.call(to, key) && key !== except)
44
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
45
- }
46
- return to;
47
- };
48
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
49
- // If the importer is in node compatibility mode or this is not an ESM
50
- // file that has been converted to a CommonJS file using a Babel-
51
- // compatible transform (i.e. "__esModule" has not been set), then set
52
- // "default" to the CommonJS "module.exports" for node compatibility.
53
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
54
- mod
55
- ));
56
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
57
-
58
- // src/index.js
59
- var src_exports = {};
60
- __export(src_exports, {
61
- buildCompleteYupSchema: () => buildCompleteYupSchema,
62
- createHeadlessForm: () => createHeadlessForm,
63
- modify: () => modify,
64
- pickXKey: () => pickXKey
65
- });
66
- module.exports = __toCommonJS(src_exports);
67
-
68
- // src/createHeadlessForm.js
69
- var import_get3 = __toESM(require("lodash/get"));
70
- var import_isNil3 = __toESM(require("lodash/isNil"));
71
- var import_omit3 = __toESM(require("lodash/omit"));
72
- var import_omitBy2 = __toESM(require("lodash/omitBy"));
73
- var import_pick2 = __toESM(require("lodash/pick"));
74
- var import_size = __toESM(require("lodash/size"));
75
-
76
- // src/calculateConditionalProperties.js
77
- var import_merge2 = __toESM(require("lodash/merge"));
78
- var import_omit2 = __toESM(require("lodash/omit"));
79
-
80
- // src/helpers.js
81
- var import_get2 = __toESM(require("lodash/get"));
82
- var import_isNil = __toESM(require("lodash/isNil"));
83
- var import_omit = __toESM(require("lodash/omit"));
84
- var import_omitBy = __toESM(require("lodash/omitBy"));
85
- var import_set = __toESM(require("lodash/set"));
86
- var import_yup2 = require("yup");
87
-
88
- // src/utils.js
89
- function convertDiskSizeFromTo(from, to) {
90
- const units = ["bytes", "kb", "mb"];
91
- return function convert(value) {
92
- return value * Math.pow(1024, units.indexOf(from.toLowerCase())) / Math.pow(1024, units.indexOf(to.toLowerCase()));
93
- };
94
- }
95
- function hasProperty(object2, propertyName) {
96
- return Object.prototype.hasOwnProperty.call(object2, propertyName);
97
- }
98
-
99
- // src/internals/checkIfConditionMatches.js
100
- function checkIfConditionMatchesProperties(node, formValues, formFields, logic) {
101
- if (typeof node.if === "boolean") {
102
- return node.if;
103
- }
104
- return Object.keys(node.if.properties ?? {}).every((name) => {
105
- const currentProperty = node.if.properties[name];
106
- const value = formValues[name];
107
- const hasEmptyValue = typeof value === "undefined" || // NOTE: This is a "Remote API" dependency, as empty fields are sent as "null".
108
- value === null;
109
- const hasIfExplicit = node.if.required?.includes(name);
110
- if (hasEmptyValue && !hasIfExplicit) {
111
- return true;
112
- }
113
- if (hasProperty(currentProperty, "const")) {
114
- return compareFormValueWithSchemaValue(value, currentProperty.const);
115
- }
116
- if (currentProperty.contains?.pattern) {
117
- const formValue = value || [];
118
- if (Array.isArray(formValue)) {
119
- const pattern = new RegExp(currentProperty.contains.pattern);
120
- return (value || []).some((item) => pattern.test(item));
121
- }
122
- }
123
- if (currentProperty.enum) {
124
- return currentProperty.enum.includes(value);
125
- }
126
- if (currentProperty.properties) {
127
- return checkIfConditionMatchesProperties(
128
- { if: currentProperty },
129
- formValues[name],
130
- getField(name, formFields).fields,
131
- logic
132
- );
133
- }
134
- const field = getField(name, formFields);
135
- return validateFieldSchema(
136
- {
137
- ...field,
138
- ...currentProperty,
139
- required: true
140
- },
141
- value
142
- );
143
- });
144
- }
145
- function checkIfMatchesValidationsAndComputedValues(node, formValues, logic, parentID) {
146
- const validationsMatch = Object.entries(node.if.validations ?? {}).every(([name, property]) => {
147
- const currentValue = logic.getScope(parentID).applyValidationRuleInCondition(name, formValues);
148
- if (Object.hasOwn(property, "const") && currentValue === property.const)
149
- return true;
150
- return false;
151
- });
152
- const computedValuesMatch = Object.entries(node.if.computedValues ?? {}).every(
153
- ([name, property]) => {
154
- const currentValue = logic.getScope(parentID).applyComputedValueRuleInCondition(name, formValues);
155
- if (Object.hasOwn(property, "const") && currentValue === property.const)
156
- return true;
157
- return false;
158
- }
159
- );
160
- return computedValuesMatch && validationsMatch;
161
- }
162
-
163
- // src/internals/helpers.js
164
- var import_merge = __toESM(require("lodash/fp/merge"));
165
- var import_get = __toESM(require("lodash/get"));
166
- var import_isEmpty = __toESM(require("lodash/isEmpty"));
167
- var import_isFunction = __toESM(require("lodash/isFunction"));
168
- function pickXKey(node, key) {
169
- const deprecatedKeys = ["presentation", "errorMessage"];
170
- return (0, import_get.default)(node, `x-jsf-${key}`, deprecatedKeys.includes(key) ? node?.[key] : void 0);
171
- }
172
- function getFieldDescription(node, customProperties = {}) {
173
- const nodeDescription = node?.description ? {
174
- description: node.description
175
- } : {};
176
- const customDescription = customProperties?.description ? {
177
- description: (0, import_isFunction.default)(customProperties.description) ? customProperties.description(node?.description, {
178
- ...node,
179
- ...customProperties
180
- }) : customProperties.description
181
- } : {};
182
- const nodePresentation = pickXKey(node, "presentation");
183
- const presentation = !(0, import_isEmpty.default)(nodePresentation) && {
184
- presentation: { ...nodePresentation, ...customDescription }
185
- };
186
- return (0, import_merge.default)(nodeDescription, { ...customDescription, ...presentation });
187
- }
188
-
189
- // src/internals/fields.js
190
- var jsonTypes = {
191
- STRING: "string",
192
- NUMBER: "number",
193
- INTEGER: "integer",
194
- OBJECT: "object",
195
- ARRAY: "array",
196
- BOOLEAN: "boolean",
197
- NULL: "null"
198
- };
199
- var supportedTypes = {
200
- TEXT: "text",
201
- NUMBER: "number",
202
- SELECT: "select",
203
- FILE: "file",
204
- RADIO: "radio",
205
- GROUP_ARRAY: "group-array",
206
- EMAIL: "email",
207
- DATE: "date",
208
- CHECKBOX: "checkbox",
209
- FIELDSET: "fieldset"
210
- };
211
- var jsonTypeToInputType = {
212
- [jsonTypes.STRING]: ({ oneOf, format }) => {
213
- if (format === "email")
214
- return supportedTypes.EMAIL;
215
- if (format === "date")
216
- return supportedTypes.DATE;
217
- if (format === "data-url")
218
- return supportedTypes.FILE;
219
- if (oneOf)
220
- return supportedTypes.RADIO;
221
- return supportedTypes.TEXT;
222
- },
223
- [jsonTypes.NUMBER]: () => supportedTypes.NUMBER,
224
- [jsonTypes.INTEGER]: () => supportedTypes.NUMBER,
225
- [jsonTypes.OBJECT]: () => supportedTypes.FIELDSET,
226
- [jsonTypes.ARRAY]: ({ items }) => {
227
- if (items.properties)
228
- return supportedTypes.GROUP_ARRAY;
229
- return supportedTypes.SELECT;
230
- },
231
- [jsonTypes.BOOLEAN]: () => supportedTypes.CHECKBOX
232
- };
233
- function getInputType(fieldProperties, strictInputType, name) {
234
- const presentation = pickXKey(fieldProperties, "presentation") ?? {};
235
- const presentationInputType = presentation?.inputType;
236
- if (presentationInputType) {
237
- return presentationInputType;
238
- }
239
- if (strictInputType) {
240
- throw Error(`Strict error: Missing inputType to field "${name || fieldProperties.title}".
241
- You can fix the json schema or skip this error by calling createHeadlessForm(schema, { strictInputType: false })`);
242
- }
243
- if (!fieldProperties.type) {
244
- if (fieldProperties.items?.properties) {
245
- return supportedTypes.GROUP_ARRAY;
246
- }
247
- if (fieldProperties.properties) {
248
- return supportedTypes.SELECT;
249
- }
250
- return jsonTypeToInputType[jsonTypes.STRING](fieldProperties);
251
- }
252
- return jsonTypeToInputType[fieldProperties.type]?.(fieldProperties);
253
- }
254
- function _composeFieldFile({ name, label, description, accept, required = true, ...attrs }) {
255
- return {
256
- type: supportedTypes.FILE,
257
- name,
258
- label,
259
- description,
260
- required,
261
- accept,
262
- ...attrs
263
- };
264
- }
265
- function _composeFieldText({ name, label, description, required = true, ...attrs }) {
266
- return {
267
- type: supportedTypes.TEXT,
268
- name,
269
- label,
270
- description,
271
- required,
272
- ...attrs
273
- };
274
- }
275
- function _composeFieldEmail({ name, label, required = true, ...attrs }) {
276
- return {
277
- type: supportedTypes.EMAIL,
278
- name,
279
- label,
280
- required,
281
- ...attrs
282
- };
283
- }
284
- function _composeFieldNumber({
285
- name,
286
- label,
287
- percentage = false,
288
- required = true,
289
- minimum,
290
- maximum,
291
- ...attrs
292
- }) {
293
- let minValue = minimum;
294
- let maxValue = maximum;
295
- if (percentage) {
296
- minValue = minValue ?? 0;
297
- maxValue = maxValue ?? 100;
298
- }
299
- return {
300
- type: supportedTypes.NUMBER,
301
- name,
302
- label,
303
- percentage,
304
- required,
305
- minimum: minValue,
306
- maximum: maxValue,
307
- ...attrs
308
- };
309
- }
310
- function _composeFieldDate({ name, label, required = true, ...attrs }) {
311
- return {
312
- type: supportedTypes.DATE,
313
- name,
314
- label,
315
- required,
316
- ...attrs
317
- };
318
- }
319
- function _composeFieldRadio({ name, label, options, required = true, ...attrs }) {
320
- return {
321
- type: supportedTypes.RADIO,
322
- name,
323
- label,
324
- options,
325
- required,
326
- ...attrs
327
- };
328
- }
329
- function _composeFieldSelect({ name, label, options, required = true, ...attrs }) {
330
- return {
331
- type: supportedTypes.SELECT,
332
- name,
333
- label,
334
- options,
335
- required,
336
- ...attrs
337
- };
338
- }
339
- function _composeNthFieldGroup({ name, label, required, nthFieldGroup, ...attrs }) {
340
- return [
341
- {
342
- ...nthFieldGroup,
343
- type: supportedTypes.GROUP_ARRAY,
344
- name,
345
- label,
346
- required,
347
- ...attrs
348
- }
349
- ];
350
- }
351
- function _composeFieldCheckbox({
352
- required = true,
353
- name,
354
- label,
355
- description,
356
- default: defaultValue,
357
- checkboxValue,
358
- ...attrs
359
- }) {
360
- return {
361
- type: supportedTypes.CHECKBOX,
362
- required,
363
- name,
364
- label,
365
- description,
366
- checkboxValue,
367
- ...defaultValue && { default: defaultValue },
368
- ...attrs
369
- };
370
- }
371
- function _composeFieldset({ name, label, fields, variant, ...attrs }) {
372
- return {
373
- type: supportedTypes.FIELDSET,
374
- name,
375
- label,
376
- fields,
377
- variant,
378
- ...attrs
379
- };
380
- }
381
- var _composeFieldArbitraryClosure = (inputType) => (attrs) => ({
382
- type: inputType,
383
- ...attrs
384
- });
385
- var inputTypeMap = {
386
- text: _composeFieldText,
387
- select: _composeFieldSelect,
388
- radio: _composeFieldRadio,
389
- date: _composeFieldDate,
390
- number: _composeFieldNumber,
391
- "group-array": _composeNthFieldGroup,
392
- fieldset: _composeFieldset,
393
- file: _composeFieldFile,
394
- email: _composeFieldEmail,
395
- checkbox: _composeFieldCheckbox
396
- };
397
- function _composeFieldCustomClosure(defaultComposeFn) {
398
- return ({ fieldCustomization, ...attrs }) => {
399
- const { description, ...restFieldCustomization } = fieldCustomization;
400
- const fieldDescription = getFieldDescription(attrs, fieldCustomization);
401
- const { nthFieldGroup, ...restAttrs } = attrs;
402
- const commonAttrs = {
403
- ...restAttrs,
404
- ...restFieldCustomization,
405
- ...fieldDescription
406
- };
407
- if (attrs.inputType === supportedTypes.GROUP_ARRAY) {
408
- return [
409
- {
410
- ...nthFieldGroup,
411
- ...commonAttrs
412
- }
413
- ];
414
- }
415
- return {
416
- ...defaultComposeFn(attrs),
417
- ...commonAttrs
418
- };
419
- };
420
- }
421
-
422
- // src/jsonLogic.js
423
- var import_json_logic_js = __toESM(require("json-logic-js"));
424
-
425
- // src/yupSchema.js
426
- var import_flow = __toESM(require("lodash/flow"));
427
- var import_noop = __toESM(require("lodash/noop"));
428
- var import_randexp = require("randexp");
429
- var import_yup = require("yup");
430
- var DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
431
- var baseString = (0, import_yup.string)().trim();
432
- var todayDateHint = (/* @__PURE__ */ new Date()).toISOString().substring(0, 10);
433
- var convertBytesToKB = convertDiskSizeFromTo("Bytes", "KB");
434
- var convertKbBytesToMB = convertDiskSizeFromTo("KB", "MB");
435
- var validateOnlyStrings = (0, import_yup.string)().trim().nullable().test(
436
- "is-string",
437
- "${path} must be a `string` type, but the final value was: `${value}`.",
438
- (value, context) => {
439
- if (context.originalValue !== null && context.originalValue !== void 0) {
440
- return typeof context.originalValue === "string";
441
- }
442
- return true;
443
- }
444
- );
445
- var compareDates = (d1, d2) => {
446
- let date1 = new Date(d1).getTime();
447
- let date2 = new Date(d2).getTime();
448
- if (date1 < date2) {
449
- return "LESSER";
450
- } else if (date1 > date2) {
451
- return "GREATER";
452
- } else {
453
- return "EQUAL";
454
- }
455
- };
456
- var validateMinDate = (value, minDate) => {
457
- const compare = compareDates(value, minDate);
458
- return compare === "GREATER" || compare === "EQUAL" ? true : false;
459
- };
460
- var validateMaxDate = (value, minDate) => {
461
- const compare = compareDates(value, minDate);
462
- return compare === "LESSER" || compare === "EQUAL" ? true : false;
463
- };
464
- var validateRadioOrSelectOptions = (value, options) => {
465
- if (value === void 0)
466
- return true;
467
- const exactMatch = options.some((option) => option.value === value);
468
- if (exactMatch)
469
- return true;
470
- const patternMatch = options.some((option) => option.pattern?.test(value));
471
- return !!patternMatch;
472
- };
473
- var yupSchemas = {
474
- text: validateOnlyStrings,
475
- radioOrSelectString: (options) => {
476
- return (0, import_yup.string)().nullable().transform((value) => {
477
- if (value === "") {
478
- return void 0;
479
- }
480
- if (options?.some((option) => option.value === null)) {
481
- return value;
482
- }
483
- return value === null ? void 0 : value;
484
- }).test(
485
- "matchesOptionOrPattern",
486
- ({ value }) => `The option ${JSON.stringify(value)} is not valid.`,
487
- (castValue, { originalValue }) => {
488
- if (castValue !== void 0 && typeof originalValue !== "string") {
489
- return false;
490
- }
491
- return validateRadioOrSelectOptions(castValue, options);
492
- }
493
- );
494
- },
495
- date: ({ minDate, maxDate }) => {
496
- let dateString = (0, import_yup.string)().nullable().transform((value) => {
497
- if (value === "") {
498
- return void 0;
499
- }
500
- return value === null ? void 0 : value;
501
- }).trim().matches(
502
- /(?:\d){4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9]|3[0-1])/,
503
- `Must be a valid date in ${DEFAULT_DATE_FORMAT.toLocaleLowerCase()} format. e.g. ${todayDateHint}`
504
- );
505
- if (minDate) {
506
- dateString = dateString.test(
507
- "minDate",
508
- `The date must be ${minDate} or after.`,
509
- (value) => validateMinDate(value, minDate)
510
- );
511
- }
512
- if (maxDate) {
513
- dateString = dateString.test(
514
- "maxDate",
515
- `The date must be ${maxDate} or before.`,
516
- (value) => validateMaxDate(value, maxDate)
517
- );
518
- }
519
- return dateString;
520
- },
521
- radioOrSelectNumber: (options) => (0, import_yup.mixed)().typeError("The value must be a number").transform((value) => {
522
- if (options?.some((option) => option.value === null)) {
523
- return value;
524
- }
525
- return value === null ? void 0 : value;
526
- }).test(
527
- "matchesOptionOrPattern",
528
- ({ value }) => {
529
- return `The option ${JSON.stringify(value)} is not valid.`;
530
- },
531
- (value) => {
532
- if (value !== void 0 && typeof value !== "number")
533
- return false;
534
- return validateRadioOrSelectOptions(value, options);
535
- }
536
- ).nullable(),
537
- radioOrSelectBoolean: (options) => {
538
- return (0, import_yup.boolean)().nullable().transform((value) => {
539
- if (options?.some((option) => option.value === null)) {
540
- return value;
541
- }
542
- return value === null ? void 0 : value;
543
- }).test(
544
- "matchesOptionOrPattern",
545
- ({ originalValue }) => {
546
- return `The option ${JSON.stringify(originalValue)} is not valid.`;
547
- },
548
- (castValue, { originalValue }) => {
549
- if (typeof originalValue !== "boolean" && castValue !== void 0)
550
- return false;
551
- return validateRadioOrSelectOptions(castValue, options);
552
- }
553
- );
554
- },
555
- number: (0, import_yup.number)().typeError("The value must be a number").nullable(),
556
- file: (0, import_yup.array)().nullable(),
557
- email: (0, import_yup.string)().trim().email("Please enter a valid email address").nullable(),
558
- fieldset: (0, import_yup.object)().nullable(),
559
- checkbox: (0, import_yup.string)().trim().nullable(),
560
- checkboxBool: (0, import_yup.boolean)().typeError('The value must be a boolean, but received "${value}"').nullable(),
561
- multiple: {
562
- select: (0, import_yup.array)().nullable(),
563
- "group-array": (0, import_yup.array)().nullable()
564
- },
565
- null: (0, import_yup.mixed)().typeError("The value must be null").test(
566
- "matchesNullValue",
567
- ({ value }) => `The value ${JSON.stringify(value)} is not valid.`,
568
- (value) => value === void 0 || value === null
569
- )
570
- };
571
- var yupSchemasToJsonTypes = {
572
- string: yupSchemas.text,
573
- number: yupSchemas.number,
574
- integer: yupSchemas.number,
575
- object: yupSchemas.fieldset,
576
- array: yupSchemas.multiple.select,
577
- boolean: yupSchemas.checkboxBool,
578
- null: yupSchemas.null
579
- };
580
- function getRequiredErrorMessage(inputType, { inlineError, configError }) {
581
- if (inlineError)
582
- return inlineError;
583
- if (configError)
584
- return configError;
585
- if (inputType === supportedTypes.CHECKBOX)
586
- return "Please acknowledge this field";
587
- return "Required field";
588
- }
589
- var getJsonTypeInArray = (jsonType) => {
590
- return Array.isArray(jsonType) ? jsonType.find((val) => val !== "null") : jsonType;
591
- };
592
- var getOptions = (field) => {
593
- const allValues = field.options?.map((option) => ({
594
- value: option.value,
595
- pattern: option.pattern ? new RegExp(option.pattern) : null
596
- }));
597
- const isOptionalWithNull = Array.isArray(field.jsonType) && // @TODO should also check the "oneOf" directly looking for "null"
598
- // option but we don't have direct access at this point.
599
- // Otherwise the JSON Schema validator will fail as explained in PR#18
600
- field.jsonType.includes("null");
601
- return isOptionalWithNull ? [...allValues, { option: null }] : allValues;
602
- };
603
- var getYupSchema = ({ inputType, ...field }) => {
604
- const jsonType = getJsonTypeInArray(field.jsonType);
605
- const hasOptions = field.options?.length > 0;
606
- const generateOptionSchema = (type) => {
607
- const optionValues = getOptions(field);
608
- switch (type) {
609
- case "number":
610
- return yupSchemas.radioOrSelectNumber(optionValues);
611
- case "boolean":
612
- return yupSchemas.radioOrSelectBoolean(optionValues);
613
- default:
614
- return yupSchemas.radioOrSelectString(optionValues);
615
- }
616
- };
617
- if (hasOptions) {
618
- if (Array.isArray(field.jsonType)) {
619
- return field.jsonType.includes("number") ? generateOptionSchema("number") : generateOptionSchema("string");
620
- }
621
- return generateOptionSchema(field.jsonType);
622
- }
623
- if (field.format === "date") {
624
- return yupSchemas.date({ minDate: field.minDate, maxDate: field.maxDate });
625
- }
626
- return yupSchemas[inputType] || yupSchemasToJsonTypes[jsonType];
627
- };
628
- function buildYupSchema(field, config, logic) {
629
- const { inputType, jsonType: jsonTypeValue, errorMessage = {}, ...propertyFields } = field;
630
- const isCheckboxBoolean = typeof propertyFields.checkboxValue === "boolean";
631
- let baseSchema;
632
- const errorMessageFromConfig = config?.inputTypes?.[inputType]?.errorMessage || {};
633
- const jsonType = getJsonTypeInArray(field.jsonType);
634
- if (propertyFields.multiple) {
635
- baseSchema = yupSchemas.multiple[inputType] || yupSchemasToJsonTypes.array;
636
- } else if (isCheckboxBoolean) {
637
- baseSchema = yupSchemas.checkboxBool;
638
- } else {
639
- baseSchema = getYupSchema(field);
640
- }
641
- if (!baseSchema) {
642
- return import_noop.default;
643
- }
644
- const randomPlaceholder = propertyFields.pattern && (0, import_randexp.randexp)(propertyFields.pattern);
645
- const requiredMessage = getRequiredErrorMessage(inputType, {
646
- inlineError: errorMessage.required,
647
- configError: errorMessageFromConfig.required
648
- });
649
- function withRequired(yupSchema) {
650
- if (isCheckboxBoolean) {
651
- return yupSchema.oneOf([true], requiredMessage).required(requiredMessage);
652
- }
653
- return yupSchema.required(requiredMessage);
654
- }
655
- function withInteger(yupSchema) {
656
- return yupSchema.integer(
657
- (message) => errorMessage.integer ?? errorMessageFromConfig.integer ?? `Must not contain decimal points. E.g. ${Math.floor(message.value)} instead of ${message.value}`
658
- );
659
- }
660
- function withMin(yupSchema) {
661
- return yupSchema.min(
662
- propertyFields.minimum,
663
- (message) => errorMessage.minimum ?? errorMessageFromConfig.minimum ?? `Must be greater or equal to ${message.min}`
664
- );
665
- }
666
- function withMinLength(yupSchema) {
667
- return yupSchema.min(
668
- propertyFields.minLength,
669
- (message) => errorMessage.minLength ?? errorMessageFromConfig.minLength ?? `Please insert at least ${message.min} characters`
670
- );
671
- }
672
- function withMax(yupSchema) {
673
- return yupSchema.max(
674
- propertyFields.maximum,
675
- (message) => errorMessage.maximum ?? errorMessageFromConfig.maximum ?? `Must be smaller or equal to ${message.max}`
676
- );
677
- }
678
- function withMaxLength(yupSchema) {
679
- return yupSchema.max(
680
- propertyFields.maxLength,
681
- (message) => errorMessage.maxLength ?? errorMessageFromConfig.maxLength ?? `Please insert up to ${message.max} characters`
682
- );
683
- }
684
- function withMatches(yupSchema) {
685
- return yupSchema.matches(
686
- propertyFields.pattern,
687
- () => errorMessage.pattern ?? errorMessageFromConfig.pattern ?? `Must have a valid format. E.g. ${randomPlaceholder}`
688
- );
689
- }
690
- function isValidFileInput(files) {
691
- return files === void 0 || files === null || files.every(
692
- (file) => file instanceof File || Object.prototype.hasOwnProperty.call(file, "name")
693
- );
694
- }
695
- function withFile(yupSchema) {
696
- return yupSchema.test("isValidFile", "Not a valid file.", isValidFileInput);
697
- }
698
- function withMaxFileSize(yupSchema) {
699
- return yupSchema.test(
700
- "isValidFileSize",
701
- errorMessage.maxFileSize ?? errorMessageFromConfig.maxFileSize ?? `File size too large. The limit is ${convertKbBytesToMB(propertyFields.maxFileSize)} MB.`,
702
- (files) => isValidFileInput(files) && !files?.some((file) => convertBytesToKB(file.size) > propertyFields.maxFileSize)
703
- );
704
- }
705
- function withFileFormat(yupSchema) {
706
- return yupSchema.test(
707
- "isSupportedFormat",
708
- errorMessage.accept ?? errorMessageFromConfig.accept ?? `Unsupported file format. The acceptable formats are ${propertyFields.accept}.`,
709
- (files) => isValidFileInput(files) && files?.length > 0 ? files.some((file) => {
710
- const fileType = file.name.split(".").pop();
711
- return propertyFields.accept.includes(fileType.toLowerCase());
712
- }) : true
713
- );
714
- }
715
- function withConst(yupSchema) {
716
- return yupSchema.test(
717
- "isConst",
718
- errorMessage.const ?? errorMessageFromConfig.const ?? `The only accepted value is ${propertyFields.const}.`,
719
- (value) => propertyFields.required === false && value === void 0 || value === null || value === propertyFields.const
720
- );
721
- }
722
- function withBaseSchema() {
723
- const customErrorMsg = errorMessage.type || errorMessageFromConfig.type;
724
- if (customErrorMsg) {
725
- return baseSchema.typeError(customErrorMsg);
726
- }
727
- return baseSchema;
728
- }
729
- function buildFieldSetSchema(innerFields) {
730
- const fieldSetShape = {};
731
- innerFields.forEach((fieldSetfield) => {
732
- if (fieldSetfield.fields) {
733
- fieldSetShape[fieldSetfield.name] = (0, import_yup.object)().shape(
734
- buildFieldSetSchema(fieldSetfield.fields)
735
- );
736
- } else {
737
- fieldSetShape[fieldSetfield.name] = buildYupSchema(
738
- {
739
- ...fieldSetfield,
740
- inputType: fieldSetfield.type
741
- },
742
- config
743
- )();
744
- }
745
- });
746
- return fieldSetShape;
747
- }
748
- function buildGroupArraySchema() {
749
- return (0, import_yup.object)().shape(
750
- propertyFields.nthFieldGroup.fields().reduce(
751
- (schema, groupArrayField) => ({
752
- ...schema,
753
- [groupArrayField.name]: buildYupSchema(groupArrayField, config)()
754
- }),
755
- {}
756
- )
757
- );
758
- }
759
- const validators = [withBaseSchema];
760
- if (inputType === supportedTypes.GROUP_ARRAY) {
761
- validators[0] = () => withBaseSchema().of(buildGroupArraySchema());
762
- } else if (inputType === supportedTypes.FIELDSET) {
763
- validators[0] = () => withBaseSchema().shape(buildFieldSetSchema(propertyFields.fields));
764
- }
765
- if (propertyFields.required) {
766
- validators.push(withRequired);
767
- }
768
- if (inputType === supportedTypes.FILE) {
769
- validators.push(withFile);
770
- }
771
- if (jsonType === "integer") {
772
- validators.push(withInteger);
773
- }
774
- if (typeof propertyFields.minimum !== "undefined") {
775
- validators.push(withMin);
776
- }
777
- if (typeof propertyFields.minLength !== "undefined") {
778
- validators.push(withMinLength);
779
- }
780
- if (propertyFields.maximum !== void 0) {
781
- validators.push(withMax);
782
- }
783
- if (propertyFields.maxLength) {
784
- validators.push(withMaxLength);
785
- }
786
- if (propertyFields.pattern) {
787
- validators.push(withMatches);
788
- }
789
- if (propertyFields.maxFileSize) {
790
- validators.push(withMaxFileSize);
791
- }
792
- if (propertyFields.accept) {
793
- validators.push(withFileFormat);
794
- }
795
- if (typeof propertyFields.const !== "undefined") {
796
- validators.push(withConst);
797
- }
798
- if (propertyFields.jsonLogicValidations) {
799
- propertyFields.jsonLogicValidations.forEach(
800
- (id) => validators.push(yupSchemaWithCustomJSONLogic({ field, id, logic, config }))
801
- );
802
- }
803
- return (0, import_flow.default)(validators);
804
- }
805
- function getNoSortEdges(fields = []) {
806
- return fields.reduce((list, field) => {
807
- if (field.noSortEdges) {
808
- list.push(field.name);
809
- }
810
- return list;
811
- }, []);
812
- }
813
- function getSchema(fields = [], config) {
814
- const newSchema = {};
815
- fields.forEach((field) => {
816
- if (field.schema) {
817
- if (field.name) {
818
- if (field.inputType === supportedTypes.FIELDSET) {
819
- const fieldsetSchema = buildYupSchema(field, config)();
820
- newSchema[field.name] = fieldsetSchema;
821
- } else {
822
- newSchema[field.name] = field.schema;
823
- }
824
- } else {
825
- Object.assign(newSchema, getSchema(field.fields, config));
826
- }
827
- }
828
- });
829
- return newSchema;
830
- }
831
- function buildCompleteYupSchema(fields, config) {
832
- return (0, import_yup.object)().shape(getSchema(fields, config), getNoSortEdges(fields));
833
- }
834
-
835
- // src/jsonLogic.js
836
- function createValidationChecker(schema) {
837
- const scopes = /* @__PURE__ */ new Map();
838
- function createScopes(jsonSchema, key = "root") {
839
- const sampleEmptyObject = buildSampleEmptyObject(schema);
840
- scopes.set(key, createValidationsScope(jsonSchema));
841
- Object.entries(jsonSchema?.properties ?? {}).filter(([, property]) => property.type === "object" || property.type === "array").forEach(([key2, property]) => {
842
- if (property.type === "array") {
843
- createScopes(property.items, `${key2}[]`);
844
- } else {
845
- createScopes(property, key2);
846
- }
847
- });
848
- validateInlineRules(jsonSchema, sampleEmptyObject);
849
- }
850
- createScopes(schema);
851
- return {
852
- scopes,
853
- getScope(name = "root") {
854
- return scopes.get(name);
855
- }
856
- };
857
- }
858
- function createValidationsScope(schema) {
859
- const validationMap = /* @__PURE__ */ new Map();
860
- const computedValuesMap = /* @__PURE__ */ new Map();
861
- const logic = schema?.["x-jsf-logic"] ?? {
862
- validations: {},
863
- computedValues: {}
864
- };
865
- const validations = Object.entries(logic.validations ?? {});
866
- const computedValues = Object.entries(logic.computedValues ?? {});
867
- const sampleEmptyObject = buildSampleEmptyObject(schema);
868
- validations.forEach(([id, validation]) => {
869
- if (!validation.rule) {
870
- throw Error(`[json-schema-form] json-logic error: Validation "${id}" has missing rule.`);
871
- }
872
- checkRuleIntegrity(validation.rule, id, sampleEmptyObject);
873
- validationMap.set(id, validation);
874
- });
875
- computedValues.forEach(([id, computedValue]) => {
876
- if (!computedValue.rule) {
877
- throw Error(`[json-schema-form] json-logic error: Computed value "${id}" has missing rule.`);
878
- }
879
- checkRuleIntegrity(computedValue.rule, id, sampleEmptyObject);
880
- computedValuesMap.set(id, computedValue);
881
- });
882
- function validate(rule, values) {
883
- return import_json_logic_js.default.apply(
884
- rule,
885
- replaceUndefinedValuesWithNulls({ ...sampleEmptyObject, ...values })
886
- );
887
- }
888
- return {
889
- validationMap,
890
- computedValuesMap,
891
- validate,
892
- applyValidationRuleInCondition(id, values) {
893
- const validation = validationMap.get(id);
894
- return validate(validation.rule, values);
895
- },
896
- applyComputedValueInField(id, values, fieldName) {
897
- const validation = computedValuesMap.get(id);
898
- if (validation === void 0) {
899
- throw Error(
900
- `[json-schema-form] json-logic error: Computed value "${id}" doesn't exist in field "${fieldName}".`
901
- );
902
- }
903
- return validate(validation.rule, values);
904
- },
905
- applyComputedValueRuleInCondition(id, values) {
906
- const validation = computedValuesMap.get(id);
907
- return validate(validation.rule, values);
908
- }
909
- };
910
- }
911
- function replaceUndefinedValuesWithNulls(values = {}) {
912
- return Object.entries(values).reduce((prev, [key, value]) => {
913
- return { ...prev, [key]: value === void 0 || value === null ? NaN : value };
914
- }, {});
915
- }
916
- function yupSchemaWithCustomJSONLogic({ field, logic, config, id }) {
917
- const { parentID = "root" } = config;
918
- const validation = logic.getScope(parentID).validationMap.get(id);
919
- if (validation === void 0) {
920
- throw Error(
921
- `[json-schema-form] json-logic error: "${field.name}" required validation "${id}" doesn't exist.`
922
- );
923
- }
924
- return (yupSchema) => yupSchema.test(
925
- `${field.name}-validation-${id}`,
926
- validation?.errorMessage ?? "This field is invalid.",
927
- (value, { parent }) => {
928
- if (value === void 0 && !field.required)
929
- return true;
930
- return import_json_logic_js.default.apply(validation.rule, parent);
931
- }
932
- );
933
- }
934
- var HANDLEBARS_REGEX = /\{\{([^{}]+)\}\}/g;
935
- function replaceHandlebarsTemplates({
936
- value: toReplace,
937
- logic,
938
- formValues,
939
- parentID,
940
- name: fieldName
941
- }) {
942
- if (typeof toReplace === "string") {
943
- return toReplace.replace(HANDLEBARS_REGEX, (match, key) => {
944
- return logic.getScope(parentID).applyComputedValueInField(key.trim(), formValues, fieldName);
945
- });
946
- } else if (typeof toReplace === "object") {
947
- const { value, ...rules } = toReplace;
948
- if (Object.keys(rules).length > 1 && !value) {
949
- throw Error("Cannot define multiple rules without a template string with key `value`.");
950
- }
951
- const computedTemplateValue = Object.entries(rules).reduce((prev, [key, rule]) => {
952
- const computedValue = logic.getScope(parentID).validate(rule, formValues);
953
- return prev.replaceAll(`{{${key}}}`, computedValue);
954
- }, value);
955
- return computedTemplateValue.replace(/\{\{([^{}]+)\}\}/g, (match, key) => {
956
- return logic.getScope(parentID).applyComputedValueInField(key.trim(), formValues, fieldName);
957
- });
958
- }
959
- return toReplace;
960
- }
961
- function calculateComputedAttributes(fieldParams, { parentID = "root" } = {}) {
962
- return ({ logic, isRequired, config, formValues }) => {
963
- const { name, computedAttributes } = fieldParams;
964
- const attributes = Object.fromEntries(
965
- Object.entries(computedAttributes).map(handleComputedAttribute(logic, formValues, parentID, name)).filter(([, value]) => value !== null)
966
- );
967
- return {
968
- ...attributes,
969
- schema: buildYupSchema(
970
- { ...fieldParams, ...attributes, required: isRequired },
971
- config,
972
- logic
973
- )
974
- };
975
- };
976
- }
977
- function handleComputedAttribute(logic, formValues, parentID, name) {
978
- return ([key, value]) => {
979
- switch (key) {
980
- case "description":
981
- return [key, replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
982
- case "title":
983
- return ["label", replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
984
- case "x-jsf-errorMessage":
985
- return [
986
- "errorMessage",
987
- handleNestedObjectForComputedValues(value, formValues, parentID, logic, name)
988
- ];
989
- case "x-jsf-presentation": {
990
- if (value.statement) {
991
- return [
992
- "statement",
993
- handleNestedObjectForComputedValues(value.statement, formValues, parentID, logic, name)
994
- ];
995
- }
996
- return [
997
- key,
998
- handleNestedObjectForComputedValues(value.statement, formValues, parentID, logic, name)
999
- ];
1000
- }
1001
- case "const":
1002
- default: {
1003
- if (typeof value === "object" && value.rule) {
1004
- return [key, logic.getScope(parentID).validate(value.rule, formValues)];
1005
- }
1006
- return [key, logic.getScope(parentID).applyComputedValueInField(value, formValues, name)];
1007
- }
1008
- }
1009
- };
1010
- }
1011
- function handleNestedObjectForComputedValues(values, formValues, parentID, logic, name) {
1012
- return Object.fromEntries(
1013
- Object.entries(values).map(([key, value]) => {
1014
- return [key, replaceHandlebarsTemplates({ value, logic, formValues, parentID, name })];
1015
- })
1016
- );
1017
- }
1018
- function buildSampleEmptyObject(schema = {}) {
1019
- const sample = {};
1020
- if (typeof schema !== "object" || !schema.properties) {
1021
- return schema;
1022
- }
1023
- for (const key in schema.properties) {
1024
- if (schema.properties[key].type === "object") {
1025
- sample[key] = buildSampleEmptyObject(schema.properties[key]);
1026
- } else if (schema.properties[key].type === "array") {
1027
- const itemSchema = schema.properties[key].items;
1028
- sample[key] = buildSampleEmptyObject(itemSchema);
1029
- } else {
1030
- sample[key] = true;
1031
- }
1032
- }
1033
- return sample;
1034
- }
1035
- function validateInlineRules(jsonSchema, sampleEmptyObject) {
1036
- const properties = (jsonSchema?.properties || jsonSchema?.items?.properties) ?? {};
1037
- Object.entries(properties).filter(([, property]) => property["x-jsf-logic-computedAttrs"] !== void 0).forEach(([fieldName, property]) => {
1038
- Object.entries(property["x-jsf-logic-computedAttrs"]).filter(([, value]) => typeof value === "object").forEach(([key, item]) => {
1039
- Object.values(item).forEach((rule) => {
1040
- checkRuleIntegrity(
1041
- rule,
1042
- fieldName,
1043
- sampleEmptyObject,
1044
- (item2) => `[json-schema-form] json-logic error: fieldName "${item2.var}" doesn't exist in field "${fieldName}.x-jsf-logic-computedAttrs.${key}".`
1045
- );
1046
- });
1047
- });
1048
- });
1049
- }
1050
- function checkRuleIntegrity(rule, id, data, errorMessage = (item) => `[json-schema-form] json-logic error: rule "${id}" has no variable "${item.var}".`) {
1051
- Object.entries(rule ?? {}).map(([operator, subRule]) => {
1052
- if (!Array.isArray(subRule) && subRule !== null && subRule !== void 0)
1053
- return;
1054
- throwIfUnknownOperator(operator, subRule, id);
1055
- subRule.map((item) => {
1056
- const isVar = item !== null && typeof item === "object" && Object.hasOwn(item, "var");
1057
- if (isVar) {
1058
- const exists = import_json_logic_js.default.apply({ var: removeIndicesFromPath(item.var) }, data);
1059
- if (exists === null) {
1060
- throw Error(errorMessage(item));
1061
- }
1062
- } else {
1063
- checkRuleIntegrity(item, id, data);
1064
- }
1065
- });
1066
- });
1067
- }
1068
- function throwIfUnknownOperator(operator, subRule, id) {
1069
- try {
1070
- import_json_logic_js.default.apply({ [operator]: subRule });
1071
- } catch (e) {
1072
- if (e.message === `Unrecognized operation ${operator}`) {
1073
- throw Error(
1074
- `[json-schema-form] json-logic error: in "${id}" rule there is an unknown operator "${operator}".`
1075
- );
1076
- }
1077
- }
1078
- }
1079
- var regexToGetIndices = /\.\d+\./g;
1080
- function removeIndicesFromPath(path) {
1081
- const intermediatePath = path.replace(regexToGetIndices, ".");
1082
- return intermediatePath.replace(/\.\d+$/, "");
1083
- }
1084
- function processJSONLogicNode({
1085
- node,
1086
- formFields,
1087
- formValues,
1088
- accRequired,
1089
- parentID,
1090
- logic
1091
- }) {
1092
- const requiredFields = new Set(accRequired);
1093
- if (node.allOf) {
1094
- node.allOf.map(
1095
- (allOfNode) => processJSONLogicNode({ node: allOfNode, formValues, formFields, logic, parentID })
1096
- ).forEach(({ required: allOfItemRequired }) => {
1097
- allOfItemRequired.forEach(requiredFields.add, requiredFields);
1098
- });
1099
- }
1100
- if (node.if) {
1101
- const matchesPropertyCondition = checkIfConditionMatchesProperties(
1102
- node,
1103
- formValues,
1104
- formFields,
1105
- logic
1106
- );
1107
- const matchesValidationsAndComputedValues = matchesPropertyCondition && checkIfMatchesValidationsAndComputedValues(node, formValues, logic, parentID);
1108
- const isConditionMatch = matchesPropertyCondition && matchesValidationsAndComputedValues;
1109
- let nextNode;
1110
- if (isConditionMatch && node.then) {
1111
- nextNode = node.then;
1112
- }
1113
- if (!isConditionMatch && node.else) {
1114
- nextNode = node.else;
1115
- }
1116
- if (nextNode) {
1117
- const { required: branchRequired } = processNode({
1118
- node: nextNode,
1119
- formValues,
1120
- formFields,
1121
- accRequired,
1122
- logic,
1123
- parentID
1124
- });
1125
- branchRequired.forEach((field) => requiredFields.add(field));
1126
- }
1127
- }
1128
- return { required: requiredFields };
1129
- }
1130
-
1131
- // src/helpers.js
1132
- var dynamicInternalJsfAttrs = [
1133
- "isVisible",
1134
- // Driven from conditionals state
1135
- "fields",
1136
- // driven from group-array
1137
- "getComputedAttributes",
1138
- // From json-logic
1139
- "computedAttributes",
1140
- // From json-logic
1141
- "calculateConditionalProperties",
1142
- // driven from conditionals
1143
- "calculateCustomValidationProperties",
1144
- // To be deprecated in favor of json-logic
1145
- "scopedJsonSchema",
1146
- // The respective JSON Schema
1147
- // HOTFIX/TODO Internal customizations, check test conditions.test.js for more info.
1148
- "Component",
1149
- "calculateDynamicProperties",
1150
- "visibilityCondition"
1151
- ];
1152
- var dynamicInternalJsfAttrsObj = Object.fromEntries(
1153
- dynamicInternalJsfAttrs.map((k) => [k, true])
1154
- );
1155
- function removeConditionalStaleAttributes(field, conditionalAttrs, rootAttrs) {
1156
- Object.keys(field).forEach((key) => {
1157
- if (conditionalAttrs[key] === void 0 && rootAttrs[key] === void 0 && // Don't remove attrs that were declared in the root field.
1158
- dynamicInternalJsfAttrsObj[key] === void 0) {
1159
- field[key] = void 0;
1160
- }
1161
- });
1162
- }
1163
- function hasType(type, typeName) {
1164
- return Array.isArray(type) ? type.includes(typeName) : type === typeName;
1165
- }
1166
- function getField(fieldName, fields) {
1167
- if (Array.isArray(fields)) {
1168
- return fields.find(({ name }) => name === fieldName);
1169
- } else {
1170
- return fields[fieldName];
1171
- }
1172
- }
1173
- function validateFieldSchema(field, value, logic) {
1174
- const validator = buildYupSchema(field, {}, logic);
1175
- return validator().isValidSync(value);
1176
- }
1177
- function compareFormValueWithSchemaValue(formValue, schemaValue) {
1178
- const currentPropertyValue = typeof schemaValue === "number" ? schemaValue : schemaValue || void 0;
1179
- return String(formValue) === String(currentPropertyValue);
1180
- }
1181
- function isFieldFilled(fieldValue) {
1182
- return Array.isArray(fieldValue) ? fieldValue.length > 0 : !!fieldValue;
1183
- }
1184
- function findFirstAnyOfMatch(nodes, formValues) {
1185
- return nodes.find(
1186
- ({ required }) => required?.some((fieldName) => isFieldFilled(formValues[fieldName]))
1187
- ) || nodes[0];
1188
- }
1189
- function getPrefillSubFieldValues(field, defaultValues, parentFieldKeyPath) {
1190
- let initialValue = defaultValues ?? {};
1191
- let fieldKeyPath = field.name;
1192
- if (parentFieldKeyPath) {
1193
- fieldKeyPath = fieldKeyPath ? `${parentFieldKeyPath}.${fieldKeyPath}` : parentFieldKeyPath;
1194
- }
1195
- const subFields = field.fields;
1196
- if (Array.isArray(subFields)) {
1197
- const subFieldValues = {};
1198
- subFields.forEach((subField) => {
1199
- Object.assign(
1200
- subFieldValues,
1201
- getPrefillSubFieldValues(subField, initialValue[field.name], fieldKeyPath)
1202
- );
1203
- });
1204
- if (field.inputType === supportedTypes.FIELDSET && field.valueGroupingDisabled) {
1205
- Object.assign(initialValue, subFieldValues);
1206
- } else {
1207
- initialValue[field.name] = subFieldValues;
1208
- }
1209
- } else {
1210
- if (typeof initialValue !== "object") {
1211
- console.warn(
1212
- `Field "${parentFieldKeyPath}"'s value is "${initialValue}", but should be type object.`
1213
- );
1214
- initialValue = getPrefillValues([field], {
1215
- // TODO nested fieldsets are not handled
1216
- });
1217
- } else {
1218
- initialValue = getPrefillValues([field], initialValue);
1219
- }
1220
- }
1221
- return initialValue;
1222
- }
1223
- function getPrefillValues(fields, initialValues = {}) {
1224
- fields.forEach((field) => {
1225
- const fieldName = field.name;
1226
- switch (field.type) {
1227
- case supportedTypes.GROUP_ARRAY: {
1228
- initialValues[fieldName] = initialValues[fieldName]?.map(
1229
- (subFieldValues) => getPrefillValues(field.fields(), subFieldValues)
1230
- );
1231
- break;
1232
- }
1233
- case supportedTypes.FIELDSET: {
1234
- const subFieldValues = getPrefillSubFieldValues(field, initialValues);
1235
- Object.assign(initialValues, subFieldValues);
1236
- break;
1237
- }
1238
- default: {
1239
- if (!initialValues[fieldName]) {
1240
- initialValues[fieldName] = field.default;
1241
- }
1242
- break;
1243
- }
1244
- }
1245
- });
1246
- return initialValues;
1247
- }
1248
- function updateField(field, requiredFields, node, formValues, logic, config) {
1249
- if (!field) {
1250
- return;
1251
- }
1252
- const fieldIsRequired = requiredFields.has(field.name);
1253
- if (node.properties && hasProperty(node.properties, field.name)) {
1254
- field.isVisible = !!node.properties[field.name];
1255
- }
1256
- if (fieldIsRequired) {
1257
- field.isVisible = true;
1258
- }
1259
- const updateAttributes = (fieldAttrs) => {
1260
- Object.entries(fieldAttrs).forEach(([key, value]) => {
1261
- field[key] = value;
1262
- if (key === "schema" && typeof value === "function") {
1263
- field[key] = value();
1264
- }
1265
- if (key === "value") {
1266
- const readOnlyPropertyWasUpdated = typeof fieldAttrs.readOnly !== "undefined";
1267
- const isReadonlyByDefault = field.readOnly;
1268
- const isReadonly = readOnlyPropertyWasUpdated ? fieldAttrs.readOnly : isReadonlyByDefault;
1269
- if (!isReadonly && (value === null || field.inputType === "checkbox")) {
1270
- field.value = void 0;
1271
- }
1272
- }
1273
- });
1274
- };
1275
- if (field.getComputedAttributes) {
1276
- const newAttributes = field.getComputedAttributes({
1277
- field,
1278
- isRequired: fieldIsRequired,
1279
- node,
1280
- formValues,
1281
- config,
1282
- logic
1283
- });
1284
- updateAttributes(newAttributes);
1285
- }
1286
- if (field.calculateConditionalProperties) {
1287
- const { rootFieldAttrs, newAttributes } = field.calculateConditionalProperties({
1288
- isRequired: fieldIsRequired,
1289
- conditionBranch: node,
1290
- formValues
1291
- });
1292
- updateAttributes(newAttributes);
1293
- removeConditionalStaleAttributes(field, newAttributes, rootFieldAttrs);
1294
- }
1295
- if (field.calculateCustomValidationProperties) {
1296
- const newAttributes = field.calculateCustomValidationProperties(
1297
- fieldIsRequired,
1298
- node,
1299
- formValues
1300
- );
1301
- updateAttributes(newAttributes);
1302
- }
1303
- }
1304
- function processNode({
1305
- node,
1306
- formValues,
1307
- formFields,
1308
- accRequired = /* @__PURE__ */ new Set(),
1309
- parentID = "root",
1310
- logic
1311
- }) {
1312
- const requiredFields = new Set(accRequired);
1313
- Object.keys(node.properties ?? []).forEach((fieldName) => {
1314
- const field = getField(fieldName, formFields);
1315
- updateField(field, requiredFields, node, formValues, logic, { parentID });
1316
- });
1317
- node.required?.forEach((fieldName) => {
1318
- requiredFields.add(fieldName);
1319
- updateField(getField(fieldName, formFields), requiredFields, node, formValues, logic, {
1320
- parentID
1321
- });
1322
- });
1323
- if (node.if !== void 0) {
1324
- const matchesCondition = checkIfConditionMatchesProperties(node, formValues, formFields, logic);
1325
- if (matchesCondition && node.then) {
1326
- const { required: branchRequired } = processNode({
1327
- node: node.then,
1328
- formValues,
1329
- formFields,
1330
- accRequired: requiredFields,
1331
- parentID,
1332
- logic
1333
- });
1334
- branchRequired.forEach((field) => requiredFields.add(field));
1335
- } else if (node.else) {
1336
- const { required: branchRequired } = processNode({
1337
- node: node.else,
1338
- formValues,
1339
- formFields,
1340
- accRequired: requiredFields,
1341
- parentID,
1342
- logic
1343
- });
1344
- branchRequired.forEach((field) => requiredFields.add(field));
1345
- }
1346
- }
1347
- if (node.anyOf) {
1348
- const firstMatchOfAnyOf = findFirstAnyOfMatch(node.anyOf, formValues);
1349
- firstMatchOfAnyOf.required?.forEach((fieldName) => {
1350
- requiredFields.add(fieldName);
1351
- });
1352
- node.anyOf.forEach(({ required = [] }) => {
1353
- required.forEach((fieldName) => {
1354
- const field = getField(fieldName, formFields);
1355
- updateField(field, requiredFields, node, formValues, logic, { parentID });
1356
- });
1357
- });
1358
- }
1359
- if (node.allOf) {
1360
- node.allOf.map(
1361
- (allOfNode) => processNode({
1362
- node: allOfNode,
1363
- formValues,
1364
- formFields,
1365
- accRequired: requiredFields,
1366
- parentID,
1367
- logic
1368
- })
1369
- ).forEach(({ required: allOfItemRequired }) => {
1370
- allOfItemRequired.forEach(requiredFields.add, requiredFields);
1371
- });
1372
- }
1373
- if (node.properties) {
1374
- Object.entries(node.properties).forEach(([name, nestedNode]) => {
1375
- const inputType = getInputType(nestedNode);
1376
- if (inputType === supportedTypes.FIELDSET) {
1377
- processNode({
1378
- node: nestedNode,
1379
- formValues: formValues[name] || {},
1380
- formFields: getField(name, formFields).fields,
1381
- parentID: name,
1382
- logic
1383
- });
1384
- }
1385
- if (inputType === supportedTypes.GROUP_ARRAY) {
1386
- const values = formValues[name];
1387
- if (Array.isArray(values)) {
1388
- const newFields = [];
1389
- const field = getField(name, formFields);
1390
- values.forEach((value) => {
1391
- const fields = field.fields();
1392
- processNode({
1393
- node: nestedNode.items,
1394
- formValues: value,
1395
- formFields: fields,
1396
- parentID: name,
1397
- logic
1398
- });
1399
- newFields.push(fields);
1400
- });
1401
- field.dynamicFields = newFields;
1402
- }
1403
- }
1404
- });
1405
- }
1406
- if (node["x-jsf-logic"]) {
1407
- const { required: requiredFromLogic } = processJSONLogicNode({
1408
- node: node["x-jsf-logic"],
1409
- formValues,
1410
- formFields,
1411
- accRequired: requiredFields,
1412
- parentID,
1413
- logic
1414
- });
1415
- requiredFromLogic.forEach((field) => requiredFields.add(field));
1416
- }
1417
- return {
1418
- required: requiredFields
1419
- };
1420
- }
1421
- function clearValuesIfNotVisible(fields, formValues) {
1422
- fields.forEach(({ isVisible = true, name, inputType, fields: nestedFields }) => {
1423
- if (!isVisible) {
1424
- formValues[name] = null;
1425
- }
1426
- if (inputType === supportedTypes.FIELDSET && nestedFields && formValues[name]) {
1427
- clearValuesIfNotVisible(nestedFields, formValues[name]);
1428
- }
1429
- });
1430
- }
1431
- function updateFieldsProperties(fields, formValues, jsonSchema, logic) {
1432
- if (!jsonSchema?.properties) {
1433
- return;
1434
- }
1435
- processNode({ node: jsonSchema, formValues, formFields: fields, logic });
1436
- clearValuesIfNotVisible(fields, formValues);
1437
- }
1438
- var notNullOption = (opt) => opt.const !== null;
1439
- function flatPresentation(item) {
1440
- return Object.entries(item).reduce((newItem, [key, value]) => {
1441
- if (key === "x-jsf-presentation") {
1442
- return {
1443
- ...newItem,
1444
- ...value
1445
- };
1446
- }
1447
- return {
1448
- ...newItem,
1449
- [key]: value
1450
- };
1451
- }, {});
1452
- }
1453
- function getFieldOptions(node, presentation) {
1454
- function convertToOptions(nodeOptions) {
1455
- return nodeOptions.filter(notNullOption).map(({ title, const: cons, ...item }) => ({
1456
- label: title,
1457
- value: cons,
1458
- ...flatPresentation(item)
1459
- }));
1460
- }
1461
- if (presentation.options) {
1462
- return presentation.options;
1463
- }
1464
- if (node.oneOf || presentation.inputType === "radio") {
1465
- return convertToOptions(node.oneOf || []);
1466
- }
1467
- if (node.items?.anyOf) {
1468
- return convertToOptions(node.items.anyOf);
1469
- }
1470
- return null;
1471
- }
1472
- function extractParametersFromNode(schemaNode) {
1473
- if (!schemaNode) {
1474
- return {};
1475
- }
1476
- const presentation = pickXKey(schemaNode, "presentation") ?? {};
1477
- const errorMessage = pickXKey(schemaNode, "errorMessage") ?? {};
1478
- const jsonLogicValidations = schemaNode["x-jsf-logic-validations"];
1479
- const computedAttributes = schemaNode["x-jsf-logic-computedAttrs"];
1480
- const decoratedComputedAttributes = getDecoratedComputedAttributes(computedAttributes);
1481
- const node = (0, import_omit.default)(schemaNode, ["x-jsf-presentation", "presentation"]);
1482
- const description = presentation?.description || node.description;
1483
- const statementDescription = presentation.statement?.description;
1484
- const value = typeof node.const !== "undefined" && typeof node.default !== "undefined" && node.const === node.default ? { forcedValue: node.const } : {};
1485
- return (0, import_omitBy.default)(
1486
- {
1487
- const: node.const,
1488
- ...value,
1489
- label: node.title,
1490
- readOnly: node.readOnly,
1491
- ...node.deprecated && {
1492
- deprecated: {
1493
- description: presentation.deprecated?.description
1494
- // @TODO/@IDEA These might be useful down the road :thinking:
1495
- // version: presentation.deprecated.version, // e.g. "1.1"
1496
- // replacement: presentation.deprecated.replacement, // e.g. ['contract_duration_type']
1497
- }
1498
- },
1499
- pattern: node.pattern,
1500
- options: getFieldOptions(node, presentation),
1501
- items: node.items,
1502
- maxLength: node.maxLength,
1503
- minLength: node.minLength,
1504
- minimum: node.minimum,
1505
- maximum: node.maximum,
1506
- maxFileSize: node.maxFileSize,
1507
- // @deprecated in favor of presentation.maxFileSize
1508
- default: node.default,
1509
- format: node.format,
1510
- // Checkboxes conditions
1511
- // — For checkboxes that only accept one value (string)
1512
- ...presentation?.inputType === "checkbox" && { checkboxValue: node.const },
1513
- // - For checkboxes with boolean value
1514
- ...presentation?.inputType === "checkbox" && node.type === "boolean" && {
1515
- // true is what describes this checkbox as a boolean, regardless if its required or not
1516
- checkboxValue: true
1517
- },
1518
- ...hasType(node.type, "array") && {
1519
- multiple: true
1520
- },
1521
- // Handle [name].presentation
1522
- ...presentation,
1523
- jsonLogicValidations,
1524
- computedAttributes: decoratedComputedAttributes,
1525
- description,
1526
- extra: presentation.extra,
1527
- statement: presentation.statement && {
1528
- ...presentation.statement,
1529
- description: statementDescription
1530
- },
1531
- // Support scoped conditions (fieldsets)
1532
- if: node.if,
1533
- then: node.then,
1534
- else: node.else,
1535
- anyOf: node.anyOf,
1536
- allOf: node.allOf,
1537
- errorMessage
1538
- },
1539
- import_isNil.default
1540
- );
1541
- }
1542
- function yupToFormErrors(yupError) {
1543
- if (!yupError) {
1544
- return yupError;
1545
- }
1546
- const errors = {};
1547
- if (yupError.inner) {
1548
- if (yupError.inner.length === 0) {
1549
- return (0, import_set.default)(errors, yupError.path, yupError.message);
1550
- }
1551
- yupError.inner.forEach((err) => {
1552
- if (!(0, import_get2.default)(errors, err.path)) {
1553
- (0, import_set.default)(errors, err.path, err.message);
1554
- }
1555
- });
1556
- }
1557
- return errors;
1558
- }
1559
- var handleValuesChange = (fields, jsonSchema, config, logic) => (values) => {
1560
- updateFieldsProperties(fields, values, jsonSchema, logic);
1561
- const lazySchema = (0, import_yup2.lazy)(() => buildCompleteYupSchema(fields, config));
1562
- let errors;
1563
- try {
1564
- lazySchema.validateSync(values, {
1565
- abortEarly: false
1566
- });
1567
- } catch (err) {
1568
- if (err.name === "ValidationError") {
1569
- errors = err;
1570
- } else {
1571
- console.warn(`Warning: An unhandled error was caught during validationSchema`, err);
1572
- }
1573
- }
1574
- return {
1575
- yupError: errors,
1576
- formErrors: yupToFormErrors(errors)
1577
- };
1578
- };
1579
- function getDecoratedComputedAttributes(computedAttributes) {
1580
- const isEqualConstAndDefault = computedAttributes?.const === computedAttributes?.default;
1581
- return {
1582
- ...computedAttributes ?? {},
1583
- ...computedAttributes?.const && computedAttributes?.default && isEqualConstAndDefault ? { forcedValue: computedAttributes.const } : {}
1584
- };
1585
- }
1586
-
1587
- // src/calculateConditionalProperties.js
1588
- function isFieldRequired(node, field) {
1589
- return (
1590
- // Check base root required
1591
- field.scopedJsonSchema?.required?.includes(field.name) || // Check conditional required
1592
- node?.required?.includes(field.name)
1593
- );
1594
- }
1595
- function rebuildFieldset(fields, property) {
1596
- if (property?.properties) {
1597
- return fields.map((field) => {
1598
- const propertyConditionals = property.properties[field.name];
1599
- if (!propertyConditionals) {
1600
- return field;
1601
- }
1602
- const newFieldParams = extractParametersFromNode(propertyConditionals);
1603
- if (field.fields) {
1604
- return {
1605
- ...field,
1606
- ...newFieldParams,
1607
- fields: rebuildFieldset(field.fields, propertyConditionals)
1608
- };
1609
- }
1610
- return {
1611
- ...field,
1612
- ...newFieldParams,
1613
- required: isFieldRequired(property, field)
1614
- };
1615
- });
1616
- }
1617
- return fields.map((field) => ({
1618
- ...field,
1619
- required: isFieldRequired(property, field)
1620
- }));
1621
- }
1622
- function calculateConditionalProperties({ fieldParams, customProperties, logic, config }) {
1623
- return ({ isRequired, conditionBranch, formValues }) => {
1624
- const conditionalProperty = conditionBranch?.properties?.[fieldParams.name];
1625
- if (conditionalProperty) {
1626
- const presentation = pickXKey(conditionalProperty, "presentation") ?? {};
1627
- const fieldDescription = getFieldDescription(conditionalProperty, customProperties);
1628
- const newFieldParams = extractParametersFromNode({
1629
- ...conditionalProperty,
1630
- ...fieldDescription
1631
- });
1632
- let fieldSetFields;
1633
- if (fieldParams.inputType === supportedTypes.FIELDSET) {
1634
- fieldSetFields = rebuildFieldset(fieldParams.fields, conditionalProperty);
1635
- newFieldParams.fields = fieldSetFields;
1636
- }
1637
- const { computedAttributes, ...restNewFieldParams } = newFieldParams;
1638
- const calculatedComputedAttributes = computedAttributes ? calculateComputedAttributes(newFieldParams, config)({ logic, formValues }) : {};
1639
- const jsonLogicValidations = [
1640
- ...fieldParams.jsonLogicValidations ?? [],
1641
- ...restNewFieldParams.jsonLogicValidations ?? []
1642
- ];
1643
- const base = {
1644
- isVisible: true,
1645
- required: isRequired,
1646
- ...presentation?.inputType && { type: presentation.inputType },
1647
- ...calculatedComputedAttributes,
1648
- ...calculatedComputedAttributes.value ? { value: calculatedComputedAttributes.value } : { value: void 0 },
1649
- schema: buildYupSchema(
1650
- {
1651
- ...fieldParams,
1652
- ...restNewFieldParams,
1653
- ...calculatedComputedAttributes,
1654
- jsonLogicValidations,
1655
- // If there are inner fields (case of fieldset) they need to be updated based on the condition
1656
- fields: fieldSetFields,
1657
- required: isRequired
1658
- },
1659
- config,
1660
- logic
1661
- )
1662
- };
1663
- return {
1664
- rootFieldAttrs: fieldParams,
1665
- newAttributes: (0, import_omit2.default)((0, import_merge2.default)(base, presentation, newFieldParams), ["inputType"])
1666
- };
1667
- }
1668
- const isVisible = isRequired;
1669
- return {
1670
- rootFieldAttrs: fieldParams,
1671
- newAttributes: {
1672
- isVisible,
1673
- required: isRequired,
1674
- schema: buildYupSchema({
1675
- ...fieldParams,
1676
- ...extractParametersFromNode(conditionBranch),
1677
- required: isRequired
1678
- })
1679
- }
1680
- };
1681
- };
1682
- }
1683
-
1684
- // src/calculateCustomValidationProperties.js
1685
- var import_inRange = __toESM(require("lodash/inRange"));
1686
- var import_isFunction2 = __toESM(require("lodash/isFunction"));
1687
- var import_isNil2 = __toESM(require("lodash/isNil"));
1688
- var import_isObject = __toESM(require("lodash/isObject"));
1689
- var import_mapValues = __toESM(require("lodash/mapValues"));
1690
- var import_pick = __toESM(require("lodash/pick"));
1691
- var SUPPORTED_CUSTOM_VALIDATION_FIELD_PARAMS = ["minimum", "maximum"];
1692
- var isCustomValidationAllowed = (fieldParams) => (customValidation, customValidationKey) => {
1693
- if ((0, import_isNil2.default)(customValidation)) {
1694
- return false;
1695
- }
1696
- const { minimum, maximum } = fieldParams;
1697
- const isAllowed = (0, import_inRange.default)(
1698
- customValidation,
1699
- minimum ?? -Infinity,
1700
- maximum ? maximum + 1 : Infinity
1701
- );
1702
- if (!isAllowed) {
1703
- const errorMessage = `Custom validation for ${fieldParams.name} is not allowed because ${customValidationKey}:${customValidation} is less strict than the original range: ${minimum} to ${maximum}`;
1704
- if (true) {
1705
- throw new Error(errorMessage);
1706
- } else {
1707
- console.warn(errorMessage);
1708
- }
1709
- }
1710
- return isAllowed;
1711
- };
1712
- function calculateCustomValidationProperties(fieldParams, customProperties) {
1713
- return (isRequired, conditionBranch, formValues) => {
1714
- const params = { ...fieldParams, ...conditionBranch?.properties?.[fieldParams.name] };
1715
- const presentation = pickXKey(params, "presentation") ?? {};
1716
- const supportedParams = (0, import_pick.default)(customProperties, SUPPORTED_CUSTOM_VALIDATION_FIELD_PARAMS);
1717
- const checkIfAllowed = isCustomValidationAllowed(params);
1718
- const customErrorMessages = [];
1719
- const fieldParamsWithNewValidation = (0, import_mapValues.default)(
1720
- supportedParams,
1721
- (customValidationValue, customValidationKey) => {
1722
- const originalValidation = params[customValidationKey];
1723
- const customValidation = (0, import_isFunction2.default)(customValidationValue) ? customValidationValue(formValues, params) : customValidationValue;
1724
- if ((0, import_isObject.default)(customValidation)) {
1725
- if (checkIfAllowed(customValidation[customValidationKey], customValidationKey)) {
1726
- customErrorMessages.push(pickXKey(customValidation, "errorMessage"));
1727
- return customValidation[customValidationKey];
1728
- }
1729
- return originalValidation;
1730
- }
1731
- return checkIfAllowed(customValidation, customValidationKey) ? customValidation : originalValidation;
1732
- }
1733
- );
1734
- const errorMessage = Object.assign({ ...params.errorMessage }, ...customErrorMessages);
1735
- return {
1736
- ...params,
1737
- ...fieldParamsWithNewValidation,
1738
- type: presentation?.inputType || params.inputType,
1739
- errorMessage,
1740
- required: isRequired,
1741
- schema: buildYupSchema({
1742
- ...params,
1743
- ...fieldParamsWithNewValidation,
1744
- errorMessage,
1745
- required: isRequired
1746
- })
1747
- };
1748
- };
1749
- }
1750
-
1751
- // src/createHeadlessForm.js
1752
- function sortByOrderOrPosition(a, b, order) {
1753
- if (order) {
1754
- return order.indexOf(a.name) - order.indexOf(b.name);
1755
- }
1756
- return a.position - b.position;
1757
- }
1758
- function removeInvalidAttributes(fields) {
1759
- return (0, import_omit3.default)(fields, ["items", "maxFileSize", "isDynamic"]);
1760
- }
1761
- function buildFieldParameters(name, fieldProperties, required = [], config = {}, logic) {
1762
- const { position } = pickXKey(fieldProperties, "presentation") ?? {};
1763
- let fields;
1764
- const inputType = getInputType(fieldProperties, config.strictInputType, name);
1765
- if (inputType === supportedTypes.FIELDSET) {
1766
- fields = getFieldsFromJSONSchema(
1767
- fieldProperties,
1768
- {
1769
- customProperties: (0, import_get3.default)(config, `customProperties.${name}.customProperties`, {}),
1770
- parentID: name
1771
- },
1772
- logic
1773
- );
1774
- }
1775
- if (inputType === supportedTypes.GROUP_ARRAY) {
1776
- fields = () => getFieldsFromJSONSchema(
1777
- fieldProperties.items,
1778
- {
1779
- customProperties: (0, import_get3.default)(config, `customProperties.${name}.customProperties`, {}),
1780
- parentID: name
1781
- },
1782
- logic
1783
- );
1784
- }
1785
- const result = {
1786
- name,
1787
- inputType,
1788
- jsonType: fieldProperties.type,
1789
- type: inputType,
1790
- // @deprecated in favor of inputType,
1791
- required: required?.includes(name) ?? false,
1792
- fields,
1793
- position,
1794
- ...extractParametersFromNode(fieldProperties)
1795
- };
1796
- return (0, import_omitBy2.default)(result, import_isNil3.default);
1797
- }
1798
- function convertJSONSchemaPropertiesToFieldParameters({ properties, required, "x-jsf-order": order }, config = {}) {
1799
- const sortFields = (a, b) => sortByOrderOrPosition(a, b, order);
1800
- return Object.entries(properties).filter(([, value]) => typeof value === "object").map(([key, value]) => buildFieldParameters(key, value, required, config)).sort(sortFields).map(({ position, ...fieldParams }) => fieldParams);
1801
- }
1802
- function applyFieldsDependencies(fieldsParameters, node) {
1803
- if (node?.then) {
1804
- fieldsParameters.filter(
1805
- ({ name }) => node.then?.properties?.[name] || node.then?.required?.includes(name) || node.else?.properties?.[name] || node.else?.required?.includes(name)
1806
- ).forEach((property) => {
1807
- property.isDynamic = true;
1808
- });
1809
- applyFieldsDependencies(fieldsParameters, node.then);
1810
- }
1811
- if (node?.anyOf) {
1812
- fieldsParameters.filter(({ name }) => node.anyOf.some(({ required }) => required?.includes(name))).forEach((property) => {
1813
- property.isDynamic = true;
1814
- });
1815
- applyFieldsDependencies(fieldsParameters, node.then);
1816
- }
1817
- if (node?.allOf) {
1818
- node.allOf.forEach((condition) => {
1819
- applyFieldsDependencies(fieldsParameters, condition);
1820
- });
1821
- }
1822
- if (node?.["x-jsf-logic"]) {
1823
- applyFieldsDependencies(fieldsParameters, node["x-jsf-logic"]);
1824
- }
1825
- }
1826
- function getCustomPropertiesForField(fieldParams, config) {
1827
- return config?.customProperties?.[fieldParams.name];
1828
- }
1829
- function getComposeFunctionForField(fieldParams, hasCustomizations) {
1830
- const composeFn = inputTypeMap[fieldParams.inputType] || _composeFieldArbitraryClosure(fieldParams.inputType);
1831
- if (hasCustomizations) {
1832
- return _composeFieldCustomClosure(composeFn);
1833
- }
1834
- return composeFn;
1835
- }
1836
- function buildField(fieldParams, config, scopedJsonSchema, logic) {
1837
- const customProperties = getCustomPropertiesForField(fieldParams, config);
1838
- const composeFn = getComposeFunctionForField(fieldParams, !!customProperties);
1839
- const yupSchema = buildYupSchema(fieldParams, config, logic);
1840
- const calculateConditionalFieldsClosure = fieldParams.isDynamic && calculateConditionalProperties({ fieldParams, customProperties, logic, config });
1841
- const calculateCustomValidationPropertiesClosure = calculateCustomValidationProperties(
1842
- fieldParams,
1843
- customProperties
1844
- );
1845
- const getComputedAttributes = Object.keys(fieldParams.computedAttributes).length > 0 && calculateComputedAttributes(fieldParams, config);
1846
- const hasCustomValidations = !!customProperties && (0, import_size.default)((0, import_pick2.default)(customProperties, SUPPORTED_CUSTOM_VALIDATION_FIELD_PARAMS)) > 0;
1847
- const finalFieldParams = {
1848
- // invalid attribute cleanup
1849
- ...removeInvalidAttributes(fieldParams),
1850
- // calculateConditionalProperties function if needed
1851
- ...!!calculateConditionalFieldsClosure && {
1852
- calculateConditionalProperties: calculateConditionalFieldsClosure
1853
- },
1854
- // calculateCustomValidationProperties function if needed
1855
- ...hasCustomValidations && {
1856
- calculateCustomValidationProperties: calculateCustomValidationPropertiesClosure
1857
- },
1858
- ...getComputedAttributes && { getComputedAttributes },
1859
- // field customization properties
1860
- ...customProperties && { fieldCustomization: customProperties },
1861
- // base schema
1862
- schema: yupSchema(),
1863
- scopedJsonSchema
1864
- };
1865
- return composeFn(finalFieldParams);
1866
- }
1867
- function getFieldsFromJSONSchema(scopedJsonSchema, config, logic) {
1868
- if (!scopedJsonSchema) {
1869
- return [];
1870
- }
1871
- const fieldParamsList = convertJSONSchemaPropertiesToFieldParameters(scopedJsonSchema, config);
1872
- applyFieldsDependencies(fieldParamsList, scopedJsonSchema);
1873
- const fields = [];
1874
- fieldParamsList.forEach((fieldParams) => {
1875
- if (fieldParams.inputType === "group-array") {
1876
- const groupArrayItems = convertJSONSchemaPropertiesToFieldParameters(fieldParams.items);
1877
- const groupArrayFields = groupArrayItems.map((groupArrayItem) => {
1878
- const customProperties = null;
1879
- const composeFn = getComposeFunctionForField(groupArrayItem, !!customProperties);
1880
- return composeFn(groupArrayItem);
1881
- });
1882
- fieldParams.nthFieldGroup = {
1883
- name: fieldParams.name,
1884
- label: fieldParams.label,
1885
- description: fieldParams.description,
1886
- fields: () => groupArrayFields,
1887
- addFieldText: fieldParams.addFieldText
1888
- };
1889
- buildField(fieldParams, config, scopedJsonSchema, logic).forEach((groupField) => {
1890
- fields.push(groupField);
1891
- });
1892
- } else {
1893
- fields.push(buildField(fieldParams, config, scopedJsonSchema, logic));
1894
- }
1895
- });
1896
- return fields;
1897
- }
1898
- function createHeadlessForm(jsonSchema, customConfig = {}) {
1899
- const config = {
1900
- strictInputType: true,
1901
- ...customConfig
1902
- };
1903
- try {
1904
- const logic = createValidationChecker(jsonSchema);
1905
- const fields = getFieldsFromJSONSchema(jsonSchema, config, logic);
1906
- const handleValidation = handleValuesChange(fields, jsonSchema, config, logic);
1907
- updateFieldsProperties(
1908
- fields,
1909
- getPrefillValues(fields, config.initialValues),
1910
- jsonSchema,
1911
- logic
1912
- );
1913
- return {
1914
- fields,
1915
- handleValidation,
1916
- isError: false
1917
- };
1918
- } catch (error) {
1919
- console.error("JSON Schema invalid!", error);
1920
- return {
1921
- fields: [],
1922
- isError: true,
1923
- error
1924
- };
1925
- }
1926
- }
1927
-
1928
- // src/modify.js
1929
- var import_difference = __toESM(require("lodash/difference"));
1930
- var import_get4 = __toESM(require("lodash/get"));
1931
- var import_intersection = __toESM(require("lodash/intersection"));
1932
- var import_merge3 = __toESM(require("lodash/merge"));
1933
- var import_mergeWith = __toESM(require("lodash/mergeWith"));
1934
- var import_set2 = __toESM(require("lodash/set"));
1935
- var WARNING_TYPES = {
1936
- FIELD_TO_CHANGE_NOT_FOUND: "FIELD_TO_CHANGE_NOT_FOUND",
1937
- ORDER_MISSING_FIELDS: "ORDER_MISSING_FIELDS",
1938
- FIELD_TO_CREATE_EXISTS: "FIELD_TO_CREATE_EXISTS",
1939
- PICK_MISSED_FIELD: "PICK_MISSED_FIELD"
1940
- };
1941
- function shortToFullPath(path) {
1942
- return path.replace(".", ".properties.");
1943
- }
1944
- function mergeReplaceArray(_, newVal) {
1945
- return Array.isArray(newVal) ? newVal : void 0;
1946
- }
1947
- function standardizeAttrs(attrs) {
1948
- const { errorMessage, presentation, properties, ...rest } = attrs;
1949
- return {
1950
- ...rest,
1951
- ...presentation ? { "x-jsf-presentation": presentation } : {},
1952
- ...errorMessage ? { "x-jsf-errorMessage": errorMessage } : {}
1953
- };
1954
- }
1955
- function isConditionalReferencingAnyPickedField(condition, fieldsToPick) {
1956
- const { if: ifCondition, then: thenCondition, else: elseCondition } = condition;
1957
- const inIf = (0, import_intersection.default)(ifCondition.required, fieldsToPick);
1958
- if (inIf.length > 0) {
1959
- return true;
1960
- }
1961
- const inThen = (0, import_intersection.default)(thenCondition.required, fieldsToPick) || (0, import_intersection.default)(Object.keys(thenCondition.properties), fieldsToPick);
1962
- if (inThen.length > 0) {
1963
- return true;
1964
- }
1965
- const inElse = (0, import_intersection.default)(elseCondition.required, fieldsToPick) || (0, import_intersection.default)(Object.keys(elseCondition.properties), fieldsToPick);
1966
- if (inElse.length > 0) {
1967
- return true;
1968
- }
1969
- return false;
1970
- }
1971
- function rewriteFields(schema, fieldsConfig) {
1972
- if (!fieldsConfig)
1973
- return { warnings: null };
1974
- const warnings = [];
1975
- const fieldsToModify = Object.entries(fieldsConfig);
1976
- fieldsToModify.forEach(([shortPath, mutation]) => {
1977
- const fieldPath = shortToFullPath(shortPath);
1978
- if (!(0, import_get4.default)(schema.properties, fieldPath)) {
1979
- warnings.push({
1980
- type: WARNING_TYPES.FIELD_TO_CHANGE_NOT_FOUND,
1981
- message: `Changing field "${shortPath}" was ignored because it does not exist.`
1982
- });
1983
- return;
1984
- }
1985
- const fieldAttrs = (0, import_get4.default)(schema.properties, fieldPath);
1986
- const fieldChanges = typeof mutation === "function" ? mutation(fieldAttrs) : mutation;
1987
- (0, import_mergeWith.default)(
1988
- (0, import_get4.default)(schema.properties, fieldPath),
1989
- {
1990
- ...fieldAttrs,
1991
- ...standardizeAttrs(fieldChanges)
1992
- },
1993
- mergeReplaceArray
1994
- );
1995
- if (fieldChanges.properties) {
1996
- const result = rewriteFields((0, import_get4.default)(schema.properties, fieldPath), fieldChanges.properties);
1997
- warnings.push(result.warnings);
1998
- }
1999
- });
2000
- return { warnings: warnings.flat() };
2001
- }
2002
- function rewriteAllFields(schema, configCallback, context) {
2003
- if (!configCallback)
2004
- return null;
2005
- const parentName = context?.parent;
2006
- Object.entries(schema.properties).forEach(([fieldName, fieldAttrs]) => {
2007
- const fullName = parentName ? `${parentName}.${fieldName}` : fieldName;
2008
- (0, import_mergeWith.default)(
2009
- (0, import_get4.default)(schema.properties, fieldName),
2010
- {
2011
- ...fieldAttrs,
2012
- ...standardizeAttrs(configCallback(fullName, fieldAttrs))
2013
- },
2014
- mergeReplaceArray
2015
- );
2016
- if (fieldAttrs.properties) {
2017
- rewriteAllFields(fieldAttrs, configCallback, {
2018
- parent: fieldName
2019
- });
2020
- }
2021
- });
2022
- }
2023
- function reorderFields(schema, configOrder) {
2024
- if (!configOrder)
2025
- return { warnings: null };
2026
- const warnings = [];
2027
- const originalOrder = schema["x-jsf-order"] || [];
2028
- const orderConfig = typeof configOrder === "function" ? configOrder(originalOrder) : configOrder;
2029
- const remaining = (0, import_difference.default)(originalOrder, orderConfig);
2030
- if (remaining.length > 0) {
2031
- warnings.push({
2032
- type: WARNING_TYPES.ORDER_MISSING_FIELDS,
2033
- message: `Some fields got forgotten in the new order. They were automatically appended: ${remaining.join(
2034
- ", "
2035
- )}`
2036
- });
2037
- }
2038
- schema["x-jsf-order"] = [...orderConfig, ...remaining];
2039
- return { warnings };
2040
- }
2041
- function createFields(schema, fieldsConfig) {
2042
- if (!fieldsConfig)
2043
- return { warnings: null };
2044
- const warnings = [];
2045
- const fieldsToCreate = Object.entries(fieldsConfig);
2046
- fieldsToCreate.forEach(([shortPath, fieldAttrs]) => {
2047
- const fieldPath = shortToFullPath(shortPath);
2048
- if (fieldAttrs.properties) {
2049
- const result = createFields((0, import_get4.default)(schema.properties, fieldPath), fieldAttrs.properties);
2050
- warnings.push(result.warnings);
2051
- }
2052
- const fieldInSchema = (0, import_get4.default)(schema.properties, fieldPath);
2053
- if (fieldInSchema) {
2054
- warnings.push({
2055
- type: WARNING_TYPES.FIELD_TO_CREATE_EXISTS,
2056
- message: `Creating field "${shortPath}" was ignored because it already exists.`
2057
- });
2058
- return;
2059
- }
2060
- const fieldInObjectPath = (0, import_set2.default)({}, fieldPath, standardizeAttrs(fieldAttrs));
2061
- (0, import_merge3.default)(schema.properties, fieldInObjectPath);
2062
- });
2063
- return { warnings: warnings.flat() };
2064
- }
2065
- function pickFields(originalSchema, fieldsToPick) {
2066
- if (!fieldsToPick) {
2067
- return { schema: originalSchema, warnings: null };
2068
- }
2069
- const newSchema = {
2070
- properties: {}
2071
- };
2072
- Object.entries(originalSchema).forEach(([attrKey, attrValue]) => {
2073
- switch (attrKey) {
2074
- case "properties":
2075
- fieldsToPick.forEach((fieldPath) => {
2076
- (0, import_set2.default)(newSchema.properties, fieldPath, attrValue[fieldPath]);
2077
- });
2078
- break;
2079
- case "x-jsf-order":
2080
- case "required":
2081
- newSchema[attrKey] = attrValue.filter((fieldName) => fieldsToPick.includes(fieldName));
2082
- break;
2083
- case "allOf": {
2084
- const newAllOf = originalSchema.allOf.filter(
2085
- (condition) => isConditionalReferencingAnyPickedField(condition, fieldsToPick)
2086
- );
2087
- newSchema[attrKey] = newAllOf;
2088
- break;
2089
- }
2090
- default:
2091
- newSchema[attrKey] = attrValue;
2092
- }
2093
- });
2094
- let missingFields = {};
2095
- newSchema.allOf?.forEach((condition) => {
2096
- const { if: ifCondition, then: thenCondition, else: elseCondition } = condition;
2097
- const index = originalSchema.allOf.indexOf(condition);
2098
- missingFields = {
2099
- ...missingFields,
2100
- ...findMissingFields(ifCondition, {
2101
- fields: fieldsToPick,
2102
- path: `allOf[${index}].if`
2103
- }),
2104
- ...findMissingFields(thenCondition, {
2105
- fields: fieldsToPick,
2106
- path: `allOf[${index}].then`
2107
- }),
2108
- ...findMissingFields(elseCondition, {
2109
- fields: fieldsToPick,
2110
- path: `allOf[${index}].else`
2111
- })
2112
- };
2113
- });
2114
- const warnings = [];
2115
- if (Object.keys(missingFields).length > 0) {
2116
- Object.entries(missingFields).forEach(([fieldName]) => {
2117
- (0, import_set2.default)(newSchema.properties, fieldName, originalSchema.properties[fieldName]);
2118
- });
2119
- warnings.push({
2120
- type: WARNING_TYPES.PICK_MISSED_FIELD,
2121
- message: `The picked fields are in conditionals that refeer other fields. They added automatically: ${Object.keys(
2122
- missingFields
2123
- ).map((name) => `"${name}"`).join(", ")}. Check "meta" for more details.`,
2124
- meta: missingFields
2125
- });
2126
- }
2127
- return { schema: newSchema, warnings };
2128
- }
2129
- function findMissingFields(conditional, { fields, path }) {
2130
- if (!conditional) {
2131
- return null;
2132
- }
2133
- let missingFields = {};
2134
- conditional.required?.forEach((fieldName) => {
2135
- if (!fields.includes(fieldName)) {
2136
- missingFields[fieldName] = {
2137
- path
2138
- };
2139
- }
2140
- });
2141
- Object.entries(conditional.properties || []).forEach(([fieldName]) => {
2142
- if (!fields.includes(fieldName)) {
2143
- missingFields[fieldName] = { path };
2144
- }
2145
- });
2146
- return missingFields;
2147
- }
2148
- function modify(originalSchema, config) {
2149
- const schema = JSON.parse(JSON.stringify(originalSchema));
2150
- const resultRewrite = rewriteFields(schema, config.fields);
2151
- rewriteAllFields(schema, config.allFields);
2152
- const resultCreate = createFields(schema, config.create);
2153
- const resultPick = pickFields(schema, config.pick);
2154
- const finalSchema = resultPick.schema;
2155
- const resultReorder = reorderFields(finalSchema, config.orderRoot);
2156
- if (!config.muteLogging) {
2157
- console.warn(
2158
- "json-schema-form modify(): We highly recommend you to handle/report the returned `warnings` as they highlight possible bugs in your modifications. To mute this log, pass `muteLogging: true` to the config."
2159
- );
2160
- }
2161
- const warnings = [
2162
- resultRewrite.warnings,
2163
- resultCreate.warnings,
2164
- resultPick.warnings,
2165
- resultReorder.warnings
2166
- ].flat().filter(Boolean);
2167
- return {
2168
- schema: finalSchema,
2169
- warnings
2170
- };
2171
- }