@lucaapp/service-utils 4.10.0 → 4.12.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 +2 -0
- package/dist/index.js +2 -0
- package/dist/lib/jobs/index.d.ts +2 -0
- package/dist/lib/jobs/index.js +18 -0
- package/dist/lib/jobs/jobTracker.d.ts +30 -0
- package/dist/lib/jobs/jobTracker.js +113 -0
- package/dist/lib/jobs/schemas.d.ts +258 -0
- package/dist/lib/jobs/schemas.js +67 -0
- package/dist/lib/serviceIdentity/serviceIdentity.js +12 -0
- package/dist/lib/validation.d.ts +430 -0
- package/dist/lib/validation.js +431 -0
- package/package.json +3 -1
package/dist/index.d.ts
CHANGED
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,18 @@
|
|
|
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);
|
|
18
|
+
__exportStar(require("./schemas"), exports);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Logger } from 'pino';
|
|
2
|
+
import { JsonSchema } from './schemas';
|
|
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;
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Schema for job execution parameters
|
|
4
|
+
*/
|
|
5
|
+
export declare const parametersSchema: z.ZodObject<{
|
|
6
|
+
job: z.ZodString;
|
|
7
|
+
method: z.ZodString;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
method: string;
|
|
10
|
+
job: string;
|
|
11
|
+
}, {
|
|
12
|
+
method: string;
|
|
13
|
+
job: string;
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Schema for job ID parameters
|
|
17
|
+
*/
|
|
18
|
+
export declare const jobIdParametersSchema: z.ZodObject<{
|
|
19
|
+
jobId: z.ZodString;
|
|
20
|
+
}, "strip", z.ZodTypeAny, {
|
|
21
|
+
jobId: string;
|
|
22
|
+
}, {
|
|
23
|
+
jobId: string;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Schema for JSON request/response bodies
|
|
27
|
+
* Allows any object structure to pass through
|
|
28
|
+
*/
|
|
29
|
+
export declare const jsonSchema: z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>;
|
|
30
|
+
export type JsonSchema = z.infer<typeof jsonSchema>;
|
|
31
|
+
/**
|
|
32
|
+
* Schema for running job information
|
|
33
|
+
*/
|
|
34
|
+
export declare const runningJobSchema: z.ZodObject<{
|
|
35
|
+
jobId: z.ZodString;
|
|
36
|
+
jobName: z.ZodString;
|
|
37
|
+
method: z.ZodString;
|
|
38
|
+
status: z.ZodEnum<["running", "completed", "failed"]>;
|
|
39
|
+
startedAt: z.ZodString;
|
|
40
|
+
completedAt: z.ZodOptional<z.ZodString>;
|
|
41
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
42
|
+
error: z.ZodOptional<z.ZodString>;
|
|
43
|
+
}, "strip", z.ZodTypeAny, {
|
|
44
|
+
status: "running" | "completed" | "failed";
|
|
45
|
+
method: string;
|
|
46
|
+
jobId: string;
|
|
47
|
+
jobName: string;
|
|
48
|
+
startedAt: string;
|
|
49
|
+
completedAt?: string | undefined;
|
|
50
|
+
duration?: number | undefined;
|
|
51
|
+
error?: string | undefined;
|
|
52
|
+
}, {
|
|
53
|
+
status: "running" | "completed" | "failed";
|
|
54
|
+
method: string;
|
|
55
|
+
jobId: string;
|
|
56
|
+
jobName: string;
|
|
57
|
+
startedAt: string;
|
|
58
|
+
completedAt?: string | undefined;
|
|
59
|
+
duration?: number | undefined;
|
|
60
|
+
error?: string | undefined;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Schema for job completion information
|
|
64
|
+
*/
|
|
65
|
+
export declare const jobCompletionSchema: z.ZodObject<{
|
|
66
|
+
completedAt: z.ZodString;
|
|
67
|
+
status: z.ZodEnum<["completed", "failed"]>;
|
|
68
|
+
error: z.ZodOptional<z.ZodString>;
|
|
69
|
+
}, "strip", z.ZodTypeAny, {
|
|
70
|
+
status: "completed" | "failed";
|
|
71
|
+
completedAt: string;
|
|
72
|
+
error?: string | undefined;
|
|
73
|
+
}, {
|
|
74
|
+
status: "completed" | "failed";
|
|
75
|
+
completedAt: string;
|
|
76
|
+
error?: string | undefined;
|
|
77
|
+
}>;
|
|
78
|
+
/**
|
|
79
|
+
* Schema for job metadata (path and methods)
|
|
80
|
+
*/
|
|
81
|
+
export declare const jobMetadataSchema: z.ZodObject<{
|
|
82
|
+
path: z.ZodString;
|
|
83
|
+
methods: z.ZodArray<z.ZodString, "many">;
|
|
84
|
+
lastExecution: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
85
|
+
startedAt: z.ZodString;
|
|
86
|
+
completedAt: z.ZodString;
|
|
87
|
+
duration: z.ZodNumber;
|
|
88
|
+
error: z.ZodNullable<z.ZodString>;
|
|
89
|
+
}, "strip", z.ZodTypeAny, {
|
|
90
|
+
error: string | null;
|
|
91
|
+
startedAt: string;
|
|
92
|
+
completedAt: string;
|
|
93
|
+
duration: number;
|
|
94
|
+
}, {
|
|
95
|
+
error: string | null;
|
|
96
|
+
startedAt: string;
|
|
97
|
+
completedAt: string;
|
|
98
|
+
duration: number;
|
|
99
|
+
}>>>;
|
|
100
|
+
}, "strip", z.ZodTypeAny, {
|
|
101
|
+
path: string;
|
|
102
|
+
methods: string[];
|
|
103
|
+
lastExecution?: {
|
|
104
|
+
error: string | null;
|
|
105
|
+
startedAt: string;
|
|
106
|
+
completedAt: string;
|
|
107
|
+
duration: number;
|
|
108
|
+
} | null | undefined;
|
|
109
|
+
}, {
|
|
110
|
+
path: string;
|
|
111
|
+
methods: string[];
|
|
112
|
+
lastExecution?: {
|
|
113
|
+
error: string | null;
|
|
114
|
+
startedAt: string;
|
|
115
|
+
completedAt: string;
|
|
116
|
+
duration: number;
|
|
117
|
+
} | null | undefined;
|
|
118
|
+
}>;
|
|
119
|
+
/**
|
|
120
|
+
* Schema for the complete jobs response
|
|
121
|
+
*/
|
|
122
|
+
export declare const jobsSchema: z.ZodObject<{
|
|
123
|
+
jobs: z.ZodArray<z.ZodObject<{
|
|
124
|
+
path: z.ZodString;
|
|
125
|
+
methods: z.ZodArray<z.ZodString, "many">;
|
|
126
|
+
lastExecution: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
127
|
+
startedAt: z.ZodString;
|
|
128
|
+
completedAt: z.ZodString;
|
|
129
|
+
duration: z.ZodNumber;
|
|
130
|
+
error: z.ZodNullable<z.ZodString>;
|
|
131
|
+
}, "strip", z.ZodTypeAny, {
|
|
132
|
+
error: string | null;
|
|
133
|
+
startedAt: string;
|
|
134
|
+
completedAt: string;
|
|
135
|
+
duration: number;
|
|
136
|
+
}, {
|
|
137
|
+
error: string | null;
|
|
138
|
+
startedAt: string;
|
|
139
|
+
completedAt: string;
|
|
140
|
+
duration: number;
|
|
141
|
+
}>>>;
|
|
142
|
+
}, "strip", z.ZodTypeAny, {
|
|
143
|
+
path: string;
|
|
144
|
+
methods: string[];
|
|
145
|
+
lastExecution?: {
|
|
146
|
+
error: string | null;
|
|
147
|
+
startedAt: string;
|
|
148
|
+
completedAt: string;
|
|
149
|
+
duration: number;
|
|
150
|
+
} | null | undefined;
|
|
151
|
+
}, {
|
|
152
|
+
path: string;
|
|
153
|
+
methods: string[];
|
|
154
|
+
lastExecution?: {
|
|
155
|
+
error: string | null;
|
|
156
|
+
startedAt: string;
|
|
157
|
+
completedAt: string;
|
|
158
|
+
duration: number;
|
|
159
|
+
} | null | undefined;
|
|
160
|
+
}>, "many">;
|
|
161
|
+
runningJobs: z.ZodArray<z.ZodObject<{
|
|
162
|
+
jobId: z.ZodString;
|
|
163
|
+
jobName: z.ZodString;
|
|
164
|
+
method: z.ZodString;
|
|
165
|
+
status: z.ZodEnum<["running", "completed", "failed"]>;
|
|
166
|
+
startedAt: z.ZodString;
|
|
167
|
+
completedAt: z.ZodOptional<z.ZodString>;
|
|
168
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
169
|
+
error: z.ZodOptional<z.ZodString>;
|
|
170
|
+
}, "strip", z.ZodTypeAny, {
|
|
171
|
+
status: "running" | "completed" | "failed";
|
|
172
|
+
method: string;
|
|
173
|
+
jobId: string;
|
|
174
|
+
jobName: string;
|
|
175
|
+
startedAt: string;
|
|
176
|
+
completedAt?: string | undefined;
|
|
177
|
+
duration?: number | undefined;
|
|
178
|
+
error?: string | undefined;
|
|
179
|
+
}, {
|
|
180
|
+
status: "running" | "completed" | "failed";
|
|
181
|
+
method: string;
|
|
182
|
+
jobId: string;
|
|
183
|
+
jobName: string;
|
|
184
|
+
startedAt: string;
|
|
185
|
+
completedAt?: string | undefined;
|
|
186
|
+
duration?: number | undefined;
|
|
187
|
+
error?: string | undefined;
|
|
188
|
+
}>, "many">;
|
|
189
|
+
jobCompletions: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
190
|
+
completedAt: z.ZodString;
|
|
191
|
+
status: z.ZodEnum<["completed", "failed"]>;
|
|
192
|
+
error: z.ZodOptional<z.ZodString>;
|
|
193
|
+
}, "strip", z.ZodTypeAny, {
|
|
194
|
+
status: "completed" | "failed";
|
|
195
|
+
completedAt: string;
|
|
196
|
+
error?: string | undefined;
|
|
197
|
+
}, {
|
|
198
|
+
status: "completed" | "failed";
|
|
199
|
+
completedAt: string;
|
|
200
|
+
error?: string | undefined;
|
|
201
|
+
}>>;
|
|
202
|
+
}, "strip", z.ZodTypeAny, {
|
|
203
|
+
jobs: {
|
|
204
|
+
path: string;
|
|
205
|
+
methods: string[];
|
|
206
|
+
lastExecution?: {
|
|
207
|
+
error: string | null;
|
|
208
|
+
startedAt: string;
|
|
209
|
+
completedAt: string;
|
|
210
|
+
duration: number;
|
|
211
|
+
} | null | undefined;
|
|
212
|
+
}[];
|
|
213
|
+
runningJobs: {
|
|
214
|
+
status: "running" | "completed" | "failed";
|
|
215
|
+
method: string;
|
|
216
|
+
jobId: string;
|
|
217
|
+
jobName: string;
|
|
218
|
+
startedAt: string;
|
|
219
|
+
completedAt?: string | undefined;
|
|
220
|
+
duration?: number | undefined;
|
|
221
|
+
error?: string | undefined;
|
|
222
|
+
}[];
|
|
223
|
+
jobCompletions: Record<string, {
|
|
224
|
+
status: "completed" | "failed";
|
|
225
|
+
completedAt: string;
|
|
226
|
+
error?: string | undefined;
|
|
227
|
+
}>;
|
|
228
|
+
}, {
|
|
229
|
+
jobs: {
|
|
230
|
+
path: string;
|
|
231
|
+
methods: string[];
|
|
232
|
+
lastExecution?: {
|
|
233
|
+
error: string | null;
|
|
234
|
+
startedAt: string;
|
|
235
|
+
completedAt: string;
|
|
236
|
+
duration: number;
|
|
237
|
+
} | null | undefined;
|
|
238
|
+
}[];
|
|
239
|
+
runningJobs: {
|
|
240
|
+
status: "running" | "completed" | "failed";
|
|
241
|
+
method: string;
|
|
242
|
+
jobId: string;
|
|
243
|
+
jobName: string;
|
|
244
|
+
startedAt: string;
|
|
245
|
+
completedAt?: string | undefined;
|
|
246
|
+
duration?: number | undefined;
|
|
247
|
+
error?: string | undefined;
|
|
248
|
+
}[];
|
|
249
|
+
jobCompletions: Record<string, {
|
|
250
|
+
status: "completed" | "failed";
|
|
251
|
+
completedAt: string;
|
|
252
|
+
error?: string | undefined;
|
|
253
|
+
}>;
|
|
254
|
+
}>;
|
|
255
|
+
export type JobsSchema = z.infer<typeof jobsSchema>;
|
|
256
|
+
export type RunningJobSchema = z.infer<typeof runningJobSchema>;
|
|
257
|
+
export type JobCompletionSchema = z.infer<typeof jobCompletionSchema>;
|
|
258
|
+
export type JobMetadataSchema = z.infer<typeof jobMetadataSchema>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.jobsSchema = exports.jobMetadataSchema = exports.jobCompletionSchema = exports.runningJobSchema = exports.jsonSchema = exports.jobIdParametersSchema = exports.parametersSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
/**
|
|
6
|
+
* Schema for job execution parameters
|
|
7
|
+
*/
|
|
8
|
+
exports.parametersSchema = zod_1.z.object({
|
|
9
|
+
job: zod_1.z.string(),
|
|
10
|
+
method: zod_1.z.string(),
|
|
11
|
+
});
|
|
12
|
+
/**
|
|
13
|
+
* Schema for job ID parameters
|
|
14
|
+
*/
|
|
15
|
+
exports.jobIdParametersSchema = zod_1.z.object({
|
|
16
|
+
jobId: zod_1.z.string(),
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* Schema for JSON request/response bodies
|
|
20
|
+
* Allows any object structure to pass through
|
|
21
|
+
*/
|
|
22
|
+
exports.jsonSchema = zod_1.z.object({}).passthrough();
|
|
23
|
+
/**
|
|
24
|
+
* Schema for running job information
|
|
25
|
+
*/
|
|
26
|
+
exports.runningJobSchema = zod_1.z.object({
|
|
27
|
+
jobId: zod_1.z.string(),
|
|
28
|
+
jobName: zod_1.z.string(),
|
|
29
|
+
method: zod_1.z.string(),
|
|
30
|
+
status: zod_1.z.enum(['running', 'completed', 'failed']),
|
|
31
|
+
startedAt: zod_1.z.string(),
|
|
32
|
+
completedAt: zod_1.z.string().optional(),
|
|
33
|
+
duration: zod_1.z.number().optional(),
|
|
34
|
+
error: zod_1.z.string().optional(),
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Schema for job completion information
|
|
38
|
+
*/
|
|
39
|
+
exports.jobCompletionSchema = zod_1.z.object({
|
|
40
|
+
completedAt: zod_1.z.string(),
|
|
41
|
+
status: zod_1.z.enum(['completed', 'failed']),
|
|
42
|
+
error: zod_1.z.string().optional(),
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* Schema for job metadata (path and methods)
|
|
46
|
+
*/
|
|
47
|
+
exports.jobMetadataSchema = zod_1.z.object({
|
|
48
|
+
path: zod_1.z.string(),
|
|
49
|
+
methods: zod_1.z.array(zod_1.z.string()),
|
|
50
|
+
lastExecution: zod_1.z
|
|
51
|
+
.object({
|
|
52
|
+
startedAt: zod_1.z.string(),
|
|
53
|
+
completedAt: zod_1.z.string(),
|
|
54
|
+
duration: zod_1.z.number(),
|
|
55
|
+
error: zod_1.z.string().nullable(),
|
|
56
|
+
})
|
|
57
|
+
.nullable()
|
|
58
|
+
.optional(),
|
|
59
|
+
});
|
|
60
|
+
/**
|
|
61
|
+
* Schema for the complete jobs response
|
|
62
|
+
*/
|
|
63
|
+
exports.jobsSchema = zod_1.z.object({
|
|
64
|
+
jobs: zod_1.z.array(exports.jobMetadataSchema),
|
|
65
|
+
runningJobs: zod_1.z.array(exports.runningJobSchema),
|
|
66
|
+
jobCompletions: zod_1.z.record(exports.jobCompletionSchema),
|
|
67
|
+
});
|
|
@@ -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) &&
|