@lucaapp/service-utils 5.24.5 → 5.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -76,6 +76,7 @@ class Api {
76
76
  },
77
77
  });
78
78
  (0, openapi_1.forcePathParamsRequired)(spec);
79
+ (0, openapi_1.hoistCombinatorRequiredFields)(spec);
79
80
  return spec;
80
81
  }
81
82
  mountSwaggerMiddlewares() {
@@ -4,3 +4,4 @@ export * from './endpoint';
4
4
  export * from './middleware';
5
5
  export * from './response';
6
6
  export * from './send';
7
+ export { hoistCombinatorRequiredFields } from './openapi';
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.extendZodWithOpenApi = void 0;
17
+ exports.hoistCombinatorRequiredFields = exports.extendZodWithOpenApi = void 0;
18
18
  var zod_to_openapi_1 = require("@asteasolutions/zod-to-openapi");
19
19
  Object.defineProperty(exports, "extendZodWithOpenApi", { enumerable: true, get: function () { return zod_to_openapi_1.extendZodWithOpenApi; } });
20
20
  __exportStar(require("./api"), exports);
@@ -22,3 +22,9 @@ __exportStar(require("./endpoint"), exports);
22
22
  __exportStar(require("./middleware"), exports);
23
23
  __exportStar(require("./response"), exports);
24
24
  __exportStar(require("./send"), exports);
25
+ // Normalizes an OpenAPI spec's anyOf/allOf combinators the same way
26
+ // generateOpenAPISpec() does, so a frozen/historical spec (a POS compat
27
+ // baseline that predates this generator's fix) can be diffed against a
28
+ // current spec on equal terms. See openapi.ts for what "normalize" means.
29
+ var openapi_1 = require("./openapi");
30
+ Object.defineProperty(exports, "hoistCombinatorRequiredFields", { enumerable: true, get: function () { return openapi_1.hoistCombinatorRequiredFields; } });
@@ -7,5 +7,23 @@ export declare const forcePathParamsRequired: (spec: {
7
7
  parameters?: Record<string, unknown>;
8
8
  };
9
9
  }) => void;
10
+ /**
11
+ * zod-to-openapi represents a same-status multi-error response as `anyOf`, and
12
+ * `.extend()` on a registered/ref'd schema as `allOf: [$ref, inline]`. Either
13
+ * shape hides required/property information that used to be flat at the parent
14
+ * level inside the combinator, which oasdiff's breaking-change diff cannot see
15
+ * through — it reports every such change as fields being removed, even when the
16
+ * change was purely additive. This walks the finished spec and restores the
17
+ * safely-hoistable subset of that information to the parent schema (see
18
+ * hoistAnyOf / hoistAllOf for what "safe" means for each combinator — they are
19
+ * NOT the same rule). Ref topology is untouched: refs are only read to compute
20
+ * what's safe to hoist, never rewritten.
21
+ */
22
+ export declare const hoistCombinatorRequiredFields: (spec: {
23
+ paths?: Record<string, unknown>;
24
+ components?: {
25
+ schemas?: Record<string, unknown>;
26
+ };
27
+ }) => void;
10
28
  export declare const registerEndpoint: (registry: OpenAPIRegistry, method: HttpMethod, prefixPath: string, path: string, summary: string, options: EndpointOptions<any, any, any, any, any, any>, tags: string[], debug: boolean) => void;
11
29
  export {};
@@ -1,15 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerEndpoint = exports.forcePathParamsRequired = void 0;
3
+ exports.registerEndpoint = exports.hoistCombinatorRequiredFields = exports.forcePathParamsRequired = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const http_1 = require("./types/http");
6
6
  const mapExpressParamsToOpenAPIParams = (path) => path.replaceAll(/\:([^\:\/]+)/g, '{$1}');
7
7
  const buildResponseConfig = (responseOptions) => {
8
8
  const responses = {};
9
9
  responseOptions.forEach(response => {
10
- // raw responses end the request outside the schema layer and carry the
11
- // placeholder status 0, which is not a valid OpenAPI response key
12
- if (response.isRawResponseEnd) {
10
+ // Raw responses that declare no real schema (rawResponseEnd()'s default,
11
+ // schema: z.void()) end the request outside the schema layer and carry
12
+ // the placeholder status 0, which is not a valid OpenAPI response key —
13
+ // skip them; the fallback below documents a generic 200 instead. A route
14
+ // that sets isRawResponseEnd but ALSO declares a real schema (bypassing
15
+ // the response() helper for streaming/binary output while still telling
16
+ // callers what the body looks like) must still be documented normally —
17
+ // skipping unconditionally silently drops real, intentional schema
18
+ // information (LUCA-35888).
19
+ if (response.isRawResponseEnd && response.schema instanceof zod_1.z.ZodVoid) {
13
20
  return;
14
21
  }
15
22
  const isAlreadyPresent = !!responses[String(response.status)];
@@ -81,6 +88,128 @@ const forcePathParamsRequired = (spec) => {
81
88
  });
82
89
  };
83
90
  exports.forcePathParamsRequired = forcePathParamsRequired;
91
+ const resolveSchemaRef = (node, schemas) => {
92
+ if (node && typeof node === 'object' && typeof node.$ref === 'string') {
93
+ return schemas[node.$ref.split('/').pop()];
94
+ }
95
+ return node;
96
+ };
97
+ // Two same-named properties across anyOf branches are only safe to assert at the
98
+ // parent level if they agree on primitive type — an enum/const literal that differs
99
+ // per branch (e.g. a discriminating `error` code) must NOT leak into the hoisted
100
+ // type, or the parent schema would wrongly forbid the other branches' literals.
101
+ const widenAnyOfProperty = (fragments) => {
102
+ if (fragments.some(f => f === undefined || typeof f !== 'object')) {
103
+ return undefined;
104
+ }
105
+ const types = new Set(fragments.map(f => f.type));
106
+ if (types.size !== 1 || fragments[0].type === undefined) {
107
+ return undefined;
108
+ }
109
+ const widened = { type: fragments[0].type };
110
+ if (fragments.some(f => f.nullable)) {
111
+ widened.nullable = true;
112
+ }
113
+ return widened;
114
+ };
115
+ // anyOf is OR — an object only ever matches ONE branch, so a field is safe to
116
+ // assert at the parent only if every branch requires it, and its type is widened
117
+ // to what every branch agrees on (see widenAnyOfProperty). A field required in
118
+ // some but not all branches must stay inside the combinator: hoisting it would
119
+ // assert something false for the branches that lack it, and would wrongly
120
+ // suppress a real breaking-change finding on that branch in the future.
121
+ const hoistAnyOf = (node, schemas) => {
122
+ const branches = node.anyOf.map(b => resolveSchemaRef(b, schemas));
123
+ if (branches.length === 0 ||
124
+ branches.some(b => !b || typeof b !== 'object')) {
125
+ return;
126
+ }
127
+ const requiredSets = branches.map(b => new Set(b.required || []));
128
+ const commonRequiredNames = [...requiredSets[0]].filter(name => requiredSets.every(set => set.has(name)));
129
+ const hoistedProperties = {};
130
+ const hoistedRequired = [];
131
+ for (const name of commonRequiredNames) {
132
+ const fragments = branches.map(b => b.properties?.[name]);
133
+ const widened = widenAnyOfProperty(fragments);
134
+ if (widened) {
135
+ hoistedProperties[name] = widened;
136
+ hoistedRequired.push(name);
137
+ }
138
+ }
139
+ if (hoistedRequired.length === 0) {
140
+ return; // nothing intersects safely — leave the combinator untouched, no-op
141
+ }
142
+ node.type = 'object';
143
+ node.properties = { ...(node.properties || {}), ...hoistedProperties };
144
+ node.required = [...new Set([...(node.required || []), ...hoistedRequired])];
145
+ };
146
+ // allOf is AND — an object must match every branch simultaneously, so unioning
147
+ // every branch's properties/required to the parent is always safe: nothing is
148
+ // asserted that isn't already true of any value satisfying the allOf. No type
149
+ // widening needed (unlike anyOf) since branches aren't alternatives.
150
+ const hoistAllOf = (node, schemas) => {
151
+ const branches = node.allOf.map(b => resolveSchemaRef(b, schemas));
152
+ if (branches.some(b => !b || typeof b !== 'object')) {
153
+ return;
154
+ }
155
+ const unionProperties = {};
156
+ const unionRequired = new Set();
157
+ for (const branch of branches) {
158
+ for (const [key, value] of Object.entries(branch.properties || {})) {
159
+ if (!(key in unionProperties)) {
160
+ unionProperties[key] = value;
161
+ }
162
+ }
163
+ for (const name of branch.required || []) {
164
+ unionRequired.add(name);
165
+ }
166
+ }
167
+ if (Object.keys(unionProperties).length === 0) {
168
+ return; // nothing to hoist — leave the combinator untouched, no-op
169
+ }
170
+ node.type = 'object';
171
+ node.properties = { ...(node.properties || {}), ...unionProperties };
172
+ node.required = [...new Set([...(node.required || []), ...unionRequired])];
173
+ };
174
+ /**
175
+ * zod-to-openapi represents a same-status multi-error response as `anyOf`, and
176
+ * `.extend()` on a registered/ref'd schema as `allOf: [$ref, inline]`. Either
177
+ * shape hides required/property information that used to be flat at the parent
178
+ * level inside the combinator, which oasdiff's breaking-change diff cannot see
179
+ * through — it reports every such change as fields being removed, even when the
180
+ * change was purely additive. This walks the finished spec and restores the
181
+ * safely-hoistable subset of that information to the parent schema (see
182
+ * hoistAnyOf / hoistAllOf for what "safe" means for each combinator — they are
183
+ * NOT the same rule). Ref topology is untouched: refs are only read to compute
184
+ * what's safe to hoist, never rewritten.
185
+ */
186
+ const hoistCombinatorRequiredFields = (spec) => {
187
+ const schemas = (spec.components?.schemas || {});
188
+ const visit = (node) => {
189
+ if (!node || typeof node !== 'object') {
190
+ return;
191
+ }
192
+ if (Array.isArray(node)) {
193
+ node.forEach(visit);
194
+ return;
195
+ }
196
+ const obj = node;
197
+ if (Array.isArray(obj.anyOf)) {
198
+ obj.anyOf.forEach(visit);
199
+ hoistAnyOf(obj, schemas);
200
+ }
201
+ else if (Array.isArray(obj.allOf)) {
202
+ obj.allOf.forEach(visit);
203
+ hoistAllOf(obj, schemas);
204
+ }
205
+ else {
206
+ Object.values(obj).forEach(visit);
207
+ }
208
+ };
209
+ visit(spec.paths || {});
210
+ visit(schemas);
211
+ };
212
+ exports.hoistCombinatorRequiredFields = hoistCombinatorRequiredFields;
84
213
  const registerEndpoint = (registry, method, prefixPath, path, summary,
85
214
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
86
215
  options, tags, debug) => {
@@ -43,6 +43,18 @@ export type Payment = PaymentBase & {
43
43
  };
44
44
  export declare const PAYMENT_EVENT_VERSION = 2;
45
45
  export type PaymentEventVersion = typeof PAYMENT_EVENT_VERSION;
46
+ /**
47
+ * Stable acquirer snapshot fields for CARD fiscalization (LUCA-35099).
48
+ * Populated by backend-pay when available on V3Payment at produce time.
49
+ * Fees are intentionally omitted — pay remains source of truth at report/close.
50
+ */
51
+ export type PaymentEventCardAcquirerFields = {
52
+ cardLast4?: string | null;
53
+ authCode?: string | null;
54
+ cardScheme?: string | null;
55
+ cardBrandLabel?: string | null;
56
+ terminalId?: string | null;
57
+ };
46
58
  export type PaymentEvent = {
47
59
  version: PaymentEventVersion;
48
60
  uuid: string;
@@ -60,7 +72,7 @@ export type PaymentEvent = {
60
72
  orderId?: string;
61
73
  statusCompleteAt?: Date | null;
62
74
  merchantDetails?: PaymentMerchantDetails;
63
- };
75
+ } & PaymentEventCardAcquirerFields;
64
76
  export declare const consumerPayAmount: (event: PaymentEvent) => number;
65
77
  export declare const operatorReceivableAmount: (event: PaymentEvent) => number;
66
78
  export type PaymentMerchantDetails = {
@@ -18,9 +18,44 @@ type LanguageOptions = typeof AVAILABLE_LANGUAGES[number];
18
18
  * ```
19
19
  */
20
20
  export declare const isInternalLanguage: (lang: string) => lang is InternalLanguages;
21
+ /**
22
+ * Canonicalises a language code for storage: `DE-de` -> `de-DE`.
23
+ * Returns null when the value is absent or not a plausible language code.
24
+ *
25
+ * The region is dropped when keeping it would not fit the `STRING(5)` language
26
+ * columns, so `fil-PH` stores as `fil` rather than overflowing or being thrown
27
+ * away — the language is the part we care about.
28
+ *
29
+ * Unlike {@link toInternalLanguage} this does NOT narrow to the languages our
30
+ * templates support — storage keeps what the user actually asked for, and
31
+ * narrowing happens on retrieval.
32
+ */
33
+ export declare const normalizeStoredLanguage: (lang?: string | null) => string | null;
34
+ /**
35
+ * Reads the caller's preferred language without narrowing it to the languages
36
+ * our templates support. Use for values that get persisted; use
37
+ * {@link getPreferredLanguageFromRequestHeaders} when the result immediately
38
+ * selects a template.
39
+ *
40
+ * Returns null when no usable `Accept-Language` was sent, so callers can leave
41
+ * the column untouched rather than writing a guessed default.
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * getStoredLanguageFromRequestHeaders({ 'accept-language': 'fr-FR,fr;q=0.9' }) // 'fr-FR'
46
+ * getStoredLanguageFromRequestHeaders({}) // null
47
+ * ```
48
+ */
49
+ export declare const getStoredLanguageFromRequestHeaders: (headers?: Record<string, string>) => string | null;
50
+ export declare const getStoredLanguageFromRequest: (request: Request) => string | null;
21
51
  /**
22
52
  * Safely converts a language code to an InternalLanguage.
23
- * If the language is not supported, falls back to 'en'.
53
+ * The region subtag is dropped before matching, so `de-DE` resolves to `de`
54
+ * rather than falling back. If the language is not supported, falls back to 'en'.
55
+ *
56
+ * This is the retrieval-side counterpart to {@link normalizeStoredLanguage}:
57
+ * columns may hold any language the user asked for, and every read that feeds
58
+ * a template goes through here.
24
59
  *
25
60
  * @param lang - Language code to convert
26
61
  * @returns The language if supported, otherwise 'en'
@@ -28,11 +63,12 @@ export declare const isInternalLanguage: (lang: string) => lang is InternalLangu
28
63
  * @example
29
64
  * ```typescript
30
65
  * toInternalLanguage('de') // returns 'de'
66
+ * toInternalLanguage('de-DE') // returns 'de'
31
67
  * toInternalLanguage('en') // returns 'en'
32
68
  * toInternalLanguage('fr') // returns 'en' (fallback)
33
69
  * ```
34
70
  */
35
- export declare const toInternalLanguage: (lang: string) => InternalLanguages;
71
+ export declare const toInternalLanguage: (lang?: string | null) => InternalLanguages;
36
72
  export declare const parseAcceptLanguageHeader: (header?: string) => LanguageOptions;
37
73
  /**
38
74
  * @deprecated Use getPreferredLanguageFromRequestHeaders instead.
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getPreferredLanguageFromRequestHeaders = exports.getPreferredLanguageFromRequest = exports.parseAcceptLanguageHeader = exports.toInternalLanguage = exports.isInternalLanguage = exports.AVAILABLE_LANGUAGES = exports.DEFAULT_LANGUAGE_WHEN_HEADER = exports.DEFAULT_LANGUAGE_WHEN_NO_HEADER = void 0;
6
+ exports.getPreferredLanguageFromRequestHeaders = exports.getPreferredLanguageFromRequest = exports.parseAcceptLanguageHeader = exports.toInternalLanguage = exports.getStoredLanguageFromRequest = exports.getStoredLanguageFromRequestHeaders = exports.normalizeStoredLanguage = exports.isInternalLanguage = exports.AVAILABLE_LANGUAGES = exports.DEFAULT_LANGUAGE_WHEN_HEADER = exports.DEFAULT_LANGUAGE_WHEN_NO_HEADER = void 0;
7
7
  const negotiator_1 = __importDefault(require("negotiator"));
8
8
  exports.DEFAULT_LANGUAGE_WHEN_NO_HEADER = 'de';
9
9
  exports.DEFAULT_LANGUAGE_WHEN_HEADER = 'en';
@@ -25,9 +25,80 @@ const isInternalLanguage = (lang) => {
25
25
  return exports.AVAILABLE_LANGUAGES.includes(lang);
26
26
  };
27
27
  exports.isInternalLanguage = isInternalLanguage;
28
+ /**
29
+ * Shape of a language code we are willing to persist: a 2-3 letter primary
30
+ * subtag, optionally followed by a 2-letter region. Deliberately narrower than
31
+ * full BCP 47 so an attacker-controlled `Accept-Language` cannot write junk;
32
+ * script subtags (`zh-Hant`) are rejected rather than truncated.
33
+ */
34
+ const STORED_LANGUAGE_PATTERN = /^[a-z]{2,3}(-[a-z]{2})?$/i;
35
+ /**
36
+ * Longest value the `language` columns accept — they are `STRING(5)`, which
37
+ * fits `pt-BR` but not a 3-letter primary subtag with a region (`fil-PH`).
38
+ */
39
+ const STORED_LANGUAGE_MAX_LENGTH = 5;
40
+ /**
41
+ * Canonicalises a language code for storage: `DE-de` -> `de-DE`.
42
+ * Returns null when the value is absent or not a plausible language code.
43
+ *
44
+ * The region is dropped when keeping it would not fit the `STRING(5)` language
45
+ * columns, so `fil-PH` stores as `fil` rather than overflowing or being thrown
46
+ * away — the language is the part we care about.
47
+ *
48
+ * Unlike {@link toInternalLanguage} this does NOT narrow to the languages our
49
+ * templates support — storage keeps what the user actually asked for, and
50
+ * narrowing happens on retrieval.
51
+ */
52
+ const normalizeStoredLanguage = (lang) => {
53
+ const trimmed = lang?.trim();
54
+ if (!trimmed || !STORED_LANGUAGE_PATTERN.test(trimmed)) {
55
+ return null;
56
+ }
57
+ const [primary, region] = trimmed.split('-');
58
+ const canonical = region
59
+ ? `${primary.toLowerCase()}-${region.toUpperCase()}`
60
+ : primary.toLowerCase();
61
+ return canonical.length > STORED_LANGUAGE_MAX_LENGTH
62
+ ? primary.toLowerCase()
63
+ : canonical;
64
+ };
65
+ exports.normalizeStoredLanguage = normalizeStoredLanguage;
66
+ /**
67
+ * Reads the caller's preferred language without narrowing it to the languages
68
+ * our templates support. Use for values that get persisted; use
69
+ * {@link getPreferredLanguageFromRequestHeaders} when the result immediately
70
+ * selects a template.
71
+ *
72
+ * Returns null when no usable `Accept-Language` was sent, so callers can leave
73
+ * the column untouched rather than writing a guessed default.
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * getStoredLanguageFromRequestHeaders({ 'accept-language': 'fr-FR,fr;q=0.9' }) // 'fr-FR'
78
+ * getStoredLanguageFromRequestHeaders({}) // null
79
+ * ```
80
+ */
81
+ const getStoredLanguageFromRequestHeaders = (headers) => {
82
+ const acceptLanguageHeader = headers?.['accept-language'];
83
+ if (!acceptLanguageHeader) {
84
+ return null;
85
+ }
86
+ // Take the highest-priority entry we can actually store, rather than giving
87
+ // up when the first one happens to be unstorable (e.g. `zh-Hant-TW, zh-TW`).
88
+ const negotiator = new negotiator_1.default({ headers });
89
+ return (negotiator.languages().map(exports.normalizeStoredLanguage).find(Boolean) ?? null);
90
+ };
91
+ exports.getStoredLanguageFromRequestHeaders = getStoredLanguageFromRequestHeaders;
92
+ const getStoredLanguageFromRequest = (request) => (0, exports.getStoredLanguageFromRequestHeaders)(request.headers);
93
+ exports.getStoredLanguageFromRequest = getStoredLanguageFromRequest;
28
94
  /**
29
95
  * Safely converts a language code to an InternalLanguage.
30
- * If the language is not supported, falls back to 'en'.
96
+ * The region subtag is dropped before matching, so `de-DE` resolves to `de`
97
+ * rather than falling back. If the language is not supported, falls back to 'en'.
98
+ *
99
+ * This is the retrieval-side counterpart to {@link normalizeStoredLanguage}:
100
+ * columns may hold any language the user asked for, and every read that feeds
101
+ * a template goes through here.
31
102
  *
32
103
  * @param lang - Language code to convert
33
104
  * @returns The language if supported, otherwise 'en'
@@ -35,12 +106,14 @@ exports.isInternalLanguage = isInternalLanguage;
35
106
  * @example
36
107
  * ```typescript
37
108
  * toInternalLanguage('de') // returns 'de'
109
+ * toInternalLanguage('de-DE') // returns 'de'
38
110
  * toInternalLanguage('en') // returns 'en'
39
111
  * toInternalLanguage('fr') // returns 'en' (fallback)
40
112
  * ```
41
113
  */
42
114
  const toInternalLanguage = (lang) => {
43
- return (0, exports.isInternalLanguage)(lang) ? lang : 'en';
115
+ const primary = lang?.trim().split('-')[0]?.toLowerCase();
116
+ return primary && (0, exports.isInternalLanguage)(primary) ? primary : 'en';
44
117
  };
45
118
  exports.toInternalLanguage = toInternalLanguage;
46
119
  const parseAcceptLanguageHeader = (header) => {
@@ -209,7 +209,7 @@ declare const z: {
209
209
  nullablePhoneNumber: () => ZodEffects<ZodEffects<ZodString, string, string>, string | null, string>;
210
210
  strongPassword: () => ZodEffects<ZodString, string, string>;
211
211
  zipCode: (locale?: validator.PostalCodeLocale) => ZodEffects<ZodString, string>;
212
- countryCode: () => ZodString;
212
+ countryCode: () => ZodEffects<ZodString, string, string>;
213
213
  moneyDecimalString: (locale?: string) => ZodEffects<ZodString, string, string>;
214
214
  moneyFromFloat: () => ZodEffects<ZodEffects<ZodNumber>, Money>;
215
215
  moneyFromInt: () => ZodEffects<ZodNumber, Money>;
@@ -386,5 +386,6 @@ declare const z: {
386
386
  Logic?: any;
387
387
  }>;
388
388
  };
389
+ export declare const isPostalCodeForCountry: (postalCode: string, countryCode: string) => boolean;
389
390
  export * from 'zod';
390
391
  export { z };
@@ -18,7 +18,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
18
18
  return (mod && mod.__esModule) ? mod : { "default": mod };
19
19
  };
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.z = exports.preprocessBooleanFromString = exports.preprocessBooleanFromQueryString = exports.preprocessNumberFromQueryString = exports.preprocessArrayFromQueryString = void 0;
21
+ exports.z = exports.isPostalCodeForCountry = exports.preprocessBooleanFromString = exports.preprocessBooleanFromQueryString = exports.preprocessNumberFromQueryString = exports.preprocessArrayFromQueryString = void 0;
22
22
  const url_1 = require("url");
23
23
  const zod_1 = require("zod");
24
24
  const validator_1 = __importDefault(require("validator"));
@@ -36,6 +36,7 @@ const ALPHANUMERIC_REGEX = /^[\da-z]+$/i;
36
36
  const ONBOARDING_BUSINESS_ENTITY_REGEX = /^[\w !&'()*+,./£À-ÿāăąćĉċčđēėęěĝğģĥħĩīįİıĵķĸĺļłńņōőœŗřśŝşšţŦũūŭůűųŵŷźżžơưếệ''-]+$/i;
37
37
  const ONBOARDING_NAME_REGEX = /^[\s'.A-Za-z£À-žơưếệ''-]+$/;
38
38
  const COUNTRY_CODE_REGEX = /^[A-Z]{2}$/;
39
+ const HAS_LETTER_REGEX = /\p{L}/u;
39
40
  const PASSWORD_REQUIREMENTS = {
40
41
  minLength: 9,
41
42
  minNumbers: 1,
@@ -108,6 +109,7 @@ const z = {
108
109
  safeBusinessStringForOnboarding: () => zod_1.z
109
110
  .string()
110
111
  .regex(ONBOARDING_BUSINESS_ENTITY_REGEX)
112
+ .regex(HAS_LETTER_REGEX)
111
113
  .max(255)
112
114
  .trim()
113
115
  .min(1)
@@ -119,6 +121,7 @@ const z = {
119
121
  safeNameForOnboarding: () => zod_1.z
120
122
  .string()
121
123
  .regex(ONBOARDING_NAME_REGEX)
124
+ .regex(HAS_LETTER_REGEX)
122
125
  .max(255)
123
126
  .trim()
124
127
  .min(1)
@@ -202,7 +205,10 @@ const z = {
202
205
  .trim()
203
206
  .max(255)
204
207
  .refine(value => validator_1.default.isPostalCode(value, locale || 'any')),
205
- countryCode: () => zod_1.z.string().regex(COUNTRY_CODE_REGEX),
208
+ countryCode: () => zod_1.z
209
+ .string()
210
+ .regex(COUNTRY_CODE_REGEX)
211
+ .refine(value => validator_1.default.isISO31661Alpha2(value)),
206
212
  // Money validators
207
213
  moneyDecimalString: (locale) => zod_1.z
208
214
  .string()
@@ -432,4 +438,12 @@ const z = {
432
438
  }),
433
439
  };
434
440
  exports.z = z;
441
+ const isPostalCodeForCountry = (postalCode, countryCode) => {
442
+ const locale = countryCode;
443
+ if (!validator_1.default.isPostalCodeLocales.includes(locale)) {
444
+ return true;
445
+ }
446
+ return validator_1.default.isPostalCode(postalCode, locale);
447
+ };
448
+ exports.isPostalCodeForCountry = isPostalCodeForCountry;
435
449
  __exportStar(require("zod"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucaapp/service-utils",
3
- "version": "5.24.5",
3
+ "version": "5.26.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [