@lucaapp/service-utils 4.9.1 → 4.11.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.
package/dist/index.d.ts CHANGED
@@ -16,4 +16,6 @@ export * from './lib/random';
16
16
  export * from './lib/crypto';
17
17
  export * from './lib/phone';
18
18
  export * from './lib/http';
19
+ export * from './lib/jobs';
20
+ export * from './lib/validation';
19
21
  export * from './types';
package/dist/index.js CHANGED
@@ -32,4 +32,6 @@ __exportStar(require("./lib/random"), exports);
32
32
  __exportStar(require("./lib/crypto"), exports);
33
33
  __exportStar(require("./lib/phone"), exports);
34
34
  __exportStar(require("./lib/http"), exports);
35
+ __exportStar(require("./lib/jobs"), exports);
36
+ __exportStar(require("./lib/validation"), exports);
35
37
  __exportStar(require("./types"), exports);
@@ -0,0 +1 @@
1
+ export * from './jobTracker';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./jobTracker"), exports);
@@ -0,0 +1,30 @@
1
+ import { Logger } from 'pino';
2
+ export type JsonSchema = Record<string, unknown>;
3
+ export interface JobStatus {
4
+ jobId: string;
5
+ jobName: string;
6
+ method: string;
7
+ status: 'running' | 'completed' | 'failed';
8
+ result?: JsonSchema;
9
+ error?: string;
10
+ startedAt: string;
11
+ completedAt?: string;
12
+ duration?: number;
13
+ }
14
+ export interface RedisClient {
15
+ setex(key: string | Buffer, seconds: number, value: string | Buffer): Promise<string | void>;
16
+ get(key: string | Buffer): Promise<string | null>;
17
+ scan(cursor: number, command: string, pattern: string, countCommand: string, count: number): Promise<[string, string[]]>;
18
+ }
19
+ export declare const createJobTracker: (logger: Logger, redis: RedisClient) => {
20
+ createJobExecution: (jobName: string, method: string) => Promise<string>;
21
+ updateJobStatus: (jobId: string, result: JsonSchema, error?: Error) => Promise<void>;
22
+ getJobStatus: (jobId: string) => Promise<JobStatus | null>;
23
+ getAllRunningJobs: () => Promise<JobStatus[]>;
24
+ getLatestCompletions: () => Promise<Record<string, {
25
+ completedAt: string;
26
+ status: "completed" | "failed";
27
+ error?: string;
28
+ }>>;
29
+ };
30
+ export type JobTracker = ReturnType<typeof createJobTracker>;
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createJobTracker = void 0;
4
+ const uuid_1 = require("uuid");
5
+ const JOB_STATUS_PREFIX = 'job:status:';
6
+ const JOB_TTL = 24 * 60 * 60;
7
+ const createJobTracker = (logger, redis) => {
8
+ const createJobExecution = async (jobName, method) => {
9
+ const jobId = (0, uuid_1.v4)();
10
+ const jobStatus = {
11
+ jobId,
12
+ jobName,
13
+ method,
14
+ status: 'running',
15
+ startedAt: new Date().toISOString(),
16
+ };
17
+ const key = `${JOB_STATUS_PREFIX}${jobId}`;
18
+ await redis.setex(key, JOB_TTL, JSON.stringify(jobStatus));
19
+ logger.info(`Created job execution: ${jobId} for job: ${jobName}, method: ${method}`);
20
+ return jobId;
21
+ };
22
+ const updateJobStatus = async (jobId, result, error) => {
23
+ const key = `${JOB_STATUS_PREFIX}${jobId}`;
24
+ const data = await redis.get(key);
25
+ if (!data) {
26
+ logger.warn(`Job status not found for jobId: ${jobId}`);
27
+ return;
28
+ }
29
+ const jobStatus = JSON.parse(data);
30
+ const completedAt = new Date();
31
+ const startedAt = new Date(jobStatus.startedAt);
32
+ const duration = completedAt.getTime() - startedAt.getTime();
33
+ if (error) {
34
+ jobStatus.status = 'failed';
35
+ jobStatus.error = error.message;
36
+ }
37
+ else {
38
+ jobStatus.status = 'completed';
39
+ jobStatus.result = result;
40
+ }
41
+ jobStatus.completedAt = completedAt.toISOString();
42
+ jobStatus.duration = duration;
43
+ await redis.setex(key, JOB_TTL, JSON.stringify(jobStatus));
44
+ logger.info(`Updated job status: ${jobId} - status: ${jobStatus.status}, duration: ${duration}ms`);
45
+ };
46
+ const getJobStatus = async (jobId) => {
47
+ const key = `${JOB_STATUS_PREFIX}${jobId}`;
48
+ const data = await redis.get(key);
49
+ if (!data) {
50
+ logger.warn(`Job status not found for jobId: ${jobId}`);
51
+ return null;
52
+ }
53
+ return JSON.parse(data);
54
+ };
55
+ const getAllRunningJobs = async () => {
56
+ const pattern = `${JOB_STATUS_PREFIX}*`;
57
+ const allJobs = [];
58
+ let cursor = 0;
59
+ do {
60
+ const [nextCursor, keys] = await redis.scan(cursor, 'MATCH', pattern, 'COUNT', 100);
61
+ cursor = Number.parseInt(nextCursor, 10);
62
+ for (const key of keys) {
63
+ const data = await redis.get(key);
64
+ if (data) {
65
+ const jobStatus = JSON.parse(data);
66
+ if (jobStatus.status === 'running') {
67
+ allJobs.push(jobStatus);
68
+ }
69
+ }
70
+ }
71
+ } while (cursor !== 0);
72
+ logger.info(`Found ${allJobs.length} running jobs`);
73
+ return allJobs;
74
+ };
75
+ const getLatestCompletions = async () => {
76
+ const pattern = `${JOB_STATUS_PREFIX}*`;
77
+ const completions = new Map();
78
+ let cursor = 0;
79
+ do {
80
+ const [nextCursor, keys] = await redis.scan(cursor, 'MATCH', pattern, 'COUNT', 100);
81
+ cursor = Number.parseInt(nextCursor, 10);
82
+ for (const key of keys) {
83
+ const data = await redis.get(key);
84
+ if (data) {
85
+ const jobStatus = JSON.parse(data);
86
+ if (jobStatus.completedAt &&
87
+ (jobStatus.status === 'completed' || jobStatus.status === 'failed')) {
88
+ const jobKey = `${jobStatus.jobName}/${jobStatus.method}`;
89
+ const existing = completions.get(jobKey);
90
+ if (!existing ||
91
+ new Date(jobStatus.completedAt) > new Date(existing.completedAt)) {
92
+ completions.set(jobKey, {
93
+ completedAt: jobStatus.completedAt,
94
+ status: jobStatus.status,
95
+ error: jobStatus.error,
96
+ });
97
+ }
98
+ }
99
+ }
100
+ }
101
+ } while (cursor !== 0);
102
+ logger.info(`Found ${completions.size} job completions`);
103
+ return Object.fromEntries(completions);
104
+ };
105
+ return {
106
+ createJobExecution,
107
+ updateJobStatus,
108
+ getJobStatus,
109
+ getAllRunningJobs,
110
+ getLatestCompletions,
111
+ };
112
+ };
113
+ exports.createJobTracker = createJobTracker;
@@ -4,6 +4,8 @@ export declare enum SupportedCurrencies {
4
4
  GBP = "GBP",
5
5
  USD = "USD",
6
6
  HUF = "HUF",
7
- CZK = "CZK"
7
+ CZK = "CZK",
8
+ PLN = "PLN",
9
+ RON = "RON"
8
10
  }
9
11
  export declare const currencySymbols: Record<SupportedCurrencies, string>;
@@ -9,6 +9,8 @@ var SupportedCurrencies;
9
9
  SupportedCurrencies["USD"] = "USD";
10
10
  SupportedCurrencies["HUF"] = "HUF";
11
11
  SupportedCurrencies["CZK"] = "CZK";
12
+ SupportedCurrencies["PLN"] = "PLN";
13
+ SupportedCurrencies["RON"] = "RON";
12
14
  })(SupportedCurrencies || (exports.SupportedCurrencies = SupportedCurrencies = {}));
13
15
  exports.currencySymbols = {
14
16
  [SupportedCurrencies.CHF]: 'CHF',
@@ -17,4 +19,6 @@ exports.currencySymbols = {
17
19
  [SupportedCurrencies.USD]: '$',
18
20
  [SupportedCurrencies.HUF]: 'Ft',
19
21
  [SupportedCurrencies.CZK]: 'Kč',
22
+ [SupportedCurrencies.PLN]: 'zł',
23
+ [SupportedCurrencies.RON]: 'lei',
20
24
  };
@@ -34,6 +34,7 @@ const api_1 = require("../api");
34
34
  const moment_1 = __importDefault(require("moment"));
35
35
  const requestTracer_1 = require("../requestTracer");
36
36
  const axios_1 = __importDefault(require("axios"));
37
+ const axios_retry_1 = __importDefault(require("axios-retry"));
37
38
  const zod_1 = require("zod");
38
39
  const validator_1 = __importDefault(require("validator"));
39
40
  const JWT_ALGORITHM = 'ES256';
@@ -188,6 +189,17 @@ class ServiceIdentity {
188
189
  this.identityPrivateKey = identityPrivateKey;
189
190
  this.identityPublicKey = identityPublicKey;
190
191
  this.axiosClient = axios_1.default.create({ proxy: false });
192
+ // Configure axios-retry to handle 429 and respect Retry-After header
193
+ (0, axios_retry_1.default)(this.axiosClient, {
194
+ retries: 3,
195
+ retryDelay: axios_retry_1.default.exponentialDelay,
196
+ shouldResetTimeout: true,
197
+ retryCondition: (error) => {
198
+ // Retry on network errors, 5xx errors, and 429 (rate limit)
199
+ return (axios_retry_1.default.isNetworkOrIdempotentRequestError(error) ||
200
+ error.response?.status === 429);
201
+ },
202
+ });
191
203
  if (!debug) {
192
204
  this.axiosClient.interceptors.response.use(response => response, error => {
193
205
  if (Array.isArray(error?.config?.headers) &&
@@ -0,0 +1,430 @@
1
+ import { z as zod, ZodEffects, ZodNumber, ZodObject, ZodString, ZodType, ZodTypeAny } from 'zod';
2
+ import validator from 'validator';
3
+ import { Money } from '../lib/money/money';
4
+ export declare const preprocessArrayFromQueryString: (input: unknown) => string[] | unknown;
5
+ export declare const preprocessNumberFromQueryString: (input: unknown) => number | unknown;
6
+ export declare const preprocessBooleanFromQueryString: (input: unknown) => boolean | unknown;
7
+ export declare const preprocessBooleanFromString: (input: unknown) => boolean | unknown;
8
+ declare const z: {
9
+ string: () => ZodString;
10
+ safeString: () => ZodString;
11
+ safeText: () => ZodString;
12
+ safeBusinessStringForOnboarding: () => zod.ZodEffects<zod.ZodString, string, string>;
13
+ safeNameForOnboarding: () => zod.ZodEffects<zod.ZodString, string, string>;
14
+ nonemptyString: () => zod.ZodString;
15
+ alphaNumericString: () => zod.ZodString;
16
+ tableId: () => zod.ZodString;
17
+ uuid: () => ZodString;
18
+ uuidv4: () => zod.ZodEffects<zod.ZodString, string, string>;
19
+ email: () => ZodEffects<ZodString>;
20
+ phoneNumber: () => ZodEffects<ZodEffects<ZodString>>;
21
+ nullablePhoneNumber: () => zod.ZodEffects<zod.ZodEffects<zod.ZodString, string, string>, string | null, string>;
22
+ strongPassword: () => zod.ZodEffects<zod.ZodString, string, string>;
23
+ zipCode: (locale?: validator.PostalCodeLocale) => ZodEffects<ZodString, string>;
24
+ countryCode: () => zod.ZodString;
25
+ moneyDecimalString: (locale?: string) => zod.ZodEffects<zod.ZodString, string, string>;
26
+ moneyFromFloat: () => ZodEffects<ZodEffects<ZodNumber>, Money>;
27
+ moneyFromInt: () => ZodEffects<ZodNumber, Money>;
28
+ convertMoneyFieldsToCurrency: <T extends ZodObject<Record<string, ZodTypeAny>>>(schema: T) => ZodEffects<T>;
29
+ unixTimestamp: () => zod.ZodNumber;
30
+ integerString: (options?: {
31
+ maxLength?: number;
32
+ lt?: number;
33
+ gt?: number;
34
+ transform?: boolean;
35
+ }) => zod.ZodEffects<zod.ZodEffects<zod.ZodType<string, zod.ZodTypeDef, string>, string, string>, number, string>;
36
+ integerStringRaw: (options?: {
37
+ maxLength?: number;
38
+ lt?: number;
39
+ gt?: number;
40
+ transform?: boolean;
41
+ }) => typeof options.transform extends true ? ZodEffects<ZodType<number>> : ZodEffects<ZodType<string>>;
42
+ bigIntegerString: () => zod.ZodEffects<zod.ZodString, string, string>;
43
+ floatString: (options?: {
44
+ maxLength?: number;
45
+ lt: number;
46
+ gt: number;
47
+ }) => zod.ZodEffects<zod.ZodEffects<zod.ZodString, string, string>, number, string>;
48
+ hex: ({ min, max, length, rawLength, }?: {
49
+ min?: number;
50
+ max?: number;
51
+ length?: number;
52
+ rawLength?: number;
53
+ }) => zod.ZodEffects<zod.ZodString, string, string>;
54
+ sha256: () => zod.ZodEffects<zod.ZodString, string, string>;
55
+ base64: ({ min, max, length, rawLength, isBase64Url, }?: {
56
+ min?: number;
57
+ max?: number;
58
+ length?: number;
59
+ rawLength?: number;
60
+ isBase64Url?: boolean;
61
+ }) => ZodEffects<ZodString>;
62
+ bearerToken: () => ZodEffects<ZodEffects<ZodString>>;
63
+ jwt: ({ max }: {
64
+ max: number;
65
+ }) => ZodEffects<ZodString>;
66
+ ecPublicKey: () => zod.ZodEffects<zod.ZodString, string, string>;
67
+ ecCompressedPublicKey: () => zod.ZodEffects<zod.ZodString, string, string>;
68
+ uncompressedECPublicKey: () => zod.ZodEffects<zod.ZodString, string, string>;
69
+ ecSignature: () => zod.ZodEffects<zod.ZodString, string, string>;
70
+ iv: () => zod.ZodEffects<zod.ZodString, string, string>;
71
+ mac: () => zod.ZodEffects<zod.ZodString, string, string>;
72
+ traceId: () => zod.ZodEffects<zod.ZodString, string, string>;
73
+ isoDateString: () => ZodEffects<ZodString, string>;
74
+ rfc3339String: () => ZodEffects<ZodString, string>;
75
+ iso8601Date: () => zod.ZodEffects<zod.ZodString, string, string>;
76
+ urlHttpsString: () => zod.ZodEffects<zod.ZodString, string, string>;
77
+ urlHttpOrHttpsString: () => zod.ZodEffects<zod.ZodString, string, string>;
78
+ language: () => ZodTypeAny;
79
+ dsgcPayload: () => zod.ZodObject<{
80
+ certificates: zod.ZodArray<zod.ZodObject<{
81
+ certificateType: zod.ZodString;
82
+ country: zod.ZodString;
83
+ kid: zod.ZodString;
84
+ rawData: zod.ZodString;
85
+ signature: zod.ZodString;
86
+ thumbprint: zod.ZodString;
87
+ timestamp: zod.ZodString;
88
+ }, "strip", zod.ZodTypeAny, {
89
+ kid: string;
90
+ timestamp: string;
91
+ signature: string;
92
+ certificateType: string;
93
+ country: string;
94
+ rawData: string;
95
+ thumbprint: string;
96
+ }, {
97
+ kid: string;
98
+ timestamp: string;
99
+ signature: string;
100
+ certificateType: string;
101
+ country: string;
102
+ rawData: string;
103
+ thumbprint: string;
104
+ }>, "many">;
105
+ }, "strip", zod.ZodTypeAny, {
106
+ certificates: {
107
+ kid: string;
108
+ timestamp: string;
109
+ signature: string;
110
+ certificateType: string;
111
+ country: string;
112
+ rawData: string;
113
+ thumbprint: string;
114
+ }[];
115
+ }, {
116
+ certificates: {
117
+ kid: string;
118
+ timestamp: string;
119
+ signature: string;
120
+ certificateType: string;
121
+ country: string;
122
+ rawData: string;
123
+ thumbprint: string;
124
+ }[];
125
+ }>;
126
+ dccListPayload: () => zod.ZodArray<zod.ZodObject<{
127
+ identifier: zod.ZodString;
128
+ version: zod.ZodString;
129
+ country: zod.ZodString;
130
+ hash: zod.ZodString;
131
+ }, "strip", zod.ZodTypeAny, {
132
+ version: string;
133
+ country: string;
134
+ identifier: string;
135
+ hash: string;
136
+ }, {
137
+ version: string;
138
+ country: string;
139
+ identifier: string;
140
+ hash: string;
141
+ }>, "many">;
142
+ dccRulePayload: () => zod.ZodObject<{
143
+ Identifier: zod.ZodString;
144
+ Type: zod.ZodString;
145
+ Country: zod.ZodString;
146
+ Version: zod.ZodString;
147
+ SchemaVersion: zod.ZodString;
148
+ Engine: zod.ZodString;
149
+ EngineVersion: zod.ZodString;
150
+ CertificateType: zod.ZodString;
151
+ Description: zod.ZodArray<zod.ZodObject<{
152
+ lang: zod.ZodString;
153
+ desc: zod.ZodString;
154
+ }, "strip", zod.ZodTypeAny, {
155
+ lang: string;
156
+ desc: string;
157
+ }, {
158
+ lang: string;
159
+ desc: string;
160
+ }>, "many">;
161
+ ValidFrom: zod.ZodString;
162
+ ValidTo: zod.ZodString;
163
+ AffectedFields: zod.ZodArray<zod.ZodString, "many">;
164
+ Logic: zod.ZodAny;
165
+ }, "strip", zod.ZodTypeAny, {
166
+ Identifier: string;
167
+ Type: string;
168
+ Country: string;
169
+ Version: string;
170
+ SchemaVersion: string;
171
+ Engine: string;
172
+ EngineVersion: string;
173
+ CertificateType: string;
174
+ Description: {
175
+ lang: string;
176
+ desc: string;
177
+ }[];
178
+ ValidFrom: string;
179
+ ValidTo: string;
180
+ AffectedFields: string[];
181
+ Logic?: any;
182
+ }, {
183
+ Identifier: string;
184
+ Type: string;
185
+ Country: string;
186
+ Version: string;
187
+ SchemaVersion: string;
188
+ Engine: string;
189
+ EngineVersion: string;
190
+ CertificateType: string;
191
+ Description: {
192
+ lang: string;
193
+ desc: string;
194
+ }[];
195
+ ValidFrom: string;
196
+ ValidTo: string;
197
+ AffectedFields: string[];
198
+ Logic?: any;
199
+ }>;
200
+ setErrorMap(map: zod.ZodErrorMap): void;
201
+ getErrorMap(): zod.ZodErrorMap;
202
+ defaultErrorMap: zod.ZodErrorMap;
203
+ addIssueToContext(ctx: zod.ParseContext, issueData: zod.IssueData): void;
204
+ makeIssue: (params: {
205
+ data: any;
206
+ path: (string | number)[];
207
+ errorMaps: zod.ZodErrorMap[];
208
+ issueData: zod.IssueData;
209
+ }) => zod.ZodIssue;
210
+ EMPTY_PATH: zod.ParsePath;
211
+ ParseStatus: typeof zod.ParseStatus;
212
+ INVALID: zod.INVALID;
213
+ DIRTY: <T>(value: T) => zod.DIRTY<T>;
214
+ OK: <T>(value: T) => zod.OK<T>;
215
+ isAborted: (x: zod.ParseReturnType<any>) => x is zod.INVALID;
216
+ isDirty: <T>(x: zod.ParseReturnType<T>) => x is zod.OK<T> | zod.DIRTY<T>;
217
+ isValid: <T>(x: zod.ParseReturnType<T>) => x is zod.OK<T> | zod.DIRTY<T>;
218
+ isAsync: <T>(x: zod.ParseReturnType<T>) => x is zod.AsyncParseReturnType<T>;
219
+ util: typeof zod.util;
220
+ objectUtil: typeof zod.objectUtil;
221
+ ZodParsedType: {
222
+ function: "function";
223
+ number: "number";
224
+ string: "string";
225
+ nan: "nan";
226
+ integer: "integer";
227
+ float: "float";
228
+ boolean: "boolean";
229
+ date: "date";
230
+ bigint: "bigint";
231
+ symbol: "symbol";
232
+ undefined: "undefined";
233
+ null: "null";
234
+ array: "array";
235
+ object: "object";
236
+ unknown: "unknown";
237
+ promise: "promise";
238
+ void: "void";
239
+ never: "never";
240
+ map: "map";
241
+ set: "set";
242
+ };
243
+ getParsedType: (data: any) => zod.ZodParsedType;
244
+ ZodType: typeof zod.ZodType;
245
+ ZodString: typeof zod.ZodString;
246
+ ZodNumber: typeof zod.ZodNumber;
247
+ ZodBigInt: typeof zod.ZodBigInt;
248
+ ZodBoolean: typeof zod.ZodBoolean;
249
+ ZodDate: typeof zod.ZodDate;
250
+ ZodSymbol: typeof zod.ZodSymbol;
251
+ ZodUndefined: typeof zod.ZodUndefined;
252
+ ZodNull: typeof zod.ZodNull;
253
+ ZodAny: typeof zod.ZodAny;
254
+ ZodUnknown: typeof zod.ZodUnknown;
255
+ ZodNever: typeof zod.ZodNever;
256
+ ZodVoid: typeof zod.ZodVoid;
257
+ ZodArray: typeof zod.ZodArray;
258
+ ZodObject: typeof zod.ZodObject;
259
+ ZodUnion: typeof zod.ZodUnion;
260
+ ZodDiscriminatedUnion: typeof zod.ZodDiscriminatedUnion;
261
+ ZodIntersection: typeof zod.ZodIntersection;
262
+ ZodTuple: typeof zod.ZodTuple;
263
+ ZodRecord: typeof zod.ZodRecord;
264
+ ZodMap: typeof zod.ZodMap;
265
+ ZodSet: typeof zod.ZodSet;
266
+ ZodFunction: typeof zod.ZodFunction;
267
+ ZodLazy: typeof zod.ZodLazy;
268
+ ZodLiteral: typeof zod.ZodLiteral;
269
+ ZodEnum: typeof zod.ZodEnum;
270
+ ZodNativeEnum: typeof zod.ZodNativeEnum;
271
+ ZodPromise: typeof zod.ZodPromise;
272
+ ZodEffects: typeof zod.ZodEffects;
273
+ ZodTransformer: typeof zod.ZodEffects;
274
+ ZodOptional: typeof zod.ZodOptional;
275
+ ZodNullable: typeof zod.ZodNullable;
276
+ ZodDefault: typeof zod.ZodDefault;
277
+ ZodCatch: typeof zod.ZodCatch;
278
+ ZodNaN: typeof zod.ZodNaN;
279
+ BRAND: typeof zod.BRAND;
280
+ ZodBranded: typeof zod.ZodBranded;
281
+ ZodPipeline: typeof zod.ZodPipeline;
282
+ ZodReadonly: typeof zod.ZodReadonly;
283
+ custom: <T>(check?: ((data: unknown) => any) | undefined, params?: string | (Partial<zod.util.Omit<zod.ZodCustomIssue, "code">> & {
284
+ fatal?: boolean;
285
+ }) | ((input: any) => Partial<zod.util.Omit<zod.ZodCustomIssue, "code">> & {
286
+ fatal?: boolean;
287
+ }), fatal?: boolean | undefined) => ZodType<T, zod.ZodTypeDef, T>;
288
+ Schema: typeof zod.ZodType;
289
+ ZodSchema: typeof zod.ZodType;
290
+ late: {
291
+ object: <T extends zod.ZodRawShape>(shape: () => T, params?: zod.RawCreateParams) => ZodObject<T, "strip", ZodTypeAny, { [k_1 in keyof zod.objectUtil.addQuestionMarks<zod.baseObjectOutputType<T>, { [k in keyof zod.baseObjectOutputType<T>]: undefined extends zod.baseObjectOutputType<T>[k] ? never : k; }[keyof T]>]: zod.objectUtil.addQuestionMarks<zod.baseObjectOutputType<T>, { [k in keyof zod.baseObjectOutputType<T>]: undefined extends zod.baseObjectOutputType<T>[k] ? never : k; }[keyof T]>[k_1]; }, { [k_2 in keyof zod.baseObjectInputType<T>]: zod.baseObjectInputType<T>[k_2]; }>;
292
+ };
293
+ ZodFirstPartyTypeKind: typeof zod.ZodFirstPartyTypeKind;
294
+ coerce: {
295
+ string: (params?: ({
296
+ errorMap?: zod.ZodErrorMap | undefined;
297
+ invalid_type_error?: string | undefined;
298
+ required_error?: string | undefined;
299
+ description?: string | undefined;
300
+ } & {
301
+ coerce?: true | undefined;
302
+ }) | undefined) => ZodString;
303
+ number: (params?: ({
304
+ errorMap?: zod.ZodErrorMap | undefined;
305
+ invalid_type_error?: string | undefined;
306
+ required_error?: string | undefined;
307
+ description?: string | undefined;
308
+ } & {
309
+ coerce?: boolean | undefined;
310
+ }) | undefined) => ZodNumber;
311
+ boolean: (params?: ({
312
+ errorMap?: zod.ZodErrorMap | undefined;
313
+ invalid_type_error?: string | undefined;
314
+ required_error?: string | undefined;
315
+ description?: string | undefined;
316
+ } & {
317
+ coerce?: boolean | undefined;
318
+ }) | undefined) => zod.ZodBoolean;
319
+ bigint: (params?: ({
320
+ errorMap?: zod.ZodErrorMap | undefined;
321
+ invalid_type_error?: string | undefined;
322
+ required_error?: string | undefined;
323
+ description?: string | undefined;
324
+ } & {
325
+ coerce?: boolean | undefined;
326
+ }) | undefined) => zod.ZodBigInt;
327
+ date: (params?: ({
328
+ errorMap?: zod.ZodErrorMap | undefined;
329
+ invalid_type_error?: string | undefined;
330
+ required_error?: string | undefined;
331
+ description?: string | undefined;
332
+ } & {
333
+ coerce?: boolean | undefined;
334
+ }) | undefined) => zod.ZodDate;
335
+ };
336
+ any: (params?: zod.RawCreateParams) => zod.ZodAny;
337
+ array: <T extends ZodTypeAny>(schema: T, params?: zod.RawCreateParams) => zod.ZodArray<T, "many">;
338
+ bigint: (params?: ({
339
+ errorMap?: zod.ZodErrorMap | undefined;
340
+ invalid_type_error?: string | undefined;
341
+ required_error?: string | undefined;
342
+ description?: string | undefined;
343
+ } & {
344
+ coerce?: boolean | undefined;
345
+ }) | undefined) => zod.ZodBigInt;
346
+ boolean: (params?: ({
347
+ errorMap?: zod.ZodErrorMap | undefined;
348
+ invalid_type_error?: string | undefined;
349
+ required_error?: string | undefined;
350
+ description?: string | undefined;
351
+ } & {
352
+ coerce?: boolean | undefined;
353
+ }) | undefined) => zod.ZodBoolean;
354
+ date: (params?: ({
355
+ errorMap?: zod.ZodErrorMap | undefined;
356
+ invalid_type_error?: string | undefined;
357
+ required_error?: string | undefined;
358
+ description?: string | undefined;
359
+ } & {
360
+ coerce?: boolean | undefined;
361
+ }) | undefined) => zod.ZodDate;
362
+ discriminatedUnion: typeof zod.ZodDiscriminatedUnion.create;
363
+ effect: <I extends ZodTypeAny>(schema: I, effect: zod.Effect<I["_output"]>, params?: zod.RawCreateParams) => ZodEffects<I, I["_output"], zod.input<I>>;
364
+ enum: {
365
+ <U extends string, T extends Readonly<[U, ...U[]]>>(values: T, params?: zod.RawCreateParams): zod.ZodEnum<zod.Writeable<T>>;
366
+ <U extends string, T extends [U, ...U[]]>(values: T, params?: zod.RawCreateParams): zod.ZodEnum<T>;
367
+ };
368
+ function: typeof zod.ZodFunction.create;
369
+ instanceof: <T extends abstract new (..._: any[]) => {}>(cls: T, params?: Partial<zod.util.Omit<zod.ZodCustomIssue, "code">> & {
370
+ fatal?: boolean;
371
+ }) => ZodType<InstanceType<T>, zod.ZodTypeDef, InstanceType<T>>;
372
+ intersection: <T extends ZodTypeAny, U extends ZodTypeAny>(left: T, right: U, params?: zod.RawCreateParams) => zod.ZodIntersection<T, U>;
373
+ lazy: <T extends ZodTypeAny>(getter: () => T, params?: zod.RawCreateParams) => zod.ZodLazy<T>;
374
+ literal: <T extends zod.Primitive>(value: T, params?: zod.RawCreateParams) => zod.ZodLiteral<T>;
375
+ map: <Key extends ZodTypeAny = zod.ZodTypeAny, Value extends ZodTypeAny = zod.ZodTypeAny>(keyType: Key, valueType: Value, params?: zod.RawCreateParams) => zod.ZodMap<Key, Value>;
376
+ nan: (params?: zod.RawCreateParams) => zod.ZodNaN;
377
+ nativeEnum: <T extends zod.EnumLike>(values: T, params?: zod.RawCreateParams) => zod.ZodNativeEnum<T>;
378
+ never: (params?: zod.RawCreateParams) => zod.ZodNever;
379
+ null: (params?: zod.RawCreateParams) => zod.ZodNull;
380
+ nullable: <T extends ZodTypeAny>(type: T, params?: zod.RawCreateParams) => zod.ZodNullable<T>;
381
+ number: (params?: ({
382
+ errorMap?: zod.ZodErrorMap | undefined;
383
+ invalid_type_error?: string | undefined;
384
+ required_error?: string | undefined;
385
+ description?: string | undefined;
386
+ } & {
387
+ coerce?: boolean | undefined;
388
+ }) | undefined) => ZodNumber;
389
+ object: <T extends zod.ZodRawShape>(shape: T, params?: zod.RawCreateParams) => ZodObject<T, "strip", ZodTypeAny, { [k_1 in keyof zod.objectUtil.addQuestionMarks<zod.baseObjectOutputType<T>, { [k in keyof zod.baseObjectOutputType<T>]: undefined extends zod.baseObjectOutputType<T>[k] ? never : k; }[keyof T]>]: zod.objectUtil.addQuestionMarks<zod.baseObjectOutputType<T>, { [k in keyof zod.baseObjectOutputType<T>]: undefined extends zod.baseObjectOutputType<T>[k] ? never : k; }[keyof T]>[k_1]; }, { [k_2 in keyof zod.baseObjectInputType<T>]: zod.baseObjectInputType<T>[k_2]; }>;
390
+ oboolean: () => zod.ZodOptional<zod.ZodBoolean>;
391
+ onumber: () => zod.ZodOptional<ZodNumber>;
392
+ optional: <T extends ZodTypeAny>(type: T, params?: zod.RawCreateParams) => zod.ZodOptional<T>;
393
+ ostring: () => zod.ZodOptional<ZodString>;
394
+ pipeline: typeof zod.ZodPipeline.create;
395
+ preprocess: <I extends ZodTypeAny>(preprocess: (arg: unknown, ctx: zod.RefinementCtx) => unknown, schema: I, params?: zod.RawCreateParams) => ZodEffects<I, I["_output"], unknown>;
396
+ promise: <T extends ZodTypeAny>(schema: T, params?: zod.RawCreateParams) => zod.ZodPromise<T>;
397
+ record: typeof zod.ZodRecord.create;
398
+ set: <Value extends ZodTypeAny = zod.ZodTypeAny>(valueType: Value, params?: zod.RawCreateParams) => zod.ZodSet<Value>;
399
+ strictObject: <T extends zod.ZodRawShape>(shape: T, params?: zod.RawCreateParams) => ZodObject<T, "strict", ZodTypeAny, { [k_1 in keyof zod.objectUtil.addQuestionMarks<zod.baseObjectOutputType<T>, { [k in keyof zod.baseObjectOutputType<T>]: undefined extends zod.baseObjectOutputType<T>[k] ? never : k; }[keyof T]>]: zod.objectUtil.addQuestionMarks<zod.baseObjectOutputType<T>, { [k in keyof zod.baseObjectOutputType<T>]: undefined extends zod.baseObjectOutputType<T>[k] ? never : k; }[keyof T]>[k_1]; }, { [k_2 in keyof zod.baseObjectInputType<T>]: zod.baseObjectInputType<T>[k_2]; }>;
400
+ symbol: (params?: zod.RawCreateParams) => zod.ZodSymbol;
401
+ transformer: <I extends ZodTypeAny>(schema: I, effect: zod.Effect<I["_output"]>, params?: zod.RawCreateParams) => ZodEffects<I, I["_output"], zod.input<I>>;
402
+ tuple: <T extends [] | [ZodTypeAny, ...ZodTypeAny[]]>(schemas: T, params?: zod.RawCreateParams) => zod.ZodTuple<T, null>;
403
+ undefined: (params?: zod.RawCreateParams) => zod.ZodUndefined;
404
+ union: <T extends readonly [ZodTypeAny, ZodTypeAny, ...ZodTypeAny[]]>(types: T, params?: zod.RawCreateParams) => zod.ZodUnion<T>;
405
+ unknown: (params?: zod.RawCreateParams) => zod.ZodUnknown;
406
+ void: (params?: zod.RawCreateParams) => zod.ZodVoid;
407
+ NEVER: never;
408
+ ZodIssueCode: {
409
+ invalid_type: "invalid_type";
410
+ invalid_literal: "invalid_literal";
411
+ custom: "custom";
412
+ invalid_union: "invalid_union";
413
+ invalid_union_discriminator: "invalid_union_discriminator";
414
+ invalid_enum_value: "invalid_enum_value";
415
+ unrecognized_keys: "unrecognized_keys";
416
+ invalid_arguments: "invalid_arguments";
417
+ invalid_return_type: "invalid_return_type";
418
+ invalid_date: "invalid_date";
419
+ invalid_string: "invalid_string";
420
+ too_small: "too_small";
421
+ too_big: "too_big";
422
+ invalid_intersection_types: "invalid_intersection_types";
423
+ not_multiple_of: "not_multiple_of";
424
+ not_finite: "not_finite";
425
+ };
426
+ quotelessJson: (obj: any) => string;
427
+ ZodError: typeof zod.ZodError;
428
+ };
429
+ export * from 'zod';
430
+ export { z };
@@ -0,0 +1,431 @@
1
+ "use strict";
2
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types, max-lines */
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
16
+ };
17
+ var __importDefault = (this && this.__importDefault) || function (mod) {
18
+ return (mod && mod.__esModule) ? mod : { "default": mod };
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.z = exports.preprocessBooleanFromString = exports.preprocessBooleanFromQueryString = exports.preprocessNumberFromQueryString = exports.preprocessArrayFromQueryString = void 0;
22
+ const url_1 = require("url");
23
+ const zod_1 = require("zod");
24
+ const validator_1 = __importDefault(require("validator"));
25
+ const min_1 = require("libphonenumber-js/min");
26
+ const api_1 = require("../lib/api");
27
+ const money_1 = require("../lib/money/money");
28
+ const supportedCurrencies_1 = require("../lib/money/supportedCurrencies");
29
+ const language_1 = require("../lib/negotiator/language");
30
+ (0, api_1.extendZodWithOpenApi)(zod_1.z);
31
+ // Regular expressions
32
+ const SAFE_CHARACTERS_REGEX = /^[\w !&()+,./:@`|£À-ÿāăąćĉċčđēėęěĝğģĥħĩīįİıĵķĸĺļłńņōőœŗřśŝşšţŦũūŭůűųŵŷźżžơưếệ–-]*$/i;
33
+ const NO_HTTP_REGEX = /^((?!http).)*$/i;
34
+ const NO_FTP_REGEX = /^((?!ftp).)*$/i;
35
+ const ALPHANUMERIC_REGEX = /^[\da-z]+$/i;
36
+ const ONBOARDING_BUSINESS_ENTITY_REGEX = /^[\w !&'()*+,./£À-ÿāăąćĉċčđēėęěĝğģĥħĩīįİıĵķĸĺļłńņōőœŗřśŝşšţŦũūŭůűųŵŷźżžơưếệ''-]+$/i;
37
+ const ONBOARDING_NAME_REGEX = /^[\s'.A-Za-z£À-žơưếệ''-]+$/;
38
+ const COUNTRY_CODE_REGEX = /^[A-Z]{2}$/;
39
+ const PASSWORD_REQUIREMENTS = {
40
+ minLength: 9,
41
+ minNumbers: 1,
42
+ minLowercase: 1,
43
+ minUppercase: 1,
44
+ minSymbols: 1,
45
+ };
46
+ // Helper functions for query string preprocessing
47
+ const preprocessArrayFromQueryString = (input) => {
48
+ if (typeof input !== 'string')
49
+ return undefined;
50
+ return input.split(',');
51
+ };
52
+ exports.preprocessArrayFromQueryString = preprocessArrayFromQueryString;
53
+ const preprocessNumberFromQueryString = (input) => {
54
+ if (input === '')
55
+ return undefined;
56
+ return input ? Number(input) : input;
57
+ };
58
+ exports.preprocessNumberFromQueryString = preprocessNumberFromQueryString;
59
+ const preprocessBooleanFromQueryString = (input) => {
60
+ if (input === '')
61
+ return undefined;
62
+ if (typeof input !== 'string')
63
+ return undefined;
64
+ return input.toLowerCase() !== 'false';
65
+ };
66
+ exports.preprocessBooleanFromQueryString = preprocessBooleanFromQueryString;
67
+ // Alias for backward compatibility
68
+ exports.preprocessBooleanFromString = exports.preprocessBooleanFromQueryString;
69
+ // Helper function for money validation
70
+ const isValidMoneyAmount = (number) => validator_1.default.isCurrency(number.toString(10), {
71
+ require_decimal: false,
72
+ allow_decimal: true,
73
+ negative_sign_after_digits: false,
74
+ allow_negatives: true,
75
+ digits_after_decimal: [1, 2],
76
+ });
77
+ // Helper function for updating money fields with currency
78
+ const updateMoneyFieldsWithCurrency = (object) => {
79
+ const currency = object.currency;
80
+ Object.keys(object).forEach(key => {
81
+ const value = object[key];
82
+ if (value instanceof money_1.Money) {
83
+ if (!currency) {
84
+ throw new Error('currency is required to convert money fields');
85
+ }
86
+ // eslint-disable-next-line no-param-reassign
87
+ object[key] = money_1.Money.fromAmount(value.getAmount(), currency);
88
+ }
89
+ });
90
+ };
91
+ // Extended zod with custom validators
92
+ const z = {
93
+ ...zod_1.z,
94
+ // Override string to always trim
95
+ string: () => zod_1.z.string().trim(),
96
+ // String validators
97
+ safeString: () => z
98
+ .string()
99
+ .trim()
100
+ .regex(SAFE_CHARACTERS_REGEX)
101
+ .regex(NO_HTTP_REGEX)
102
+ .regex(NO_FTP_REGEX),
103
+ safeText: () => z.string().trim().regex(NO_HTTP_REGEX).regex(NO_FTP_REGEX),
104
+ safeBusinessStringForOnboarding: () => zod_1.z
105
+ .string()
106
+ .regex(ONBOARDING_BUSINESS_ENTITY_REGEX)
107
+ .max(255)
108
+ .trim()
109
+ .min(1)
110
+ .transform(value => value.trimEnd())
111
+ .openapi({
112
+ type: 'string',
113
+ example: 'My Business',
114
+ }),
115
+ safeNameForOnboarding: () => zod_1.z
116
+ .string()
117
+ .regex(ONBOARDING_NAME_REGEX)
118
+ .max(255)
119
+ .trim()
120
+ .min(1)
121
+ .transform(value => value.trimEnd())
122
+ .openapi({
123
+ type: 'string',
124
+ example: 'Alex',
125
+ }),
126
+ nonemptyString: () => z.string().trim().min(1),
127
+ alphaNumericString: () => zod_1.z.string().regex(ALPHANUMERIC_REGEX),
128
+ tableId: () => z.string(),
129
+ // UUID validators
130
+ uuid: () => zod_1.z.string().trim().uuid(),
131
+ uuidv4: () => zod_1.z
132
+ .string()
133
+ .length(36)
134
+ .refine(value => validator_1.default.isUUID(value, '4')),
135
+ // Email validator
136
+ email: () => zod_1.z
137
+ .string()
138
+ .trim()
139
+ .email()
140
+ .max(255)
141
+ .refine(value => validator_1.default.isEmail(value, {
142
+ allow_display_name: false,
143
+ require_display_name: false,
144
+ allow_utf8_local_part: true,
145
+ require_tld: true,
146
+ allow_ip_domain: false,
147
+ // @ts-expect-error: double escape as this is passed into new RegExp("[${blacklisted_chars}]")
148
+ blacklisted_chars: "=',\\\\",
149
+ }))
150
+ .openapi({
151
+ type: 'string',
152
+ example: 'test@nexenio.com',
153
+ }),
154
+ // Phone number validators
155
+ phoneNumber: () => zod_1.z
156
+ .string()
157
+ .trim()
158
+ .max(32)
159
+ .refine(value => {
160
+ return (0, min_1.parsePhoneNumber)(value, 'DE').isValid();
161
+ }, {
162
+ message: 'invalid phone number',
163
+ })
164
+ .transform(value => {
165
+ return (0, min_1.parsePhoneNumber)(value, 'DE').formatInternational();
166
+ })
167
+ .openapi({
168
+ type: 'string',
169
+ example: '+4917712345678',
170
+ }),
171
+ nullablePhoneNumber: () => zod_1.z
172
+ .string()
173
+ .trim()
174
+ .max(32)
175
+ .refine(value => {
176
+ if (value === '')
177
+ return true;
178
+ return (0, min_1.parsePhoneNumber)(value, 'DE').isValid();
179
+ }, {
180
+ message: 'invalid phone number',
181
+ })
182
+ .transform(value => {
183
+ if (value === '')
184
+ return null;
185
+ return (0, min_1.parsePhoneNumber)(value, 'DE').formatInternational();
186
+ })
187
+ .openapi({
188
+ type: 'string',
189
+ example: '+4917712345678',
190
+ }),
191
+ // Password validator
192
+ strongPassword: () => zod_1.z
193
+ .string()
194
+ .refine(value => validator_1.default.isStrongPassword(value, PASSWORD_REQUIREMENTS)),
195
+ // Postal code validators
196
+ zipCode: (locale) => z
197
+ .string()
198
+ .trim()
199
+ .max(255)
200
+ .refine(value => validator_1.default.isPostalCode(value, locale || 'any')),
201
+ countryCode: () => zod_1.z.string().regex(COUNTRY_CODE_REGEX),
202
+ // Money validators
203
+ moneyDecimalString: (locale) => zod_1.z
204
+ .string()
205
+ .max(255)
206
+ .refine(value => validator_1.default.isCurrency(value, {
207
+ require_decimal: true,
208
+ negative_sign_after_digits: false,
209
+ allow_negatives: true,
210
+ digits_after_decimal: [2],
211
+ symbol: '€',
212
+ symbol_after_digits: true,
213
+ allow_space_after_digits: true,
214
+ decimal_separator: locale === 'de' ? ',' : '.',
215
+ thousands_separator: locale === 'de' ? '.' : ',',
216
+ })),
217
+ moneyFromFloat: () => z
218
+ .number()
219
+ .refine(isValidMoneyAmount)
220
+ .transform(value => money_1.Money.fromFloat(value, supportedCurrencies_1.SupportedCurrencies.EUR))
221
+ .openapi({
222
+ type: 'number',
223
+ }),
224
+ moneyFromInt: () => z
225
+ .number()
226
+ .int()
227
+ .nonnegative()
228
+ .transform(value => money_1.Money.fromAmount(value, supportedCurrencies_1.SupportedCurrencies.EUR))
229
+ .openapi({
230
+ type: 'integer',
231
+ }),
232
+ convertMoneyFieldsToCurrency: (schema) => {
233
+ return schema.refine((object) => {
234
+ try {
235
+ updateMoneyFieldsWithCurrency(object);
236
+ return true;
237
+ }
238
+ catch {
239
+ return false;
240
+ }
241
+ }, {
242
+ message: 'Currency is required to convert money fields.',
243
+ path: ['currency'],
244
+ });
245
+ },
246
+ // Number validators
247
+ unixTimestamp: () => zod_1.z.number().int().positive(),
248
+ integerString: (options = {}) => z
249
+ .integerStringRaw(options)
250
+ .transform(value => Number.parseInt(value, 10))
251
+ .openapi({
252
+ type: 'string',
253
+ }),
254
+ integerStringRaw: (options = {}) => zod_1.z
255
+ .string()
256
+ .max(options.maxLength || 17)
257
+ .refine(value => validator_1.default.isInt(value, {
258
+ lt: options.lt || Number.MAX_SAFE_INTEGER,
259
+ gt: options.gt || Number.MIN_SAFE_INTEGER,
260
+ allow_leading_zeroes: false,
261
+ })),
262
+ bigIntegerString: () => zod_1.z
263
+ .string()
264
+ .max(2048)
265
+ .refine(value => validator_1.default.isInt(value, {
266
+ allow_leading_zeroes: false,
267
+ })),
268
+ floatString: (options) => zod_1.z
269
+ .string()
270
+ .max(options?.maxLength || 17)
271
+ .refine(value => validator_1.default.isFloat(value, {
272
+ lt: options?.lt || Number.MAX_SAFE_INTEGER,
273
+ gt: options?.gt || Number.MIN_SAFE_INTEGER,
274
+ }))
275
+ .transform(value => Number.parseFloat(value))
276
+ .openapi({
277
+ type: 'string',
278
+ }),
279
+ // Encoding validators
280
+ hex: ({ min, max, length, rawLength, } = {}) => zod_1.z
281
+ .string()
282
+ .min(min)
283
+ .max(max)
284
+ .length(length)
285
+ .refine(value => {
286
+ if (!validator_1.default.isHexadecimal(value))
287
+ return false;
288
+ if (!rawLength)
289
+ return true;
290
+ return Buffer.from(value, 'hex').length === rawLength;
291
+ }),
292
+ sha256: () => z.hex({ length: 64, rawLength: 32 }),
293
+ base64: ({ min, max, length, rawLength, isBase64Url = false, } = {}) => {
294
+ let schema = zod_1.z.string();
295
+ if (min !== undefined)
296
+ schema = schema.min(min);
297
+ if (max !== undefined)
298
+ schema = schema.max(max);
299
+ if (length !== undefined)
300
+ schema = schema.length(length);
301
+ return schema.refine(value => {
302
+ if (!validator_1.default.isBase64(value, { urlSafe: isBase64Url }))
303
+ return false;
304
+ if (!rawLength)
305
+ return true;
306
+ return (Buffer.from(value, isBase64Url ? 'base64url' : 'base64').length ===
307
+ rawLength);
308
+ });
309
+ },
310
+ bearerToken: () => zod_1.z
311
+ .string()
312
+ .trim()
313
+ .min(28)
314
+ .max(255)
315
+ .refine(value => {
316
+ return validator_1.default.isBase64(value.replace('Bearer ', ''));
317
+ })
318
+ .transform(value => value.replace('Bearer ', ''))
319
+ .openapi({
320
+ type: 'string',
321
+ }),
322
+ jwt: ({ max }) => z
323
+ .string()
324
+ .trim()
325
+ .max(max)
326
+ .refine(value => validator_1.default.isJWT(value)),
327
+ // Cryptographic validators
328
+ ecPublicKey: () => z.base64({ length: 88, rawLength: 65 }),
329
+ ecCompressedPublicKey: () => z.base64({ length: 44, rawLength: 33 }),
330
+ uncompressedECPublicKey: () => {
331
+ let schema = zod_1.z.string();
332
+ schema = schema.length(88);
333
+ return schema.refine(value => {
334
+ if (!validator_1.default.isBase64(value))
335
+ return false;
336
+ return Buffer.from(value, 'base64').length === 65;
337
+ });
338
+ },
339
+ ecSignature: () => z.base64({ max: 120 }),
340
+ iv: () => z.base64({ length: 24, rawLength: 16 }),
341
+ mac: () => z.base64({ length: 44, rawLength: 32 }),
342
+ traceId: () => z.base64({ length: 24, rawLength: 16 }),
343
+ // Date validators
344
+ isoDateString: () => z
345
+ .string()
346
+ .trim()
347
+ .refine(value => {
348
+ return validator_1.default.isISO8601(value, {
349
+ strict: true,
350
+ });
351
+ })
352
+ .openapi({
353
+ type: 'string',
354
+ format: 'date-time',
355
+ example: '2021-01-30T08:30:00Z',
356
+ }),
357
+ rfc3339String: () => z
358
+ .string()
359
+ .trim()
360
+ .refine(value => {
361
+ return validator_1.default.isRFC3339(value);
362
+ })
363
+ .openapi({
364
+ type: 'string',
365
+ format: 'date-time',
366
+ example: '2021-01-30T08:30:00Z',
367
+ }),
368
+ iso8601Date: () => zod_1.z.string().refine(value => validator_1.default.isISO8601(value)),
369
+ // URL validators
370
+ urlHttpsString: () => zod_1.z
371
+ .string()
372
+ .max(255)
373
+ .refine(value => validator_1.default.isURL(value, {
374
+ require_protocol: true,
375
+ protocols: ['https'],
376
+ allow_protocol_relative_urls: false,
377
+ disallow_auth: true,
378
+ require_host: true,
379
+ }) && validator_1.default.isFQDN(new url_1.URL(value).hostname), {
380
+ message: 'invalid url',
381
+ }),
382
+ urlHttpOrHttpsString: () => zod_1.z
383
+ .string()
384
+ .max(255)
385
+ .refine(value => validator_1.default.isURL(value, {
386
+ require_protocol: true,
387
+ protocols: ['http', 'https'],
388
+ allow_protocol_relative_urls: false,
389
+ disallow_auth: true,
390
+ require_host: true,
391
+ }) && validator_1.default.isFQDN(new url_1.URL(value).hostname), {
392
+ message: 'invalid url',
393
+ }),
394
+ // Language validator
395
+ language: () => z.enum(language_1.AVAILABLE_LANGUAGES),
396
+ // DCC/DSGC validators (for COVID certificate validation)
397
+ dsgcPayload: () => zod_1.z.object({
398
+ certificates: zod_1.z.array(zod_1.z.object({
399
+ certificateType: zod_1.z.string(),
400
+ country: zod_1.z.string(),
401
+ kid: zod_1.z.string(),
402
+ rawData: zod_1.z.string(),
403
+ signature: zod_1.z.string(),
404
+ thumbprint: zod_1.z.string(),
405
+ timestamp: zod_1.z.string(),
406
+ })),
407
+ }),
408
+ dccListPayload: () => zod_1.z.array(zod_1.z.object({
409
+ identifier: zod_1.z.string(),
410
+ version: zod_1.z.string(),
411
+ country: zod_1.z.string().length(2),
412
+ hash: zod_1.z.string(),
413
+ })),
414
+ dccRulePayload: () => zod_1.z.object({
415
+ Identifier: zod_1.z.string(),
416
+ Type: zod_1.z.string(),
417
+ Country: zod_1.z.string().length(2),
418
+ Version: zod_1.z.string(),
419
+ SchemaVersion: zod_1.z.string(),
420
+ Engine: zod_1.z.string(),
421
+ EngineVersion: zod_1.z.string(),
422
+ CertificateType: zod_1.z.string(),
423
+ Description: zod_1.z.array(zod_1.z.object({ lang: zod_1.z.string().length(2), desc: zod_1.z.string() })),
424
+ ValidFrom: zod_1.z.string(),
425
+ ValidTo: zod_1.z.string(),
426
+ AffectedFields: zod_1.z.array(zod_1.z.string()),
427
+ Logic: zod_1.z.any(),
428
+ }),
429
+ };
430
+ exports.z = z;
431
+ __exportStar(require("zod"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucaapp/service-utils",
3
- "version": "4.9.1",
3
+ "version": "4.11.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [
@@ -28,6 +28,7 @@
28
28
  "@types/node": "22.13.11",
29
29
  "@types/response-time": "^2.3.9",
30
30
  "@types/swagger-ui-express": "4.1.8",
31
+ "@types/validator": "13.7.1",
31
32
  "axios": "^1.12.2",
32
33
  "axios-rate-limit": "^1.4.0",
33
34
  "axios-retry": "^4.5.0",
@@ -52,6 +53,7 @@
52
53
  "swagger-ui-express": "5.0.1",
53
54
  "url-value-parser": "^2.2.0",
54
55
  "uuid": "^9.0.0",
56
+ "validator": "13.7.0",
55
57
  "zod": "3.22.3"
56
58
  },
57
59
  "devDependencies": {