@incodetech/core 0.0.0-dev-20260309-4e885f1 → 0.0.0-dev-20260309-dec7ce7

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.
@@ -62,7 +62,7 @@ declare const documentUploadMachine: xstate0.StateMachine<DocumentUploadContext,
62
62
  type: "stopStream";
63
63
  params: xstate0.NonReducibleUnknown;
64
64
  };
65
- }>, never, never, "error" | "initCamera" | "idle" | "capturing" | "closed" | "uploading" | "finished", string, DocumentUploadInput, xstate0.NonReducibleUnknown, xstate0.EventObject, xstate0.MetaObject, {
65
+ }>, never, never, "initCamera" | "error" | "idle" | "capturing" | "closed" | "uploading" | "finished", string, DocumentUploadInput, xstate0.NonReducibleUnknown, xstate0.EventObject, xstate0.MetaObject, {
66
66
  readonly id: "documentUpload";
67
67
  readonly initial: "idle";
68
68
  readonly context: ({
package/dist/ekyc.d.ts CHANGED
@@ -93,6 +93,7 @@ type EkycFormState = {
93
93
  values: EkycFormValues;
94
94
  errors: Record<string, string>;
95
95
  displayErrors: Record<string, string>;
96
+ errorParams: Record<string, Record<string, string | number>>;
96
97
  isValid: boolean;
97
98
  addressSuggestions: AddressSuggestion[];
98
99
  prefilled: Record<string, boolean>;
package/dist/ekyc.esm.js CHANGED
@@ -9919,19 +9919,64 @@ const PO_BOX_REGEX = /POBox|P\.O\.Box|pobox|postofficebox|postbox|po\.box|p\.o\.
9919
9919
  function isPoBox(address) {
9920
9920
  return PO_BOX_REGEX.test(address.replace(/\s/g, ""));
9921
9921
  }
9922
- const POSTAL_CODE_MAX_LENGTH = {
9923
- US: 9,
9924
- CA: 6
9925
- };
9922
+ const US_POSTAL_CODE_REGEX = /^(?:\d{5}|\d{9}|\d{5}-\d{4})$/;
9923
+ const BR_POSTAL_CODE_REGEX = /^(?:\d{5}|\d{8}|\d{5}-\d{3})$/;
9924
+ const CA_POSTAL_CODE_REGEX = /^[A-Za-z]\d[A-Za-z] \d[A-Za-z]\d$/;
9925
+ const ES_POSTAL_CODE_REGEX = /^\d{5}$/;
9926
+ const UK_POSTAL_CODE_REGEX = /^(?:[A-Z]{2}\d|[A-Z]{2}\d{2}|[A-Z]\d|[A-Z]\d{2}|[A-Z]\d[A-Z]|[A-Z]{2}\d[A-Z]) \d[A-Z]{2}$/;
9926
9927
  const DEFAULT_POSTAL_CODE_MAX = 8;
9927
- /** Validates postal code by country (US accepts 5-digit and ZIP+4) */
9928
+ /**
9929
+ * Validates postal code by country:
9930
+ * - US: 5-digit, 9-digit, or ZIP+4
9931
+ * - BR: 5-digit, 8-digit, or 5+3 with dash
9932
+ * - CA: A1A 1A1
9933
+ * - ES: 5 digits
9934
+ * - GB/UK: A0 0AA, AA0 0AA, AA0A 0AA
9935
+ * - Others: 5 or 8 numeric digits
9936
+ */
9928
9937
  function validatePostalCode(postalCode, country) {
9929
9938
  if (!postalCode) return false;
9930
- if (country === "US") return /^\d{5}(-\d{4})?$/.test(postalCode);
9931
- if (country === "CA") return /^[A-Za-z]\d[A-Za-z]\s?\d[A-Za-z]\d$/.test(postalCode);
9932
- const stripped = postalCode.replace(/[\s-]/g, "");
9933
- const maxLen = POSTAL_CODE_MAX_LENGTH[country] ?? DEFAULT_POSTAL_CODE_MAX;
9934
- return stripped.length > 0 && stripped.length <= maxLen;
9939
+ if (country === "US") return US_POSTAL_CODE_REGEX.test(postalCode);
9940
+ if (country === "BR") return BR_POSTAL_CODE_REGEX.test(postalCode);
9941
+ if (country === "CA") return CA_POSTAL_CODE_REGEX.test(postalCode);
9942
+ if (country === "ES") return ES_POSTAL_CODE_REGEX.test(postalCode);
9943
+ if (country === "GB" || country === "UK") return UK_POSTAL_CODE_REGEX.test(postalCode.toUpperCase());
9944
+ const stripped = postalCode.replace(/\D/g, "");
9945
+ const maxLen = DEFAULT_POSTAL_CODE_MAX;
9946
+ return /^\d+$/.test(stripped) && [5, maxLen].includes(stripped.length);
9947
+ }
9948
+ /**
9949
+ * Normalizes ZIP input to digits and an optional dash after 5 digits.
9950
+ * Examples:
9951
+ * - 12345
9952
+ * - 123456789
9953
+ * - 12345-6789
9954
+ */
9955
+ function formatPostalCode(value, country) {
9956
+ if (country === "US" || country === "BR" || country === "ES") {
9957
+ let maxLength = getPostalCodeMaxLength(country);
9958
+ if (country !== "ES") maxLength = maxLength - 1;
9959
+ const hasDash = value.includes("-");
9960
+ const digits = value.replace(/\D/g, "").slice(0, maxLength);
9961
+ if (country === "ES") return digits;
9962
+ if (digits.length <= 5) {
9963
+ if (hasDash && digits.length === 5) return `${digits}-`;
9964
+ return digits;
9965
+ }
9966
+ if (hasDash) return `${digits.slice(0, 5)}-${digits.slice(5)}`;
9967
+ return digits;
9968
+ }
9969
+ if (country === "CA") {
9970
+ const normalized = value.replace(/[^A-Za-z0-9]/g, "").toUpperCase().slice(0, 6);
9971
+ if (normalized.length <= 3) return normalized;
9972
+ return `${normalized.slice(0, 3)} ${normalized.slice(3)}`;
9973
+ }
9974
+ if (country === "GB" || country === "UK") {
9975
+ const normalized = value.replace(/[^A-Za-z0-9]/g, "").toUpperCase().slice(0, 7);
9976
+ if (normalized.length <= 3) return normalized;
9977
+ return `${normalized.slice(0, -3)} ${normalized.slice(-3)}`;
9978
+ }
9979
+ return value.replace(/\D/g, "").slice(0, DEFAULT_POSTAL_CODE_MAX);
9935
9980
  }
9936
9981
  /** Validates a phone number using libphonenumber-js country rules */
9937
9982
  function validatePhone(phone, country) {
@@ -9982,6 +10027,25 @@ const STATE_CODE_HINTS = {
9982
10027
  AR: "B, C, K"
9983
10028
  };
9984
10029
  const REQUIRED_ERROR_KEYS = new Set(["verification.errors.required"]);
10030
+ const POSTAL_CODE_MAX_LENGTH_BY_COUNTRY = {
10031
+ US: 10,
10032
+ BR: 9,
10033
+ CA: 7,
10034
+ ES: 5,
10035
+ GB: 8,
10036
+ UK: 8
10037
+ };
10038
+ const DEFAULT_POSTAL_CODE_MAX_LENGTH = 8;
10039
+ function getPostalCodeMaxLength(country) {
10040
+ return POSTAL_CODE_MAX_LENGTH_BY_COUNTRY[country] ?? DEFAULT_POSTAL_CODE_MAX_LENGTH;
10041
+ }
10042
+ function getPostalCodeErrorKey(country) {
10043
+ if (country === "CA") return "verification.errors.CAPostalCodeInvalidFormat";
10044
+ if (country === "ES") return "verification.errors.ESPostalCodeInvalidFormat";
10045
+ if (country === "GB" || country === "UK") return "verification.errors.UKPostalCodeInvalidFormat";
10046
+ if (country === "US") return "verification.errors.zipCodeInvalidFormat";
10047
+ return "verification.errors.postalCodeInvalidFormat";
10048
+ }
9985
10049
  function buildFieldDefs(fields, configSource, country, enablePhoneRisk = false) {
9986
10050
  const defs = [];
9987
10051
  const add = (name, type, label, source, opts) => {
@@ -10068,7 +10132,10 @@ function buildFieldDefs(fields, configSource, country, enablePhoneRisk = false)
10068
10132
  placeholderParams: { states: STATE_CODE_HINTS[country] ?? "" }
10069
10133
  }
10070
10134
  });
10071
- add("postalCode", "text", country === "US" ? "verification.labels.zipCode" : "verification.labels.postalCode", fields.address, { section: "address" });
10135
+ add("postalCode", "text", country === "US" ? "verification.labels.zipCode" : "verification.labels.postalCode", fields.address, {
10136
+ section: "address",
10137
+ maxLength: getPostalCodeMaxLength(country)
10138
+ });
10072
10139
  }
10073
10140
  if (fields.dlNumber) add("dlNumber", "text", "verification.labels.dlNumber", fields.dlNumber, { section: "driverLicense" });
10074
10141
  if (fields.dlState) add("dlState", "dropdown", "verification.labels.dlState", fields.dlState, {
@@ -10086,7 +10153,7 @@ function validateField(name, value, required, country, configSource) {
10086
10153
  if (name === "taxId" && country === "BR") return validateCPF(val) ? void 0 : "verification.errors.invalidTaxId";
10087
10154
  if (name === "ssn" && (country === "US" || country === "CA")) return /^\d{3}-\d{2}-\d{4}$/.test(val) ? void 0 : "verification.errors.invalidSsn";
10088
10155
  if (name === "street" && isPoBox(val)) return "verification.errors.poBox";
10089
- if (name === "postalCode") return validatePostalCode(val, country) ? void 0 : "verification.errors.invalidPostalCode";
10156
+ if (name === "postalCode") return validatePostalCode(val, country) ? void 0 : getPostalCodeErrorKey(country);
10090
10157
  if (name === "phone") {
10091
10158
  if (!isFieldOptional("phone", configSource) || val) return validatePhone(val, country) ? void 0 : "verification.errors.invalidPhone";
10092
10159
  }
@@ -10103,14 +10170,30 @@ function computeValidation(fields, values, country, configSource) {
10103
10170
  isValid: Object.keys(errors).length === 0
10104
10171
  };
10105
10172
  }
10106
- function computeDisplayErrors(errors, touched, submitAttempted) {
10173
+ const POSTAL_CODE_FORMAT_ERROR_KEYS = new Set([
10174
+ "verification.errors.zipCodeInvalidFormat",
10175
+ "verification.errors.postalCodeInvalidFormat",
10176
+ "verification.errors.CAPostalCodeInvalidFormat",
10177
+ "verification.errors.ESPostalCodeInvalidFormat",
10178
+ "verification.errors.UKPostalCodeInvalidFormat"
10179
+ ]);
10180
+ function computeDisplayErrors(errors, touched, submitAttempted, country) {
10107
10181
  const display = {};
10182
+ const errorParams = {};
10108
10183
  for (const [field, err] of Object.entries(errors)) {
10109
10184
  const isRequiredErr = REQUIRED_ERROR_KEYS.has(err);
10110
10185
  if (isRequiredErr && submitAttempted) display[field] = err;
10111
10186
  else if (!isRequiredErr && touched[field]) display[field] = err;
10187
+ if (display[field] && field === "postalCode" && POSTAL_CODE_FORMAT_ERROR_KEYS.has(err)) {
10188
+ let length = getPostalCodeMaxLength(country);
10189
+ if (country === "US" || country === "BR") length = length - 1;
10190
+ errorParams[field] = { maxLength: length };
10191
+ }
10112
10192
  }
10113
- return display;
10193
+ return {
10194
+ displayErrors: display,
10195
+ errorParams
10196
+ };
10114
10197
  }
10115
10198
  function applyPrefill(fields, ocrData, otpStatus, _country) {
10116
10199
  const values = {};
@@ -10214,6 +10297,7 @@ function buildSubmitPayload(ctx) {
10214
10297
  function processFieldValue(name, value, fieldsCountry) {
10215
10298
  if ((name === "firstName" || name === "surName" || name === "middleName" || name === "maternalSurname") && value) return cleanName(value);
10216
10299
  if (name === "ssn" && (fieldsCountry === "US" || fieldsCountry === "CA")) return formatSSN(value);
10300
+ if (name === "postalCode") return formatPostalCode(value, fieldsCountry);
10217
10301
  return value;
10218
10302
  }
10219
10303
  function fillFromSuggestion(values, touched, suggestion) {
@@ -10369,7 +10453,8 @@ const ekycMachine = setup({
10369
10453
  fields: updatedFields,
10370
10454
  errors,
10371
10455
  isValid,
10372
- displayErrors: {}
10456
+ displayErrors: {},
10457
+ errorParams: {}
10373
10458
  };
10374
10459
  }),
10375
10460
  updateField: assign(({ context, event }) => {
@@ -10384,34 +10469,42 @@ const ekycMachine = setup({
10384
10469
  [name]: true
10385
10470
  };
10386
10471
  const { errors, isValid } = computeValidation(context.fields, newValues, context.fieldsCountry, context.configSource);
10472
+ const { displayErrors, errorParams } = computeDisplayErrors(errors, newTouched, context.submitAttempted, context.fieldsCountry);
10387
10473
  return {
10388
10474
  values: newValues,
10389
10475
  errors,
10390
10476
  isValid,
10391
10477
  touched: newTouched,
10392
- displayErrors: computeDisplayErrors(errors, newTouched, context.submitAttempted)
10478
+ displayErrors,
10479
+ errorParams
10393
10480
  };
10394
10481
  }),
10395
10482
  fillAddressFields: assign(({ context, event }) => {
10396
10483
  const { suggestion } = event;
10397
10484
  const { values: newValues, touched: newTouched } = fillFromSuggestion(context.values, context.touched, suggestion);
10398
10485
  const { errors, isValid } = computeValidation(context.fields, newValues, context.fieldsCountry, context.configSource);
10486
+ const { displayErrors, errorParams } = computeDisplayErrors(errors, newTouched, context.submitAttempted, context.fieldsCountry);
10399
10487
  return {
10400
10488
  values: newValues,
10401
10489
  addressSuggestions: [],
10402
10490
  errors,
10403
10491
  isValid,
10404
10492
  touched: newTouched,
10405
- displayErrors: computeDisplayErrors(errors, newTouched, context.submitAttempted)
10493
+ displayErrors,
10494
+ errorParams
10406
10495
  };
10407
10496
  }),
10408
10497
  setAddressSuggestions: assign(({ event }) => ({ addressSuggestions: event.suggestions })),
10409
10498
  setErrorMessage: assign(({ event }) => ({ errorMessage: String(event.error ?? "verification.error") })),
10410
10499
  clearErrorMessage: assign({ errorMessage: () => "" }),
10411
- markSubmitAttempted: assign(({ context }) => ({
10412
- submitAttempted: true,
10413
- displayErrors: computeDisplayErrors(context.errors, context.touched, true)
10414
- })),
10500
+ markSubmitAttempted: assign(({ context }) => {
10501
+ const { displayErrors, errorParams } = computeDisplayErrors(context.errors, context.touched, true, context.fieldsCountry);
10502
+ return {
10503
+ submitAttempted: true,
10504
+ displayErrors,
10505
+ errorParams
10506
+ };
10507
+ }),
10415
10508
  trackModuleOpen: () => {
10416
10509
  addEvent({
10417
10510
  code: "open",
@@ -10465,6 +10558,7 @@ const ekycMachine = setup({
10465
10558
  values: {},
10466
10559
  errors: {},
10467
10560
  displayErrors: {},
10561
+ errorParams: {},
10468
10562
  isValid: false,
10469
10563
  addressSuggestions: [],
10470
10564
  prefilled: {},
@@ -10619,6 +10713,7 @@ function mapState(snapshot) {
10619
10713
  values: context.values,
10620
10714
  errors: context.errors,
10621
10715
  displayErrors: context.displayErrors,
10716
+ errorParams: context.errorParams,
10622
10717
  isValid: context.isValid,
10623
10718
  addressSuggestions: context.addressSuggestions,
10624
10719
  prefilled: context.prefilled,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@incodetech/core",
3
- "version": "0.0.0-dev-20260309-4e885f1",
3
+ "version": "0.0.0-dev-20260309-dec7ce7",
4
4
  "type": "module",
5
5
  "main": "./dist/index.esm.js",
6
6
  "types": "./dist/index.d.ts",