@opencrvs/toolkit 1.9.8-rc.34c53bf → 1.9.8-rc.48851c5
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/commons/api/router.d.ts +1792 -352
- package/dist/commons/conditionals/conditionals.d.ts +1027 -0
- package/dist/commons/events/ActionConfig.d.ts +3130 -1330
- package/dist/commons/events/EventConfig.d.ts +2504 -1064
- package/dist/commons/events/FieldConfig.d.ts +664 -280
- package/dist/commons/events/FormConfig.d.ts +1878 -798
- package/dist/commons/events/PageConfig.d.ts +1252 -532
- package/dist/commons/events/defineConfig.d.ts +1792 -352
- package/dist/commons/events/field.d.ts +154 -0
- package/dist/commons/events/utils.d.ts +3649 -769
- package/dist/conditionals/index.js +110 -0
- package/dist/events/index.js +166 -24
- package/dist/notification/index.js +141 -3
- package/package.json +1 -1
|
@@ -36,6 +36,7 @@ __export(conditionals_exports, {
|
|
|
36
36
|
defineConditional: () => defineConditional,
|
|
37
37
|
defineFormConditional: () => defineFormConditional,
|
|
38
38
|
flag: () => flag,
|
|
39
|
+
isCodeToEvaluate: () => isCodeToEvaluate,
|
|
39
40
|
isFieldReference: () => isFieldReference,
|
|
40
41
|
never: () => never,
|
|
41
42
|
not: () => not,
|
|
@@ -249,6 +250,9 @@ var user = Object.assign(userSerializer, {
|
|
|
249
250
|
function isFieldReference(value) {
|
|
250
251
|
return typeof value === "object" && value !== null && "$$field" in value;
|
|
251
252
|
}
|
|
253
|
+
function isCodeToEvaluate(value) {
|
|
254
|
+
return typeof value === "object" && value !== null && "$$code" in value;
|
|
255
|
+
}
|
|
252
256
|
function getDateRangeToFieldReference(field, comparedField, clause) {
|
|
253
257
|
return {
|
|
254
258
|
type: "object",
|
|
@@ -685,6 +689,112 @@ function createFieldConditionals(fieldId) {
|
|
|
685
689
|
required: [fieldId]
|
|
686
690
|
});
|
|
687
691
|
},
|
|
692
|
+
/**
|
|
693
|
+
* Executes a custom validation function defined by country configuration.
|
|
694
|
+
*
|
|
695
|
+
* **Use this as a last resort when predefined toolkit methods are insufficient.**
|
|
696
|
+
*
|
|
697
|
+
* The function is serialized via toString() and transmitted as part of the JSON Schema.
|
|
698
|
+
* It is deserialized just-in-time during validation on the client.
|
|
699
|
+
*
|
|
700
|
+
* @param fn - Validation function that receives the field value and form context.
|
|
701
|
+
* Must return true if valid, false if invalid.
|
|
702
|
+
*
|
|
703
|
+
* @returns JSONSchema conditional for AJV validation
|
|
704
|
+
*
|
|
705
|
+
* @example
|
|
706
|
+
* // Simple age validation
|
|
707
|
+
* field('age').customClientValidator((value, ctx) => {
|
|
708
|
+
* return value >= 18
|
|
709
|
+
* })
|
|
710
|
+
*
|
|
711
|
+
* @example
|
|
712
|
+
* // Cross-field validation: child DOB must be after mother DOB
|
|
713
|
+
* field('child.dob').customClientValidator((value, ctx) => {
|
|
714
|
+
* const motherDob = ctx.$form['mother.dob']
|
|
715
|
+
* if (!motherDob || !value) return false
|
|
716
|
+
* return new Date(value) > new Date(motherDob)
|
|
717
|
+
* })
|
|
718
|
+
*
|
|
719
|
+
* @example
|
|
720
|
+
* // Two number fields validated together
|
|
721
|
+
* field('fieldA').customClientValidator((value, ctx) => {
|
|
722
|
+
* const fieldB = ctx.$form['fieldB']
|
|
723
|
+
* if (!fieldB) return false
|
|
724
|
+
* return value + fieldB > 100
|
|
725
|
+
* })
|
|
726
|
+
*
|
|
727
|
+
* @remarks
|
|
728
|
+
* Limitations:
|
|
729
|
+
* - Client-side only. For backend validation, use country config event triggers.
|
|
730
|
+
* - Cannot reference external libraries (lodash, etc.)
|
|
731
|
+
* - Function must be pure — no side effects, no closures over external state
|
|
732
|
+
* - Function must be serialisable via toString()
|
|
733
|
+
* - Receives (value, context) parameters where context contains $form, $now, $online, etc.
|
|
734
|
+
* - Must return boolean: true = valid, false = invalid
|
|
735
|
+
*
|
|
736
|
+
* @see https://github.com/opencrvs/opencrvs-core/issues/11653
|
|
737
|
+
*/
|
|
738
|
+
customClientValidator(fn) {
|
|
739
|
+
const serializedFn = fn.toString();
|
|
740
|
+
return defineFormConditional({
|
|
741
|
+
type: "object",
|
|
742
|
+
properties: {
|
|
743
|
+
[fieldId]: wrapToPath(
|
|
744
|
+
{
|
|
745
|
+
customClientValidator: serializedFn
|
|
746
|
+
},
|
|
747
|
+
this.$$subfield
|
|
748
|
+
)
|
|
749
|
+
},
|
|
750
|
+
required: [fieldId]
|
|
751
|
+
});
|
|
752
|
+
},
|
|
753
|
+
/**
|
|
754
|
+
* Defines a client-side computation function for dynamic field values.
|
|
755
|
+
*
|
|
756
|
+
* **Use this as a last resort when predefined toolkit methods are insufficient.**
|
|
757
|
+
*
|
|
758
|
+
* The function is serialized via toString() and transmitted to the client,
|
|
759
|
+
* where it is deserialized and executed to compute field values dynamically.
|
|
760
|
+
*
|
|
761
|
+
* @param fn - Computation function that receives the field value and form context.
|
|
762
|
+
* Returns the computed value (any type).
|
|
763
|
+
*
|
|
764
|
+
* @returns CodeToEvaluate object for client-side execution
|
|
765
|
+
*
|
|
766
|
+
* @example
|
|
767
|
+
* // Concatenate first and last name
|
|
768
|
+
* field('fullName').customClientEvaluation((value, ctx) => {
|
|
769
|
+
* return `${ctx.$form.firstName} ${ctx.$form.lastName}`
|
|
770
|
+
* })
|
|
771
|
+
*
|
|
772
|
+
* @example
|
|
773
|
+
* // Calculate age from date of birth
|
|
774
|
+
* field('childAge').customClientEvaluation((value, ctx) => {
|
|
775
|
+
* const dob = ctx.$form['child.dob']
|
|
776
|
+
* if (!dob) return undefined
|
|
777
|
+
* const age = Math.floor((new Date(ctx.$now) - new Date(dob)) / (365.25 * 24 * 60 * 60 * 1000))
|
|
778
|
+
* return age
|
|
779
|
+
* })
|
|
780
|
+
*
|
|
781
|
+
* @remarks
|
|
782
|
+
* Limitations:
|
|
783
|
+
* - Client-side only. For backend computation, use country config event triggers.
|
|
784
|
+
* - Cannot reference external libraries (lodash, etc.)
|
|
785
|
+
* - Function must be pure — no side effects, no closures over external state
|
|
786
|
+
* - Function must be serialisable via toString()
|
|
787
|
+
* - Receives (value, context) parameters where context contains $form, $now, $online, etc.
|
|
788
|
+
* - Should return the computed value (any type)
|
|
789
|
+
*
|
|
790
|
+
* @see https://github.com/opencrvs/opencrvs-core/issues/11653
|
|
791
|
+
*/
|
|
792
|
+
customClientEvaluation(fn) {
|
|
793
|
+
const serializedFn = fn.toString();
|
|
794
|
+
return {
|
|
795
|
+
$$code: serializedFn
|
|
796
|
+
};
|
|
797
|
+
},
|
|
688
798
|
getId: () => ({ fieldId }),
|
|
689
799
|
/**
|
|
690
800
|
* @deprecated
|
package/dist/events/index.js
CHANGED
|
@@ -71,6 +71,7 @@ __export(events_exports, {
|
|
|
71
71
|
CheckboxFieldValue: () => CheckboxFieldValue,
|
|
72
72
|
Clause: () => Clause,
|
|
73
73
|
ClientSpecificAction: () => ClientSpecificAction,
|
|
74
|
+
CodeToEvaluate: () => CodeToEvaluate,
|
|
74
75
|
Conditional: () => Conditional,
|
|
75
76
|
ConditionalType: () => ConditionalType,
|
|
76
77
|
ConfirmableActions: () => ConfirmableActions,
|
|
@@ -322,6 +323,7 @@ __export(events_exports, {
|
|
|
322
323
|
isBulletListFieldType: () => isBulletListFieldType,
|
|
323
324
|
isButtonFieldType: () => isButtonFieldType,
|
|
324
325
|
isCheckboxFieldType: () => isCheckboxFieldType,
|
|
326
|
+
isCodeToEvaluate: () => isCodeToEvaluate,
|
|
325
327
|
isConditionMet: () => isConditionMet,
|
|
326
328
|
isCountryFieldType: () => isCountryFieldType,
|
|
327
329
|
isCustomFieldType: () => isCustomFieldType,
|
|
@@ -2036,6 +2038,11 @@ var FieldReference = z17.object({
|
|
|
2036
2038
|
'If the FieldValue is an object, subfield can be used to refer to e.g. `["foo", "bar"]` in `{ foo: { bar: 3 } }`'
|
|
2037
2039
|
)
|
|
2038
2040
|
}).describe("Reference to a field by its ID");
|
|
2041
|
+
var CodeToEvaluate = z17.object({
|
|
2042
|
+
$$code: z17.string().describe(
|
|
2043
|
+
"Serialized function string that will be evaluated at runtime with the field value and context"
|
|
2044
|
+
)
|
|
2045
|
+
}).describe("Custom evaluation function for computed field values");
|
|
2039
2046
|
var ValidationConfig = z17.object({
|
|
2040
2047
|
validator: Conditional,
|
|
2041
2048
|
message: TranslationConfig
|
|
@@ -2068,7 +2075,7 @@ var BaseField = z17.object({
|
|
|
2068
2075
|
uncorrectable: z17.boolean().default(false).optional().describe(
|
|
2069
2076
|
"Indicates whether the field can be modified during record correction."
|
|
2070
2077
|
),
|
|
2071
|
-
value: FieldReference.or(z17.array(FieldReference)).optional().describe(
|
|
2078
|
+
value: FieldReference.or(CodeToEvaluate).or(z17.array(FieldReference.or(CodeToEvaluate))).optional().describe(
|
|
2072
2079
|
"Reference to the source field or fields. When a value is defined, it is copied from the parent field when changed. If multiple references are provided, the first truthy value is used."
|
|
2073
2080
|
),
|
|
2074
2081
|
analytics: z17.boolean().default(false).optional().describe(
|
|
@@ -2410,7 +2417,7 @@ var Address = BaseField.extend({
|
|
|
2410
2417
|
var StaticDataEntry = z17.object({
|
|
2411
2418
|
id: z17.string().describe("ID for the data entry."),
|
|
2412
2419
|
label: TranslationConfig,
|
|
2413
|
-
value: TranslationConfig.or(z17.string()).or(FieldReference)
|
|
2420
|
+
value: TranslationConfig.or(z17.string()).or(FieldReference).or(CodeToEvaluate)
|
|
2414
2421
|
}).describe("Static data entry");
|
|
2415
2422
|
var DataEntry = z17.union([StaticDataEntry, z17.object({ fieldId: z17.string() })]).describe(
|
|
2416
2423
|
"Data entry can be either a static data entry, or a reference to another field in the current form or the declaration."
|
|
@@ -2452,7 +2459,7 @@ var HttpField = BaseField.extend({
|
|
|
2452
2459
|
headers: z17.record(z17.string(), z17.string()).optional(),
|
|
2453
2460
|
body: z17.record(z17.string(), z17.any()).optional(),
|
|
2454
2461
|
errorValue: z17.any().optional().describe("Value to set if the request fails"),
|
|
2455
|
-
params: z17.record(z17.string(), z17.union([z17.string(), FieldReference])).optional(),
|
|
2462
|
+
params: z17.record(z17.string(), z17.union([z17.string(), FieldReference, CodeToEvaluate])).optional(),
|
|
2456
2463
|
timeout: z17.number().default(15e3).describe("Request timeout in milliseconds")
|
|
2457
2464
|
})
|
|
2458
2465
|
}).describe("HTTP request function triggered by a button click or other event");
|
|
@@ -3594,31 +3601,57 @@ ajv.addKeyword({
|
|
|
3594
3601
|
return locations.some((location) => location.id === locationIdInput);
|
|
3595
3602
|
}
|
|
3596
3603
|
});
|
|
3604
|
+
ajv.addKeyword({
|
|
3605
|
+
keyword: "customClientValidator",
|
|
3606
|
+
type: ["string", "number", "boolean", "object", "array", "null"],
|
|
3607
|
+
schemaType: "string",
|
|
3608
|
+
$data: false,
|
|
3609
|
+
errors: false,
|
|
3610
|
+
validate(schema, data, _, dataContext) {
|
|
3611
|
+
if (!schema || typeof schema !== "string") {
|
|
3612
|
+
return true;
|
|
3613
|
+
}
|
|
3614
|
+
if (typeof window === "undefined") {
|
|
3615
|
+
return true;
|
|
3616
|
+
}
|
|
3617
|
+
try {
|
|
3618
|
+
const validatorFn = new Function(
|
|
3619
|
+
"value",
|
|
3620
|
+
"context",
|
|
3621
|
+
`return (${schema})(value, context)`
|
|
3622
|
+
);
|
|
3623
|
+
const context = dataContext?.rootData || {};
|
|
3624
|
+
const result = validatorFn(data, context);
|
|
3625
|
+
return Boolean(result);
|
|
3626
|
+
} catch (error) {
|
|
3627
|
+
return false;
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3630
|
+
});
|
|
3631
|
+
function isAgeValue(value) {
|
|
3632
|
+
return typeof value === "object" && value !== null && "age" in value && typeof value.age === "number";
|
|
3633
|
+
}
|
|
3597
3634
|
function validate(schema, data) {
|
|
3598
3635
|
const validator = ajv.getSchema(schema.$id) || ajv.compile(schema);
|
|
3599
3636
|
if ("$form" in data) {
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
const maybeAgeValue = AgeValue.safeParse(value);
|
|
3603
|
-
if (maybeAgeValue.success) {
|
|
3604
|
-
const age = maybeAgeValue.data.age;
|
|
3605
|
-
const maybeAsOfDate = DateValue.safeParse(
|
|
3606
|
-
data.$form[maybeAgeValue.data.asOfDateRef]
|
|
3607
|
-
);
|
|
3608
|
-
return [
|
|
3609
|
-
key,
|
|
3610
|
-
{
|
|
3611
|
-
age,
|
|
3612
|
-
dob: ageToDate(
|
|
3613
|
-
age,
|
|
3614
|
-
maybeAsOfDate.success ? maybeAsOfDate.data : data.$now
|
|
3615
|
-
)
|
|
3616
|
-
}
|
|
3617
|
-
];
|
|
3618
|
-
}
|
|
3637
|
+
const entries = Object.entries(data.$form).map(([key, value]) => {
|
|
3638
|
+
if (!isAgeValue(value)) {
|
|
3619
3639
|
return [key, value];
|
|
3620
|
-
}
|
|
3621
|
-
|
|
3640
|
+
}
|
|
3641
|
+
const age = value.age;
|
|
3642
|
+
const maybeAsOfDate = DateValue.safeParse(data.$form[value.asOfDateRef]);
|
|
3643
|
+
return [
|
|
3644
|
+
key,
|
|
3645
|
+
{
|
|
3646
|
+
age,
|
|
3647
|
+
dob: ageToDate(
|
|
3648
|
+
age,
|
|
3649
|
+
maybeAsOfDate.success ? maybeAsOfDate.data : data.$now
|
|
3650
|
+
)
|
|
3651
|
+
}
|
|
3652
|
+
];
|
|
3653
|
+
});
|
|
3654
|
+
data.$form = Object.fromEntries(entries);
|
|
3622
3655
|
}
|
|
3623
3656
|
const result = validator(data);
|
|
3624
3657
|
return result;
|
|
@@ -4629,6 +4662,9 @@ var user = Object.assign(userSerializer, {
|
|
|
4629
4662
|
function isFieldReference(value) {
|
|
4630
4663
|
return typeof value === "object" && value !== null && "$$field" in value;
|
|
4631
4664
|
}
|
|
4665
|
+
function isCodeToEvaluate(value) {
|
|
4666
|
+
return typeof value === "object" && value !== null && "$$code" in value;
|
|
4667
|
+
}
|
|
4632
4668
|
function getDateRangeToFieldReference(field3, comparedField, clause) {
|
|
4633
4669
|
return {
|
|
4634
4670
|
type: "object",
|
|
@@ -5065,6 +5101,112 @@ function createFieldConditionals(fieldId) {
|
|
|
5065
5101
|
required: [fieldId]
|
|
5066
5102
|
});
|
|
5067
5103
|
},
|
|
5104
|
+
/**
|
|
5105
|
+
* Executes a custom validation function defined by country configuration.
|
|
5106
|
+
*
|
|
5107
|
+
* **Use this as a last resort when predefined toolkit methods are insufficient.**
|
|
5108
|
+
*
|
|
5109
|
+
* The function is serialized via toString() and transmitted as part of the JSON Schema.
|
|
5110
|
+
* It is deserialized just-in-time during validation on the client.
|
|
5111
|
+
*
|
|
5112
|
+
* @param fn - Validation function that receives the field value and form context.
|
|
5113
|
+
* Must return true if valid, false if invalid.
|
|
5114
|
+
*
|
|
5115
|
+
* @returns JSONSchema conditional for AJV validation
|
|
5116
|
+
*
|
|
5117
|
+
* @example
|
|
5118
|
+
* // Simple age validation
|
|
5119
|
+
* field('age').customClientValidator((value, ctx) => {
|
|
5120
|
+
* return value >= 18
|
|
5121
|
+
* })
|
|
5122
|
+
*
|
|
5123
|
+
* @example
|
|
5124
|
+
* // Cross-field validation: child DOB must be after mother DOB
|
|
5125
|
+
* field('child.dob').customClientValidator((value, ctx) => {
|
|
5126
|
+
* const motherDob = ctx.$form['mother.dob']
|
|
5127
|
+
* if (!motherDob || !value) return false
|
|
5128
|
+
* return new Date(value) > new Date(motherDob)
|
|
5129
|
+
* })
|
|
5130
|
+
*
|
|
5131
|
+
* @example
|
|
5132
|
+
* // Two number fields validated together
|
|
5133
|
+
* field('fieldA').customClientValidator((value, ctx) => {
|
|
5134
|
+
* const fieldB = ctx.$form['fieldB']
|
|
5135
|
+
* if (!fieldB) return false
|
|
5136
|
+
* return value + fieldB > 100
|
|
5137
|
+
* })
|
|
5138
|
+
*
|
|
5139
|
+
* @remarks
|
|
5140
|
+
* Limitations:
|
|
5141
|
+
* - Client-side only. For backend validation, use country config event triggers.
|
|
5142
|
+
* - Cannot reference external libraries (lodash, etc.)
|
|
5143
|
+
* - Function must be pure — no side effects, no closures over external state
|
|
5144
|
+
* - Function must be serialisable via toString()
|
|
5145
|
+
* - Receives (value, context) parameters where context contains $form, $now, $online, etc.
|
|
5146
|
+
* - Must return boolean: true = valid, false = invalid
|
|
5147
|
+
*
|
|
5148
|
+
* @see https://github.com/opencrvs/opencrvs-core/issues/11653
|
|
5149
|
+
*/
|
|
5150
|
+
customClientValidator(fn) {
|
|
5151
|
+
const serializedFn = fn.toString();
|
|
5152
|
+
return defineFormConditional({
|
|
5153
|
+
type: "object",
|
|
5154
|
+
properties: {
|
|
5155
|
+
[fieldId]: wrapToPath(
|
|
5156
|
+
{
|
|
5157
|
+
customClientValidator: serializedFn
|
|
5158
|
+
},
|
|
5159
|
+
this.$$subfield
|
|
5160
|
+
)
|
|
5161
|
+
},
|
|
5162
|
+
required: [fieldId]
|
|
5163
|
+
});
|
|
5164
|
+
},
|
|
5165
|
+
/**
|
|
5166
|
+
* Defines a client-side computation function for dynamic field values.
|
|
5167
|
+
*
|
|
5168
|
+
* **Use this as a last resort when predefined toolkit methods are insufficient.**
|
|
5169
|
+
*
|
|
5170
|
+
* The function is serialized via toString() and transmitted to the client,
|
|
5171
|
+
* where it is deserialized and executed to compute field values dynamically.
|
|
5172
|
+
*
|
|
5173
|
+
* @param fn - Computation function that receives the field value and form context.
|
|
5174
|
+
* Returns the computed value (any type).
|
|
5175
|
+
*
|
|
5176
|
+
* @returns CodeToEvaluate object for client-side execution
|
|
5177
|
+
*
|
|
5178
|
+
* @example
|
|
5179
|
+
* // Concatenate first and last name
|
|
5180
|
+
* field('fullName').customClientEvaluation((value, ctx) => {
|
|
5181
|
+
* return `${ctx.$form.firstName} ${ctx.$form.lastName}`
|
|
5182
|
+
* })
|
|
5183
|
+
*
|
|
5184
|
+
* @example
|
|
5185
|
+
* // Calculate age from date of birth
|
|
5186
|
+
* field('childAge').customClientEvaluation((value, ctx) => {
|
|
5187
|
+
* const dob = ctx.$form['child.dob']
|
|
5188
|
+
* if (!dob) return undefined
|
|
5189
|
+
* const age = Math.floor((new Date(ctx.$now) - new Date(dob)) / (365.25 * 24 * 60 * 60 * 1000))
|
|
5190
|
+
* return age
|
|
5191
|
+
* })
|
|
5192
|
+
*
|
|
5193
|
+
* @remarks
|
|
5194
|
+
* Limitations:
|
|
5195
|
+
* - Client-side only. For backend computation, use country config event triggers.
|
|
5196
|
+
* - Cannot reference external libraries (lodash, etc.)
|
|
5197
|
+
* - Function must be pure — no side effects, no closures over external state
|
|
5198
|
+
* - Function must be serialisable via toString()
|
|
5199
|
+
* - Receives (value, context) parameters where context contains $form, $now, $online, etc.
|
|
5200
|
+
* - Should return the computed value (any type)
|
|
5201
|
+
*
|
|
5202
|
+
* @see https://github.com/opencrvs/opencrvs-core/issues/11653
|
|
5203
|
+
*/
|
|
5204
|
+
customClientEvaluation(fn) {
|
|
5205
|
+
const serializedFn = fn.toString();
|
|
5206
|
+
return {
|
|
5207
|
+
$$code: serializedFn
|
|
5208
|
+
};
|
|
5209
|
+
},
|
|
5068
5210
|
getId: () => ({ fieldId }),
|
|
5069
5211
|
/**
|
|
5070
5212
|
* @deprecated
|
|
@@ -1532,6 +1532,11 @@ var FieldReference = z17.object({
|
|
|
1532
1532
|
'If the FieldValue is an object, subfield can be used to refer to e.g. `["foo", "bar"]` in `{ foo: { bar: 3 } }`'
|
|
1533
1533
|
)
|
|
1534
1534
|
}).describe("Reference to a field by its ID");
|
|
1535
|
+
var CodeToEvaluate = z17.object({
|
|
1536
|
+
$$code: z17.string().describe(
|
|
1537
|
+
"Serialized function string that will be evaluated at runtime with the field value and context"
|
|
1538
|
+
)
|
|
1539
|
+
}).describe("Custom evaluation function for computed field values");
|
|
1535
1540
|
var ValidationConfig = z17.object({
|
|
1536
1541
|
validator: Conditional,
|
|
1537
1542
|
message: TranslationConfig
|
|
@@ -1564,7 +1569,7 @@ var BaseField = z17.object({
|
|
|
1564
1569
|
uncorrectable: z17.boolean().default(false).optional().describe(
|
|
1565
1570
|
"Indicates whether the field can be modified during record correction."
|
|
1566
1571
|
),
|
|
1567
|
-
value: FieldReference.or(z17.array(FieldReference)).optional().describe(
|
|
1572
|
+
value: FieldReference.or(CodeToEvaluate).or(z17.array(FieldReference.or(CodeToEvaluate))).optional().describe(
|
|
1568
1573
|
"Reference to the source field or fields. When a value is defined, it is copied from the parent field when changed. If multiple references are provided, the first truthy value is used."
|
|
1569
1574
|
),
|
|
1570
1575
|
analytics: z17.boolean().default(false).optional().describe(
|
|
@@ -1906,7 +1911,7 @@ var Address = BaseField.extend({
|
|
|
1906
1911
|
var StaticDataEntry = z17.object({
|
|
1907
1912
|
id: z17.string().describe("ID for the data entry."),
|
|
1908
1913
|
label: TranslationConfig,
|
|
1909
|
-
value: TranslationConfig.or(z17.string()).or(FieldReference)
|
|
1914
|
+
value: TranslationConfig.or(z17.string()).or(FieldReference).or(CodeToEvaluate)
|
|
1910
1915
|
}).describe("Static data entry");
|
|
1911
1916
|
var DataEntry = z17.union([StaticDataEntry, z17.object({ fieldId: z17.string() })]).describe(
|
|
1912
1917
|
"Data entry can be either a static data entry, or a reference to another field in the current form or the declaration."
|
|
@@ -1948,7 +1953,7 @@ var HttpField = BaseField.extend({
|
|
|
1948
1953
|
headers: z17.record(z17.string(), z17.string()).optional(),
|
|
1949
1954
|
body: z17.record(z17.string(), z17.any()).optional(),
|
|
1950
1955
|
errorValue: z17.any().optional().describe("Value to set if the request fails"),
|
|
1951
|
-
params: z17.record(z17.string(), z17.union([z17.string(), FieldReference])).optional(),
|
|
1956
|
+
params: z17.record(z17.string(), z17.union([z17.string(), FieldReference, CodeToEvaluate])).optional(),
|
|
1952
1957
|
timeout: z17.number().default(15e3).describe("Request timeout in milliseconds")
|
|
1953
1958
|
})
|
|
1954
1959
|
}).describe("HTTP request function triggered by a button click or other event");
|
|
@@ -2780,6 +2785,33 @@ ajv.addKeyword({
|
|
|
2780
2785
|
return locations.some((location) => location.id === locationIdInput);
|
|
2781
2786
|
}
|
|
2782
2787
|
});
|
|
2788
|
+
ajv.addKeyword({
|
|
2789
|
+
keyword: "customClientValidator",
|
|
2790
|
+
type: ["string", "number", "boolean", "object", "array", "null"],
|
|
2791
|
+
schemaType: "string",
|
|
2792
|
+
$data: false,
|
|
2793
|
+
errors: false,
|
|
2794
|
+
validate(schema, data, _, dataContext) {
|
|
2795
|
+
if (!schema || typeof schema !== "string") {
|
|
2796
|
+
return true;
|
|
2797
|
+
}
|
|
2798
|
+
if (typeof window === "undefined") {
|
|
2799
|
+
return true;
|
|
2800
|
+
}
|
|
2801
|
+
try {
|
|
2802
|
+
const validatorFn = new Function(
|
|
2803
|
+
"value",
|
|
2804
|
+
"context",
|
|
2805
|
+
`return (${schema})(value, context)`
|
|
2806
|
+
);
|
|
2807
|
+
const context = dataContext?.rootData || {};
|
|
2808
|
+
const result = validatorFn(data, context);
|
|
2809
|
+
return Boolean(result);
|
|
2810
|
+
} catch (error) {
|
|
2811
|
+
return false;
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
});
|
|
2783
2815
|
|
|
2784
2816
|
// ../commons/src/utils.ts
|
|
2785
2817
|
var z29 = __toESM(require("zod/v4"));
|
|
@@ -3535,6 +3567,112 @@ function createFieldConditionals(fieldId) {
|
|
|
3535
3567
|
required: [fieldId]
|
|
3536
3568
|
});
|
|
3537
3569
|
},
|
|
3570
|
+
/**
|
|
3571
|
+
* Executes a custom validation function defined by country configuration.
|
|
3572
|
+
*
|
|
3573
|
+
* **Use this as a last resort when predefined toolkit methods are insufficient.**
|
|
3574
|
+
*
|
|
3575
|
+
* The function is serialized via toString() and transmitted as part of the JSON Schema.
|
|
3576
|
+
* It is deserialized just-in-time during validation on the client.
|
|
3577
|
+
*
|
|
3578
|
+
* @param fn - Validation function that receives the field value and form context.
|
|
3579
|
+
* Must return true if valid, false if invalid.
|
|
3580
|
+
*
|
|
3581
|
+
* @returns JSONSchema conditional for AJV validation
|
|
3582
|
+
*
|
|
3583
|
+
* @example
|
|
3584
|
+
* // Simple age validation
|
|
3585
|
+
* field('age').customClientValidator((value, ctx) => {
|
|
3586
|
+
* return value >= 18
|
|
3587
|
+
* })
|
|
3588
|
+
*
|
|
3589
|
+
* @example
|
|
3590
|
+
* // Cross-field validation: child DOB must be after mother DOB
|
|
3591
|
+
* field('child.dob').customClientValidator((value, ctx) => {
|
|
3592
|
+
* const motherDob = ctx.$form['mother.dob']
|
|
3593
|
+
* if (!motherDob || !value) return false
|
|
3594
|
+
* return new Date(value) > new Date(motherDob)
|
|
3595
|
+
* })
|
|
3596
|
+
*
|
|
3597
|
+
* @example
|
|
3598
|
+
* // Two number fields validated together
|
|
3599
|
+
* field('fieldA').customClientValidator((value, ctx) => {
|
|
3600
|
+
* const fieldB = ctx.$form['fieldB']
|
|
3601
|
+
* if (!fieldB) return false
|
|
3602
|
+
* return value + fieldB > 100
|
|
3603
|
+
* })
|
|
3604
|
+
*
|
|
3605
|
+
* @remarks
|
|
3606
|
+
* Limitations:
|
|
3607
|
+
* - Client-side only. For backend validation, use country config event triggers.
|
|
3608
|
+
* - Cannot reference external libraries (lodash, etc.)
|
|
3609
|
+
* - Function must be pure — no side effects, no closures over external state
|
|
3610
|
+
* - Function must be serialisable via toString()
|
|
3611
|
+
* - Receives (value, context) parameters where context contains $form, $now, $online, etc.
|
|
3612
|
+
* - Must return boolean: true = valid, false = invalid
|
|
3613
|
+
*
|
|
3614
|
+
* @see https://github.com/opencrvs/opencrvs-core/issues/11653
|
|
3615
|
+
*/
|
|
3616
|
+
customClientValidator(fn) {
|
|
3617
|
+
const serializedFn = fn.toString();
|
|
3618
|
+
return defineFormConditional({
|
|
3619
|
+
type: "object",
|
|
3620
|
+
properties: {
|
|
3621
|
+
[fieldId]: wrapToPath(
|
|
3622
|
+
{
|
|
3623
|
+
customClientValidator: serializedFn
|
|
3624
|
+
},
|
|
3625
|
+
this.$$subfield
|
|
3626
|
+
)
|
|
3627
|
+
},
|
|
3628
|
+
required: [fieldId]
|
|
3629
|
+
});
|
|
3630
|
+
},
|
|
3631
|
+
/**
|
|
3632
|
+
* Defines a client-side computation function for dynamic field values.
|
|
3633
|
+
*
|
|
3634
|
+
* **Use this as a last resort when predefined toolkit methods are insufficient.**
|
|
3635
|
+
*
|
|
3636
|
+
* The function is serialized via toString() and transmitted to the client,
|
|
3637
|
+
* where it is deserialized and executed to compute field values dynamically.
|
|
3638
|
+
*
|
|
3639
|
+
* @param fn - Computation function that receives the field value and form context.
|
|
3640
|
+
* Returns the computed value (any type).
|
|
3641
|
+
*
|
|
3642
|
+
* @returns CodeToEvaluate object for client-side execution
|
|
3643
|
+
*
|
|
3644
|
+
* @example
|
|
3645
|
+
* // Concatenate first and last name
|
|
3646
|
+
* field('fullName').customClientEvaluation((value, ctx) => {
|
|
3647
|
+
* return `${ctx.$form.firstName} ${ctx.$form.lastName}`
|
|
3648
|
+
* })
|
|
3649
|
+
*
|
|
3650
|
+
* @example
|
|
3651
|
+
* // Calculate age from date of birth
|
|
3652
|
+
* field('childAge').customClientEvaluation((value, ctx) => {
|
|
3653
|
+
* const dob = ctx.$form['child.dob']
|
|
3654
|
+
* if (!dob) return undefined
|
|
3655
|
+
* const age = Math.floor((new Date(ctx.$now) - new Date(dob)) / (365.25 * 24 * 60 * 60 * 1000))
|
|
3656
|
+
* return age
|
|
3657
|
+
* })
|
|
3658
|
+
*
|
|
3659
|
+
* @remarks
|
|
3660
|
+
* Limitations:
|
|
3661
|
+
* - Client-side only. For backend computation, use country config event triggers.
|
|
3662
|
+
* - Cannot reference external libraries (lodash, etc.)
|
|
3663
|
+
* - Function must be pure — no side effects, no closures over external state
|
|
3664
|
+
* - Function must be serialisable via toString()
|
|
3665
|
+
* - Receives (value, context) parameters where context contains $form, $now, $online, etc.
|
|
3666
|
+
* - Should return the computed value (any type)
|
|
3667
|
+
*
|
|
3668
|
+
* @see https://github.com/opencrvs/opencrvs-core/issues/11653
|
|
3669
|
+
*/
|
|
3670
|
+
customClientEvaluation(fn) {
|
|
3671
|
+
const serializedFn = fn.toString();
|
|
3672
|
+
return {
|
|
3673
|
+
$$code: serializedFn
|
|
3674
|
+
};
|
|
3675
|
+
},
|
|
3538
3676
|
getId: () => ({ fieldId }),
|
|
3539
3677
|
/**
|
|
3540
3678
|
* @deprecated
|