@spooled/sdk 1.3.0 → 1.4.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/README.md +79 -8
- package/dist/{index-BVWKEr6p.d.cts → index-DzTC8ld3.d.cts} +569 -95
- package/dist/{index-BVWKEr6p.d.ts → index-DzTC8ld3.d.ts} +569 -95
- package/dist/index.cjs +341 -125
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -186
- package/dist/index.d.ts +3 -186
- package/dist/index.js +341 -126
- package/dist/index.js.map +1 -1
- package/dist/worker/index.cjs +1 -1
- 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 +1 -1
- package/dist/worker/index.js.map +1 -1
- package/package.json +4 -3
- package/proto/spooled.proto +19 -1
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.4.0",
|
|
52
52
|
autoRefreshToken: true
|
|
53
53
|
};
|
|
54
|
-
var SDK_VERSION = "1.
|
|
54
|
+
var SDK_VERSION = "1.4.0";
|
|
55
55
|
var API_VERSION = "v1";
|
|
56
56
|
var API_BASE_PATH = `/api/${API_VERSION}`;
|
|
57
57
|
function normalizeCredential(value) {
|
|
@@ -364,6 +364,10 @@ function parseRateLimitHeaders(headers) {
|
|
|
364
364
|
info.reset = new Date(timestamp * 1e3);
|
|
365
365
|
}
|
|
366
366
|
}
|
|
367
|
+
const scope = headers.get("X-RateLimit-Scope");
|
|
368
|
+
if (scope) {
|
|
369
|
+
info.scope = scope.trim();
|
|
370
|
+
}
|
|
367
371
|
return info;
|
|
368
372
|
}
|
|
369
373
|
var MAX_RAW_ERROR_BODY_CHARS = 500;
|
|
@@ -643,6 +647,8 @@ var HttpClient = class {
|
|
|
643
647
|
* refresh and fail even though the token was about to be replaced.
|
|
644
648
|
*/
|
|
645
649
|
refreshInFlight = null;
|
|
650
|
+
/** Rate-limit headers of the most recent response that carried them */
|
|
651
|
+
lastRateLimit;
|
|
646
652
|
constructor(config, circuitBreaker) {
|
|
647
653
|
this.config = config;
|
|
648
654
|
this.circuitBreaker = circuitBreaker;
|
|
@@ -655,6 +661,14 @@ var HttpClient = class {
|
|
|
655
661
|
setAuthToken(token) {
|
|
656
662
|
this.authToken = token;
|
|
657
663
|
}
|
|
664
|
+
/**
|
|
665
|
+
* The `X-RateLimit-*` (and `Retry-After`) values of the most recent
|
|
666
|
+
* response that carried them, successful or not. `undefined` until such a
|
|
667
|
+
* response arrives. With concurrent requests it is the last one to finish.
|
|
668
|
+
*/
|
|
669
|
+
getLastRateLimitInfo() {
|
|
670
|
+
return this.lastRateLimit ? { ...this.lastRateLimit } : void 0;
|
|
671
|
+
}
|
|
658
672
|
/**
|
|
659
673
|
* Set the token refresh function.
|
|
660
674
|
*
|
|
@@ -801,6 +815,7 @@ var HttpClient = class {
|
|
|
801
815
|
);
|
|
802
816
|
}
|
|
803
817
|
this.debug?.(`Response: ${response.status}`, { url });
|
|
818
|
+
this.recordRateLimit(response.headers);
|
|
804
819
|
if (!response.ok) {
|
|
805
820
|
if (response.status === 401 && this.refreshTokenFn && !hasRefreshed && !options.skipAuthRefresh) {
|
|
806
821
|
const alreadyRefreshed = this.authToken !== tokenUsed;
|
|
@@ -827,6 +842,13 @@ var HttpClient = class {
|
|
|
827
842
|
data
|
|
828
843
|
};
|
|
829
844
|
}
|
|
845
|
+
/** Remember the rate-limit headers of a response that has them. */
|
|
846
|
+
recordRateLimit(headers) {
|
|
847
|
+
if (!headers || typeof headers.get !== "function" || headers.get("X-RateLimit-Limit") === null && headers.get("X-RateLimit-Remaining") === null) {
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
this.lastRateLimit = parseRateLimitHeaders(headers);
|
|
851
|
+
}
|
|
830
852
|
/**
|
|
831
853
|
* Make an HTTP request with retry and circuit breaker
|
|
832
854
|
*/
|
|
@@ -954,6 +976,12 @@ var CircuitState = /* @__PURE__ */ ((CircuitState2) => {
|
|
|
954
976
|
CircuitState2["HALF_OPEN"] = "HALF_OPEN";
|
|
955
977
|
return CircuitState2;
|
|
956
978
|
})(CircuitState || {});
|
|
979
|
+
function isCircuitBreakerFailure(error) {
|
|
980
|
+
if (error instanceof RateLimitError) {
|
|
981
|
+
return false;
|
|
982
|
+
}
|
|
983
|
+
return isRetryableError(error);
|
|
984
|
+
}
|
|
957
985
|
var CircuitBreaker = class {
|
|
958
986
|
state = "CLOSED" /* CLOSED */;
|
|
959
987
|
failureCount = 0;
|
|
@@ -1014,7 +1042,7 @@ var CircuitBreaker = class {
|
|
|
1014
1042
|
if (!this.config.enabled) {
|
|
1015
1043
|
return;
|
|
1016
1044
|
}
|
|
1017
|
-
const shouldCount = this.config.isFailure ? this.config.isFailure(error) :
|
|
1045
|
+
const shouldCount = this.config.isFailure ? this.config.isFailure(error) : isCircuitBreakerFailure(error);
|
|
1018
1046
|
if (!shouldCount) {
|
|
1019
1047
|
return;
|
|
1020
1048
|
}
|
|
@@ -1212,12 +1240,31 @@ function coerceFailureMessage(message) {
|
|
|
1212
1240
|
return codePoints.length <= MAX_FAILURE_MESSAGE_CHARS ? text : codePoints.slice(0, MAX_FAILURE_MESSAGE_CHARS).join("");
|
|
1213
1241
|
}
|
|
1214
1242
|
|
|
1243
|
+
// src/utils/paging.ts
|
|
1244
|
+
var HAS_MORE_HEADER = "X-Has-More";
|
|
1245
|
+
function hasMoreFromHeaders(headers, count, limit, defaultLimit, maxLimit) {
|
|
1246
|
+
const header = headers.get(HAS_MORE_HEADER);
|
|
1247
|
+
if (header !== null) {
|
|
1248
|
+
return header.trim().toLowerCase() === "true";
|
|
1249
|
+
}
|
|
1250
|
+
const applied = Math.min(Math.max(limit ?? defaultLimit, 1), maxLimit);
|
|
1251
|
+
return count >= applied;
|
|
1252
|
+
}
|
|
1253
|
+
function toQueryTimestamp(value) {
|
|
1254
|
+
return value instanceof Date ? value.toISOString() : value;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1215
1257
|
// src/resources/jobs.ts
|
|
1258
|
+
var JOB_LIST_DEFAULT_LIMIT = 50;
|
|
1259
|
+
var JOB_LIST_MAX_LIMIT = 100;
|
|
1260
|
+
var JOB_HISTORY_DEFAULT_LIMIT = 100;
|
|
1261
|
+
var JOB_HISTORY_MAX_LIMIT = 500;
|
|
1216
1262
|
var JobsResource = class {
|
|
1217
1263
|
constructor(http) {
|
|
1218
1264
|
this.http = http;
|
|
1219
1265
|
this.dlq = {
|
|
1220
1266
|
list: this.listDlq.bind(this),
|
|
1267
|
+
listPage: this.listDlqPage.bind(this),
|
|
1221
1268
|
retry: this.retryDlq.bind(this),
|
|
1222
1269
|
purge: this.purgeDlq.bind(this)
|
|
1223
1270
|
};
|
|
@@ -1241,15 +1288,36 @@ var JobsResource = class {
|
|
|
1241
1288
|
}
|
|
1242
1289
|
/**
|
|
1243
1290
|
* List jobs with optional filtering
|
|
1291
|
+
*
|
|
1292
|
+
* `status` takes one status or several, and `q`, `jobType`, `createdAfter`
|
|
1293
|
+
* and `createdBefore` filter on the server (API 0.1.122+). Use
|
|
1294
|
+
* {@link JobsResource.listPage} to also learn whether another page follows.
|
|
1244
1295
|
*/
|
|
1245
1296
|
async list(params) {
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1297
|
+
return (await this.listPage(params)).data;
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1300
|
+
* List one page of jobs with `hasMore` from the `X-Has-More` header
|
|
1301
|
+
* (API 0.1.122+). Takes the same filters as {@link JobsResource.list}.
|
|
1302
|
+
*
|
|
1303
|
+
* @example
|
|
1304
|
+
* ```typescript
|
|
1305
|
+
* let offset = 0;
|
|
1306
|
+
* for (;;) {
|
|
1307
|
+
* const page = await client.jobs.listPage({
|
|
1308
|
+
* status: ["failed", "deadletter"],
|
|
1309
|
+
* createdAfter: new Date(Date.now() - 86_400_000),
|
|
1310
|
+
* limit: 100,
|
|
1311
|
+
* offset,
|
|
1312
|
+
* });
|
|
1313
|
+
* handle(page.data);
|
|
1314
|
+
* if (!page.hasMore) break;
|
|
1315
|
+
* offset += page.data.length;
|
|
1316
|
+
* }
|
|
1317
|
+
* ```
|
|
1318
|
+
*/
|
|
1319
|
+
async listPage(params) {
|
|
1320
|
+
return this.listSummaries("/jobs", params);
|
|
1253
1321
|
}
|
|
1254
1322
|
/**
|
|
1255
1323
|
* Get a job by ID
|
|
@@ -1279,6 +1347,46 @@ var JobsResource = class {
|
|
|
1279
1347
|
priority
|
|
1280
1348
|
});
|
|
1281
1349
|
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Get the recorded lifecycle events of a job (`GET /jobs/{id}/history`,
|
|
1352
|
+
* API 0.1.122+), oldest first unless `order: "desc"`.
|
|
1353
|
+
*
|
|
1354
|
+
* The history is sparse: the server records REST claims (`processing`),
|
|
1355
|
+
* completions (`completed`) and failures (`retry_scheduled`,
|
|
1356
|
+
* `deadlettered`) and the scheduler's dead-letter sweeps, so a job settled
|
|
1357
|
+
* over gRPC may have few or no events. An unknown job is a `NotFoundError`.
|
|
1358
|
+
*/
|
|
1359
|
+
async history(id, params) {
|
|
1360
|
+
return (await this.historyPage(id, params)).data;
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* One page of {@link JobsResource.history} with `hasMore` from the
|
|
1364
|
+
* `X-Has-More` header. `limit` defaults to 100 (at most 500).
|
|
1365
|
+
*/
|
|
1366
|
+
async historyPage(id, params) {
|
|
1367
|
+
const response = await this.http.request(
|
|
1368
|
+
`/jobs/${id}/history`,
|
|
1369
|
+
{
|
|
1370
|
+
method: "GET",
|
|
1371
|
+
params: {
|
|
1372
|
+
order: params?.order,
|
|
1373
|
+
limit: params?.limit,
|
|
1374
|
+
offset: params?.offset
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
);
|
|
1378
|
+
const data = response.data ?? [];
|
|
1379
|
+
return {
|
|
1380
|
+
data,
|
|
1381
|
+
hasMore: hasMoreFromHeaders(
|
|
1382
|
+
response.headers,
|
|
1383
|
+
data.length,
|
|
1384
|
+
params?.limit,
|
|
1385
|
+
JOB_HISTORY_DEFAULT_LIMIT,
|
|
1386
|
+
JOB_HISTORY_MAX_LIMIT
|
|
1387
|
+
)
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1282
1390
|
/**
|
|
1283
1391
|
* Get job statistics
|
|
1284
1392
|
*/
|
|
@@ -1355,13 +1463,30 @@ var JobsResource = class {
|
|
|
1355
1463
|
}
|
|
1356
1464
|
// DLQ operations (private implementations)
|
|
1357
1465
|
async listDlq(params) {
|
|
1358
|
-
|
|
1359
|
-
|
|
1466
|
+
return (await this.listDlqPage(params)).data;
|
|
1467
|
+
}
|
|
1468
|
+
async listDlqPage(params) {
|
|
1469
|
+
return this.listSummaries("/jobs/dlq", params);
|
|
1470
|
+
}
|
|
1471
|
+
async listSummaries(path, params) {
|
|
1472
|
+
const response = await this.http.request(path, {
|
|
1473
|
+
method: "GET",
|
|
1474
|
+
params: jobListQuery(params)
|
|
1360
1475
|
});
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1476
|
+
const rows = response.data ?? [];
|
|
1477
|
+
return {
|
|
1478
|
+
data: rows.map((row) => ({
|
|
1479
|
+
...row,
|
|
1480
|
+
jobType: row.jobType ?? jobTypeFromPayload(row)
|
|
1481
|
+
})),
|
|
1482
|
+
hasMore: hasMoreFromHeaders(
|
|
1483
|
+
response.headers,
|
|
1484
|
+
rows.length,
|
|
1485
|
+
params?.limit,
|
|
1486
|
+
JOB_LIST_DEFAULT_LIMIT,
|
|
1487
|
+
JOB_LIST_MAX_LIMIT
|
|
1488
|
+
)
|
|
1489
|
+
};
|
|
1365
1490
|
}
|
|
1366
1491
|
async retryDlq(params) {
|
|
1367
1492
|
return this.http.post("/jobs/dlq/retry", params);
|
|
@@ -1370,6 +1495,24 @@ var JobsResource = class {
|
|
|
1370
1495
|
return this.http.post("/jobs/dlq/purge", params);
|
|
1371
1496
|
}
|
|
1372
1497
|
};
|
|
1498
|
+
function jobListQuery(params) {
|
|
1499
|
+
if (!params) {
|
|
1500
|
+
return void 0;
|
|
1501
|
+
}
|
|
1502
|
+
const { status: status2, createdAfter, createdBefore, ...rest } = params;
|
|
1503
|
+
let statusParam;
|
|
1504
|
+
if (typeof status2 === "string") {
|
|
1505
|
+
statusParam = status2;
|
|
1506
|
+
} else if (status2 && status2.length > 0) {
|
|
1507
|
+
statusParam = status2.join(",");
|
|
1508
|
+
}
|
|
1509
|
+
return {
|
|
1510
|
+
...rest,
|
|
1511
|
+
status: statusParam,
|
|
1512
|
+
createdAfter: toQueryTimestamp(createdAfter),
|
|
1513
|
+
createdBefore: toQueryTimestamp(createdBefore)
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1373
1516
|
function withJobType(job) {
|
|
1374
1517
|
return {
|
|
1375
1518
|
...job,
|
|
@@ -2193,6 +2336,31 @@ var WebhookIngestionResource = class {
|
|
|
2193
2336
|
status: raw.status
|
|
2194
2337
|
};
|
|
2195
2338
|
}
|
|
2339
|
+
/**
|
|
2340
|
+
* The inbound webhook log: accepted requests to
|
|
2341
|
+
* `POST /webhooks/{org_id}/custom`, newest first
|
|
2342
|
+
* (`GET /webhooks/deliveries`, API 0.1.122+).
|
|
2343
|
+
*
|
|
2344
|
+
* Needs an API key without a queue restriction (a queue-scoped key gets a
|
|
2345
|
+
* 403 `AuthorizationError`). Entries carry no payload and no signature;
|
|
2346
|
+
* requests that were rejected are not logged. `limit` defaults to 50 (at
|
|
2347
|
+
* most 100); an invalid filter is a 400 `ValidationError`.
|
|
2348
|
+
*/
|
|
2349
|
+
async listDeliveries(params) {
|
|
2350
|
+
const rows = await this.http.get(
|
|
2351
|
+
"/webhooks/deliveries",
|
|
2352
|
+
{
|
|
2353
|
+
params: {
|
|
2354
|
+
status: params?.status,
|
|
2355
|
+
since: toQueryTimestamp(params?.since),
|
|
2356
|
+
queueName: params?.queueName,
|
|
2357
|
+
limit: params?.limit,
|
|
2358
|
+
offset: params?.offset
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
);
|
|
2362
|
+
return rows ?? [];
|
|
2363
|
+
}
|
|
2196
2364
|
};
|
|
2197
2365
|
|
|
2198
2366
|
// src/realtime/event-map.ts
|
|
@@ -3030,6 +3198,120 @@ function clearProtoCache() {
|
|
|
3030
3198
|
function encodeFailRequest(params) {
|
|
3031
3199
|
return { ...params, retry: params.retry ?? true };
|
|
3032
3200
|
}
|
|
3201
|
+
function encodeStruct(value) {
|
|
3202
|
+
if (value == null) {
|
|
3203
|
+
return void 0;
|
|
3204
|
+
}
|
|
3205
|
+
return {
|
|
3206
|
+
fields: Object.fromEntries(
|
|
3207
|
+
Object.entries(value).map(([key, entry]) => [key, encodeValue(entry)])
|
|
3208
|
+
)
|
|
3209
|
+
};
|
|
3210
|
+
}
|
|
3211
|
+
function encodeValue(value) {
|
|
3212
|
+
if (value == null) {
|
|
3213
|
+
return { nullValue: 0 };
|
|
3214
|
+
}
|
|
3215
|
+
if (Array.isArray(value)) {
|
|
3216
|
+
return { listValue: { values: value.map(encodeValue) } };
|
|
3217
|
+
}
|
|
3218
|
+
switch (typeof value) {
|
|
3219
|
+
case "number":
|
|
3220
|
+
return { numberValue: value };
|
|
3221
|
+
case "string":
|
|
3222
|
+
return { stringValue: value };
|
|
3223
|
+
case "boolean":
|
|
3224
|
+
return { boolValue: value };
|
|
3225
|
+
case "object":
|
|
3226
|
+
return { structValue: encodeStruct(value) };
|
|
3227
|
+
default:
|
|
3228
|
+
return { stringValue: String(value) };
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
function decodeStruct(value) {
|
|
3232
|
+
if (!value || typeof value !== "object" || !("fields" in value)) {
|
|
3233
|
+
return value;
|
|
3234
|
+
}
|
|
3235
|
+
const fields = value.fields ?? {};
|
|
3236
|
+
return Object.fromEntries(
|
|
3237
|
+
Object.entries(fields).map(([key, entry]) => [key, decodeValue(entry)])
|
|
3238
|
+
);
|
|
3239
|
+
}
|
|
3240
|
+
function decodeValue(value) {
|
|
3241
|
+
if ("nullValue" in value) {
|
|
3242
|
+
return null;
|
|
3243
|
+
}
|
|
3244
|
+
if ("numberValue" in value) {
|
|
3245
|
+
return value.numberValue;
|
|
3246
|
+
}
|
|
3247
|
+
if ("stringValue" in value) {
|
|
3248
|
+
return value.stringValue;
|
|
3249
|
+
}
|
|
3250
|
+
if ("boolValue" in value) {
|
|
3251
|
+
return value.boolValue;
|
|
3252
|
+
}
|
|
3253
|
+
if ("structValue" in value) {
|
|
3254
|
+
return decodeStruct(value.structValue);
|
|
3255
|
+
}
|
|
3256
|
+
if ("listValue" in value) {
|
|
3257
|
+
return (value.listValue.values ?? []).map(decodeValue);
|
|
3258
|
+
}
|
|
3259
|
+
return void 0;
|
|
3260
|
+
}
|
|
3261
|
+
function encodeEnqueueRequest(params) {
|
|
3262
|
+
const encoded = {
|
|
3263
|
+
queueName: params.queueName,
|
|
3264
|
+
payload: encodeStruct(params.payload)
|
|
3265
|
+
};
|
|
3266
|
+
if (params.priority !== void 0) {
|
|
3267
|
+
encoded.priority = params.priority;
|
|
3268
|
+
}
|
|
3269
|
+
if (params.maxRetries !== void 0) {
|
|
3270
|
+
encoded.maxRetries = params.maxRetries;
|
|
3271
|
+
}
|
|
3272
|
+
if (params.timeoutSeconds !== void 0) {
|
|
3273
|
+
encoded.timeoutSeconds = params.timeoutSeconds;
|
|
3274
|
+
}
|
|
3275
|
+
if (params.idempotencyKey !== void 0 && params.idempotencyKey !== "") {
|
|
3276
|
+
encoded.idempotencyKey = params.idempotencyKey;
|
|
3277
|
+
}
|
|
3278
|
+
if (params.scheduledAt !== void 0) {
|
|
3279
|
+
encoded.scheduledAt = params.scheduledAt;
|
|
3280
|
+
}
|
|
3281
|
+
if (params.tags !== void 0) {
|
|
3282
|
+
encoded.tags = params.tags;
|
|
3283
|
+
}
|
|
3284
|
+
return encoded;
|
|
3285
|
+
}
|
|
3286
|
+
function encodeCompleteRequest(params) {
|
|
3287
|
+
return {
|
|
3288
|
+
...params,
|
|
3289
|
+
result: encodeStruct(params.result)
|
|
3290
|
+
};
|
|
3291
|
+
}
|
|
3292
|
+
function decodeJob(job) {
|
|
3293
|
+
return {
|
|
3294
|
+
...job,
|
|
3295
|
+
// proto-loader keeps an enum number it does not know (a status added to
|
|
3296
|
+
// the server's proto after this SDK) as a number; keep it a string so
|
|
3297
|
+
// `status` always matches its type.
|
|
3298
|
+
status: typeof job.status === "number" ? String(job.status) : job.status,
|
|
3299
|
+
payload: decodeStruct(job.payload),
|
|
3300
|
+
result: decodeStruct(job.result)
|
|
3301
|
+
};
|
|
3302
|
+
}
|
|
3303
|
+
function encodeProcessRequest(request) {
|
|
3304
|
+
if (request.fail) {
|
|
3305
|
+
return { ...request, fail: encodeFailRequest(request.fail) };
|
|
3306
|
+
}
|
|
3307
|
+
if (request.complete) {
|
|
3308
|
+
return { ...request, complete: encodeCompleteRequest(request.complete) };
|
|
3309
|
+
}
|
|
3310
|
+
return request;
|
|
3311
|
+
}
|
|
3312
|
+
function decodeProcessResponse(response) {
|
|
3313
|
+
return response.job ? { ...response, job: decodeJob(response.job) } : response;
|
|
3314
|
+
}
|
|
3033
3315
|
|
|
3034
3316
|
// src/grpc/streaming.ts
|
|
3035
3317
|
async function* asyncIterableFromStream(stream, options) {
|
|
@@ -3048,21 +3330,39 @@ async function* asyncIterableFromStream(stream, options) {
|
|
|
3048
3330
|
var AsyncIterableStream = class {
|
|
3049
3331
|
stream;
|
|
3050
3332
|
options;
|
|
3051
|
-
|
|
3333
|
+
decode;
|
|
3334
|
+
constructor(stream, options = {}, decode = (item) => item) {
|
|
3052
3335
|
this.stream = stream;
|
|
3053
3336
|
this.options = options;
|
|
3337
|
+
this.decode = decode;
|
|
3054
3338
|
}
|
|
3055
3339
|
async *[Symbol.asyncIterator]() {
|
|
3056
3340
|
const { onConnected, onError, onEnd } = this.options;
|
|
3057
3341
|
const queue = [];
|
|
3058
3342
|
let error = null;
|
|
3059
3343
|
let ended = false;
|
|
3344
|
+
let decodeFailed = false;
|
|
3060
3345
|
let resolve = null;
|
|
3061
3346
|
this.stream.on("data", (data) => {
|
|
3062
|
-
|
|
3347
|
+
if (decodeFailed) {
|
|
3348
|
+
return;
|
|
3349
|
+
}
|
|
3350
|
+
try {
|
|
3351
|
+
queue.push(this.decode(data));
|
|
3352
|
+
} catch (decodeError) {
|
|
3353
|
+
decodeFailed = true;
|
|
3354
|
+
error = decodeError instanceof Error ? decodeError : new Error(String(decodeError));
|
|
3355
|
+
try {
|
|
3356
|
+
this.stream.cancel?.();
|
|
3357
|
+
} catch {
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3063
3360
|
resolve?.();
|
|
3064
3361
|
});
|
|
3065
3362
|
this.stream.on("error", (err) => {
|
|
3363
|
+
if (decodeFailed) {
|
|
3364
|
+
return;
|
|
3365
|
+
}
|
|
3066
3366
|
error = err;
|
|
3067
3367
|
onError?.(err);
|
|
3068
3368
|
resolve?.();
|
|
@@ -3103,12 +3403,14 @@ function createProcessJobsStream(call, options) {
|
|
|
3103
3403
|
}
|
|
3104
3404
|
return {
|
|
3105
3405
|
send(request) {
|
|
3106
|
-
call.write(
|
|
3107
|
-
request.fail ? { ...request, fail: encodeFailRequest(request.fail) } : request
|
|
3108
|
-
);
|
|
3406
|
+
call.write(encodeProcessRequest(request));
|
|
3109
3407
|
},
|
|
3110
3408
|
receive() {
|
|
3111
|
-
return new AsyncIterableStream(
|
|
3409
|
+
return new AsyncIterableStream(
|
|
3410
|
+
call,
|
|
3411
|
+
{ onError, onEnd },
|
|
3412
|
+
decodeProcessResponse
|
|
3413
|
+
);
|
|
3112
3414
|
},
|
|
3113
3415
|
end() {
|
|
3114
3416
|
call.end();
|
|
@@ -3125,11 +3427,11 @@ function createJobStream(call, options) {
|
|
|
3125
3427
|
call.cancel();
|
|
3126
3428
|
});
|
|
3127
3429
|
}
|
|
3128
|
-
const iterable = new AsyncIterableStream(
|
|
3129
|
-
|
|
3130
|
-
onError,
|
|
3131
|
-
|
|
3132
|
-
|
|
3430
|
+
const iterable = new AsyncIterableStream(
|
|
3431
|
+
call,
|
|
3432
|
+
{ onConnected, onError, onEnd },
|
|
3433
|
+
decodeJob
|
|
3434
|
+
);
|
|
3133
3435
|
return {
|
|
3134
3436
|
[Symbol.asyncIterator]() {
|
|
3135
3437
|
return iterable[Symbol.asyncIterator]();
|
|
@@ -3141,104 +3443,6 @@ function createJobStream(call, options) {
|
|
|
3141
3443
|
}
|
|
3142
3444
|
|
|
3143
3445
|
// src/grpc/client.ts
|
|
3144
|
-
function encodeStruct(value) {
|
|
3145
|
-
if (value == null) {
|
|
3146
|
-
return void 0;
|
|
3147
|
-
}
|
|
3148
|
-
return {
|
|
3149
|
-
fields: Object.fromEntries(
|
|
3150
|
-
Object.entries(value).map(([key, entry]) => [key, encodeValue(entry)])
|
|
3151
|
-
)
|
|
3152
|
-
};
|
|
3153
|
-
}
|
|
3154
|
-
function encodeValue(value) {
|
|
3155
|
-
if (value == null) {
|
|
3156
|
-
return { nullValue: 0 };
|
|
3157
|
-
}
|
|
3158
|
-
if (Array.isArray(value)) {
|
|
3159
|
-
return { listValue: { values: value.map(encodeValue) } };
|
|
3160
|
-
}
|
|
3161
|
-
switch (typeof value) {
|
|
3162
|
-
case "number":
|
|
3163
|
-
return { numberValue: value };
|
|
3164
|
-
case "string":
|
|
3165
|
-
return { stringValue: value };
|
|
3166
|
-
case "boolean":
|
|
3167
|
-
return { boolValue: value };
|
|
3168
|
-
case "object":
|
|
3169
|
-
return { structValue: encodeStruct(value) };
|
|
3170
|
-
default:
|
|
3171
|
-
return { stringValue: String(value) };
|
|
3172
|
-
}
|
|
3173
|
-
}
|
|
3174
|
-
function decodeStruct(value) {
|
|
3175
|
-
if (!value || typeof value !== "object" || !("fields" in value)) {
|
|
3176
|
-
return value;
|
|
3177
|
-
}
|
|
3178
|
-
const fields = value.fields ?? {};
|
|
3179
|
-
return Object.fromEntries(
|
|
3180
|
-
Object.entries(fields).map(([key, entry]) => [key, decodeValue(entry)])
|
|
3181
|
-
);
|
|
3182
|
-
}
|
|
3183
|
-
function decodeValue(value) {
|
|
3184
|
-
if ("nullValue" in value) {
|
|
3185
|
-
return null;
|
|
3186
|
-
}
|
|
3187
|
-
if ("numberValue" in value) {
|
|
3188
|
-
return value.numberValue;
|
|
3189
|
-
}
|
|
3190
|
-
if ("stringValue" in value) {
|
|
3191
|
-
return value.stringValue;
|
|
3192
|
-
}
|
|
3193
|
-
if ("boolValue" in value) {
|
|
3194
|
-
return value.boolValue;
|
|
3195
|
-
}
|
|
3196
|
-
if ("structValue" in value) {
|
|
3197
|
-
return decodeStruct(value.structValue);
|
|
3198
|
-
}
|
|
3199
|
-
if ("listValue" in value) {
|
|
3200
|
-
return (value.listValue.values ?? []).map(decodeValue);
|
|
3201
|
-
}
|
|
3202
|
-
return void 0;
|
|
3203
|
-
}
|
|
3204
|
-
function encodeEnqueueRequest(params) {
|
|
3205
|
-
const encoded = {
|
|
3206
|
-
queueName: params.queueName,
|
|
3207
|
-
payload: encodeStruct(params.payload)
|
|
3208
|
-
};
|
|
3209
|
-
if (params.priority !== void 0) {
|
|
3210
|
-
encoded.priority = params.priority;
|
|
3211
|
-
}
|
|
3212
|
-
if (params.maxRetries !== void 0) {
|
|
3213
|
-
encoded.maxRetries = params.maxRetries;
|
|
3214
|
-
}
|
|
3215
|
-
if (params.timeoutSeconds !== void 0) {
|
|
3216
|
-
encoded.timeoutSeconds = params.timeoutSeconds;
|
|
3217
|
-
}
|
|
3218
|
-
if (params.idempotencyKey !== void 0 && params.idempotencyKey !== "") {
|
|
3219
|
-
encoded.idempotencyKey = params.idempotencyKey;
|
|
3220
|
-
}
|
|
3221
|
-
if (params.scheduledAt !== void 0) {
|
|
3222
|
-
encoded.scheduledAt = params.scheduledAt;
|
|
3223
|
-
}
|
|
3224
|
-
if (params.tags !== void 0) {
|
|
3225
|
-
encoded.tags = params.tags;
|
|
3226
|
-
}
|
|
3227
|
-
return encoded;
|
|
3228
|
-
}
|
|
3229
|
-
function encodeCompleteRequest(params) {
|
|
3230
|
-
return {
|
|
3231
|
-
...params,
|
|
3232
|
-
result: encodeStruct(params.result)
|
|
3233
|
-
};
|
|
3234
|
-
}
|
|
3235
|
-
function decodeJob(job) {
|
|
3236
|
-
return {
|
|
3237
|
-
...job,
|
|
3238
|
-
payload: decodeStruct(job.payload),
|
|
3239
|
-
result: decodeStruct(job.result)
|
|
3240
|
-
};
|
|
3241
|
-
}
|
|
3242
3446
|
function promisify(client, method, request, metadata, timeoutMs) {
|
|
3243
3447
|
return new Promise((resolve, reject) => {
|
|
3244
3448
|
const callback = (error, response) => {
|
|
@@ -3818,6 +4022,17 @@ var SpooledClient = class _SpooledClient {
|
|
|
3818
4022
|
getConfig() {
|
|
3819
4023
|
return this.config;
|
|
3820
4024
|
}
|
|
4025
|
+
/**
|
|
4026
|
+
* Rate-limit state from the most recent response that carried
|
|
4027
|
+
* `X-RateLimit-*` headers (API 0.1.122+): `limit`, `remaining`, `reset`
|
|
4028
|
+
* (from the Unix-seconds header), `scope` and, after a 429, `retryAfter`.
|
|
4029
|
+
* `undefined` until such a response arrives, and for servers or routes that
|
|
4030
|
+
* do not send the headers. A 429 also carries the same values on
|
|
4031
|
+
* `RateLimitError.rateLimitInfo`.
|
|
4032
|
+
*/
|
|
4033
|
+
getRateLimitInfo() {
|
|
4034
|
+
return this.http.getLastRateLimitInfo();
|
|
4035
|
+
}
|
|
3821
4036
|
/**
|
|
3822
4037
|
* Get circuit breaker statistics
|
|
3823
4038
|
*/
|
|
@@ -4478,6 +4693,7 @@ exports.createProcessJobsStream = createProcessJobsStream;
|
|
|
4478
4693
|
exports.createRetryWrapper = createRetryWrapper;
|
|
4479
4694
|
exports.dateToTimestamp = dateToTimestamp;
|
|
4480
4695
|
exports.getProtoPath = getProtoPath;
|
|
4696
|
+
exports.isCircuitBreakerFailure = isCircuitBreakerFailure;
|
|
4481
4697
|
exports.isRetryableError = isRetryableError;
|
|
4482
4698
|
exports.isSpooledError = isSpooledError;
|
|
4483
4699
|
exports.loadProtoDefinition = loadProtoDefinition;
|