@spooled/sdk 1.0.33 → 1.0.35

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.
@@ -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.33";
110
+ readonly userAgent: "@spooled/sdk-nodejs/1.0.35";
111
111
  readonly autoRefreshToken: true;
112
112
  };
113
113
  /** SDK version */
114
- declare const SDK_VERSION = "1.0.33";
114
+ declare const SDK_VERSION = "1.0.35";
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;
@@ -647,6 +647,13 @@ interface ClaimedJob {
647
647
  maxRetries: number;
648
648
  timeoutSeconds: number;
649
649
  leaseExpiresAt?: string;
650
+ /**
651
+ * Lease fencing token for this claim. Echo it back on
652
+ * complete/fail/heartbeat so the operation applies only to the lease this
653
+ * worker actually holds (backend rejects stale leases with 409
654
+ * LEASE_EXPIRED). Omitted = legacy behavior.
655
+ */
656
+ leaseId?: string | null;
650
657
  }
651
658
  /** Parameters for claiming jobs */
652
659
  interface ClaimJobsParams {
@@ -669,6 +676,8 @@ interface CompleteJobParams {
669
676
  workerId: string;
670
677
  /** Optional result payload */
671
678
  result?: JsonObject;
679
+ /** Lease fencing token from the claimed job (rejects stale leases when set) */
680
+ leaseId?: string;
672
681
  }
673
682
  /** Parameters for failing a job */
674
683
  interface FailJobParams {
@@ -676,6 +685,8 @@ interface FailJobParams {
676
685
  workerId: string;
677
686
  /** Error message (1-2048 chars) */
678
687
  error: string;
688
+ /** Lease fencing token from the claimed job (rejects stale leases when set) */
689
+ leaseId?: string;
679
690
  }
680
691
  /** Parameters for job heartbeat */
681
692
  interface HeartbeatJobParams {
@@ -683,6 +694,8 @@ interface HeartbeatJobParams {
683
694
  workerId: string;
684
695
  /** Lease duration in seconds (5-3600) */
685
696
  leaseDurationSecs: number;
697
+ /** Lease fencing token from the claimed job (rejects stale leases when set) */
698
+ leaseId?: string;
686
699
  }
687
700
  /** Response for priority boost */
688
701
  interface BoostPriorityResponse {
@@ -2602,6 +2615,12 @@ interface GrpcJob {
2602
2615
  leaseExpiresAt?: GrpcTimestamp | string | null;
2603
2616
  assignedWorkerId?: string | null;
2604
2617
  idempotencyKey?: string | null;
2618
+ /**
2619
+ * Lease fencing token (proto `lease_id`). Returned on dequeue; echo it back
2620
+ * in Complete/Fail/RenewLease so the operation applies only to the lease
2621
+ * this worker actually holds (empty = legacy worker_id fence).
2622
+ */
2623
+ leaseId?: string | null;
2605
2624
  }
2606
2625
  /** Enqueue request */
2607
2626
  interface GrpcEnqueueRequest {
@@ -2637,6 +2656,8 @@ interface GrpcCompleteRequest {
2637
2656
  workerId: string;
2638
2657
  /** Result as object (will be converted to google.protobuf.Struct) */
2639
2658
  result?: Record<string, unknown>;
2659
+ /** Lease fencing token from the dequeued job (proto `lease_id`) */
2660
+ leaseId?: string;
2640
2661
  }
2641
2662
  /** Complete response */
2642
2663
  interface GrpcCompleteResponse {
@@ -2648,6 +2669,8 @@ interface GrpcFailRequest {
2648
2669
  workerId: string;
2649
2670
  error: string;
2650
2671
  retry?: boolean;
2672
+ /** Lease fencing token from the dequeued job (proto `lease_id`) */
2673
+ leaseId?: string;
2651
2674
  }
2652
2675
  /** Fail response */
2653
2676
  interface GrpcFailResponse {
@@ -2660,6 +2683,8 @@ interface GrpcRenewLeaseRequest {
2660
2683
  jobId: string;
2661
2684
  workerId: string;
2662
2685
  extensionSecs: number;
2686
+ /** Lease fencing token from the dequeued job (proto `lease_id`) */
2687
+ leaseId?: string;
2663
2688
  }
2664
2689
  /** Renew lease response */
2665
2690
  interface GrpcRenewLeaseResponse {
@@ -3344,6 +3369,7 @@ interface WorkerEventData {
3344
3369
  }
3345
3370
  /** Active job tracking */
3346
3371
  interface ActiveJob {
3372
+ readonly executionId: symbol;
3347
3373
  job: ClaimedJob;
3348
3374
  startedAt: Date;
3349
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.33";
110
+ readonly userAgent: "@spooled/sdk-nodejs/1.0.35";
111
111
  readonly autoRefreshToken: true;
112
112
  };
113
113
  /** SDK version */
114
- declare const SDK_VERSION = "1.0.33";
114
+ declare const SDK_VERSION = "1.0.35";
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;
@@ -647,6 +647,13 @@ interface ClaimedJob {
647
647
  maxRetries: number;
648
648
  timeoutSeconds: number;
649
649
  leaseExpiresAt?: string;
650
+ /**
651
+ * Lease fencing token for this claim. Echo it back on
652
+ * complete/fail/heartbeat so the operation applies only to the lease this
653
+ * worker actually holds (backend rejects stale leases with 409
654
+ * LEASE_EXPIRED). Omitted = legacy behavior.
655
+ */
656
+ leaseId?: string | null;
650
657
  }
651
658
  /** Parameters for claiming jobs */
652
659
  interface ClaimJobsParams {
@@ -669,6 +676,8 @@ interface CompleteJobParams {
669
676
  workerId: string;
670
677
  /** Optional result payload */
671
678
  result?: JsonObject;
679
+ /** Lease fencing token from the claimed job (rejects stale leases when set) */
680
+ leaseId?: string;
672
681
  }
673
682
  /** Parameters for failing a job */
674
683
  interface FailJobParams {
@@ -676,6 +685,8 @@ interface FailJobParams {
676
685
  workerId: string;
677
686
  /** Error message (1-2048 chars) */
678
687
  error: string;
688
+ /** Lease fencing token from the claimed job (rejects stale leases when set) */
689
+ leaseId?: string;
679
690
  }
680
691
  /** Parameters for job heartbeat */
681
692
  interface HeartbeatJobParams {
@@ -683,6 +694,8 @@ interface HeartbeatJobParams {
683
694
  workerId: string;
684
695
  /** Lease duration in seconds (5-3600) */
685
696
  leaseDurationSecs: number;
697
+ /** Lease fencing token from the claimed job (rejects stale leases when set) */
698
+ leaseId?: string;
686
699
  }
687
700
  /** Response for priority boost */
688
701
  interface BoostPriorityResponse {
@@ -2602,6 +2615,12 @@ interface GrpcJob {
2602
2615
  leaseExpiresAt?: GrpcTimestamp | string | null;
2603
2616
  assignedWorkerId?: string | null;
2604
2617
  idempotencyKey?: string | null;
2618
+ /**
2619
+ * Lease fencing token (proto `lease_id`). Returned on dequeue; echo it back
2620
+ * in Complete/Fail/RenewLease so the operation applies only to the lease
2621
+ * this worker actually holds (empty = legacy worker_id fence).
2622
+ */
2623
+ leaseId?: string | null;
2605
2624
  }
2606
2625
  /** Enqueue request */
2607
2626
  interface GrpcEnqueueRequest {
@@ -2637,6 +2656,8 @@ interface GrpcCompleteRequest {
2637
2656
  workerId: string;
2638
2657
  /** Result as object (will be converted to google.protobuf.Struct) */
2639
2658
  result?: Record<string, unknown>;
2659
+ /** Lease fencing token from the dequeued job (proto `lease_id`) */
2660
+ leaseId?: string;
2640
2661
  }
2641
2662
  /** Complete response */
2642
2663
  interface GrpcCompleteResponse {
@@ -2648,6 +2669,8 @@ interface GrpcFailRequest {
2648
2669
  workerId: string;
2649
2670
  error: string;
2650
2671
  retry?: boolean;
2672
+ /** Lease fencing token from the dequeued job (proto `lease_id`) */
2673
+ leaseId?: string;
2651
2674
  }
2652
2675
  /** Fail response */
2653
2676
  interface GrpcFailResponse {
@@ -2660,6 +2683,8 @@ interface GrpcRenewLeaseRequest {
2660
2683
  jobId: string;
2661
2684
  workerId: string;
2662
2685
  extensionSecs: number;
2686
+ /** Lease fencing token from the dequeued job (proto `lease_id`) */
2687
+ leaseId?: string;
2663
2688
  }
2664
2689
  /** Renew lease response */
2665
2690
  interface GrpcRenewLeaseResponse {
@@ -3344,6 +3369,7 @@ interface WorkerEventData {
3344
3369
  }
3345
3370
  /** Active job tracking */
3346
3371
  interface ActiveJob {
3372
+ readonly executionId: symbol;
3347
3373
  job: ClaimedJob;
3348
3374
  startedAt: Date;
3349
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.33",
52
+ userAgent: "@spooled/sdk-nodejs/1.0.35",
53
53
  autoRefreshToken: true
54
54
  };
55
- var SDK_VERSION = "1.0.33";
55
+ var SDK_VERSION = "1.0.35";
56
56
  var API_VERSION = "v1";
57
57
  var API_BASE_PATH = `/api/${API_VERSION}`;
58
58
  function normalizeCredential(value) {
@@ -3288,7 +3288,7 @@ var SpooledWorker = class {
3288
3288
  ...DEFAULT_OPTIONS,
3289
3289
  hostname: os.hostname(),
3290
3290
  workerType: "nodejs",
3291
- version: "1.0.26",
3291
+ version: SDK_VERSION,
3292
3292
  metadata: {},
3293
3293
  ...options
3294
3294
  };
@@ -3395,11 +3395,11 @@ var SpooledWorker = class {
3395
3395
  this.waitForActiveJobs(),
3396
3396
  this.sleep(this.options.shutdownTimeout)
3397
3397
  ]);
3398
- const remaining = Array.from(this.activeJobs.entries());
3399
- for (const [jobId, active] of remaining) {
3400
- this.debug(`Force-failing job ${jobId} due to shutdown timeout`);
3398
+ const remaining = Array.from(this.activeJobs.values());
3399
+ for (const active of remaining) {
3400
+ this.debug(`Force-failing job ${active.job.id} due to shutdown timeout`);
3401
3401
  await this.failJob(active.job, "Worker shutdown timeout");
3402
- this.cleanupJob(jobId);
3402
+ this.cleanupJob(active);
3403
3403
  }
3404
3404
  }
3405
3405
  if (this.workerId) {
@@ -3482,14 +3482,15 @@ var SpooledWorker = class {
3482
3482
  this.emit("job:claimed", { jobId: job.id, queueName: job.queueName });
3483
3483
  const abortController = new AbortController();
3484
3484
  const activeJob = {
3485
+ executionId: Symbol(job.id),
3485
3486
  job,
3486
3487
  startedAt: /* @__PURE__ */ new Date(),
3487
3488
  abortController
3488
3489
  };
3489
- this.activeJobs.set(job.id, activeJob);
3490
+ this.activeJobs.set(activeJob.executionId, activeJob);
3490
3491
  const heartbeatInterval = this.options.leaseDuration * this.options.heartbeatFraction * 1e3;
3491
3492
  activeJob.heartbeatTimer = setInterval(() => {
3492
- this.sendJobHeartbeat(job.id);
3493
+ this.sendJobHeartbeat(activeJob);
3493
3494
  }, heartbeatInterval);
3494
3495
  this.executeHandler(activeJob);
3495
3496
  }
@@ -3525,7 +3526,7 @@ var SpooledWorker = class {
3525
3526
  const errorMessage = error instanceof Error ? error.message : String(error);
3526
3527
  await this.failJob(job, errorMessage);
3527
3528
  } finally {
3528
- this.cleanupJob(job.id);
3529
+ this.cleanupJob(activeJob);
3529
3530
  }
3530
3531
  }
3531
3532
  async completeJob(job, result) {
@@ -3533,7 +3534,8 @@ var SpooledWorker = class {
3533
3534
  try {
3534
3535
  await this.client.jobs.complete(job.id, {
3535
3536
  workerId: this.workerId,
3536
- result
3537
+ result,
3538
+ ...job.leaseId != null && { leaseId: job.leaseId }
3537
3539
  });
3538
3540
  this.emit("job:completed", {
3539
3541
  jobId: job.id,
@@ -3550,7 +3552,8 @@ var SpooledWorker = class {
3550
3552
  try {
3551
3553
  await this.client.jobs.fail(job.id, {
3552
3554
  workerId: this.workerId,
3553
- error: errorMessage
3555
+ error: errorMessage,
3556
+ ...job.leaseId != null && { leaseId: job.leaseId }
3554
3557
  });
3555
3558
  this.emit("job:failed", {
3556
3559
  jobId: job.id,
@@ -3562,22 +3565,26 @@ var SpooledWorker = class {
3562
3565
  this.debug(`Failed to fail job ${job.id}`, error);
3563
3566
  }
3564
3567
  }
3565
- cleanupJob(jobId) {
3566
- const activeJob = this.activeJobs.get(jobId);
3567
- if (activeJob?.heartbeatTimer) {
3568
+ cleanupJob(activeJob) {
3569
+ if (activeJob.heartbeatTimer) {
3568
3570
  clearInterval(activeJob.heartbeatTimer);
3571
+ activeJob.heartbeatTimer = void 0;
3572
+ }
3573
+ if (this.activeJobs.get(activeJob.executionId) === activeJob) {
3574
+ this.activeJobs.delete(activeJob.executionId);
3569
3575
  }
3570
- this.activeJobs.delete(jobId);
3571
3576
  }
3572
- async sendJobHeartbeat(jobId) {
3577
+ async sendJobHeartbeat(activeJob) {
3573
3578
  if (!this.workerId) return;
3579
+ const { job } = activeJob;
3574
3580
  try {
3575
- await this.client.jobs.heartbeat(jobId, {
3581
+ await this.client.jobs.heartbeat(job.id, {
3576
3582
  workerId: this.workerId,
3577
- leaseDurationSecs: this.options.leaseDuration
3583
+ leaseDurationSecs: this.options.leaseDuration,
3584
+ ...job.leaseId != null && { leaseId: job.leaseId }
3578
3585
  });
3579
3586
  } catch (error) {
3580
- this.debug(`Job heartbeat failed for ${jobId}`, error);
3587
+ this.debug(`Job heartbeat failed for ${job.id}`, error);
3581
3588
  }
3582
3589
  }
3583
3590
  async sendWorkerHeartbeat() {