@opencrvs/toolkit 2.0.0-rc.fe35c20 → 2.0.0-rc.fe577a4

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.
@@ -2394,14 +2394,20 @@ var encodeScope = (scope) => {
2394
2394
  encode: false
2395
2395
  });
2396
2396
  };
2397
+ var decodedScopeCache = /* @__PURE__ */ new Map();
2397
2398
  var decodeScope = (query) => {
2399
+ if (decodedScopeCache.has(query)) {
2400
+ return decodedScopeCache.get(query);
2401
+ }
2398
2402
  const scope = qs.parse(query, {
2399
2403
  ignoreQueryPrefix: true,
2400
2404
  comma: true,
2401
2405
  allowDots: true
2402
2406
  });
2403
2407
  const unflattenedScope = unflattenScope(scope);
2404
- return Scope2.safeParse(unflattenedScope)?.data;
2408
+ const result = Scope2.safeParse(unflattenedScope)?.data;
2409
+ decodedScopeCache.set(query, result);
2410
+ return result;
2405
2411
  };
2406
2412
  var DEFAULT_SCOPE_OPTIONS = {
2407
2413
  placeOfEvent: JurisdictionFilter.enum.all,
@@ -917,6 +917,7 @@ var DateRangeFieldValue = z12.object({
917
917
  var EmailValue = z12.email();
918
918
  var CheckboxFieldValue = z12.boolean();
919
919
  var NumberFieldValue = z12.number();
920
+ var SignatureFieldValue = FileFieldValue;
920
921
  var ButtonFieldValue = z12.number();
921
922
  var VerificationStatusValue = z12.enum([
922
923
  "verified",
@@ -1215,8 +1216,9 @@ var RequestedCorrectionAction = ActionBase.extend(
1215
1216
  var ApprovedCorrectionAction = ActionBase.extend(
1216
1217
  z14.object({
1217
1218
  type: z14.literal(ActionType.APPROVE_CORRECTION),
1218
- requestId: z14.string()
1219
+ requestId: z14.string(),
1219
1220
  // TODO move into 'content' property
1221
+ content: z14.object({ immediateCorrection: z14.boolean().optional() }).optional()
1220
1222
  }).shape
1221
1223
  );
1222
1224
  var RejectedCorrectionAction = ActionBase.extend(
@@ -1665,8 +1667,18 @@ var FieldReference = import_v43.default.object({
1665
1667
  $$field: FieldId.describe("Id of the field to reference"),
1666
1668
  $$subfield: import_v43.default.array(import_v43.default.string()).optional().default([]).describe(
1667
1669
  'If the FieldValue is an object, subfield can be used to refer to e.g. `["foo", "bar"]` in `{ foo: { bar: 3 } }`'
1670
+ ),
1671
+ $$code: import_v43.default.string().optional().describe(
1672
+ "Serialised client-side function body. When present the expression is evaluated rather than dereferenced."
1668
1673
  )
1669
- }).describe("Reference to a field by its ID");
1674
+ }).describe(
1675
+ "Reference to a field value, with an optional client-side computation applied."
1676
+ );
1677
+ var ComputedDefaultValue = import_v43.default.object({
1678
+ $$code: import_v43.default.string().describe(
1679
+ "Serialised client-side function body. Receives (undefined, context) where context includes $now, $online, and system variables."
1680
+ )
1681
+ }).describe("A context-only computation used as a field default value.");
1670
1682
  var ValidationConfig = import_v43.default.object({
1671
1683
  validator: Conditional.describe(
1672
1684
  "Conditional expression that must hold for the field value to be considered valid."
@@ -1707,7 +1719,7 @@ var BaseField = import_v43.default.object({
1707
1719
  "Indicates whether the field can be modified during record correction."
1708
1720
  ),
1709
1721
  value: FieldReference.or(import_v43.default.array(FieldReference)).optional().describe(
1710
- "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."
1722
+ "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. A FieldReference with $$code computes the value via a custom client-side function."
1711
1723
  ),
1712
1724
  analytics: import_v43.default.boolean().default(false).optional().describe(
1713
1725
  "Indicates whether the field is included in analytics. When enabled, its value becomes available in the analytics dashboard."
@@ -1721,7 +1733,7 @@ var Divider = BaseField.extend({
1721
1733
  });
1722
1734
  var TextField = BaseField.extend({
1723
1735
  type: import_v43.default.literal(FieldType.TEXT),
1724
- defaultValue: import_v43.default.union([NonEmptyTextValue, SerializedUserField]).optional(),
1736
+ defaultValue: import_v43.default.union([NonEmptyTextValue, SerializedUserField, ComputedDefaultValue]).optional(),
1725
1737
  configuration: import_v43.default.object({
1726
1738
  maxLength: import_v43.default.number().optional().describe("Maximum length of the text"),
1727
1739
  type: import_v43.default.enum(["text", "password"]).optional(),
@@ -1734,7 +1746,7 @@ var TextField = BaseField.extend({
1734
1746
  });
1735
1747
  var NumberField = BaseField.extend({
1736
1748
  type: import_v43.default.literal(FieldType.NUMBER),
1737
- defaultValue: NumberFieldValue.optional(),
1749
+ defaultValue: NumberFieldValue.or(ComputedDefaultValue).optional(),
1738
1750
  configuration: import_v43.default.object({
1739
1751
  min: import_v43.default.number().optional().describe("Minimum value"),
1740
1752
  max: import_v43.default.number().optional().describe("Maximum value"),
@@ -1747,7 +1759,7 @@ var NumberField = BaseField.extend({
1747
1759
  });
1748
1760
  var TextAreaField = BaseField.extend({
1749
1761
  type: import_v43.default.literal(FieldType.TEXTAREA),
1750
- defaultValue: NonEmptyTextValue.optional(),
1762
+ defaultValue: NonEmptyTextValue.or(ComputedDefaultValue).optional(),
1751
1763
  configuration: import_v43.default.object({
1752
1764
  maxLength: import_v43.default.number().optional().describe("Maximum length of the text"),
1753
1765
  rows: import_v43.default.number().optional().describe("Number of visible text lines"),
@@ -1781,7 +1793,7 @@ var SignatureField = BaseField.extend({
1781
1793
  signaturePromptLabel: TranslationConfig.describe(
1782
1794
  "Title of the signature modal"
1783
1795
  ),
1784
- defaultValue: FieldValue.optional(),
1796
+ defaultValue: SignatureFieldValue.or(ComputedDefaultValue).optional(),
1785
1797
  configuration: import_v43.default.object({
1786
1798
  maxFileSize: import_v43.default.number().describe("Maximum file size in bytes").default(DEFAULT_MAX_FILE_SIZE_BYTES),
1787
1799
  acceptedFileTypes: MimeType.array().optional().describe("List of allowed file formats for the signature")
@@ -1797,14 +1809,14 @@ var EmailField = BaseField.extend({
1797
1809
  configuration: import_v43.default.object({
1798
1810
  maxLength: import_v43.default.number().optional().describe("Maximum length of the text")
1799
1811
  }).default({ maxLength: 255 }).optional(),
1800
- defaultValue: NonEmptyTextValue.optional()
1812
+ defaultValue: NonEmptyTextValue.or(ComputedDefaultValue).optional()
1801
1813
  }).meta({
1802
1814
  description: "An email input field",
1803
1815
  id: "EmailField"
1804
1816
  });
1805
1817
  var DateField = BaseField.extend({
1806
1818
  type: import_v43.default.literal(FieldType.DATE),
1807
- defaultValue: SerializedNowDateTime.or(PlainDate).optional().describe("Default date value(yyyy-MM-dd)"),
1819
+ defaultValue: SerializedNowDateTime.or(PlainDate).or(ComputedDefaultValue).optional().describe("Default date value(yyyy-MM-dd)"),
1808
1820
  configuration: import_v43.default.object({
1809
1821
  notice: TranslationConfig.describe(
1810
1822
  "Text to display above the date input"
@@ -1816,7 +1828,7 @@ var DateField = BaseField.extend({
1816
1828
  });
1817
1829
  var AgeField = BaseField.extend({
1818
1830
  type: import_v43.default.literal(FieldType.AGE),
1819
- defaultValue: NumberFieldValue.optional(),
1831
+ defaultValue: NumberFieldValue.or(ComputedDefaultValue).optional(),
1820
1832
  configuration: import_v43.default.object({
1821
1833
  asOfDate: FieldReference,
1822
1834
  prefix: TranslationConfig.optional(),
@@ -1828,7 +1840,7 @@ var AgeField = BaseField.extend({
1828
1840
  });
1829
1841
  var TimeField = BaseField.extend({
1830
1842
  type: import_v43.default.literal(FieldType.TIME),
1831
- defaultValue: SerializedNowDateTime.or(TimeValue).optional().describe("Default time value (HH-mm)"),
1843
+ defaultValue: SerializedNowDateTime.or(TimeValue).or(ComputedDefaultValue).optional().describe("Default time value (HH-mm)"),
1832
1844
  configuration: import_v43.default.object({
1833
1845
  use12HourFormat: import_v43.default.boolean().optional().describe("Whether to use 12-hour format"),
1834
1846
  notice: TranslationConfig.describe(
@@ -1841,7 +1853,7 @@ var TimeField = BaseField.extend({
1841
1853
  });
1842
1854
  var DateRangeField = BaseField.extend({
1843
1855
  type: import_v43.default.literal(FieldType.DATE_RANGE),
1844
- defaultValue: DateRangeFieldValue.optional(),
1856
+ defaultValue: DateRangeFieldValue.or(ComputedDefaultValue).optional(),
1845
1857
  configuration: import_v43.default.object({
1846
1858
  notice: TranslationConfig.describe(
1847
1859
  "Text to display above the date input"
@@ -1920,7 +1932,7 @@ var PageHeader = BaseField.extend({
1920
1932
  });
1921
1933
  var File = BaseField.extend({
1922
1934
  type: import_v43.default.literal(FieldType.FILE),
1923
- defaultValue: FileFieldValue.optional(),
1935
+ defaultValue: FileFieldValue.or(ComputedDefaultValue).optional(),
1924
1936
  configuration: import_v43.default.object({
1925
1937
  maxFileSize: import_v43.default.number().describe("Maximum file size in bytes").default(DEFAULT_MAX_FILE_SIZE_BYTES),
1926
1938
  acceptedFileTypes: MimeType.array().optional().describe("List of allowed file formats for the signature"),
@@ -1947,7 +1959,7 @@ var SelectOption = import_v43.default.object({
1947
1959
  });
1948
1960
  var NumberWithUnitField = BaseField.extend({
1949
1961
  type: import_v43.default.literal(FieldType.NUMBER_WITH_UNIT),
1950
- defaultValue: NumberWithUnitFieldValue.optional(),
1962
+ defaultValue: NumberWithUnitFieldValue.or(ComputedDefaultValue).optional(),
1951
1963
  options: import_v43.default.array(SelectOption).describe("A list of options for the unit select"),
1952
1964
  configuration: import_v43.default.object({
1953
1965
  min: import_v43.default.number().optional().describe("Minimum value of the number field"),
@@ -1962,7 +1974,7 @@ var NumberWithUnitField = BaseField.extend({
1962
1974
  });
1963
1975
  var RadioGroup = BaseField.extend({
1964
1976
  type: import_v43.default.literal(FieldType.RADIO_GROUP),
1965
- defaultValue: TextValue.optional(),
1977
+ defaultValue: TextValue.or(ComputedDefaultValue).optional(),
1966
1978
  options: import_v43.default.array(SelectOption).describe("A list of options"),
1967
1979
  configuration: import_v43.default.object({
1968
1980
  styles: import_v43.default.object({
@@ -1987,7 +1999,7 @@ var BulletList = BaseField.extend({
1987
1999
  });
1988
2000
  var Select = BaseField.extend({
1989
2001
  type: import_v43.default.literal(FieldType.SELECT),
1990
- defaultValue: TextValue.optional(),
2002
+ defaultValue: TextValue.or(ComputedDefaultValue).optional(),
1991
2003
  options: import_v43.default.array(SelectOption).describe("A list of options"),
1992
2004
  noOptionsMessage: TranslationConfig.optional().describe(
1993
2005
  `
@@ -2009,7 +2021,7 @@ var SelectDateRangeOption = import_v43.default.object({
2009
2021
  });
2010
2022
  var SelectDateRangeField = BaseField.extend({
2011
2023
  type: import_v43.default.literal(FieldType.SELECT_DATE_RANGE),
2012
- defaultValue: SelectDateRangeValue.optional(),
2024
+ defaultValue: SelectDateRangeValue.or(ComputedDefaultValue).optional(),
2013
2025
  options: import_v43.default.array(SelectDateRangeOption).describe("A list of options")
2014
2026
  }).meta({
2015
2027
  description: "A date range selection field",
@@ -2026,7 +2038,7 @@ var NameField = BaseField.extend({
2026
2038
  firstname: SerializedUserField.or(NonEmptyTextValue).optional(),
2027
2039
  middlename: SerializedUserField.or(NonEmptyTextValue).optional(),
2028
2040
  surname: SerializedUserField.or(NonEmptyTextValue).optional()
2029
- }).optional(),
2041
+ }).or(ComputedDefaultValue).optional(),
2030
2042
  configuration: import_v43.default.object({
2031
2043
  name: NameConfig.default({
2032
2044
  firstname: { required: true },
@@ -2050,14 +2062,14 @@ var NameField = BaseField.extend({
2050
2062
  id: "NameField"
2051
2063
  });
2052
2064
  var PhoneField = BaseField.extend({
2053
- defaultValue: NonEmptyTextValue.optional(),
2065
+ defaultValue: NonEmptyTextValue.or(ComputedDefaultValue).optional(),
2054
2066
  type: import_v43.default.literal(FieldType.PHONE)
2055
2067
  }).meta({
2056
2068
  description: "A field for entering a phone number",
2057
2069
  id: "PhoneField"
2058
2070
  });
2059
2071
  var IdField = BaseField.extend({
2060
- defaultValue: NonEmptyTextValue.optional(),
2072
+ defaultValue: NonEmptyTextValue.or(ComputedDefaultValue).optional(),
2061
2073
  type: import_v43.default.literal(FieldType.ID)
2062
2074
  }).meta({
2063
2075
  description: "A field for entering an ID",
@@ -2065,14 +2077,14 @@ var IdField = BaseField.extend({
2065
2077
  });
2066
2078
  var Checkbox = BaseField.extend({
2067
2079
  type: import_v43.default.literal(FieldType.CHECKBOX),
2068
- defaultValue: CheckboxFieldValue.default(false)
2080
+ defaultValue: CheckboxFieldValue.or(ComputedDefaultValue).default(false)
2069
2081
  }).meta({
2070
2082
  description: "A boolean checkbox field",
2071
2083
  id: "Checkbox"
2072
2084
  });
2073
2085
  var Country = BaseField.extend({
2074
2086
  type: import_v43.default.literal(FieldType.COUNTRY),
2075
- defaultValue: NonEmptyTextValue.optional(),
2087
+ defaultValue: NonEmptyTextValue.or(ComputedDefaultValue).optional(),
2076
2088
  optionOverrides: import_v43.default.array(SelectOption.omit({ label: true })).optional().describe(
2077
2089
  "Conditionals for specific countries. Countries not listed are always shown and enabled."
2078
2090
  )
@@ -2090,7 +2102,7 @@ var AdministrativeAreas = import_v43.default.enum([
2090
2102
  ]);
2091
2103
  var AdministrativeAreaField = BaseField.extend({
2092
2104
  type: import_v43.default.literal(FieldType.ADMINISTRATIVE_AREA),
2093
- defaultValue: import_v43.default.union([NonEmptyTextValue, SerializedUserField]).optional(),
2105
+ defaultValue: import_v43.default.union([NonEmptyTextValue, SerializedUserField, ComputedDefaultValue]).optional(),
2094
2106
  configuration: import_v43.default.object({
2095
2107
  partOf: FieldReference.optional().describe("Parent location"),
2096
2108
  type: AdministrativeAreas,
@@ -2102,7 +2114,7 @@ var AdministrativeAreaField = BaseField.extend({
2102
2114
  });
2103
2115
  var LocationInput = BaseField.extend({
2104
2116
  type: import_v43.default.literal(FieldType.LOCATION),
2105
- defaultValue: import_v43.default.union([NonEmptyTextValue, SerializedUserField]).optional(),
2117
+ defaultValue: import_v43.default.union([NonEmptyTextValue, SerializedUserField, ComputedDefaultValue]).optional(),
2106
2118
  configuration: import_v43.default.object({
2107
2119
  locationTypes: import_v43.default.array(import_v43.default.string()).optional().describe("Types of the locations that are available for selection."),
2108
2120
  allowedLocations: AllowedLocations
@@ -2114,7 +2126,7 @@ var LocationInput = BaseField.extend({
2114
2126
  var FileUploadWithOptions = BaseField.extend({
2115
2127
  type: import_v43.default.literal(FieldType.FILE_WITH_OPTIONS),
2116
2128
  options: import_v43.default.array(SelectOption).describe("A list of options"),
2117
- defaultValue: FileFieldWithOptionValue.optional(),
2129
+ defaultValue: FileFieldWithOptionValue.or(ComputedDefaultValue).optional(),
2118
2130
  configuration: import_v43.default.object({
2119
2131
  maxFileSize: import_v43.default.number().describe("Maximum file size in bytes").default(DEFAULT_MAX_FILE_SIZE_BYTES),
2120
2132
  maxImageSize: import_v43.default.object({
@@ -2130,12 +2142,12 @@ var FileUploadWithOptions = BaseField.extend({
2130
2142
  });
2131
2143
  var Facility = BaseField.extend({
2132
2144
  type: import_v43.default.literal(FieldType.FACILITY),
2133
- defaultValue: NonEmptyTextValue.optional(),
2145
+ defaultValue: NonEmptyTextValue.or(ComputedDefaultValue).optional(),
2134
2146
  configuration: import_v43.default.object({ allowedLocations: AllowedLocations }).optional()
2135
2147
  }).describe("Input field for a facility");
2136
2148
  var Office = BaseField.extend({
2137
2149
  type: import_v43.default.literal(FieldType.OFFICE),
2138
- defaultValue: NonEmptyTextValue.optional(),
2150
+ defaultValue: NonEmptyTextValue.or(ComputedDefaultValue).optional(),
2139
2151
  configuration: import_v43.default.object({ allowedLocations: AllowedLocations }).optional()
2140
2152
  }).describe("Input field for an office");
2141
2153
  var DefaultAddressFieldValue = DomesticAddressFieldValue.extend({
@@ -2171,7 +2183,7 @@ var Address = BaseField.extend({
2171
2183
  ).optional(),
2172
2184
  allowedLocations: AllowedLocations
2173
2185
  }).optional(),
2174
- defaultValue: DefaultAddressFieldValue.optional()
2186
+ defaultValue: DefaultAddressFieldValue.or(ComputedDefaultValue).optional()
2175
2187
  }).meta({
2176
2188
  description: "Address input field \u2013 a combination of location and text fields",
2177
2189
  id: "Address"
@@ -2218,7 +2230,7 @@ var ButtonConfiguration = import_v43.default.object({
2218
2230
  });
2219
2231
  var ButtonField = BaseField.extend({
2220
2232
  type: import_v43.default.literal(FieldType.BUTTON),
2221
- defaultValue: ButtonFieldValue.optional(),
2233
+ defaultValue: ButtonFieldValue.or(ComputedDefaultValue).optional(),
2222
2234
  configuration: ButtonConfiguration
2223
2235
  }).meta({
2224
2236
  description: "A generic button that can be used to trigger an action",
@@ -2246,7 +2258,7 @@ var AlphaPrintButton = BaseField.extend({
2246
2258
  });
2247
2259
  var HttpField = BaseField.extend({
2248
2260
  type: import_v43.default.literal(FieldType.HTTP),
2249
- defaultValue: HttpFieldValue.optional(),
2261
+ defaultValue: HttpFieldValue.or(ComputedDefaultValue).optional(),
2250
2262
  configuration: import_v43.default.object({
2251
2263
  trigger: FieldReference.optional().describe(
2252
2264
  "Reference to the field that triggers the HTTP request when its value changes. If not provided, the HTTP request is triggered once on component mount."
@@ -2332,7 +2344,7 @@ var LinkButtonField = BaseField.extend({
2332
2344
  });
2333
2345
  var VerificationStatus = BaseField.extend({
2334
2346
  type: import_v43.default.literal(FieldType.VERIFICATION_STATUS),
2335
- defaultValue: VerificationStatusValue.optional(),
2347
+ defaultValue: VerificationStatusValue.or(ComputedDefaultValue).optional(),
2336
2348
  configuration: import_v43.default.object({
2337
2349
  status: TranslationConfig.describe("Text to display on the status pill."),
2338
2350
  description: TranslationConfig.describe(
@@ -2354,7 +2366,7 @@ var QueryParamReaderField = BaseField.extend({
2354
2366
  });
2355
2367
  var QrReaderField = BaseField.extend({
2356
2368
  type: import_v43.default.literal(FieldType.QR_READER),
2357
- defaultValue: QrReaderFieldValue.optional(),
2369
+ defaultValue: QrReaderFieldValue.or(ComputedDefaultValue).optional(),
2358
2370
  configuration: import_v43.default.object({
2359
2371
  validator: import_v43.default.any().meta({
2360
2372
  description: "JSON Schema to validate the scanned QR code data against before populating the form fields.",
@@ -2367,7 +2379,7 @@ var QrReaderField = BaseField.extend({
2367
2379
  });
2368
2380
  var IdReaderField = BaseField.extend({
2369
2381
  type: import_v43.default.literal(FieldType.ID_READER),
2370
- defaultValue: IdReaderFieldValue.optional(),
2382
+ defaultValue: IdReaderFieldValue.or(ComputedDefaultValue).optional(),
2371
2383
  methods: import_v43.default.array(
2372
2384
  import_v43.default.union([QrReaderField, LinkButtonField]).describe("Methods for reading an ID")
2373
2385
  )
@@ -3032,7 +3044,37 @@ function resolveDataPath(rootData, dataPath, instancePath) {
3032
3044
  }
3033
3045
  return current;
3034
3046
  }
3047
+ function todayISO() {
3048
+ return (0, import_date_fns.formatISO)(/* @__PURE__ */ new Date(), { representation: "date" });
3049
+ }
3050
+ var compiledFunctionCache = /* @__PURE__ */ new Map();
3051
+ function compileClientFunction(code) {
3052
+ let fn = compiledFunctionCache.get(code);
3053
+ if (!fn) {
3054
+ fn = new Function(`return (${code})`)();
3055
+ compiledFunctionCache.set(code, fn);
3056
+ }
3057
+ return fn;
3058
+ }
3035
3059
  (0, import_ajv_formats.default)(ajv);
3060
+ function buildClientFunctionContext(input) {
3061
+ return {
3062
+ $form: input.form,
3063
+ $now: todayISO(),
3064
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
3065
+ $online: isOnline(),
3066
+ $user: input.validatorContext?.user,
3067
+ $event: input.validatorContext?.event,
3068
+ $leafAdminStructureLocationIds: input.validatorContext?.leafAdminStructureLocationIds ?? [],
3069
+ user: input.systemVariables?.user,
3070
+ $window: input.systemVariables?.$window,
3071
+ locations: input.locations,
3072
+ adminLevelIds: input.adminLevelIds
3073
+ };
3074
+ }
3075
+ function runClientFunction(code, data, context) {
3076
+ return compileClientFunction(code)(data, context);
3077
+ }
3036
3078
  ajv.addKeyword({
3037
3079
  keyword: "daysFromDate",
3038
3080
  type: "string",
@@ -3077,12 +3119,35 @@ ajv.addKeyword({
3077
3119
  $data: true,
3078
3120
  errors: true,
3079
3121
  // @ts-ignore -- Force type. We will move this away from AJV next. Parsing the array will take seconds and is only called by core.
3080
- validate(schema, data, _2, dataContext) {
3122
+ validate(_schema, data, _2, dataContext) {
3081
3123
  const locationIdInput = data;
3082
3124
  const locations = dataContext?.rootData.$leafAdminStructureLocationIds ?? [];
3083
3125
  return locations.some((location) => location.id === locationIdInput);
3084
3126
  }
3085
3127
  });
3128
+ ajv.addKeyword({
3129
+ keyword: "customClientValidator",
3130
+ schemaType: "object",
3131
+ errors: true,
3132
+ // @ts-expect-error -- AJV's public types don't expose `rootData`. All
3133
+ // `validate()` callers build root data via `buildClientFunctionContext`,
3134
+ // so the cast holds.
3135
+ validate(schema, data, _2, dataContext) {
3136
+ return Boolean(
3137
+ runClientFunction(
3138
+ schema.code,
3139
+ data,
3140
+ dataContext?.rootData ?? buildClientFunctionContext({ form: {} })
3141
+ )
3142
+ );
3143
+ }
3144
+ });
3145
+ function isOnline() {
3146
+ if (typeof window !== "undefined" && typeof navigator !== "undefined") {
3147
+ return navigator.onLine;
3148
+ }
3149
+ return true;
3150
+ }
3086
3151
 
3087
3152
  // ../commons/src/utils.ts
3088
3153
  var z32 = __toESM(require("zod/v4"));
@@ -3701,7 +3766,54 @@ function createFieldConditionals(fieldId) {
3701
3766
  }
3702
3767
  },
3703
3768
  required: [fieldId]
3704
- })
3769
+ }),
3770
+ /**
3771
+ * Custom client-side validator. The provided function is serialised and executed
3772
+ * just-in-time on the client only. External references (e.g. lodash) are not
3773
+ * available inside the function body — all logic must be self-contained.
3774
+ *
3775
+ * @example
3776
+ * field('nid').customClientValidator((value) => {
3777
+ * // LUHN check — all logic must be inline
3778
+ * const digits = String(value).split('').map(Number)
3779
+ * // ...
3780
+ * return isValid
3781
+ * })
3782
+ */
3783
+ customClientValidator(validationFn) {
3784
+ const code = validationFn.toString();
3785
+ return defineFormConditional({
3786
+ type: "object",
3787
+ properties: wrapToPath(
3788
+ { [fieldId]: { customClientValidator: { code } } },
3789
+ this.$$subfield
3790
+ ),
3791
+ required: [fieldId]
3792
+ });
3793
+ },
3794
+ /**
3795
+ * Custom client-side evaluation. Returns a {@link FieldReference} descriptor
3796
+ * that can be used as the `value` property or a DATA component entry.
3797
+ * The function receives the referenced field's value as the first argument and
3798
+ * the full form context as the second; its return value replaces the field reference.
3799
+ * The function is serialised and executed just-in-time on the client only.
3800
+ * External references (e.g. lodash) are not available inside the function body.
3801
+ *
3802
+ * For computing a default value without referencing a specific field, use
3803
+ * `evaluate(fn)` in the `defaultValue` property instead.
3804
+ *
3805
+ * @example
3806
+ * field('a').customClientEvaluation((aValue, ctx) =>
3807
+ * Number(aValue) + Number(ctx.$form.b)
3808
+ * )
3809
+ */
3810
+ customClientEvaluation(computationFn) {
3811
+ return {
3812
+ $$code: computationFn.toString(),
3813
+ $$field: fieldId,
3814
+ $$subfield: this.$$subfield
3815
+ };
3816
+ }
3705
3817
  };
3706
3818
  }
3707
3819
 
@@ -4469,7 +4581,8 @@ var RejectCorrectionActionInput = BaseActionInput.extend(
4469
4581
  var ApproveCorrectionActionInput = BaseActionInput.extend(
4470
4582
  z38.object({
4471
4583
  requestId: z38.string(),
4472
- type: z38.literal(ActionType.APPROVE_CORRECTION).default(ActionType.APPROVE_CORRECTION)
4584
+ type: z38.literal(ActionType.APPROVE_CORRECTION).default(ActionType.APPROVE_CORRECTION),
4585
+ content: z38.object({ immediateCorrection: z38.boolean().optional() }).optional()
4473
4586
  }).shape
4474
4587
  );
4475
4588
  var ReadActionInput = BaseActionInput.extend(
@@ -354,14 +354,20 @@ var encodeScope = (scope) => {
354
354
  encode: false
355
355
  });
356
356
  };
357
+ var decodedScopeCache = /* @__PURE__ */ new Map();
357
358
  var decodeScope = (query) => {
359
+ if (decodedScopeCache.has(query)) {
360
+ return decodedScopeCache.get(query);
361
+ }
358
362
  const scope = qs.parse(query, {
359
363
  ignoreQueryPrefix: true,
360
364
  comma: true,
361
365
  allowDots: true
362
366
  });
363
367
  const unflattenedScope = unflattenScope(scope);
364
- return Scope2.safeParse(unflattenedScope)?.data;
368
+ const result = Scope2.safeParse(unflattenedScope)?.data;
369
+ decodedScopeCache.set(query, result);
370
+ return result;
365
371
  };
366
372
  var DEFAULT_SCOPE_OPTIONS = {
367
373
  placeOfEvent: JurisdictionFilter.enum.all,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencrvs/toolkit",
3
- "version": "2.0.0-rc.fe35c20",
3
+ "version": "2.0.0-rc.fe577a4",
4
4
  "description": "OpenCRVS toolkit for building country configurations",
5
5
  "license": "MPL-2.0",
6
6
  "bin": {