@spooled/sdk 1.4.1 → 1.5.1
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-Bqgiu-NN.d.cts → index-C76lW6vm.d.cts} +48 -9
- package/dist/{index-Bqgiu-NN.d.ts → index-C76lW6vm.d.ts} +48 -9
- package/dist/index.cjs +248 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -8
- package/dist/index.d.ts +2 -8
- package/dist/index.js +248 -25
- package/dist/index.js.map +1 -1
- package/dist/worker/index.cjs +102 -10
- 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 +102 -10
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -224,11 +224,11 @@ declare const DEFAULT_CONFIG: {
|
|
|
224
224
|
successThreshold: number;
|
|
225
225
|
timeout: number;
|
|
226
226
|
};
|
|
227
|
-
readonly userAgent: "@spooled/sdk-nodejs/1.
|
|
227
|
+
readonly userAgent: "@spooled/sdk-nodejs/1.5.1";
|
|
228
228
|
readonly autoRefreshToken: true;
|
|
229
229
|
};
|
|
230
230
|
/** SDK version */
|
|
231
|
-
declare const SDK_VERSION = "1.
|
|
231
|
+
declare const SDK_VERSION = "1.5.1";
|
|
232
232
|
/** API version prefix */
|
|
233
233
|
declare const API_VERSION = "v1";
|
|
234
234
|
/**
|
|
@@ -750,7 +750,17 @@ declare class AuthResource {
|
|
|
750
750
|
*/
|
|
751
751
|
/** JSON primitive types */
|
|
752
752
|
type JsonPrimitive = string | number | boolean | null;
|
|
753
|
-
/**
|
|
753
|
+
/**
|
|
754
|
+
* JSON value type.
|
|
755
|
+
*
|
|
756
|
+
* Inside user-owned JSON (`payload`, `result`, `tags`, and the other
|
|
757
|
+
* free-form fields) an integer whose magnitude is greater than 2^53 is
|
|
758
|
+
* returned as a decimal string, so the digits survive decoding. A float64
|
|
759
|
+
* cannot hold those integers, and a string is already a `JsonValue`. Every
|
|
760
|
+
* smaller number, and every number outside those fields, stays a `number`.
|
|
761
|
+
* gRPC payloads cannot do this: `google.protobuf.Struct` has already stored
|
|
762
|
+
* the value as a float64.
|
|
763
|
+
*/
|
|
754
764
|
type JsonValue = JsonPrimitive | JsonObject | JsonArray;
|
|
755
765
|
/** JSON object type */
|
|
756
766
|
interface JsonObject {
|
|
@@ -1139,6 +1149,12 @@ interface RetryDlqParams {
|
|
|
1139
1149
|
interface RetryDlqResponse {
|
|
1140
1150
|
retriedCount: number;
|
|
1141
1151
|
retriedJobs: string[];
|
|
1152
|
+
/**
|
|
1153
|
+
* Jobs left dead-lettered because reviving their workflow would pass the
|
|
1154
|
+
* plan's workflow cap. A partial retry is still HTTP 200, so these ids are
|
|
1155
|
+
* the only sign those jobs were not retried. Sent by backend 0.1.121+.
|
|
1156
|
+
*/
|
|
1157
|
+
skippedJobs?: string[];
|
|
1142
1158
|
}
|
|
1143
1159
|
/** Parameters for purging DLQ */
|
|
1144
1160
|
interface PurgeDlqParams {
|
|
@@ -3224,9 +3240,18 @@ interface GrpcJob {
|
|
|
3224
3240
|
organizationId: string;
|
|
3225
3241
|
queueName: string;
|
|
3226
3242
|
status: GrpcJobStatus | string;
|
|
3227
|
-
/**
|
|
3243
|
+
/**
|
|
3244
|
+
* Payload as google.protobuf.Struct (object).
|
|
3245
|
+
*
|
|
3246
|
+
* Struct stores every number as a float64, so an integer above 2^53 is
|
|
3247
|
+
* already rounded before this SDK sees it. REST keeps those integers as
|
|
3248
|
+
* decimal strings; send them as strings on this path.
|
|
3249
|
+
*/
|
|
3228
3250
|
payload?: Record<string, unknown> | null;
|
|
3229
|
-
/**
|
|
3251
|
+
/**
|
|
3252
|
+
* Result as google.protobuf.Struct (object). Same float64 limit as
|
|
3253
|
+
* `payload`: an integer above 2^53 cannot round-trip.
|
|
3254
|
+
*/
|
|
3230
3255
|
result?: Record<string, unknown> | null;
|
|
3231
3256
|
retryCount: number;
|
|
3232
3257
|
maxRetries: number;
|
|
@@ -4004,10 +4029,13 @@ interface JobContext {
|
|
|
4004
4029
|
/** Maximum retries */
|
|
4005
4030
|
maxRetries: number;
|
|
4006
4031
|
/**
|
|
4007
|
-
* Abort signal. Aborted when the job exceeds its `timeoutSeconds
|
|
4008
|
-
* stop outlives `shutdownTimeout
|
|
4009
|
-
*
|
|
4010
|
-
*
|
|
4032
|
+
* Abort signal. Aborted when the job exceeds its `timeoutSeconds`, when a
|
|
4033
|
+
* stop outlives `shutdownTimeout`, or when this worker loses the lease
|
|
4034
|
+
* (a heartbeat of 409 `LEASE_EXPIRED`, which is also how a lease mismatch
|
|
4035
|
+
* is reported). NOT aborted merely because a stop was requested (the
|
|
4036
|
+
* handler gets the grace period to finish and be acked). `signal.reason`
|
|
4037
|
+
* carries the cause. A lost lease is not acked: the handler's later result
|
|
4038
|
+
* is discarded, because the scheduler has already given the job away.
|
|
4011
4039
|
*/
|
|
4012
4040
|
signal: AbortSignal;
|
|
4013
4041
|
/** Log local job progress (0-100); backend progress persistence is not yet available. */
|
|
@@ -4076,6 +4104,12 @@ interface ActiveJob {
|
|
|
4076
4104
|
* else may ack it. Set when the handler settles or the shutdown fails it.
|
|
4077
4105
|
*/
|
|
4078
4106
|
settled?: boolean;
|
|
4107
|
+
/**
|
|
4108
|
+
* Heartbeat returned 409 `LEASE_EXPIRED` (the lease timed out or the
|
|
4109
|
+
* `lease_id` no longer matches). The scheduler has reclaimed the job, so
|
|
4110
|
+
* this execution must not complete or fail it.
|
|
4111
|
+
*/
|
|
4112
|
+
leaseLost?: boolean;
|
|
4079
4113
|
/** The complete/fail request for this execution, once started. */
|
|
4080
4114
|
ack?: Promise<void>;
|
|
4081
4115
|
}
|
|
@@ -4190,6 +4224,11 @@ declare class SpooledWorker {
|
|
|
4190
4224
|
private clearTimeoutTimer;
|
|
4191
4225
|
private cleanupJob;
|
|
4192
4226
|
private sendJobHeartbeat;
|
|
4227
|
+
/**
|
|
4228
|
+
* The lease is gone. Abort the handler if it listens, stop renewing, and
|
|
4229
|
+
* refuse a later complete/fail with the stale lease.
|
|
4230
|
+
*/
|
|
4231
|
+
private noteLeaseLost;
|
|
4193
4232
|
private sendWorkerHeartbeat;
|
|
4194
4233
|
/**
|
|
4195
4234
|
* Resolve once no job is active, or after `timeoutMs`. The timer is always
|
|
@@ -224,11 +224,11 @@ declare const DEFAULT_CONFIG: {
|
|
|
224
224
|
successThreshold: number;
|
|
225
225
|
timeout: number;
|
|
226
226
|
};
|
|
227
|
-
readonly userAgent: "@spooled/sdk-nodejs/1.
|
|
227
|
+
readonly userAgent: "@spooled/sdk-nodejs/1.5.1";
|
|
228
228
|
readonly autoRefreshToken: true;
|
|
229
229
|
};
|
|
230
230
|
/** SDK version */
|
|
231
|
-
declare const SDK_VERSION = "1.
|
|
231
|
+
declare const SDK_VERSION = "1.5.1";
|
|
232
232
|
/** API version prefix */
|
|
233
233
|
declare const API_VERSION = "v1";
|
|
234
234
|
/**
|
|
@@ -750,7 +750,17 @@ declare class AuthResource {
|
|
|
750
750
|
*/
|
|
751
751
|
/** JSON primitive types */
|
|
752
752
|
type JsonPrimitive = string | number | boolean | null;
|
|
753
|
-
/**
|
|
753
|
+
/**
|
|
754
|
+
* JSON value type.
|
|
755
|
+
*
|
|
756
|
+
* Inside user-owned JSON (`payload`, `result`, `tags`, and the other
|
|
757
|
+
* free-form fields) an integer whose magnitude is greater than 2^53 is
|
|
758
|
+
* returned as a decimal string, so the digits survive decoding. A float64
|
|
759
|
+
* cannot hold those integers, and a string is already a `JsonValue`. Every
|
|
760
|
+
* smaller number, and every number outside those fields, stays a `number`.
|
|
761
|
+
* gRPC payloads cannot do this: `google.protobuf.Struct` has already stored
|
|
762
|
+
* the value as a float64.
|
|
763
|
+
*/
|
|
754
764
|
type JsonValue = JsonPrimitive | JsonObject | JsonArray;
|
|
755
765
|
/** JSON object type */
|
|
756
766
|
interface JsonObject {
|
|
@@ -1139,6 +1149,12 @@ interface RetryDlqParams {
|
|
|
1139
1149
|
interface RetryDlqResponse {
|
|
1140
1150
|
retriedCount: number;
|
|
1141
1151
|
retriedJobs: string[];
|
|
1152
|
+
/**
|
|
1153
|
+
* Jobs left dead-lettered because reviving their workflow would pass the
|
|
1154
|
+
* plan's workflow cap. A partial retry is still HTTP 200, so these ids are
|
|
1155
|
+
* the only sign those jobs were not retried. Sent by backend 0.1.121+.
|
|
1156
|
+
*/
|
|
1157
|
+
skippedJobs?: string[];
|
|
1142
1158
|
}
|
|
1143
1159
|
/** Parameters for purging DLQ */
|
|
1144
1160
|
interface PurgeDlqParams {
|
|
@@ -3224,9 +3240,18 @@ interface GrpcJob {
|
|
|
3224
3240
|
organizationId: string;
|
|
3225
3241
|
queueName: string;
|
|
3226
3242
|
status: GrpcJobStatus | string;
|
|
3227
|
-
/**
|
|
3243
|
+
/**
|
|
3244
|
+
* Payload as google.protobuf.Struct (object).
|
|
3245
|
+
*
|
|
3246
|
+
* Struct stores every number as a float64, so an integer above 2^53 is
|
|
3247
|
+
* already rounded before this SDK sees it. REST keeps those integers as
|
|
3248
|
+
* decimal strings; send them as strings on this path.
|
|
3249
|
+
*/
|
|
3228
3250
|
payload?: Record<string, unknown> | null;
|
|
3229
|
-
/**
|
|
3251
|
+
/**
|
|
3252
|
+
* Result as google.protobuf.Struct (object). Same float64 limit as
|
|
3253
|
+
* `payload`: an integer above 2^53 cannot round-trip.
|
|
3254
|
+
*/
|
|
3230
3255
|
result?: Record<string, unknown> | null;
|
|
3231
3256
|
retryCount: number;
|
|
3232
3257
|
maxRetries: number;
|
|
@@ -4004,10 +4029,13 @@ interface JobContext {
|
|
|
4004
4029
|
/** Maximum retries */
|
|
4005
4030
|
maxRetries: number;
|
|
4006
4031
|
/**
|
|
4007
|
-
* Abort signal. Aborted when the job exceeds its `timeoutSeconds
|
|
4008
|
-
* stop outlives `shutdownTimeout
|
|
4009
|
-
*
|
|
4010
|
-
*
|
|
4032
|
+
* Abort signal. Aborted when the job exceeds its `timeoutSeconds`, when a
|
|
4033
|
+
* stop outlives `shutdownTimeout`, or when this worker loses the lease
|
|
4034
|
+
* (a heartbeat of 409 `LEASE_EXPIRED`, which is also how a lease mismatch
|
|
4035
|
+
* is reported). NOT aborted merely because a stop was requested (the
|
|
4036
|
+
* handler gets the grace period to finish and be acked). `signal.reason`
|
|
4037
|
+
* carries the cause. A lost lease is not acked: the handler's later result
|
|
4038
|
+
* is discarded, because the scheduler has already given the job away.
|
|
4011
4039
|
*/
|
|
4012
4040
|
signal: AbortSignal;
|
|
4013
4041
|
/** Log local job progress (0-100); backend progress persistence is not yet available. */
|
|
@@ -4076,6 +4104,12 @@ interface ActiveJob {
|
|
|
4076
4104
|
* else may ack it. Set when the handler settles or the shutdown fails it.
|
|
4077
4105
|
*/
|
|
4078
4106
|
settled?: boolean;
|
|
4107
|
+
/**
|
|
4108
|
+
* Heartbeat returned 409 `LEASE_EXPIRED` (the lease timed out or the
|
|
4109
|
+
* `lease_id` no longer matches). The scheduler has reclaimed the job, so
|
|
4110
|
+
* this execution must not complete or fail it.
|
|
4111
|
+
*/
|
|
4112
|
+
leaseLost?: boolean;
|
|
4079
4113
|
/** The complete/fail request for this execution, once started. */
|
|
4080
4114
|
ack?: Promise<void>;
|
|
4081
4115
|
}
|
|
@@ -4190,6 +4224,11 @@ declare class SpooledWorker {
|
|
|
4190
4224
|
private clearTimeoutTimer;
|
|
4191
4225
|
private cleanupJob;
|
|
4192
4226
|
private sendJobHeartbeat;
|
|
4227
|
+
/**
|
|
4228
|
+
* The lease is gone. Abort the handler if it listens, stop renewing, and
|
|
4229
|
+
* refuse a later complete/fail with the stale lease.
|
|
4230
|
+
*/
|
|
4231
|
+
private noteLeaseLost;
|
|
4193
4232
|
private sendWorkerHeartbeat;
|
|
4194
4233
|
/**
|
|
4195
4234
|
* Resolve once no job is active, or after `timeoutMs`. The timer is always
|
package/dist/index.cjs
CHANGED
|
@@ -48,10 +48,10 @@ var DEFAULT_CONFIG = {
|
|
|
48
48
|
successThreshold: 3,
|
|
49
49
|
timeout: 3e4
|
|
50
50
|
},
|
|
51
|
-
userAgent: "@spooled/sdk-nodejs/1.
|
|
51
|
+
userAgent: "@spooled/sdk-nodejs/1.5.1",
|
|
52
52
|
autoRefreshToken: true
|
|
53
53
|
};
|
|
54
|
-
var SDK_VERSION = "1.
|
|
54
|
+
var SDK_VERSION = "1.5.1";
|
|
55
55
|
var API_VERSION = "v1";
|
|
56
56
|
var API_BASE_PATH = `/api/${API_VERSION}`;
|
|
57
57
|
function normalizeCredential(value) {
|
|
@@ -631,6 +631,175 @@ function createRetryWrapper(config) {
|
|
|
631
631
|
};
|
|
632
632
|
}
|
|
633
633
|
|
|
634
|
+
// src/utils/json.ts
|
|
635
|
+
var TWO_POW_53 = "9007199254740992";
|
|
636
|
+
function parseApiJson(text) {
|
|
637
|
+
return JSON.parse(preserveExactIntegers(text));
|
|
638
|
+
}
|
|
639
|
+
function magnitudeExceedsTwoPow53(digits) {
|
|
640
|
+
let start = 0;
|
|
641
|
+
while (start < digits.length - 1 && digits.charCodeAt(start) === 48) {
|
|
642
|
+
start++;
|
|
643
|
+
}
|
|
644
|
+
const len = digits.length - start;
|
|
645
|
+
if (len !== TWO_POW_53.length) {
|
|
646
|
+
return len > TWO_POW_53.length;
|
|
647
|
+
}
|
|
648
|
+
for (let i = 0; i < len; i++) {
|
|
649
|
+
const digit = digits.charCodeAt(start + i);
|
|
650
|
+
const limit = TWO_POW_53.charCodeAt(i);
|
|
651
|
+
if (digit !== limit) {
|
|
652
|
+
return digit > limit;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
function inExactValue(frames) {
|
|
658
|
+
const top = frames[frames.length - 1];
|
|
659
|
+
if (!top || top.expect !== "value") {
|
|
660
|
+
return false;
|
|
661
|
+
}
|
|
662
|
+
return top.exact || top.keyIsExact;
|
|
663
|
+
}
|
|
664
|
+
function preserveExactIntegers(text) {
|
|
665
|
+
const out = [];
|
|
666
|
+
const frames = [];
|
|
667
|
+
const n = text.length;
|
|
668
|
+
let i = 0;
|
|
669
|
+
const valueEnded = () => {
|
|
670
|
+
const top = frames[frames.length - 1];
|
|
671
|
+
if (top) {
|
|
672
|
+
top.expect = "after";
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
while (i < n) {
|
|
676
|
+
const c = text[i];
|
|
677
|
+
if (c === " " || c === "\n" || c === "\r" || c === " ") {
|
|
678
|
+
out.push(c);
|
|
679
|
+
i++;
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
if (c === "{") {
|
|
683
|
+
frames.push({
|
|
684
|
+
kind: "object",
|
|
685
|
+
exact: inExactValue(frames),
|
|
686
|
+
expect: "key",
|
|
687
|
+
keyIsExact: false
|
|
688
|
+
});
|
|
689
|
+
out.push(c);
|
|
690
|
+
i++;
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
if (c === "[") {
|
|
694
|
+
frames.push({
|
|
695
|
+
kind: "array",
|
|
696
|
+
exact: inExactValue(frames),
|
|
697
|
+
expect: "value",
|
|
698
|
+
keyIsExact: false
|
|
699
|
+
});
|
|
700
|
+
out.push(c);
|
|
701
|
+
i++;
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
if (c === "}" || c === "]") {
|
|
705
|
+
frames.pop();
|
|
706
|
+
out.push(c);
|
|
707
|
+
i++;
|
|
708
|
+
valueEnded();
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
if (c === ":") {
|
|
712
|
+
const top = frames[frames.length - 1];
|
|
713
|
+
if (top && top.kind === "object") {
|
|
714
|
+
top.expect = "value";
|
|
715
|
+
}
|
|
716
|
+
out.push(c);
|
|
717
|
+
i++;
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
if (c === ",") {
|
|
721
|
+
const top = frames[frames.length - 1];
|
|
722
|
+
if (top) {
|
|
723
|
+
top.expect = top.kind === "object" ? "key" : "value";
|
|
724
|
+
top.keyIsExact = false;
|
|
725
|
+
}
|
|
726
|
+
out.push(c);
|
|
727
|
+
i++;
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
if (c === '"') {
|
|
731
|
+
const start = i;
|
|
732
|
+
i++;
|
|
733
|
+
while (i < n) {
|
|
734
|
+
if (text[i] === "\\") {
|
|
735
|
+
i++;
|
|
736
|
+
if (i < n) i++;
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
if (text[i] === '"') {
|
|
740
|
+
i++;
|
|
741
|
+
break;
|
|
742
|
+
}
|
|
743
|
+
i++;
|
|
744
|
+
}
|
|
745
|
+
const raw = text.slice(start, i);
|
|
746
|
+
const top = frames[frames.length - 1];
|
|
747
|
+
if (top && top.kind === "object" && top.expect === "key") {
|
|
748
|
+
let key = "";
|
|
749
|
+
try {
|
|
750
|
+
key = JSON.parse(raw);
|
|
751
|
+
} catch {
|
|
752
|
+
key = "";
|
|
753
|
+
}
|
|
754
|
+
top.keyIsExact = typeof key === "string" && SKIP_CONVERSION_KEYS.has(key);
|
|
755
|
+
top.expect = "colon";
|
|
756
|
+
} else {
|
|
757
|
+
valueEnded();
|
|
758
|
+
}
|
|
759
|
+
out.push(raw);
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
if (c === "-" || c >= "0" && c <= "9") {
|
|
763
|
+
const start = i;
|
|
764
|
+
if (text[i] === "-") i++;
|
|
765
|
+
const digitsAt = i;
|
|
766
|
+
while (i < n && text[i] >= "0" && text[i] <= "9") i++;
|
|
767
|
+
const digitsEnd = i;
|
|
768
|
+
let isInteger = true;
|
|
769
|
+
if (i < n && text[i] === ".") {
|
|
770
|
+
isInteger = false;
|
|
771
|
+
i++;
|
|
772
|
+
while (i < n && text[i] >= "0" && text[i] <= "9") i++;
|
|
773
|
+
}
|
|
774
|
+
if (i < n && (text[i] === "e" || text[i] === "E")) {
|
|
775
|
+
isInteger = false;
|
|
776
|
+
i++;
|
|
777
|
+
if (i < n && (text[i] === "+" || text[i] === "-")) i++;
|
|
778
|
+
while (i < n && text[i] >= "0" && text[i] <= "9") i++;
|
|
779
|
+
}
|
|
780
|
+
const raw = text.slice(start, i);
|
|
781
|
+
const digits = text.slice(digitsAt, digitsEnd);
|
|
782
|
+
if (isInteger && digits.length > 0 && inExactValue(frames) && magnitudeExceedsTwoPow53(digits)) {
|
|
783
|
+
out.push(`"${raw}"`);
|
|
784
|
+
} else {
|
|
785
|
+
out.push(raw);
|
|
786
|
+
}
|
|
787
|
+
valueEnded();
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
790
|
+
if (c >= "a" && c <= "z") {
|
|
791
|
+
const start = i;
|
|
792
|
+
while (i < n && text[i] >= "a" && text[i] <= "z") i++;
|
|
793
|
+
out.push(text.slice(start, i));
|
|
794
|
+
valueEnded();
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
out.push(c);
|
|
798
|
+
i++;
|
|
799
|
+
}
|
|
800
|
+
return out.join("");
|
|
801
|
+
}
|
|
802
|
+
|
|
634
803
|
// src/utils/http.ts
|
|
635
804
|
var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "PUT", "DELETE"]);
|
|
636
805
|
var HttpClient = class {
|
|
@@ -831,7 +1000,7 @@ var HttpClient = class {
|
|
|
831
1000
|
if (response.status === 204) {
|
|
832
1001
|
data = void 0;
|
|
833
1002
|
} else if (contentType.includes("application/json")) {
|
|
834
|
-
const rawData = await response.
|
|
1003
|
+
const rawData = parseApiJson(await response.text());
|
|
835
1004
|
data = options.skipResponseConversion ? rawData : convertResponse(rawData);
|
|
836
1005
|
} else {
|
|
837
1006
|
data = await response.text();
|
|
@@ -2589,7 +2758,7 @@ var WebSocketRealtimeClient = class {
|
|
|
2589
2758
|
}
|
|
2590
2759
|
handleMessage(data) {
|
|
2591
2760
|
try {
|
|
2592
|
-
const message = convertResponse(
|
|
2761
|
+
const message = convertResponse(parseApiJson(data));
|
|
2593
2762
|
const eventType = mapEventType(String(message.type));
|
|
2594
2763
|
message.type = eventType;
|
|
2595
2764
|
const event = message;
|
|
@@ -2774,8 +2943,9 @@ var SseRealtimeClient = class {
|
|
|
2774
2943
|
this.setState("connecting");
|
|
2775
2944
|
const sseUrl = this.buildSseUrl(filter);
|
|
2776
2945
|
this.options.debug(`Connecting to SSE: ${sseUrl}`);
|
|
2946
|
+
const usingPlatformEventSource = typeof EventSource !== "undefined";
|
|
2777
2947
|
let EventSourceImpl;
|
|
2778
|
-
if (
|
|
2948
|
+
if (usingPlatformEventSource) {
|
|
2779
2949
|
EventSourceImpl = EventSource;
|
|
2780
2950
|
} else {
|
|
2781
2951
|
try {
|
|
@@ -2799,16 +2969,20 @@ var SseRealtimeClient = class {
|
|
|
2799
2969
|
}
|
|
2800
2970
|
return new Promise((resolve, reject) => {
|
|
2801
2971
|
try {
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2972
|
+
const connectUrl = usingPlatformEventSource ? appendSseToken(sseUrl, token) : sseUrl;
|
|
2973
|
+
this.eventSource = new EventSourceImpl(
|
|
2974
|
+
connectUrl,
|
|
2975
|
+
usingPlatformEventSource ? void 0 : {
|
|
2976
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2977
|
+
fetch: (input, init) => fetch(input, {
|
|
2978
|
+
...init,
|
|
2979
|
+
headers: {
|
|
2980
|
+
...init?.headers,
|
|
2981
|
+
Authorization: `Bearer ${token}`
|
|
2982
|
+
}
|
|
2983
|
+
})
|
|
2984
|
+
}
|
|
2985
|
+
);
|
|
2812
2986
|
this.eventSource.onopen = () => {
|
|
2813
2987
|
this.options.debug("SSE connected");
|
|
2814
2988
|
this.setState("connected");
|
|
@@ -2920,7 +3094,7 @@ var SseRealtimeClient = class {
|
|
|
2920
3094
|
}
|
|
2921
3095
|
handleMessage(data) {
|
|
2922
3096
|
try {
|
|
2923
|
-
const parsed = convertResponse(
|
|
3097
|
+
const parsed = convertResponse(parseApiJson(data));
|
|
2924
3098
|
const eventType = mapEventType(String(parsed.type));
|
|
2925
3099
|
parsed.type = eventType;
|
|
2926
3100
|
const event = parsed;
|
|
@@ -2995,6 +3169,10 @@ var SseRealtimeClient = class {
|
|
|
2995
3169
|
}, delay);
|
|
2996
3170
|
}
|
|
2997
3171
|
};
|
|
3172
|
+
function appendSseToken(url, token) {
|
|
3173
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
3174
|
+
return `${url}${separator}token=${encodeURIComponent(token)}`;
|
|
3175
|
+
}
|
|
2998
3176
|
function isStreamEndingEvent(eventType, data) {
|
|
2999
3177
|
if (eventType === "error" || eventType === "job.completed") {
|
|
3000
3178
|
return true;
|
|
@@ -4460,6 +4638,10 @@ var SpooledWorker = class {
|
|
|
4460
4638
|
};
|
|
4461
4639
|
try {
|
|
4462
4640
|
const outcome = await this.runHandler(activeJob, context);
|
|
4641
|
+
if (activeJob.leaseLost) {
|
|
4642
|
+
this.debug(`Job ${job.id} lost its lease; dropping late result`);
|
|
4643
|
+
return;
|
|
4644
|
+
}
|
|
4463
4645
|
if (activeJob.settled) {
|
|
4464
4646
|
this.debug(`Job ${job.id} was already settled; dropping late result`);
|
|
4465
4647
|
return;
|
|
@@ -4467,11 +4649,14 @@ var SpooledWorker = class {
|
|
|
4467
4649
|
activeJob.settled = true;
|
|
4468
4650
|
switch (outcome.kind) {
|
|
4469
4651
|
case "returned":
|
|
4470
|
-
activeJob.ack = this.completeJob(
|
|
4652
|
+
activeJob.ack = this.completeJob(
|
|
4653
|
+
activeJob,
|
|
4654
|
+
outcome.value ?? void 0
|
|
4655
|
+
);
|
|
4471
4656
|
break;
|
|
4472
4657
|
case "threw":
|
|
4473
4658
|
activeJob.ack = this.failJob(
|
|
4474
|
-
|
|
4659
|
+
activeJob,
|
|
4475
4660
|
outcome.error instanceof Error ? outcome.error.message : String(outcome.error)
|
|
4476
4661
|
);
|
|
4477
4662
|
break;
|
|
@@ -4481,7 +4666,7 @@ var SpooledWorker = class {
|
|
|
4481
4666
|
jobId: job.id,
|
|
4482
4667
|
queueName: job.queueName
|
|
4483
4668
|
});
|
|
4484
|
-
activeJob.ack = this.failJob(
|
|
4669
|
+
activeJob.ack = this.failJob(activeJob, JOB_TIMEOUT_MESSAGE);
|
|
4485
4670
|
break;
|
|
4486
4671
|
}
|
|
4487
4672
|
await activeJob.ack;
|
|
@@ -4521,12 +4706,13 @@ var SpooledWorker = class {
|
|
|
4521
4706
|
this.debug(`Force-failing job ${activeJob.job.id} due to shutdown timeout`);
|
|
4522
4707
|
activeJob.settled = true;
|
|
4523
4708
|
activeJob.abortController.abort(new Error(SHUTDOWN_TIMEOUT_MESSAGE));
|
|
4524
|
-
activeJob.ack = this.failJob(activeJob
|
|
4709
|
+
activeJob.ack = this.failJob(activeJob, SHUTDOWN_TIMEOUT_MESSAGE);
|
|
4525
4710
|
await activeJob.ack;
|
|
4526
4711
|
this.cleanupJob(activeJob);
|
|
4527
4712
|
}
|
|
4528
|
-
async completeJob(
|
|
4529
|
-
if (!this.workerId) return;
|
|
4713
|
+
async completeJob(activeJob, result) {
|
|
4714
|
+
if (!this.workerId || activeJob.leaseLost) return;
|
|
4715
|
+
const { job } = activeJob;
|
|
4530
4716
|
try {
|
|
4531
4717
|
await this.client.jobs.complete(job.id, {
|
|
4532
4718
|
workerId: this.workerId,
|
|
@@ -4539,12 +4725,17 @@ var SpooledWorker = class {
|
|
|
4539
4725
|
result
|
|
4540
4726
|
});
|
|
4541
4727
|
} catch (error) {
|
|
4728
|
+
if (isLeaseExpired(error)) {
|
|
4729
|
+
this.noteLeaseLost(activeJob, error);
|
|
4730
|
+
return;
|
|
4731
|
+
}
|
|
4542
4732
|
this.debug(`Failed to complete job ${job.id}`, error);
|
|
4543
4733
|
this.emit("error", { error });
|
|
4544
4734
|
}
|
|
4545
4735
|
}
|
|
4546
|
-
async failJob(
|
|
4547
|
-
if (!this.workerId) return;
|
|
4736
|
+
async failJob(activeJob, errorMessage) {
|
|
4737
|
+
if (!this.workerId || activeJob.leaseLost) return;
|
|
4738
|
+
const { job } = activeJob;
|
|
4548
4739
|
const willRetry = job.retryCount < job.maxRetries;
|
|
4549
4740
|
const message = coerceFailureMessage(errorMessage);
|
|
4550
4741
|
try {
|
|
@@ -4560,6 +4751,10 @@ var SpooledWorker = class {
|
|
|
4560
4751
|
willRetry
|
|
4561
4752
|
});
|
|
4562
4753
|
} catch (error) {
|
|
4754
|
+
if (isLeaseExpired(error)) {
|
|
4755
|
+
this.noteLeaseLost(activeJob, error);
|
|
4756
|
+
return;
|
|
4757
|
+
}
|
|
4563
4758
|
this.debug(`Failed to fail job ${job.id}`, error);
|
|
4564
4759
|
this.emit("error", { error });
|
|
4565
4760
|
}
|
|
@@ -4584,7 +4779,7 @@ var SpooledWorker = class {
|
|
|
4584
4779
|
}
|
|
4585
4780
|
}
|
|
4586
4781
|
async sendJobHeartbeat(activeJob) {
|
|
4587
|
-
if (!this.workerId) return;
|
|
4782
|
+
if (!this.workerId || activeJob.leaseLost) return;
|
|
4588
4783
|
const { job } = activeJob;
|
|
4589
4784
|
try {
|
|
4590
4785
|
await this.client.jobs.heartbeat(job.id, {
|
|
@@ -4593,9 +4788,34 @@ var SpooledWorker = class {
|
|
|
4593
4788
|
...job.leaseId != null && { leaseId: job.leaseId }
|
|
4594
4789
|
});
|
|
4595
4790
|
} catch (error) {
|
|
4791
|
+
if (isLeaseExpired(error)) {
|
|
4792
|
+
this.noteLeaseLost(activeJob, error);
|
|
4793
|
+
return;
|
|
4794
|
+
}
|
|
4596
4795
|
this.debug(`Job heartbeat failed for ${job.id}`, error);
|
|
4597
4796
|
}
|
|
4598
4797
|
}
|
|
4798
|
+
/**
|
|
4799
|
+
* The lease is gone. Abort the handler if it listens, stop renewing, and
|
|
4800
|
+
* refuse a later complete/fail with the stale lease.
|
|
4801
|
+
*/
|
|
4802
|
+
noteLeaseLost(activeJob, error) {
|
|
4803
|
+
if (activeJob.leaseLost) return;
|
|
4804
|
+
activeJob.leaseLost = true;
|
|
4805
|
+
if (activeJob.heartbeatTimer) {
|
|
4806
|
+
clearInterval(activeJob.heartbeatTimer);
|
|
4807
|
+
activeJob.heartbeatTimer = void 0;
|
|
4808
|
+
}
|
|
4809
|
+
this.clearTimeoutTimer(activeJob);
|
|
4810
|
+
const reason = error instanceof Error ? error : new Error("Job lease expired");
|
|
4811
|
+
activeJob.abortController.abort(reason);
|
|
4812
|
+
this.debug(
|
|
4813
|
+
`Job lease lost for ${activeJob.job.id}; stopping handler`,
|
|
4814
|
+
error
|
|
4815
|
+
);
|
|
4816
|
+
activeJob.settled = true;
|
|
4817
|
+
this.cleanupJob(activeJob);
|
|
4818
|
+
}
|
|
4599
4819
|
async sendWorkerHeartbeat() {
|
|
4600
4820
|
if (!this.workerId || this.state !== "running" && this.state !== "stopping") {
|
|
4601
4821
|
return;
|
|
@@ -4630,6 +4850,9 @@ var SpooledWorker = class {
|
|
|
4630
4850
|
});
|
|
4631
4851
|
}
|
|
4632
4852
|
};
|
|
4853
|
+
function isLeaseExpired(error) {
|
|
4854
|
+
return error instanceof SpooledError && error.statusCode === 409 && error.code.toUpperCase() === "LEASE_EXPIRED";
|
|
4855
|
+
}
|
|
4633
4856
|
|
|
4634
4857
|
exports.API_VERSION = API_VERSION;
|
|
4635
4858
|
exports.AdminResource = AdminResource;
|