@spooled/sdk 1.0.34 → 1.0.36
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/README.md +49 -4
- package/dist/{index-CYEamGDj.d.cts → index-CNhKw4vH.d.cts} +6 -5
- package/dist/{index-CYEamGDj.d.ts → index-CNhKw4vH.d.ts} +6 -5
- package/dist/index.cjs +120 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +120 -31
- package/dist/index.js.map +1 -1
- package/dist/worker/index.cjs +24 -20
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +1 -1
- package/dist/worker/index.d.ts +1 -1
- package/dist/worker/index.js +24 -20
- package/dist/worker/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -321,7 +321,10 @@ All operations automatically enforce tier-based limits:
|
|
|
321
321
|
- ✅ Webhook creation
|
|
322
322
|
|
|
323
323
|
When a plan quota or limit is exceeded, you'll receive an `HTTP 429` response with
|
|
324
|
-
`code: "QUOTA_EXCEEDED"
|
|
324
|
+
`code: "QUOTA_EXCEEDED"`. The SDK reliably preserves the HTTP status, error code, message,
|
|
325
|
+
request ID, and rate-limit headers. `error.details` is populated only when the server nests
|
|
326
|
+
metadata under a JSON `details` field; quota fields sent at the top level of the server payload
|
|
327
|
+
(such as `resource`, `current`, `limit`, and `plan`) are not currently exposed by the SDK.
|
|
325
328
|
|
|
326
329
|
```typescript
|
|
327
330
|
import { RateLimitError } from '@spooled/sdk';
|
|
@@ -330,9 +333,10 @@ try {
|
|
|
330
333
|
await client.jobs.create({ /* ... */ });
|
|
331
334
|
} catch (error) {
|
|
332
335
|
if (error instanceof RateLimitError && error.code === 'QUOTA_EXCEEDED') {
|
|
333
|
-
// Plan quota reached (HTTP 429); details carry resource/current/limit/plan
|
|
334
336
|
console.log(`Plan limit: ${error.message}`);
|
|
335
|
-
console.log(
|
|
337
|
+
console.log(`Request ID: ${error.requestId ?? 'not provided'}`);
|
|
338
|
+
// Present only if this server response included a nested `details` object.
|
|
339
|
+
if (error.details) console.log('Details:', error.details);
|
|
336
340
|
}
|
|
337
341
|
}
|
|
338
342
|
```
|
|
@@ -406,12 +410,13 @@ const { jobs } = await grpcClient.queue.dequeue({
|
|
|
406
410
|
leaseDurationSecs: 300
|
|
407
411
|
});
|
|
408
412
|
|
|
409
|
-
// Process and complete
|
|
413
|
+
// Process and complete. Echo leaseId when present so current servers can fence stale executions; legacy servers may omit it and then use worker-ID-only fencing.
|
|
410
414
|
for (const job of jobs) {
|
|
411
415
|
await processJob(job);
|
|
412
416
|
await grpcClient.queue.complete({
|
|
413
417
|
jobId: job.id,
|
|
414
418
|
workerId,
|
|
419
|
+
leaseId: job.leaseId ?? undefined,
|
|
415
420
|
result: { success: true }
|
|
416
421
|
});
|
|
417
422
|
}
|
|
@@ -441,6 +446,46 @@ import type {
|
|
|
441
446
|
} from '@spooled/sdk';
|
|
442
447
|
```
|
|
443
448
|
|
|
449
|
+
## Development and releases
|
|
450
|
+
|
|
451
|
+
Before opening a pull request or tagging a release, run the same checks used by the package and publish workflow:
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
npm ci
|
|
455
|
+
npm run lint
|
|
456
|
+
npm run typecheck
|
|
457
|
+
npm run build
|
|
458
|
+
npm test
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
Production-safe API verification is separate and credentialed: `npm run verify:production`. Integration tests use `npm run test:integration` against a local or isolated test backend.
|
|
462
|
+
|
|
463
|
+
Maintainers release by pushing a matching `vX.Y.Z` tag. The tag workflow verifies all same-artifact version surfaces before publishing through npm Trusted Publishing (OIDC) with provenance.
|
|
464
|
+
|
|
465
|
+
### Maintainer release checklist (advisory)
|
|
466
|
+
|
|
467
|
+
This checklist records evidence and does not block ordinary commits, experiments, or compatibility research. A mismatch among values that identify the same npm artifact is a release error and cannot be waived for publication. Publishing the SDK is not a service deployment; consumers report new metadata only after upgrading and restarting or redeploying their applications.
|
|
468
|
+
|
|
469
|
+
Before pushing `vX.Y.Z`:
|
|
470
|
+
|
|
471
|
+
- [ ] Set `package.json.version` to `X.Y.Z`.
|
|
472
|
+
- [ ] Regenerate `package-lock.json`; verify both top-level `version` and `packages[""].version` are `X.Y.Z`.
|
|
473
|
+
- [ ] Set `SDK_VERSION` and the default User-Agent in `src/config.ts` to `X.Y.Z`.
|
|
474
|
+
- [ ] Confirm `SpooledWorker` derives its default registration version from `SDK_VERSION`.
|
|
475
|
+
- [ ] Add `## [X.Y.Z] - YYYY-MM-DD` to `CHANGELOG.md` and review version-bearing worker documentation.
|
|
476
|
+
- [ ] Run `npm ci`, lint, typecheck, build, tests, and `npm pack --dry-run`.
|
|
477
|
+
- [ ] Review the packed file list and compiled output; confirm the package contains no local logs, caches, credentials, or unintended generated files.
|
|
478
|
+
- [ ] Record any cross-repository compatibility difference separately; backend, SDK, and application versions are not required to match numerically.
|
|
479
|
+
|
|
480
|
+
Publish and verify:
|
|
481
|
+
|
|
482
|
+
- [ ] Create one immutable `vX.Y.Z` tag on the reviewed commit and never move or recreate a pushed tag. Fix forward with a new patch version after external visibility.
|
|
483
|
+
- [ ] Record the tag commit and publish workflow URL.
|
|
484
|
+
- [ ] Verify `npm view @spooled/sdk@X.Y.Z version gitHead dist.integrity dist.attestations --json` against the tag and commit.
|
|
485
|
+
- [ ] Confirm `latest` points to `X.Y.Z` when intended and inspect the published tarball's `SDK_VERSION`, User-Agent, and worker registration default.
|
|
486
|
+
- [ ] Confirm npm provenance names this repository, `.github/workflows/publish.yml`, `refs/tags/vX.Y.Z`, and the release commit.
|
|
487
|
+
- [ ] Record registry publication separately from consumer rollout, backend deployment, or documentation deployment.
|
|
488
|
+
|
|
444
489
|
## License
|
|
445
490
|
|
|
446
491
|
Apache-2.0 - see [LICENSE](LICENSE) for details.
|
|
@@ -107,11 +107,11 @@ declare const DEFAULT_CONFIG: {
|
|
|
107
107
|
successThreshold: number;
|
|
108
108
|
timeout: number;
|
|
109
109
|
};
|
|
110
|
-
readonly userAgent: "@spooled/sdk-nodejs/1.0.
|
|
110
|
+
readonly userAgent: "@spooled/sdk-nodejs/1.0.36";
|
|
111
111
|
readonly autoRefreshToken: true;
|
|
112
112
|
};
|
|
113
113
|
/** SDK version */
|
|
114
|
-
declare const SDK_VERSION = "1.0.
|
|
114
|
+
declare const SDK_VERSION = "1.0.36";
|
|
115
115
|
/** API version prefix */
|
|
116
116
|
declare const API_VERSION = "v1";
|
|
117
117
|
/**
|
|
@@ -559,7 +559,7 @@ interface Job {
|
|
|
559
559
|
parentJobId?: string;
|
|
560
560
|
completionWebhook?: string;
|
|
561
561
|
assignedWorkerId?: string;
|
|
562
|
-
leaseId?: string;
|
|
562
|
+
leaseId?: string | null;
|
|
563
563
|
leaseExpiresAt?: string;
|
|
564
564
|
idempotencyKey?: string;
|
|
565
565
|
updatedAt: string;
|
|
@@ -653,7 +653,7 @@ interface ClaimedJob {
|
|
|
653
653
|
* worker actually holds (backend rejects stale leases with 409
|
|
654
654
|
* LEASE_EXPIRED). Omitted = legacy behavior.
|
|
655
655
|
*/
|
|
656
|
-
leaseId?: string;
|
|
656
|
+
leaseId?: string | null;
|
|
657
657
|
}
|
|
658
658
|
/** Parameters for claiming jobs */
|
|
659
659
|
interface ClaimJobsParams {
|
|
@@ -2620,7 +2620,7 @@ interface GrpcJob {
|
|
|
2620
2620
|
* in Complete/Fail/RenewLease so the operation applies only to the lease
|
|
2621
2621
|
* this worker actually holds (empty = legacy worker_id fence).
|
|
2622
2622
|
*/
|
|
2623
|
-
leaseId?: string;
|
|
2623
|
+
leaseId?: string | null;
|
|
2624
2624
|
}
|
|
2625
2625
|
/** Enqueue request */
|
|
2626
2626
|
interface GrpcEnqueueRequest {
|
|
@@ -3369,6 +3369,7 @@ interface WorkerEventData {
|
|
|
3369
3369
|
}
|
|
3370
3370
|
/** Active job tracking */
|
|
3371
3371
|
interface ActiveJob {
|
|
3372
|
+
readonly executionId: symbol;
|
|
3372
3373
|
job: ClaimedJob;
|
|
3373
3374
|
startedAt: Date;
|
|
3374
3375
|
heartbeatTimer?: ReturnType<typeof setInterval>;
|
|
@@ -107,11 +107,11 @@ declare const DEFAULT_CONFIG: {
|
|
|
107
107
|
successThreshold: number;
|
|
108
108
|
timeout: number;
|
|
109
109
|
};
|
|
110
|
-
readonly userAgent: "@spooled/sdk-nodejs/1.0.
|
|
110
|
+
readonly userAgent: "@spooled/sdk-nodejs/1.0.36";
|
|
111
111
|
readonly autoRefreshToken: true;
|
|
112
112
|
};
|
|
113
113
|
/** SDK version */
|
|
114
|
-
declare const SDK_VERSION = "1.0.
|
|
114
|
+
declare const SDK_VERSION = "1.0.36";
|
|
115
115
|
/** API version prefix */
|
|
116
116
|
declare const API_VERSION = "v1";
|
|
117
117
|
/**
|
|
@@ -559,7 +559,7 @@ interface Job {
|
|
|
559
559
|
parentJobId?: string;
|
|
560
560
|
completionWebhook?: string;
|
|
561
561
|
assignedWorkerId?: string;
|
|
562
|
-
leaseId?: string;
|
|
562
|
+
leaseId?: string | null;
|
|
563
563
|
leaseExpiresAt?: string;
|
|
564
564
|
idempotencyKey?: string;
|
|
565
565
|
updatedAt: string;
|
|
@@ -653,7 +653,7 @@ interface ClaimedJob {
|
|
|
653
653
|
* worker actually holds (backend rejects stale leases with 409
|
|
654
654
|
* LEASE_EXPIRED). Omitted = legacy behavior.
|
|
655
655
|
*/
|
|
656
|
-
leaseId?: string;
|
|
656
|
+
leaseId?: string | null;
|
|
657
657
|
}
|
|
658
658
|
/** Parameters for claiming jobs */
|
|
659
659
|
interface ClaimJobsParams {
|
|
@@ -2620,7 +2620,7 @@ interface GrpcJob {
|
|
|
2620
2620
|
* in Complete/Fail/RenewLease so the operation applies only to the lease
|
|
2621
2621
|
* this worker actually holds (empty = legacy worker_id fence).
|
|
2622
2622
|
*/
|
|
2623
|
-
leaseId?: string;
|
|
2623
|
+
leaseId?: string | null;
|
|
2624
2624
|
}
|
|
2625
2625
|
/** Enqueue request */
|
|
2626
2626
|
interface GrpcEnqueueRequest {
|
|
@@ -3369,6 +3369,7 @@ interface WorkerEventData {
|
|
|
3369
3369
|
}
|
|
3370
3370
|
/** Active job tracking */
|
|
3371
3371
|
interface ActiveJob {
|
|
3372
|
+
readonly executionId: symbol;
|
|
3372
3373
|
job: ClaimedJob;
|
|
3373
3374
|
startedAt: Date;
|
|
3374
3375
|
heartbeatTimer?: ReturnType<typeof setInterval>;
|
package/dist/index.cjs
CHANGED
|
@@ -49,10 +49,10 @@ var DEFAULT_CONFIG = {
|
|
|
49
49
|
successThreshold: 3,
|
|
50
50
|
timeout: 3e4
|
|
51
51
|
},
|
|
52
|
-
userAgent: "@spooled/sdk-nodejs/1.0.
|
|
52
|
+
userAgent: "@spooled/sdk-nodejs/1.0.36",
|
|
53
53
|
autoRefreshToken: true
|
|
54
54
|
};
|
|
55
|
-
var SDK_VERSION = "1.0.
|
|
55
|
+
var SDK_VERSION = "1.0.36";
|
|
56
56
|
var API_VERSION = "v1";
|
|
57
57
|
var API_BASE_PATH = `/api/${API_VERSION}`;
|
|
58
58
|
function normalizeCredential(value) {
|
|
@@ -774,15 +774,17 @@ var HttpClient = class {
|
|
|
774
774
|
*/
|
|
775
775
|
resolveRetryConfig(method, options) {
|
|
776
776
|
const base = this.config.retry;
|
|
777
|
-
if (base.retryOn) {
|
|
778
|
-
return base;
|
|
779
|
-
}
|
|
780
777
|
if (IDEMPOTENT_METHODS.has(method) || this.hasIdempotencyKey(options)) {
|
|
781
778
|
return base;
|
|
782
779
|
}
|
|
783
780
|
return {
|
|
784
781
|
...base,
|
|
785
|
-
retryOn: (error) =>
|
|
782
|
+
retryOn: (error, attempt) => {
|
|
783
|
+
if (error instanceof RateLimitError) {
|
|
784
|
+
return base.retryOn ? base.retryOn(error, attempt) : true;
|
|
785
|
+
}
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
786
788
|
};
|
|
787
789
|
}
|
|
788
790
|
/**
|
|
@@ -1964,8 +1966,9 @@ var WebSocketRealtimeClient = class {
|
|
|
1964
1966
|
};
|
|
1965
1967
|
this.ws.onclose = (event) => {
|
|
1966
1968
|
this.options.debug(`WebSocket closed: ${event.code} ${event.reason}`);
|
|
1969
|
+
const wasConnecting = this.state === "connecting";
|
|
1967
1970
|
this.handleDisconnect();
|
|
1968
|
-
if (
|
|
1971
|
+
if (wasConnecting) {
|
|
1969
1972
|
reject(new Error(`WebSocket connection failed: ${event.reason || "Unknown error"}`));
|
|
1970
1973
|
}
|
|
1971
1974
|
};
|
|
@@ -2127,6 +2130,11 @@ var WebSocketRealtimeClient = class {
|
|
|
2127
2130
|
await this.connect();
|
|
2128
2131
|
} catch (error) {
|
|
2129
2132
|
this.options.debug("Reconnect failed", error);
|
|
2133
|
+
if (this.options.autoReconnect && this.reconnectAttempts < this.options.maxReconnectAttempts) {
|
|
2134
|
+
this.scheduleReconnect();
|
|
2135
|
+
} else {
|
|
2136
|
+
this.setState("disconnected");
|
|
2137
|
+
}
|
|
2130
2138
|
}
|
|
2131
2139
|
}, delay);
|
|
2132
2140
|
}
|
|
@@ -2241,7 +2249,7 @@ var SseRealtimeClient = class {
|
|
|
2241
2249
|
}
|
|
2242
2250
|
let token;
|
|
2243
2251
|
try {
|
|
2244
|
-
token = await this.options.tokenProvider();
|
|
2252
|
+
token = await this.options.tokenProvider(this.reconnectAttempts > 0);
|
|
2245
2253
|
} catch (error) {
|
|
2246
2254
|
this.setState("disconnected");
|
|
2247
2255
|
throw new Error(`Failed to acquire realtime token: ${error}`);
|
|
@@ -2411,6 +2419,11 @@ var SseRealtimeClient = class {
|
|
|
2411
2419
|
await this.connect(this.filter || void 0);
|
|
2412
2420
|
} catch (error) {
|
|
2413
2421
|
this.options.debug("SSE reconnect failed", error);
|
|
2422
|
+
if (this.options.autoReconnect && this.reconnectAttempts < this.options.maxReconnectAttempts) {
|
|
2423
|
+
this.scheduleReconnect();
|
|
2424
|
+
} else {
|
|
2425
|
+
this.setState("disconnected");
|
|
2426
|
+
}
|
|
2414
2427
|
}
|
|
2415
2428
|
}, delay);
|
|
2416
2429
|
}
|
|
@@ -2682,6 +2695,77 @@ function createJobStream(call, options) {
|
|
|
2682
2695
|
}
|
|
2683
2696
|
|
|
2684
2697
|
// src/grpc/client.ts
|
|
2698
|
+
function encodeStruct(value) {
|
|
2699
|
+
if (value == null) {
|
|
2700
|
+
return void 0;
|
|
2701
|
+
}
|
|
2702
|
+
return {
|
|
2703
|
+
fields: Object.fromEntries(
|
|
2704
|
+
Object.entries(value).map(([key, entry]) => [key, encodeValue(entry)])
|
|
2705
|
+
)
|
|
2706
|
+
};
|
|
2707
|
+
}
|
|
2708
|
+
function encodeValue(value) {
|
|
2709
|
+
if (value == null) {
|
|
2710
|
+
return { nullValue: 0 };
|
|
2711
|
+
}
|
|
2712
|
+
if (Array.isArray(value)) {
|
|
2713
|
+
return { listValue: { values: value.map(encodeValue) } };
|
|
2714
|
+
}
|
|
2715
|
+
switch (typeof value) {
|
|
2716
|
+
case "number":
|
|
2717
|
+
return { numberValue: value };
|
|
2718
|
+
case "string":
|
|
2719
|
+
return { stringValue: value };
|
|
2720
|
+
case "boolean":
|
|
2721
|
+
return { boolValue: value };
|
|
2722
|
+
case "object":
|
|
2723
|
+
return { structValue: encodeStruct(value) };
|
|
2724
|
+
default:
|
|
2725
|
+
return { stringValue: String(value) };
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
function decodeStruct(value) {
|
|
2729
|
+
if (!value || typeof value !== "object" || !("fields" in value)) {
|
|
2730
|
+
return value;
|
|
2731
|
+
}
|
|
2732
|
+
const fields = value.fields ?? {};
|
|
2733
|
+
return Object.fromEntries(Object.entries(fields).map(([key, entry]) => [key, decodeValue(entry)]));
|
|
2734
|
+
}
|
|
2735
|
+
function decodeValue(value) {
|
|
2736
|
+
if ("nullValue" in value) {
|
|
2737
|
+
return null;
|
|
2738
|
+
}
|
|
2739
|
+
if ("numberValue" in value) {
|
|
2740
|
+
return value.numberValue;
|
|
2741
|
+
}
|
|
2742
|
+
if ("stringValue" in value) {
|
|
2743
|
+
return value.stringValue;
|
|
2744
|
+
}
|
|
2745
|
+
if ("boolValue" in value) {
|
|
2746
|
+
return value.boolValue;
|
|
2747
|
+
}
|
|
2748
|
+
if ("structValue" in value) {
|
|
2749
|
+
return decodeStruct(value.structValue);
|
|
2750
|
+
}
|
|
2751
|
+
if ("listValue" in value) {
|
|
2752
|
+
return (value.listValue.values ?? []).map(decodeValue);
|
|
2753
|
+
}
|
|
2754
|
+
return void 0;
|
|
2755
|
+
}
|
|
2756
|
+
function encodeEnqueueRequest(params) {
|
|
2757
|
+
return { ...params, payload: encodeStruct(params.payload) };
|
|
2758
|
+
}
|
|
2759
|
+
function encodeCompleteRequest(params) {
|
|
2760
|
+
return { ...params, result: encodeStruct(params.result) };
|
|
2761
|
+
}
|
|
2762
|
+
function decodeJob(job) {
|
|
2763
|
+
return {
|
|
2764
|
+
...job,
|
|
2765
|
+
payload: decodeStruct(job.payload),
|
|
2766
|
+
result: decodeStruct(job.result)
|
|
2767
|
+
};
|
|
2768
|
+
}
|
|
2685
2769
|
function promisify(client, method, request, metadata) {
|
|
2686
2770
|
return new Promise((resolve, reject) => {
|
|
2687
2771
|
method.call(client, request, metadata, (error, response) => {
|
|
@@ -2709,19 +2793,20 @@ var GrpcQueueOperations = class {
|
|
|
2709
2793
|
* Enqueue a new job
|
|
2710
2794
|
*/
|
|
2711
2795
|
async enqueue(params) {
|
|
2712
|
-
return promisify(this.client, this.client.Enqueue, params, this.metadata);
|
|
2796
|
+
return promisify(this.client, this.client.Enqueue, encodeEnqueueRequest(params), this.metadata);
|
|
2713
2797
|
}
|
|
2714
2798
|
/**
|
|
2715
2799
|
* Dequeue a job (for workers)
|
|
2716
2800
|
*/
|
|
2717
2801
|
async dequeue(params) {
|
|
2718
|
-
|
|
2802
|
+
const response = await promisify(this.client, this.client.Dequeue, params, this.metadata);
|
|
2803
|
+
return { ...response, jobs: response.jobs.map(decodeJob) };
|
|
2719
2804
|
}
|
|
2720
2805
|
/**
|
|
2721
2806
|
* Complete a job successfully
|
|
2722
2807
|
*/
|
|
2723
2808
|
async complete(params) {
|
|
2724
|
-
return promisify(this.client, this.client.Complete, params, this.metadata);
|
|
2809
|
+
return promisify(this.client, this.client.Complete, encodeCompleteRequest(params), this.metadata);
|
|
2725
2810
|
}
|
|
2726
2811
|
/**
|
|
2727
2812
|
* Fail a job
|
|
@@ -2739,7 +2824,8 @@ var GrpcQueueOperations = class {
|
|
|
2739
2824
|
* Get a job by ID
|
|
2740
2825
|
*/
|
|
2741
2826
|
async getJob(jobId) {
|
|
2742
|
-
|
|
2827
|
+
const response = await promisify(this.client, this.client.GetJob, { jobId }, this.metadata);
|
|
2828
|
+
return { ...response, job: response.job ? decodeJob(response.job) : response.job };
|
|
2743
2829
|
}
|
|
2744
2830
|
/**
|
|
2745
2831
|
* Get queue statistics
|
|
@@ -3288,7 +3374,7 @@ var SpooledWorker = class {
|
|
|
3288
3374
|
...DEFAULT_OPTIONS,
|
|
3289
3375
|
hostname: os.hostname(),
|
|
3290
3376
|
workerType: "nodejs",
|
|
3291
|
-
version:
|
|
3377
|
+
version: SDK_VERSION,
|
|
3292
3378
|
metadata: {},
|
|
3293
3379
|
...options
|
|
3294
3380
|
};
|
|
@@ -3395,11 +3481,11 @@ var SpooledWorker = class {
|
|
|
3395
3481
|
this.waitForActiveJobs(),
|
|
3396
3482
|
this.sleep(this.options.shutdownTimeout)
|
|
3397
3483
|
]);
|
|
3398
|
-
const remaining = Array.from(this.activeJobs.
|
|
3399
|
-
for (const
|
|
3400
|
-
this.debug(`Force-failing job ${
|
|
3484
|
+
const remaining = Array.from(this.activeJobs.values());
|
|
3485
|
+
for (const active of remaining) {
|
|
3486
|
+
this.debug(`Force-failing job ${active.job.id} due to shutdown timeout`);
|
|
3401
3487
|
await this.failJob(active.job, "Worker shutdown timeout");
|
|
3402
|
-
this.cleanupJob(
|
|
3488
|
+
this.cleanupJob(active);
|
|
3403
3489
|
}
|
|
3404
3490
|
}
|
|
3405
3491
|
if (this.workerId) {
|
|
@@ -3482,14 +3568,15 @@ var SpooledWorker = class {
|
|
|
3482
3568
|
this.emit("job:claimed", { jobId: job.id, queueName: job.queueName });
|
|
3483
3569
|
const abortController = new AbortController();
|
|
3484
3570
|
const activeJob = {
|
|
3571
|
+
executionId: Symbol(job.id),
|
|
3485
3572
|
job,
|
|
3486
3573
|
startedAt: /* @__PURE__ */ new Date(),
|
|
3487
3574
|
abortController
|
|
3488
3575
|
};
|
|
3489
|
-
this.activeJobs.set(
|
|
3576
|
+
this.activeJobs.set(activeJob.executionId, activeJob);
|
|
3490
3577
|
const heartbeatInterval = this.options.leaseDuration * this.options.heartbeatFraction * 1e3;
|
|
3491
3578
|
activeJob.heartbeatTimer = setInterval(() => {
|
|
3492
|
-
this.sendJobHeartbeat(
|
|
3579
|
+
this.sendJobHeartbeat(activeJob);
|
|
3493
3580
|
}, heartbeatInterval);
|
|
3494
3581
|
this.executeHandler(activeJob);
|
|
3495
3582
|
}
|
|
@@ -3525,7 +3612,7 @@ var SpooledWorker = class {
|
|
|
3525
3612
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3526
3613
|
await this.failJob(job, errorMessage);
|
|
3527
3614
|
} finally {
|
|
3528
|
-
this.cleanupJob(
|
|
3615
|
+
this.cleanupJob(activeJob);
|
|
3529
3616
|
}
|
|
3530
3617
|
}
|
|
3531
3618
|
async completeJob(job, result) {
|
|
@@ -3534,7 +3621,7 @@ var SpooledWorker = class {
|
|
|
3534
3621
|
await this.client.jobs.complete(job.id, {
|
|
3535
3622
|
workerId: this.workerId,
|
|
3536
3623
|
result,
|
|
3537
|
-
...job.leaseId
|
|
3624
|
+
...job.leaseId != null && { leaseId: job.leaseId }
|
|
3538
3625
|
});
|
|
3539
3626
|
this.emit("job:completed", {
|
|
3540
3627
|
jobId: job.id,
|
|
@@ -3552,7 +3639,7 @@ var SpooledWorker = class {
|
|
|
3552
3639
|
await this.client.jobs.fail(job.id, {
|
|
3553
3640
|
workerId: this.workerId,
|
|
3554
3641
|
error: errorMessage,
|
|
3555
|
-
...job.leaseId
|
|
3642
|
+
...job.leaseId != null && { leaseId: job.leaseId }
|
|
3556
3643
|
});
|
|
3557
3644
|
this.emit("job:failed", {
|
|
3558
3645
|
jobId: job.id,
|
|
@@ -3564,24 +3651,26 @@ var SpooledWorker = class {
|
|
|
3564
3651
|
this.debug(`Failed to fail job ${job.id}`, error);
|
|
3565
3652
|
}
|
|
3566
3653
|
}
|
|
3567
|
-
cleanupJob(
|
|
3568
|
-
|
|
3569
|
-
if (activeJob?.heartbeatTimer) {
|
|
3654
|
+
cleanupJob(activeJob) {
|
|
3655
|
+
if (activeJob.heartbeatTimer) {
|
|
3570
3656
|
clearInterval(activeJob.heartbeatTimer);
|
|
3657
|
+
activeJob.heartbeatTimer = void 0;
|
|
3658
|
+
}
|
|
3659
|
+
if (this.activeJobs.get(activeJob.executionId) === activeJob) {
|
|
3660
|
+
this.activeJobs.delete(activeJob.executionId);
|
|
3571
3661
|
}
|
|
3572
|
-
this.activeJobs.delete(jobId);
|
|
3573
3662
|
}
|
|
3574
|
-
async sendJobHeartbeat(
|
|
3663
|
+
async sendJobHeartbeat(activeJob) {
|
|
3575
3664
|
if (!this.workerId) return;
|
|
3576
|
-
const
|
|
3665
|
+
const { job } = activeJob;
|
|
3577
3666
|
try {
|
|
3578
|
-
await this.client.jobs.heartbeat(
|
|
3667
|
+
await this.client.jobs.heartbeat(job.id, {
|
|
3579
3668
|
workerId: this.workerId,
|
|
3580
3669
|
leaseDurationSecs: this.options.leaseDuration,
|
|
3581
|
-
...leaseId
|
|
3670
|
+
...job.leaseId != null && { leaseId: job.leaseId }
|
|
3582
3671
|
});
|
|
3583
3672
|
} catch (error) {
|
|
3584
|
-
this.debug(`Job heartbeat failed for ${
|
|
3673
|
+
this.debug(`Job heartbeat failed for ${job.id}`, error);
|
|
3585
3674
|
}
|
|
3586
3675
|
}
|
|
3587
3676
|
async sendWorkerHeartbeat() {
|