@spooled/sdk 1.3.0 → 1.4.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.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.3.0",
51
+ userAgent: "@spooled/sdk-nodejs/1.4.1",
52
52
  autoRefreshToken: true
53
53
  };
54
- var SDK_VERSION = "1.3.0";
54
+ var SDK_VERSION = "1.4.1";
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) : isRetryableError(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
- const rows = await this.http.get("/jobs", {
1247
- params
1248
- });
1249
- return rows.map((row) => ({
1250
- ...row,
1251
- jobType: row.jobType ?? jobTypeFromPayload(row)
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
- const rows = await this.http.get("/jobs/dlq", {
1359
- params
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
- return rows.map((row) => ({
1362
- ...row,
1363
- jobType: row.jobType ?? jobTypeFromPayload(row)
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
- constructor(stream, options = {}) {
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
- queue.push(data);
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(call, { onError, onEnd });
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(call, {
3129
- onConnected,
3130
- onError,
3131
- onEnd
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;