@holo-js/queue 0.1.4 → 0.1.6
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 +15 -10
- package/dist/index.mjs +154 -44
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -76,10 +76,11 @@ interface QueueRegisteredJob<TPayload extends QueueJsonValue = QueueJsonValue, T
|
|
|
76
76
|
interface HoloQueueJobRegistry {
|
|
77
77
|
}
|
|
78
78
|
type KnownQueueJobName = Extract<keyof HoloQueueJobRegistry, string>;
|
|
79
|
-
type
|
|
80
|
-
type
|
|
81
|
-
type
|
|
82
|
-
type
|
|
79
|
+
type DynamicQueueJobName<TJobName extends string> = [KnownQueueJobName] extends [never] ? TJobName : TJobName extends KnownQueueJobName ? never : KnownQueueJobName extends TJobName ? never : TJobName;
|
|
80
|
+
type ResolveRegisteredQueueJobDefinition<TJobName extends string> = TJobName extends KnownQueueJobName ? HoloQueueJobRegistry[TJobName] extends QueueJobDefinition<infer TPayload, infer TResult> ? QueueJobDefinition<TPayload, TResult> : QueueJobDefinition : QueueJobDefinition;
|
|
81
|
+
type ExportedQueueJobDefinition<TValue> = TValue extends QueueJobDefinition<infer TPayload, infer TResult> ? QueueJobDefinition<TPayload, TResult> : QueueJobDefinition;
|
|
82
|
+
type QueuePayloadFor<TJobName extends string> = ResolveRegisteredQueueJobDefinition<TJobName> extends QueueJobDefinition<infer TPayload, infer _TResult> ? TPayload : QueueJsonValue;
|
|
83
|
+
type QueueResultFor<TJobName extends string> = ResolveRegisteredQueueJobDefinition<TJobName> extends QueueJobDefinition<infer _TPayload, infer TResult> ? TResult : unknown;
|
|
83
84
|
interface RegisterQueueJobOptions {
|
|
84
85
|
readonly name?: string;
|
|
85
86
|
readonly sourcePath?: string;
|
|
@@ -100,9 +101,9 @@ interface QueuePendingDispatch<TPayload extends QueueJsonValue = QueueJsonValue>
|
|
|
100
101
|
interface QueueConnectionFacade {
|
|
101
102
|
readonly name: string;
|
|
102
103
|
dispatch<TJobName extends KnownQueueJobName>(jobName: TJobName, payload: QueuePayloadFor<TJobName>, options?: QueueDispatchOptions): QueuePendingDispatch<QueuePayloadFor<TJobName>>;
|
|
103
|
-
dispatch<
|
|
104
|
+
dispatch<TPayload extends QueueJsonValue = QueueJsonValue, const TJobName extends string = string>(jobName: TJobName & DynamicQueueJobName<TJobName>, payload: TPayload, options?: QueueDispatchOptions): QueuePendingDispatch<TPayload>;
|
|
104
105
|
dispatchSync<TJobName extends KnownQueueJobName>(jobName: TJobName, payload: QueuePayloadFor<TJobName>): Promise<QueueResultFor<TJobName>>;
|
|
105
|
-
dispatchSync<TPayload extends QueueJsonValue = QueueJsonValue, TResult = unknown>(jobName:
|
|
106
|
+
dispatchSync<TPayload extends QueueJsonValue = QueueJsonValue, TResult = unknown, const TJobName extends string = string>(jobName: TJobName & DynamicQueueJobName<TJobName>, payload: TPayload): Promise<TResult>;
|
|
106
107
|
}
|
|
107
108
|
interface QueueEnqueueResult {
|
|
108
109
|
readonly jobId: string;
|
|
@@ -486,10 +487,12 @@ declare function shutdownQueueRuntime(): Promise<void>;
|
|
|
486
487
|
declare function resetQueueRuntime(): void;
|
|
487
488
|
declare function getQueueRuntime(): QueueRuntimeBinding;
|
|
488
489
|
declare function useQueueConnection(name?: string): QueueConnectionFacade;
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
declare function
|
|
492
|
-
declare function
|
|
490
|
+
type KnownRuntimeQueueJobName = Extract<keyof HoloQueueJobRegistry, string>;
|
|
491
|
+
type DynamicRuntimeQueueJobName<TJobName extends string> = [KnownRuntimeQueueJobName] extends [never] ? TJobName : TJobName extends KnownRuntimeQueueJobName ? never : KnownRuntimeQueueJobName extends TJobName ? never : TJobName;
|
|
492
|
+
declare function dispatchSync<TJobName extends KnownRuntimeQueueJobName>(jobName: TJobName, payload: QueuePayloadFor<TJobName>, options?: QueueDispatchOptions): Promise<QueueResultFor<TJobName>>;
|
|
493
|
+
declare function dispatchSync<TPayload extends QueueJsonValue = QueueJsonValue, TResult = unknown, const TJobName extends string = string>(jobName: TJobName & DynamicRuntimeQueueJobName<TJobName>, payload: TPayload, options?: QueueDispatchOptions): Promise<TResult>;
|
|
494
|
+
declare function dispatch<TJobName extends KnownRuntimeQueueJobName>(jobName: TJobName, payload: QueuePayloadFor<TJobName>, options?: QueueDispatchOptions): QueuePendingDispatch<QueuePayloadFor<TJobName>>;
|
|
495
|
+
declare function dispatch<TPayload extends QueueJsonValue = QueueJsonValue, const TJobName extends string = string>(jobName: TJobName & DynamicRuntimeQueueJobName<TJobName>, payload: TPayload, options?: QueueDispatchOptions): QueuePendingDispatch<TPayload>;
|
|
493
496
|
declare const Queue: {
|
|
494
497
|
connection(name?: string): QueueConnectionFacade;
|
|
495
498
|
dispatch: typeof dispatch;
|
|
@@ -578,6 +581,7 @@ declare function normalizeNonNegativeInteger(value: number | undefined, label: s
|
|
|
578
581
|
declare function normalizePositiveInteger(value: number | undefined, label: string): number | undefined;
|
|
579
582
|
declare function createWorkerEvent(envelope: QueueJobEnvelope, maxAttempts: number): QueueWorkerJobEvent;
|
|
580
583
|
declare function resolveRetryDelaySeconds(definition: QueueJobDefinition, attempt: number): number | undefined;
|
|
584
|
+
declare function runWorkerHook(jobName: string, hookName: 'onJobFailed' | 'onJobProcessed' | 'onJobReleased', callback: () => void | Promise<void>): Promise<void>;
|
|
581
585
|
declare function runWithTimeout<TResult>(callback: Promise<TResult>, jobName: string, timeoutSeconds: number | undefined, onTimeout?: () => void): Promise<TResult>;
|
|
582
586
|
declare function processReservedJob(driver: QueueAsyncDriver, reserved: QueueReservedJob<QueueJsonValue>, options: Pick<QueueWorkerRunOptions, 'timeout' | 'tries' | 'onJobFailed' | 'onJobProcessed' | 'onJobReleased'>): Promise<QueueWorkerProcessingOutcome>;
|
|
583
587
|
declare function resolveWorkerStopReason(options: QueueWorkerOptions, state: {
|
|
@@ -597,6 +601,7 @@ declare const queueWorkerInternals: {
|
|
|
597
601
|
resolveRetryDelaySeconds: typeof resolveRetryDelaySeconds;
|
|
598
602
|
resolveWorkerStopReason: typeof resolveWorkerStopReason;
|
|
599
603
|
runWithTimeout: typeof runWithTimeout;
|
|
604
|
+
runWorkerHook: typeof runWorkerHook;
|
|
600
605
|
sleep: typeof sleep;
|
|
601
606
|
};
|
|
602
607
|
|
package/dist/index.mjs
CHANGED
|
@@ -89,6 +89,9 @@ var DEFAULT_QUEUE_SLEEP = 1;
|
|
|
89
89
|
var DEFAULT_FAILED_JOBS_CONNECTION = "default";
|
|
90
90
|
var DEFAULT_FAILED_JOBS_TABLE = "failed_jobs";
|
|
91
91
|
var DEFAULT_DATABASE_QUEUE_TABLE = "jobs";
|
|
92
|
+
var DEFAULT_REDIS_HOST = "127.0.0.1";
|
|
93
|
+
var DEFAULT_REDIS_PORT = 6379;
|
|
94
|
+
var DEFAULT_REDIS_DB = 0;
|
|
92
95
|
var DEFAULT_QUEUE_CONFIG = Object.freeze({
|
|
93
96
|
default: DEFAULT_QUEUE_CONNECTION,
|
|
94
97
|
failed: Object.freeze({
|
|
@@ -108,14 +111,18 @@ function parseInteger(value, fallback, label, options = {}) {
|
|
|
108
111
|
if (typeof value === "undefined") {
|
|
109
112
|
return fallback;
|
|
110
113
|
}
|
|
111
|
-
const normalized = typeof value === "number" ? value :
|
|
112
|
-
if (
|
|
114
|
+
const normalized = typeof value === "number" ? value : value.trim();
|
|
115
|
+
if (typeof normalized === "string" && !/^[+-]?\d+$/.test(normalized)) {
|
|
113
116
|
throw new Error(`[Holo Queue] ${label} must be an integer.`);
|
|
114
117
|
}
|
|
115
|
-
|
|
118
|
+
const integer = typeof normalized === "number" ? normalized : Number(normalized);
|
|
119
|
+
if (!Number.isInteger(integer)) {
|
|
120
|
+
throw new Error(`[Holo Queue] ${label} must be an integer.`);
|
|
121
|
+
}
|
|
122
|
+
if (typeof options.minimum === "number" && integer < options.minimum) {
|
|
116
123
|
throw new Error(`[Holo Queue] ${label} must be greater than or equal to ${options.minimum}.`);
|
|
117
124
|
}
|
|
118
|
-
return
|
|
125
|
+
return integer;
|
|
119
126
|
}
|
|
120
127
|
function normalizeConnectionName(value, label) {
|
|
121
128
|
const normalized = value?.trim();
|
|
@@ -127,6 +134,9 @@ function normalizeConnectionName(value, label) {
|
|
|
127
134
|
function normalizeQueueName(value) {
|
|
128
135
|
return value?.trim() || DEFAULT_QUEUE_NAME;
|
|
129
136
|
}
|
|
137
|
+
function normalizeOptionalRedisString(value) {
|
|
138
|
+
return value?.trim() || void 0;
|
|
139
|
+
}
|
|
130
140
|
function normalizeSyncConnection(name, config) {
|
|
131
141
|
return Object.freeze({
|
|
132
142
|
name,
|
|
@@ -144,24 +154,92 @@ function resolveSharedRedisConnection(redisConfig, connectionName) {
|
|
|
144
154
|
}
|
|
145
155
|
return resolvedConnection;
|
|
146
156
|
}
|
|
157
|
+
function parseRedisDatabaseFromUrl(url, label) {
|
|
158
|
+
if (typeof url === "undefined") {
|
|
159
|
+
return void 0;
|
|
160
|
+
}
|
|
161
|
+
let parsed;
|
|
162
|
+
try {
|
|
163
|
+
parsed = new URL(url);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
throw new Error(`[Holo Queue] ${label} is invalid: ${String(error)}`);
|
|
166
|
+
}
|
|
167
|
+
if (parsed.protocol !== "redis:" && parsed.protocol !== "rediss:") {
|
|
168
|
+
throw new Error(`[Holo Queue] ${label} is invalid: unsupported protocol "${parsed.protocol}".`);
|
|
169
|
+
}
|
|
170
|
+
const pathname = parsed.pathname.replace(/^\/+/, "");
|
|
171
|
+
if (!pathname) {
|
|
172
|
+
return void 0;
|
|
173
|
+
}
|
|
174
|
+
const [databaseSegment] = pathname.split("/");
|
|
175
|
+
if (!databaseSegment || !/^\d+$/.test(databaseSegment) || pathname !== databaseSegment) {
|
|
176
|
+
throw new Error(`[Holo Queue] ${label} must include at most one integer database path segment.`);
|
|
177
|
+
}
|
|
178
|
+
return Number(databaseSegment);
|
|
179
|
+
}
|
|
180
|
+
function normalizeRedisClusterNodes(connectionName, nodes) {
|
|
181
|
+
if (!nodes || nodes.length === 0) {
|
|
182
|
+
return void 0;
|
|
183
|
+
}
|
|
184
|
+
return Object.freeze(nodes.map((node, index) => {
|
|
185
|
+
const label = `queue connection "${connectionName}" redis cluster node ${index + 1}`;
|
|
186
|
+
const url = normalizeOptionalRedisString(node.url);
|
|
187
|
+
if (typeof url !== "undefined") {
|
|
188
|
+
const database = parseRedisDatabaseFromUrl(url, `${label} url`);
|
|
189
|
+
if (typeof database !== "undefined") {
|
|
190
|
+
throw new Error(`[Holo Queue] ${label} url cannot include a database path in cluster mode.`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return Object.freeze({
|
|
194
|
+
...typeof url === "undefined" ? {} : { url },
|
|
195
|
+
host: normalizeOptionalRedisString(node.host) ?? DEFAULT_REDIS_HOST,
|
|
196
|
+
port: parseInteger(node.port, DEFAULT_REDIS_PORT, `${label} port`, {
|
|
197
|
+
minimum: 1
|
|
198
|
+
})
|
|
199
|
+
});
|
|
200
|
+
}));
|
|
201
|
+
}
|
|
202
|
+
function normalizeRedisOptions(connectionName, base, overrides) {
|
|
203
|
+
const hasTargetOverride = typeof overrides?.url !== "undefined" || typeof overrides?.clusters !== "undefined" || typeof overrides?.host !== "undefined" || typeof overrides?.port !== "undefined";
|
|
204
|
+
const url = normalizeOptionalRedisString(overrides?.url) ?? (hasTargetOverride ? void 0 : base?.url);
|
|
205
|
+
const clusters = typeof overrides?.clusters === "undefined" ? hasTargetOverride ? void 0 : base?.clusters : normalizeRedisClusterNodes(connectionName, overrides.clusters);
|
|
206
|
+
const dbFromUrl = parseRedisDatabaseFromUrl(url, `queue connection "${connectionName}" redis url`);
|
|
207
|
+
const db = parseInteger(overrides?.db ?? base?.db ?? dbFromUrl, DEFAULT_REDIS_DB, `queue connection "${connectionName}" redis db`, {
|
|
208
|
+
minimum: 0
|
|
209
|
+
});
|
|
210
|
+
if (typeof clusters !== "undefined" && db !== 0) {
|
|
211
|
+
throw new Error(`[Holo Queue] queue connection "${connectionName}" cannot select redis.db=${db} in cluster mode; Redis Cluster only supports database 0.`);
|
|
212
|
+
}
|
|
213
|
+
return Object.freeze({
|
|
214
|
+
...typeof url === "undefined" ? {} : { url },
|
|
215
|
+
...typeof clusters === "undefined" ? {} : { clusters },
|
|
216
|
+
host: normalizeOptionalRedisString(overrides?.host) ?? base?.host ?? DEFAULT_REDIS_HOST,
|
|
217
|
+
port: parseInteger(overrides?.port ?? base?.port, DEFAULT_REDIS_PORT, `queue connection "${connectionName}" redis port`, {
|
|
218
|
+
minimum: 1
|
|
219
|
+
}),
|
|
220
|
+
password: normalizeOptionalRedisString(overrides?.password) ?? base?.password,
|
|
221
|
+
username: normalizeOptionalRedisString(overrides?.username) ?? base?.username,
|
|
222
|
+
db
|
|
223
|
+
});
|
|
224
|
+
}
|
|
147
225
|
function normalizeRedisConnection(name, config, redisConfig) {
|
|
148
226
|
const explicitConnectionName = config.connection?.trim();
|
|
149
227
|
const connectionName = explicitConnectionName || redisConfig?.default;
|
|
150
|
-
if (!connectionName) {
|
|
228
|
+
if (!connectionName && !config.redis) {
|
|
151
229
|
throw new Error(
|
|
152
230
|
`[Holo Queue] Queue Redis connection "${name}" requires a shared Redis config with a default connection or an explicit connection name.`
|
|
153
231
|
);
|
|
154
232
|
}
|
|
155
|
-
if (!redisConfig) {
|
|
233
|
+
if (!redisConfig && !config.redis) {
|
|
156
234
|
throw new Error(
|
|
157
235
|
`[Holo Queue] Queue Redis connection "${name}" references shared Redis connection "${connectionName}" but no shared Redis config was provided.`
|
|
158
236
|
);
|
|
159
237
|
}
|
|
160
|
-
const resolvedRedisConnection = resolveSharedRedisConnection(redisConfig, connectionName);
|
|
238
|
+
const resolvedRedisConnection = redisConfig && connectionName ? resolveSharedRedisConnection(redisConfig, connectionName) : void 0;
|
|
161
239
|
return Object.freeze({
|
|
162
240
|
name,
|
|
163
241
|
driver: "redis",
|
|
164
|
-
connection: resolvedRedisConnection
|
|
242
|
+
connection: resolvedRedisConnection?.name ?? connectionName ?? name,
|
|
165
243
|
queue: normalizeQueueName(config.queue),
|
|
166
244
|
retryAfter: parseInteger(config.retryAfter, DEFAULT_QUEUE_RETRY_AFTER, `queue connection "${name}" retryAfter`, {
|
|
167
245
|
minimum: 0
|
|
@@ -169,15 +247,7 @@ function normalizeRedisConnection(name, config, redisConfig) {
|
|
|
169
247
|
blockFor: parseInteger(config.blockFor, DEFAULT_QUEUE_BLOCK_FOR, `queue connection "${name}" blockFor`, {
|
|
170
248
|
minimum: 0
|
|
171
249
|
}),
|
|
172
|
-
redis:
|
|
173
|
-
...typeof resolvedRedisConnection.url === "undefined" ? {} : { url: resolvedRedisConnection.url },
|
|
174
|
-
...typeof resolvedRedisConnection.clusters === "undefined" ? {} : { clusters: resolvedRedisConnection.clusters },
|
|
175
|
-
host: resolvedRedisConnection.host,
|
|
176
|
-
port: resolvedRedisConnection.port,
|
|
177
|
-
password: resolvedRedisConnection.password,
|
|
178
|
-
username: resolvedRedisConnection.username,
|
|
179
|
-
db: resolvedRedisConnection.db
|
|
180
|
-
})
|
|
250
|
+
redis: normalizeRedisOptions(name, resolvedRedisConnection, config.redis)
|
|
181
251
|
});
|
|
182
252
|
}
|
|
183
253
|
function normalizeDatabaseConnection(name, config) {
|
|
@@ -1200,6 +1270,17 @@ function resolveRetryDelaySeconds(definition, attempt) {
|
|
|
1200
1270
|
}
|
|
1201
1271
|
return backoff[Math.min(Math.max(attempt - 1, 0), backoff.length - 1)];
|
|
1202
1272
|
}
|
|
1273
|
+
function normalizeHookError(error) {
|
|
1274
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
1275
|
+
}
|
|
1276
|
+
async function runWorkerHook(jobName, hookName, callback) {
|
|
1277
|
+
try {
|
|
1278
|
+
await callback();
|
|
1279
|
+
} catch (error) {
|
|
1280
|
+
const resolvedError = normalizeHookError(error);
|
|
1281
|
+
console.warn(`[Holo Queue] ${hookName} hook failed for job "${jobName}": ${resolvedError.message}`);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1203
1284
|
async function runWithTimeout(callback, jobName, timeoutSeconds, onTimeout) {
|
|
1204
1285
|
if (typeof timeoutSeconds === "undefined") {
|
|
1205
1286
|
return await callback;
|
|
@@ -1258,10 +1339,14 @@ async function processReservedJob(driver, reserved, options) {
|
|
|
1258
1339
|
);
|
|
1259
1340
|
if (action === "release") {
|
|
1260
1341
|
await driver.release(reserved, typeof requestedReleaseDelay === "number" ? { delaySeconds: requestedReleaseDelay } : void 0);
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1342
|
+
if (options.onJobReleased) {
|
|
1343
|
+
await runWorkerHook(envelope.name, "onJobReleased", async () => {
|
|
1344
|
+
await options.onJobReleased?.({
|
|
1345
|
+
...event,
|
|
1346
|
+
...typeof requestedReleaseDelay === "number" ? { delaySeconds: requestedReleaseDelay } : {}
|
|
1347
|
+
});
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1265
1350
|
return {
|
|
1266
1351
|
kind: "released",
|
|
1267
1352
|
...typeof requestedReleaseDelay === "number" ? { delaySeconds: requestedReleaseDelay } : {}
|
|
@@ -1274,17 +1359,25 @@ async function processReservedJob(driver, reserved, options) {
|
|
|
1274
1359
|
await queueRuntimeInternals.executeRegisteredQueueJobFailedHook(envelope, failure, {
|
|
1275
1360
|
maxAttempts
|
|
1276
1361
|
});
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1362
|
+
if (options.onJobFailed) {
|
|
1363
|
+
await runWorkerHook(envelope.name, "onJobFailed", async () => {
|
|
1364
|
+
await options.onJobFailed?.({
|
|
1365
|
+
...event,
|
|
1366
|
+
error: failure
|
|
1367
|
+
});
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1281
1370
|
return {
|
|
1282
1371
|
kind: "failed",
|
|
1283
1372
|
error: failure
|
|
1284
1373
|
};
|
|
1285
1374
|
}
|
|
1286
1375
|
await driver.acknowledge(reserved);
|
|
1287
|
-
|
|
1376
|
+
if (options.onJobProcessed) {
|
|
1377
|
+
await runWorkerHook(envelope.name, "onJobProcessed", async () => {
|
|
1378
|
+
await options.onJobProcessed?.(event);
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1288
1381
|
return { kind: "processed" };
|
|
1289
1382
|
} catch (error) {
|
|
1290
1383
|
const resolvedError = error instanceof Error ? error : new Error(String(error));
|
|
@@ -1295,10 +1388,14 @@ async function processReservedJob(driver, reserved, options) {
|
|
|
1295
1388
|
await queueRuntimeInternals.executeRegisteredQueueJobFailedHook(envelope, failure, {
|
|
1296
1389
|
maxAttempts
|
|
1297
1390
|
});
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1391
|
+
if (options.onJobFailed) {
|
|
1392
|
+
await runWorkerHook(envelope.name, "onJobFailed", async () => {
|
|
1393
|
+
await options.onJobFailed?.({
|
|
1394
|
+
...event,
|
|
1395
|
+
error: failure
|
|
1396
|
+
});
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1302
1399
|
return {
|
|
1303
1400
|
kind: "failed",
|
|
1304
1401
|
error: failure
|
|
@@ -1306,11 +1403,15 @@ async function processReservedJob(driver, reserved, options) {
|
|
|
1306
1403
|
}
|
|
1307
1404
|
if (action === "release") {
|
|
1308
1405
|
await driver.release(reserved, typeof requestedReleaseDelay === "number" ? { delaySeconds: requestedReleaseDelay } : void 0);
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1406
|
+
if (options.onJobReleased) {
|
|
1407
|
+
await runWorkerHook(envelope.name, "onJobReleased", async () => {
|
|
1408
|
+
await options.onJobReleased?.({
|
|
1409
|
+
...event,
|
|
1410
|
+
...typeof requestedReleaseDelay === "number" ? { delaySeconds: requestedReleaseDelay } : {},
|
|
1411
|
+
error: failure
|
|
1412
|
+
});
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1314
1415
|
return {
|
|
1315
1416
|
kind: "released",
|
|
1316
1417
|
...typeof requestedReleaseDelay === "number" ? { delaySeconds: requestedReleaseDelay } : {},
|
|
@@ -1323,10 +1424,14 @@ async function processReservedJob(driver, reserved, options) {
|
|
|
1323
1424
|
await queueRuntimeInternals.executeRegisteredQueueJobFailedHook(envelope, failure, {
|
|
1324
1425
|
maxAttempts
|
|
1325
1426
|
});
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1427
|
+
if (options.onJobFailed) {
|
|
1428
|
+
await runWorkerHook(envelope.name, "onJobFailed", async () => {
|
|
1429
|
+
await options.onJobFailed?.({
|
|
1430
|
+
...event,
|
|
1431
|
+
error: failure
|
|
1432
|
+
});
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1330
1435
|
return {
|
|
1331
1436
|
kind: "failed",
|
|
1332
1437
|
error: failure
|
|
@@ -1334,11 +1439,15 @@ async function processReservedJob(driver, reserved, options) {
|
|
|
1334
1439
|
}
|
|
1335
1440
|
const delaySeconds = resolveRetryDelaySeconds(definition, event.attempt);
|
|
1336
1441
|
await driver.release(reserved, typeof delaySeconds === "number" ? { delaySeconds } : void 0);
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1442
|
+
if (options.onJobReleased) {
|
|
1443
|
+
await runWorkerHook(envelope.name, "onJobReleased", async () => {
|
|
1444
|
+
await options.onJobReleased?.({
|
|
1445
|
+
...event,
|
|
1446
|
+
...typeof delaySeconds === "number" ? { delaySeconds } : {},
|
|
1447
|
+
error: failure
|
|
1448
|
+
});
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1342
1451
|
return {
|
|
1343
1452
|
kind: "released",
|
|
1344
1453
|
...typeof delaySeconds === "number" ? { delaySeconds } : {},
|
|
@@ -1464,6 +1573,7 @@ var queueWorkerInternals = {
|
|
|
1464
1573
|
resolveRetryDelaySeconds,
|
|
1465
1574
|
resolveWorkerStopReason,
|
|
1466
1575
|
runWithTimeout,
|
|
1576
|
+
runWorkerHook,
|
|
1467
1577
|
sleep
|
|
1468
1578
|
};
|
|
1469
1579
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holo-js/queue",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Holo-JS Framework - queue contracts and config helpers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"test": "vitest --run"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@holo-js/queue-redis": "^0.1.
|
|
26
|
+
"@holo-js/queue-redis": "^0.1.6"
|
|
27
27
|
},
|
|
28
28
|
"peerDependenciesMeta": {
|
|
29
29
|
"@holo-js/queue-redis": {
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@holo-js/queue-redis": "
|
|
34
|
+
"@holo-js/queue-redis": "^0.1.6",
|
|
35
35
|
"@types/node": "^22.10.2",
|
|
36
36
|
"tsup": "^8.3.5",
|
|
37
37
|
"typescript": "^5.7.2",
|
|
38
|
-
"vitest": "^
|
|
38
|
+
"vitest": "^4.1.5"
|
|
39
39
|
}
|
|
40
40
|
}
|