@spooled/sdk 1.0.39 → 1.1.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/dist/index.cjs CHANGED
@@ -50,10 +50,10 @@ var DEFAULT_CONFIG = {
50
50
  successThreshold: 3,
51
51
  timeout: 3e4
52
52
  },
53
- userAgent: "@spooled/sdk-nodejs/1.0.39",
53
+ userAgent: "@spooled/sdk-nodejs/1.1.0",
54
54
  autoRefreshToken: true
55
55
  };
56
- var SDK_VERSION = "1.0.39";
56
+ var SDK_VERSION = "1.1.0";
57
57
  var API_VERSION = "v1";
58
58
  var API_BASE_PATH = `/api/${API_VERSION}`;
59
59
  function normalizeCredential(value) {
@@ -379,13 +379,31 @@ async function createErrorFromResponse(response) {
379
379
  return new PayloadTooLargeError(message, code, details, requestId);
380
380
  case 429: {
381
381
  const rateLimitInfo = parseRateLimitHeaders(response.headers);
382
- return new RateLimitError(message, code, rateLimitInfo, details, requestId);
382
+ return new RateLimitError(
383
+ message,
384
+ code,
385
+ rateLimitInfo,
386
+ details,
387
+ requestId
388
+ );
383
389
  }
384
390
  default:
385
391
  if (response.status >= 500) {
386
- return new ServerError(message, response.status, code, details, requestId);
392
+ return new ServerError(
393
+ message,
394
+ response.status,
395
+ code,
396
+ details,
397
+ requestId
398
+ );
387
399
  }
388
- return new SpooledError(message, response.status, code, details, requestId);
400
+ return new SpooledError(
401
+ message,
402
+ response.status,
403
+ code,
404
+ details,
405
+ requestId
406
+ );
389
407
  }
390
408
  }
391
409
  function isSpooledError(error) {
@@ -643,7 +661,9 @@ var HttpClient = class {
643
661
  if (externalSignal.aborted) {
644
662
  controller.abort();
645
663
  } else {
646
- externalSignal.addEventListener("abort", () => controller.abort(), { once: true });
664
+ externalSignal.addEventListener("abort", () => controller.abort(), {
665
+ once: true
666
+ });
647
667
  }
648
668
  }
649
669
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
@@ -658,7 +678,10 @@ var HttpClient = class {
658
678
  if (externalSignal?.aborted) {
659
679
  throw error;
660
680
  }
661
- throw new TimeoutError(`Request timed out after ${timeoutMs}ms`, timeoutMs);
681
+ throw new TimeoutError(
682
+ `Request timed out after ${timeoutMs}ms`,
683
+ timeoutMs
684
+ );
662
685
  }
663
686
  throw error;
664
687
  } finally {
@@ -687,13 +710,21 @@ var HttpClient = class {
687
710
  this.debug?.(`${method} ${url}`, { timeout, hasBody: !!options.body });
688
711
  let response;
689
712
  try {
690
- response = await this.fetchWithTimeout(url, init, timeout, options.signal);
713
+ response = await this.fetchWithTimeout(
714
+ url,
715
+ init,
716
+ timeout,
717
+ options.signal
718
+ );
691
719
  } catch (error) {
692
720
  if (error instanceof TimeoutError || error instanceof SpooledError) {
693
721
  throw error;
694
722
  }
695
723
  const cause = error instanceof Error ? error : void 0;
696
- throw new NetworkError(`Network request failed: ${cause?.message ?? "Unknown error"}`, cause);
724
+ throw new NetworkError(
725
+ `Network request failed: ${cause?.message ?? "Unknown error"}`,
726
+ cause
727
+ );
697
728
  }
698
729
  this.debug?.(`Response: ${response.status}`, { url });
699
730
  if (!response.ok) {
@@ -740,7 +771,9 @@ var HttpClient = class {
740
771
  const url = this.buildUrl(path, options.params, options.skipApiPrefix);
741
772
  const method = options.method ?? "GET";
742
773
  const execute = async () => {
743
- return this.circuitBreaker.execute(() => this.executeRequest(url, options));
774
+ return this.circuitBreaker.execute(
775
+ () => this.executeRequest(url, options)
776
+ );
744
777
  };
745
778
  if (options.skipRetry) {
746
779
  return execute();
@@ -749,7 +782,9 @@ var HttpClient = class {
749
782
  config: this.resolveRetryConfig(method, options),
750
783
  signal: options.signal,
751
784
  onRetry: (attempt, error, delayMs) => {
752
- this.debug?.(`Retry attempt ${attempt} after ${delayMs}ms`, { error: error.message });
785
+ this.debug?.(`Retry attempt ${attempt} after ${delayMs}ms`, {
786
+ error: error.message
787
+ });
753
788
  }
754
789
  });
755
790
  }
@@ -761,7 +796,9 @@ var HttpClient = class {
761
796
  if (!options.headers) {
762
797
  return false;
763
798
  }
764
- return Object.keys(options.headers).some((key) => key.toLowerCase() === "idempotency-key");
799
+ return Object.keys(options.headers).some(
800
+ (key) => key.toLowerCase() === "idempotency-key"
801
+ );
765
802
  }
766
803
  /**
767
804
  * Resolve the retry configuration for a request.
@@ -799,28 +836,43 @@ var HttpClient = class {
799
836
  * Make a POST request
800
837
  */
801
838
  async post(path, body, options) {
802
- const response = await this.request(path, { ...options, method: "POST", body });
839
+ const response = await this.request(path, {
840
+ ...options,
841
+ method: "POST",
842
+ body
843
+ });
803
844
  return response.data;
804
845
  }
805
846
  /**
806
847
  * Make a PUT request
807
848
  */
808
849
  async put(path, body, options) {
809
- const response = await this.request(path, { ...options, method: "PUT", body });
850
+ const response = await this.request(path, {
851
+ ...options,
852
+ method: "PUT",
853
+ body
854
+ });
810
855
  return response.data;
811
856
  }
812
857
  /**
813
858
  * Make a PATCH request
814
859
  */
815
860
  async patch(path, body, options) {
816
- const response = await this.request(path, { ...options, method: "PATCH", body });
861
+ const response = await this.request(path, {
862
+ ...options,
863
+ method: "PATCH",
864
+ body
865
+ });
817
866
  return response.data;
818
867
  }
819
868
  /**
820
869
  * Make a DELETE request
821
870
  */
822
871
  async delete(path, options) {
823
- const response = await this.request(path, { ...options, method: "DELETE" });
872
+ const response = await this.request(path, {
873
+ ...options,
874
+ method: "DELETE"
875
+ });
824
876
  return response.data;
825
877
  }
826
878
  };
@@ -1014,7 +1066,10 @@ var AuthResource = class {
1014
1066
  */
1015
1067
  async logout(refreshToken) {
1016
1068
  const token = refreshToken ?? this.getRefreshToken?.();
1017
- await this.http.post("/auth/logout", token ? { refreshToken: token } : void 0);
1069
+ await this.http.post(
1070
+ "/auth/logout",
1071
+ token ? { refreshToken: token } : void 0
1072
+ );
1018
1073
  }
1019
1074
  /**
1020
1075
  * Get current user/session info
@@ -1032,7 +1087,10 @@ var AuthResource = class {
1032
1087
  * Start email-based login flow (sends magic link)
1033
1088
  */
1034
1089
  async startEmailLogin(email) {
1035
- return this.http.post("/auth/email/start", { email });
1090
+ return this.http.post(
1091
+ "/auth/email/start",
1092
+ { email }
1093
+ );
1036
1094
  }
1037
1095
  /**
1038
1096
  * Check if an email address exists in the system
@@ -1073,7 +1131,9 @@ var JobsResource = class {
1073
1131
  * List jobs with optional filtering
1074
1132
  */
1075
1133
  async list(params) {
1076
- return this.http.get("/jobs", { params });
1134
+ return this.http.get("/jobs", {
1135
+ params
1136
+ });
1077
1137
  }
1078
1138
  /**
1079
1139
  * Get a job by ID
@@ -1097,7 +1157,9 @@ var JobsResource = class {
1097
1157
  * Boost job priority
1098
1158
  */
1099
1159
  async boostPriority(id, priority) {
1100
- return this.http.put(`/jobs/${id}/priority`, { priority });
1160
+ return this.http.put(`/jobs/${id}/priority`, {
1161
+ priority
1162
+ });
1101
1163
  }
1102
1164
  /**
1103
1165
  * Get job statistics
@@ -1142,17 +1204,25 @@ var JobsResource = class {
1142
1204
  * Fail a job (worker nack)
1143
1205
  */
1144
1206
  async fail(id, params) {
1145
- return this.http.post(`/jobs/${id}/fail`, params);
1207
+ return this.http.post(
1208
+ `/jobs/${id}/fail`,
1209
+ params
1210
+ );
1146
1211
  }
1147
1212
  /**
1148
1213
  * Extend job lease (heartbeat)
1149
1214
  */
1150
1215
  async heartbeat(id, params) {
1151
- return this.http.post(`/jobs/${id}/heartbeat`, params);
1216
+ return this.http.post(
1217
+ `/jobs/${id}/heartbeat`,
1218
+ params
1219
+ );
1152
1220
  }
1153
1221
  // DLQ operations (private implementations)
1154
1222
  async listDlq(params) {
1155
- return this.http.get("/jobs/dlq", { params });
1223
+ return this.http.get("/jobs/dlq", {
1224
+ params
1225
+ });
1156
1226
  }
1157
1227
  async retryDlq(params) {
1158
1228
  return this.http.post("/jobs/dlq/retry", params);
@@ -1183,13 +1253,18 @@ var QueuesResource = class {
1183
1253
  * Update queue configuration
1184
1254
  */
1185
1255
  async updateConfig(name, params) {
1186
- return this.http.put(`/queues/${encodeURIComponent(name)}/config`, params);
1256
+ return this.http.put(
1257
+ `/queues/${encodeURIComponent(name)}/config`,
1258
+ params
1259
+ );
1187
1260
  }
1188
1261
  /**
1189
1262
  * Get queue statistics
1190
1263
  */
1191
1264
  async getStats(name) {
1192
- return this.http.get(`/queues/${encodeURIComponent(name)}/stats`);
1265
+ return this.http.get(
1266
+ `/queues/${encodeURIComponent(name)}/stats`
1267
+ );
1193
1268
  }
1194
1269
  /**
1195
1270
  * Pause a queue
@@ -1204,7 +1279,9 @@ var QueuesResource = class {
1204
1279
  * Resume a paused queue
1205
1280
  */
1206
1281
  async resume(name) {
1207
- return this.http.post(`/queues/${encodeURIComponent(name)}/resume`);
1282
+ return this.http.post(
1283
+ `/queues/${encodeURIComponent(name)}/resume`
1284
+ );
1208
1285
  }
1209
1286
  /**
1210
1287
  * Delete a queue configuration
@@ -1260,7 +1337,9 @@ var SchedulesResource = class {
1260
1337
  * List all schedules
1261
1338
  */
1262
1339
  async list(params) {
1263
- return this.http.get("/schedules", { params });
1340
+ return this.http.get("/schedules", {
1341
+ params
1342
+ });
1264
1343
  }
1265
1344
  /**
1266
1345
  * Create a new schedule
@@ -1332,7 +1411,9 @@ var WorkflowsResource = class {
1332
1411
  * List all workflows
1333
1412
  */
1334
1413
  async list(params) {
1335
- return this.http.get("/workflows", { params });
1414
+ return this.http.get("/workflows", {
1415
+ params
1416
+ });
1336
1417
  }
1337
1418
  /**
1338
1419
  * Create a new workflow
@@ -1369,13 +1450,18 @@ var WorkflowsResource = class {
1369
1450
  return this.http.get(`/workflows/${workflowId}/jobs/${jobId}`);
1370
1451
  }
1371
1452
  async getWorkflowJobsStatus(workflowId) {
1372
- return this.http.get(`/workflows/${workflowId}/jobs/status`);
1453
+ return this.http.get(
1454
+ `/workflows/${workflowId}/jobs/status`
1455
+ );
1373
1456
  }
1374
1457
  async getJobDependencies(jobId) {
1375
1458
  return this.http.get(`/jobs/${jobId}/dependencies`);
1376
1459
  }
1377
1460
  async addJobDependencies(jobId, params) {
1378
- return this.http.post(`/jobs/${jobId}/dependencies`, params);
1461
+ return this.http.post(
1462
+ `/jobs/${jobId}/dependencies`,
1463
+ params
1464
+ );
1379
1465
  }
1380
1466
  };
1381
1467
 
@@ -1424,7 +1510,9 @@ var WebhooksResource = class {
1424
1510
  * Get webhook delivery history
1425
1511
  */
1426
1512
  async getDeliveries(id) {
1427
- return this.http.get(`/outgoing-webhooks/${id}/deliveries`);
1513
+ return this.http.get(
1514
+ `/outgoing-webhooks/${id}/deliveries`
1515
+ );
1428
1516
  }
1429
1517
  /**
1430
1518
  * Retry a specific webhook delivery
@@ -1532,7 +1620,9 @@ var OrganizationsResource = class {
1532
1620
  * Generate a unique slug from an organization name
1533
1621
  */
1534
1622
  async generateSlug(name) {
1535
- return this.http.post("/organizations/generate-slug", { name });
1623
+ return this.http.post("/organizations/generate-slug", {
1624
+ name
1625
+ });
1536
1626
  }
1537
1627
  /**
1538
1628
  * Get the webhook token for the current organization.
@@ -1562,7 +1652,9 @@ var OrganizationsResource = class {
1562
1652
  * ```
1563
1653
  */
1564
1654
  async regenerateWebhookToken() {
1565
- return this.http.post("/organizations/webhook-token/regenerate");
1655
+ return this.http.post(
1656
+ "/organizations/webhook-token/regenerate"
1657
+ );
1566
1658
  }
1567
1659
  /**
1568
1660
  * Clear/delete the webhook token for the current organization.
@@ -1599,7 +1691,10 @@ var BillingResource = class {
1599
1691
  * POST /api/v1/billing/portal
1600
1692
  */
1601
1693
  async createPortal(params) {
1602
- return this.http.post("/billing/portal", params);
1694
+ return this.http.post(
1695
+ "/billing/portal",
1696
+ params
1697
+ );
1603
1698
  }
1604
1699
  };
1605
1700
 
@@ -1694,7 +1789,9 @@ var AdminResource = class {
1694
1789
  }
1695
1790
  headers() {
1696
1791
  if (!this.adminKey) {
1697
- throw new AuthenticationError("Admin key is required for admin endpoints. Provide `adminKey` in SpooledClient config.");
1792
+ throw new AuthenticationError(
1793
+ "Admin key is required for admin endpoints. Provide `adminKey` in SpooledClient config."
1794
+ );
1698
1795
  }
1699
1796
  return { "X-Admin-Key": this.adminKey };
1700
1797
  }
@@ -1715,15 +1812,22 @@ var AdminResource = class {
1715
1812
  }
1716
1813
  /** POST /api/v1/admin/organizations */
1717
1814
  async createOrganization(params) {
1718
- return this.http.post("/admin/organizations", params, {
1719
- headers: this.headers()
1720
- });
1815
+ return this.http.post(
1816
+ "/admin/organizations",
1817
+ params,
1818
+ {
1819
+ headers: this.headers()
1820
+ }
1821
+ );
1721
1822
  }
1722
1823
  /** GET /api/v1/admin/organizations/{id} */
1723
1824
  async getOrganization(id) {
1724
- return this.http.get(`/admin/organizations/${id}`, {
1725
- headers: this.headers()
1726
- });
1825
+ return this.http.get(
1826
+ `/admin/organizations/${id}`,
1827
+ {
1828
+ headers: this.headers()
1829
+ }
1830
+ );
1727
1831
  }
1728
1832
  /** PATCH /api/v1/admin/organizations/{id} */
1729
1833
  async updateOrganization(id, params) {
@@ -1741,15 +1845,23 @@ var AdminResource = class {
1741
1845
  }
1742
1846
  /** POST /api/v1/admin/organizations/{id}/api-keys */
1743
1847
  async createApiKey(orgId, params) {
1744
- return this.http.post(`/admin/organizations/${orgId}/api-keys`, params, {
1745
- headers: this.headers()
1746
- });
1848
+ return this.http.post(
1849
+ `/admin/organizations/${orgId}/api-keys`,
1850
+ params,
1851
+ {
1852
+ headers: this.headers()
1853
+ }
1854
+ );
1747
1855
  }
1748
1856
  /** POST /api/v1/admin/organizations/{id}/reset-usage */
1749
1857
  async resetUsage(orgId) {
1750
- await this.http.post(`/admin/organizations/${orgId}/reset-usage`, void 0, {
1751
- headers: this.headers()
1752
- });
1858
+ await this.http.post(
1859
+ `/admin/organizations/${orgId}/reset-usage`,
1860
+ void 0,
1861
+ {
1862
+ headers: this.headers()
1863
+ }
1864
+ );
1753
1865
  }
1754
1866
  /** GET /api/v1/admin/stats */
1755
1867
  async getStats() {
@@ -1795,7 +1907,9 @@ var WebhookIngestionResource = class {
1795
1907
  const bytes = toBytes(body);
1796
1908
  const signature = options.signature ?? (options.secret ? githubSignature(options.secret, bytes) : void 0);
1797
1909
  if (!signature) {
1798
- throw new ValidationError("GitHub webhook signature is required (provide `signature` or `secret`)");
1910
+ throw new ValidationError(
1911
+ "GitHub webhook signature is required (provide `signature` or `secret`)"
1912
+ );
1799
1913
  }
1800
1914
  const headers = {
1801
1915
  "Content-Type": "application/json",
@@ -1826,7 +1940,9 @@ var WebhookIngestionResource = class {
1826
1940
  const timestamp = options.timestamp ?? Math.floor(Date.now() / 1e3);
1827
1941
  const signature = options.signature ?? (options.secret ? stripeSignature(options.secret, bytes, timestamp) : void 0);
1828
1942
  if (!signature) {
1829
- throw new ValidationError("Stripe webhook signature is required (provide `signature` or `secret`)");
1943
+ throw new ValidationError(
1944
+ "Stripe webhook signature is required (provide `signature` or `secret`)"
1945
+ );
1830
1946
  }
1831
1947
  const headers = {
1832
1948
  "Content-Type": "application/json",
@@ -1963,14 +2079,20 @@ var WebSocketRealtimeClient = class {
1963
2079
  resolve();
1964
2080
  };
1965
2081
  this.ws.onmessage = (event) => {
1966
- this.handleMessage(typeof event.data === "string" ? event.data : String(event.data));
2082
+ this.handleMessage(
2083
+ typeof event.data === "string" ? event.data : String(event.data)
2084
+ );
1967
2085
  };
1968
2086
  this.ws.onclose = (event) => {
1969
2087
  this.options.debug(`WebSocket closed: ${event.code} ${event.reason}`);
1970
2088
  const wasConnecting = this.state === "connecting";
1971
2089
  this.handleDisconnect();
1972
2090
  if (wasConnecting) {
1973
- reject(new Error(`WebSocket connection failed: ${event.reason || "Unknown error"}`));
2091
+ reject(
2092
+ new Error(
2093
+ `WebSocket connection failed: ${event.reason || "Unknown error"}`
2094
+ )
2095
+ );
1974
2096
  }
1975
2097
  };
1976
2098
  this.ws.onerror = (event) => {
@@ -2125,7 +2247,9 @@ var WebSocketRealtimeClient = class {
2125
2247
  this.options.maxReconnectDelay
2126
2248
  );
2127
2249
  const delay = Math.floor(baseDelay + Math.random() * baseDelay * 0.25);
2128
- this.options.debug(`Scheduling reconnect attempt ${this.reconnectAttempts} in ${delay}ms`);
2250
+ this.options.debug(
2251
+ `Scheduling reconnect attempt ${this.reconnectAttempts} in ${delay}ms`
2252
+ );
2129
2253
  this.reconnectTimer = setTimeout(async () => {
2130
2254
  try {
2131
2255
  await this.connect();
@@ -2178,11 +2302,13 @@ function isAuthFailureEvent(event) {
2178
2302
  if (!event) {
2179
2303
  return false;
2180
2304
  }
2181
- const status = event.status ?? event.statusCode ?? event.error?.status;
2182
- if (status === 401 || status === 403) {
2305
+ const status2 = event.status ?? event.statusCode ?? event.error?.status;
2306
+ if (status2 === 401 || status2 === 403) {
2183
2307
  return true;
2184
2308
  }
2185
- const text = String(event.message ?? event.error?.message ?? event.reason ?? "");
2309
+ const text = String(
2310
+ event.message ?? event.error?.message ?? event.reason ?? ""
2311
+ );
2186
2312
  return /\b(401|403)\b/.test(text);
2187
2313
  }
2188
2314
 
@@ -2245,7 +2371,9 @@ var SseRealtimeClient = class {
2245
2371
  EventSourceImpl = mod.default || mod.EventSource || mod;
2246
2372
  } catch {
2247
2373
  this.setState("disconnected");
2248
- throw new Error("eventsource package is required for SSE in Node.js. Install it with: npm install eventsource");
2374
+ throw new Error(
2375
+ "eventsource package is required for SSE in Node.js. Install it with: npm install eventsource"
2376
+ );
2249
2377
  }
2250
2378
  }
2251
2379
  let token;
@@ -2414,7 +2542,9 @@ var SseRealtimeClient = class {
2414
2542
  this.options.maxReconnectDelay
2415
2543
  );
2416
2544
  const delay = Math.floor(baseDelay + Math.random() * baseDelay * 0.25);
2417
- this.options.debug(`Scheduling SSE reconnect attempt ${this.reconnectAttempts} in ${delay}ms`);
2545
+ this.options.debug(
2546
+ `Scheduling SSE reconnect attempt ${this.reconnectAttempts} in ${delay}ms`
2547
+ );
2418
2548
  this.reconnectTimer = setTimeout(async () => {
2419
2549
  try {
2420
2550
  await this.connect(this.filter || void 0);
@@ -2590,7 +2720,10 @@ function loadProtoDefinition() {
2590
2720
  return cachedDefinition;
2591
2721
  }
2592
2722
  const protoPath = getProtoPath();
2593
- const packageDefinition = protoLoader__namespace.loadSync(protoPath, PROTO_LOADER_OPTIONS);
2723
+ const packageDefinition = protoLoader__namespace.loadSync(
2724
+ protoPath,
2725
+ PROTO_LOADER_OPTIONS
2726
+ );
2594
2727
  const grpcObject = grpc2__namespace.loadPackageDefinition(packageDefinition);
2595
2728
  cachedDefinition = grpcObject;
2596
2729
  return cachedDefinition;
@@ -2600,7 +2733,10 @@ async function loadProtoDefinitionAsync() {
2600
2733
  return cachedDefinition;
2601
2734
  }
2602
2735
  const protoPath = getProtoPath();
2603
- const packageDefinition = await protoLoader__namespace.load(protoPath, PROTO_LOADER_OPTIONS);
2736
+ const packageDefinition = await protoLoader__namespace.load(
2737
+ protoPath,
2738
+ PROTO_LOADER_OPTIONS
2739
+ );
2604
2740
  const grpcObject = grpc2__namespace.loadPackageDefinition(packageDefinition);
2605
2741
  cachedDefinition = grpcObject;
2606
2742
  return cachedDefinition;
@@ -2617,7 +2753,11 @@ async function* asyncIterableFromStream(stream, options) {
2617
2753
  stream.cancel();
2618
2754
  });
2619
2755
  }
2620
- return yield* new AsyncIterableStream(stream, { onConnected, onError, onEnd });
2756
+ return yield* new AsyncIterableStream(stream, {
2757
+ onConnected,
2758
+ onError,
2759
+ onEnd
2760
+ });
2621
2761
  }
2622
2762
  var AsyncIterableStream = class {
2623
2763
  stream;
@@ -2646,8 +2786,8 @@ var AsyncIterableStream = class {
2646
2786
  onEnd?.();
2647
2787
  resolve?.();
2648
2788
  });
2649
- this.stream.on("status", (status) => {
2650
- if (status.code === 0) {
2789
+ this.stream.on("status", (status2) => {
2790
+ if (status2.code === 0) {
2651
2791
  onConnected?.();
2652
2792
  }
2653
2793
  });
@@ -2748,7 +2888,9 @@ function decodeStruct(value) {
2748
2888
  return value;
2749
2889
  }
2750
2890
  const fields = value.fields ?? {};
2751
- return Object.fromEntries(Object.entries(fields).map(([key, entry]) => [key, decodeValue(entry)]));
2891
+ return Object.fromEntries(
2892
+ Object.entries(fields).map(([key, entry]) => [key, decodeValue(entry)])
2893
+ );
2752
2894
  }
2753
2895
  function decodeValue(value) {
2754
2896
  if ("nullValue" in value) {
@@ -2797,7 +2939,10 @@ function encodeEnqueueRequest(params) {
2797
2939
  return encoded;
2798
2940
  }
2799
2941
  function encodeCompleteRequest(params) {
2800
- return { ...params, result: encodeStruct(params.result) };
2942
+ return {
2943
+ ...params,
2944
+ result: encodeStruct(params.result)
2945
+ };
2801
2946
  }
2802
2947
  function decodeJob(job) {
2803
2948
  return {
@@ -2806,17 +2951,60 @@ function decodeJob(job) {
2806
2951
  result: decodeStruct(job.result)
2807
2952
  };
2808
2953
  }
2809
- function promisify(client, method, request, metadata) {
2954
+ function promisify(client, method, request, metadata, timeoutMs) {
2810
2955
  return new Promise((resolve, reject) => {
2811
- method.call(client, request, metadata, (error, response) => {
2956
+ const callback = (error, response) => {
2812
2957
  if (error) {
2813
- reject(error);
2958
+ reject(mapGrpcError(error, timeoutMs));
2814
2959
  } else {
2815
2960
  resolve(response);
2816
2961
  }
2817
- });
2962
+ };
2963
+ method.call(
2964
+ client,
2965
+ request,
2966
+ metadata,
2967
+ createUnaryCallOptions(timeoutMs),
2968
+ callback
2969
+ );
2818
2970
  });
2819
2971
  }
2972
+ function createUnaryCallOptions(timeoutMs) {
2973
+ if (timeoutMs === void 0) {
2974
+ return {};
2975
+ }
2976
+ return { deadline: new Date(Date.now() + timeoutMs) };
2977
+ }
2978
+ function mapGrpcError(error, timeoutMs) {
2979
+ const message = error.details || error.message || "gRPC request failed";
2980
+ switch (error.code) {
2981
+ case grpc2__namespace.status.DEADLINE_EXCEEDED:
2982
+ return new TimeoutError(message, timeoutMs ?? 0);
2983
+ case grpc2__namespace.status.UNAUTHENTICATED:
2984
+ return new AuthenticationError(message, "AUTHENTICATION_FAILED");
2985
+ case grpc2__namespace.status.PERMISSION_DENIED:
2986
+ return new AuthorizationError(message, "ACCESS_DENIED");
2987
+ case grpc2__namespace.status.NOT_FOUND:
2988
+ return new NotFoundError(message, "NOT_FOUND");
2989
+ case grpc2__namespace.status.INVALID_ARGUMENT:
2990
+ case grpc2__namespace.status.OUT_OF_RANGE:
2991
+ return new ValidationError(message, "VALIDATION_ERROR");
2992
+ case grpc2__namespace.status.ALREADY_EXISTS:
2993
+ case grpc2__namespace.status.ABORTED:
2994
+ case grpc2__namespace.status.FAILED_PRECONDITION:
2995
+ return new ConflictError(message, "CONFLICT");
2996
+ case grpc2__namespace.status.RESOURCE_EXHAUSTED:
2997
+ return new RateLimitError(message, "RATE_LIMIT_EXCEEDED");
2998
+ case grpc2__namespace.status.UNAVAILABLE:
2999
+ case grpc2__namespace.status.CANCELLED:
3000
+ return new NetworkError(message, error);
3001
+ default:
3002
+ if (error.code >= grpc2__namespace.status.INTERNAL) {
3003
+ return new ServerError(message, 500, "GRPC_SERVER_ERROR");
3004
+ }
3005
+ return new ServerError(message, 500, "GRPC_ERROR");
3006
+ }
3007
+ }
2820
3008
  function shouldUseTls(address, explicitTls) {
2821
3009
  if (explicitTls !== void 0) {
2822
3010
  return explicitTls;
@@ -2825,53 +3013,99 @@ function shouldUseTls(address, explicitTls) {
2825
3013
  return host !== "localhost" && host !== "127.0.0.1" && !host.startsWith("[::1]");
2826
3014
  }
2827
3015
  var GrpcQueueOperations = class {
2828
- constructor(client, metadata) {
3016
+ constructor(client, metadata, timeoutMs) {
2829
3017
  this.client = client;
2830
3018
  this.metadata = metadata;
3019
+ this.timeoutMs = timeoutMs;
2831
3020
  }
2832
3021
  /**
2833
3022
  * Enqueue a new job
2834
3023
  */
2835
3024
  async enqueue(params) {
2836
- return promisify(this.client, this.client.Enqueue, encodeEnqueueRequest(params), this.metadata);
3025
+ return promisify(
3026
+ this.client,
3027
+ this.client.Enqueue,
3028
+ encodeEnqueueRequest(params),
3029
+ this.metadata,
3030
+ this.timeoutMs
3031
+ );
2837
3032
  }
2838
3033
  /**
2839
3034
  * Dequeue a job (for workers)
2840
3035
  */
2841
3036
  async dequeue(params) {
2842
- const response = await promisify(this.client, this.client.Dequeue, params, this.metadata);
3037
+ const response = await promisify(
3038
+ this.client,
3039
+ this.client.Dequeue,
3040
+ params,
3041
+ this.metadata,
3042
+ this.timeoutMs
3043
+ );
2843
3044
  return { ...response, jobs: response.jobs.map(decodeJob) };
2844
3045
  }
2845
3046
  /**
2846
3047
  * Complete a job successfully
2847
3048
  */
2848
3049
  async complete(params) {
2849
- return promisify(this.client, this.client.Complete, encodeCompleteRequest(params), this.metadata);
3050
+ return promisify(
3051
+ this.client,
3052
+ this.client.Complete,
3053
+ encodeCompleteRequest(params),
3054
+ this.metadata,
3055
+ this.timeoutMs
3056
+ );
2850
3057
  }
2851
3058
  /**
2852
3059
  * Fail a job
2853
3060
  */
2854
3061
  async fail(params) {
2855
- return promisify(this.client, this.client.Fail, params, this.metadata);
3062
+ return promisify(
3063
+ this.client,
3064
+ this.client.Fail,
3065
+ params,
3066
+ this.metadata,
3067
+ this.timeoutMs
3068
+ );
2856
3069
  }
2857
3070
  /**
2858
3071
  * Renew a job's lease
2859
3072
  */
2860
3073
  async renewLease(params) {
2861
- return promisify(this.client, this.client.RenewLease, params, this.metadata);
3074
+ return promisify(
3075
+ this.client,
3076
+ this.client.RenewLease,
3077
+ params,
3078
+ this.metadata,
3079
+ this.timeoutMs
3080
+ );
2862
3081
  }
2863
3082
  /**
2864
3083
  * Get a job by ID
2865
3084
  */
2866
3085
  async getJob(jobId) {
2867
- const response = await promisify(this.client, this.client.GetJob, { jobId }, this.metadata);
2868
- return { ...response, job: response.job ? decodeJob(response.job) : response.job };
3086
+ const response = await promisify(
3087
+ this.client,
3088
+ this.client.GetJob,
3089
+ { jobId },
3090
+ this.metadata,
3091
+ this.timeoutMs
3092
+ );
3093
+ return {
3094
+ ...response,
3095
+ job: response.job ? decodeJob(response.job) : response.job
3096
+ };
2869
3097
  }
2870
3098
  /**
2871
3099
  * Get queue statistics
2872
3100
  */
2873
3101
  async getQueueStats(queueName) {
2874
- return promisify(this.client, this.client.GetQueueStats, { queueName }, this.metadata);
3102
+ return promisify(
3103
+ this.client,
3104
+ this.client.GetQueueStats,
3105
+ { queueName },
3106
+ this.metadata,
3107
+ this.timeoutMs
3108
+ );
2875
3109
  }
2876
3110
  /**
2877
3111
  * Stream jobs from a queue (server-side streaming)
@@ -2919,27 +3153,46 @@ var GrpcQueueOperations = class {
2919
3153
  }
2920
3154
  };
2921
3155
  var GrpcWorkerOperations = class {
2922
- constructor(client, metadata) {
3156
+ constructor(client, metadata, timeoutMs) {
2923
3157
  this.client = client;
2924
3158
  this.metadata = metadata;
3159
+ this.timeoutMs = timeoutMs;
2925
3160
  }
2926
3161
  /**
2927
3162
  * Register a new worker
2928
3163
  */
2929
3164
  async register(params) {
2930
- return promisify(this.client, this.client.Register, params, this.metadata);
3165
+ return promisify(
3166
+ this.client,
3167
+ this.client.Register,
3168
+ params,
3169
+ this.metadata,
3170
+ this.timeoutMs
3171
+ );
2931
3172
  }
2932
3173
  /**
2933
3174
  * Send heartbeat
2934
3175
  */
2935
3176
  async heartbeat(params) {
2936
- return promisify(this.client, this.client.Heartbeat, params, this.metadata);
3177
+ return promisify(
3178
+ this.client,
3179
+ this.client.Heartbeat,
3180
+ params,
3181
+ this.metadata,
3182
+ this.timeoutMs
3183
+ );
2937
3184
  }
2938
3185
  /**
2939
3186
  * Deregister a worker
2940
3187
  */
2941
3188
  async deregister(workerId) {
2942
- return promisify(this.client, this.client.Deregister, { workerId }, this.metadata);
3189
+ return promisify(
3190
+ this.client,
3191
+ this.client.Deregister,
3192
+ { workerId },
3193
+ this.metadata,
3194
+ this.timeoutMs
3195
+ );
2943
3196
  }
2944
3197
  };
2945
3198
  var SpooledGrpcClient = class {
@@ -2951,7 +3204,7 @@ var SpooledGrpcClient = class {
2951
3204
  /** Worker operations */
2952
3205
  workers;
2953
3206
  constructor(options) {
2954
- const { address, apiKey, useTls, credentials: credentials2, channelOptions } = options;
3207
+ const { address, apiKey, useTls, credentials: credentials2, channelOptions, timeout } = options;
2955
3208
  const creds = credentials2 ?? (shouldUseTls(address, useTls) ? grpc2__namespace.credentials.createSsl() : grpc2__namespace.credentials.createInsecure());
2956
3209
  this.metadata = new grpc2__namespace.Metadata();
2957
3210
  this.metadata.add("x-api-key", apiKey);
@@ -2966,8 +3219,16 @@ var SpooledGrpcClient = class {
2966
3219
  creds,
2967
3220
  channelOptions
2968
3221
  );
2969
- this.queue = new GrpcQueueOperations(this.queueClient, this.metadata);
2970
- this.workers = new GrpcWorkerOperations(this.workerClient, this.metadata);
3222
+ this.queue = new GrpcQueueOperations(
3223
+ this.queueClient,
3224
+ this.metadata,
3225
+ timeout
3226
+ );
3227
+ this.workers = new GrpcWorkerOperations(
3228
+ this.workerClient,
3229
+ this.metadata,
3230
+ timeout
3231
+ );
2971
3232
  }
2972
3233
  /**
2973
3234
  * Close all gRPC connections
@@ -3116,7 +3377,8 @@ var SpooledClient = class _SpooledClient {
3116
3377
  }
3117
3378
  this._grpc = new SpooledGrpcClient({
3118
3379
  address: grpcAddress,
3119
- apiKey
3380
+ apiKey,
3381
+ timeout: this.config.timeout
3120
3382
  });
3121
3383
  }
3122
3384
  return this._grpc;
@@ -3183,7 +3445,9 @@ var SpooledClient = class _SpooledClient {
3183
3445
  }
3184
3446
  return this.loginForJwt();
3185
3447
  }
3186
- throw new AuthenticationError("No authentication method available for realtime connection");
3448
+ throw new AuthenticationError(
3449
+ "No authentication method available for realtime connection"
3450
+ );
3187
3451
  }
3188
3452
  /**
3189
3453
  * Exchange the API key for a JWT via /auth/login and cache the result.
@@ -3397,6 +3661,11 @@ var DEFAULT_OPTIONS = {
3397
3661
  };
3398
3662
  var SpooledWorker = class {
3399
3663
  client;
3664
+ /**
3665
+ * Options with every defaultable field filled in. `workerId` stays optional:
3666
+ * there is no sensible default for it, and omitting it is what makes the
3667
+ * server mint a UUID.
3668
+ */
3400
3669
  options;
3401
3670
  debug;
3402
3671
  state = "idle";
@@ -3467,6 +3736,7 @@ var SpooledWorker = class {
3467
3736
  try {
3468
3737
  const registration = await this.client.workers.register({
3469
3738
  queueName: this.options.queueName,
3739
+ workerId: this.options.workerId,
3470
3740
  hostname: this.options.hostname,
3471
3741
  workerType: this.options.workerType,
3472
3742
  maxConcurrency: this.options.concurrency,
@@ -3480,7 +3750,10 @@ var SpooledWorker = class {
3480
3750
  this.sendWorkerHeartbeat();
3481
3751
  }, heartbeatInterval);
3482
3752
  this.state = "running";
3483
- this.emit("started", { workerId: this.workerId, queueName: this.options.queueName });
3753
+ this.emit("started", {
3754
+ workerId: this.workerId,
3755
+ queueName: this.options.queueName
3756
+ });
3484
3757
  this.schedulePoll();
3485
3758
  } catch (error) {
3486
3759
  this.state = "error";
@@ -3516,14 +3789,18 @@ var SpooledWorker = class {
3516
3789
  active.abortController.abort();
3517
3790
  }
3518
3791
  if (this.activeJobs.size > 0) {
3519
- this.debug(`Waiting for ${this.activeJobs.size} active jobs to complete...`);
3792
+ this.debug(
3793
+ `Waiting for ${this.activeJobs.size} active jobs to complete...`
3794
+ );
3520
3795
  await Promise.race([
3521
3796
  this.waitForActiveJobs(),
3522
3797
  this.sleep(this.options.shutdownTimeout)
3523
3798
  ]);
3524
3799
  const remaining = Array.from(this.activeJobs.values());
3525
3800
  for (const active of remaining) {
3526
- this.debug(`Force-failing job ${active.job.id} due to shutdown timeout`);
3801
+ this.debug(
3802
+ `Force-failing job ${active.job.id} due to shutdown timeout`
3803
+ );
3527
3804
  await this.failJob(active.job, "Worker shutdown timeout");
3528
3805
  this.cleanupJob(active);
3529
3806
  }
@@ -3631,7 +3908,9 @@ var SpooledWorker = class {
3631
3908
  retryCount: job.retryCount,
3632
3909
  maxRetries: job.maxRetries,
3633
3910
  signal,
3634
- progress: async (_percent, _message) => {
3911
+ progress: async (percent, message) => {
3912
+ const boundedPercent = Math.max(0, Math.min(100, percent));
3913
+ this.debug(`[${job.id}] progress ${boundedPercent}%`, message);
3635
3914
  },
3636
3915
  log: (level, message, meta) => {
3637
3916
  this.debug(`[${job.id}] [${level}] ${message}`, meta);