@spooled/sdk 1.0.38 → 1.0.40

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
@@ -4,6 +4,7 @@ var crypto = require('crypto');
4
4
  var buffer = require('buffer');
5
5
  var grpc2 = require('@grpc/grpc-js');
6
6
  var protoLoader = require('@grpc/proto-loader');
7
+ var fs = require('fs');
7
8
  var url = require('url');
8
9
  var path = require('path');
9
10
  var os = require('os');
@@ -49,10 +50,10 @@ var DEFAULT_CONFIG = {
49
50
  successThreshold: 3,
50
51
  timeout: 3e4
51
52
  },
52
- userAgent: "@spooled/sdk-nodejs/1.0.38",
53
+ userAgent: "@spooled/sdk-nodejs/1.0.40",
53
54
  autoRefreshToken: true
54
55
  };
55
- var SDK_VERSION = "1.0.38";
56
+ var SDK_VERSION = "1.0.40";
56
57
  var API_VERSION = "v1";
57
58
  var API_BASE_PATH = `/api/${API_VERSION}`;
58
59
  function normalizeCredential(value) {
@@ -378,13 +379,31 @@ async function createErrorFromResponse(response) {
378
379
  return new PayloadTooLargeError(message, code, details, requestId);
379
380
  case 429: {
380
381
  const rateLimitInfo = parseRateLimitHeaders(response.headers);
381
- return new RateLimitError(message, code, rateLimitInfo, details, requestId);
382
+ return new RateLimitError(
383
+ message,
384
+ code,
385
+ rateLimitInfo,
386
+ details,
387
+ requestId
388
+ );
382
389
  }
383
390
  default:
384
391
  if (response.status >= 500) {
385
- 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
+ );
386
399
  }
387
- 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
+ );
388
407
  }
389
408
  }
390
409
  function isSpooledError(error) {
@@ -642,7 +661,9 @@ var HttpClient = class {
642
661
  if (externalSignal.aborted) {
643
662
  controller.abort();
644
663
  } else {
645
- externalSignal.addEventListener("abort", () => controller.abort(), { once: true });
664
+ externalSignal.addEventListener("abort", () => controller.abort(), {
665
+ once: true
666
+ });
646
667
  }
647
668
  }
648
669
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
@@ -657,7 +678,10 @@ var HttpClient = class {
657
678
  if (externalSignal?.aborted) {
658
679
  throw error;
659
680
  }
660
- throw new TimeoutError(`Request timed out after ${timeoutMs}ms`, timeoutMs);
681
+ throw new TimeoutError(
682
+ `Request timed out after ${timeoutMs}ms`,
683
+ timeoutMs
684
+ );
661
685
  }
662
686
  throw error;
663
687
  } finally {
@@ -686,13 +710,21 @@ var HttpClient = class {
686
710
  this.debug?.(`${method} ${url}`, { timeout, hasBody: !!options.body });
687
711
  let response;
688
712
  try {
689
- response = await this.fetchWithTimeout(url, init, timeout, options.signal);
713
+ response = await this.fetchWithTimeout(
714
+ url,
715
+ init,
716
+ timeout,
717
+ options.signal
718
+ );
690
719
  } catch (error) {
691
720
  if (error instanceof TimeoutError || error instanceof SpooledError) {
692
721
  throw error;
693
722
  }
694
723
  const cause = error instanceof Error ? error : void 0;
695
- 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
+ );
696
728
  }
697
729
  this.debug?.(`Response: ${response.status}`, { url });
698
730
  if (!response.ok) {
@@ -739,7 +771,9 @@ var HttpClient = class {
739
771
  const url = this.buildUrl(path, options.params, options.skipApiPrefix);
740
772
  const method = options.method ?? "GET";
741
773
  const execute = async () => {
742
- return this.circuitBreaker.execute(() => this.executeRequest(url, options));
774
+ return this.circuitBreaker.execute(
775
+ () => this.executeRequest(url, options)
776
+ );
743
777
  };
744
778
  if (options.skipRetry) {
745
779
  return execute();
@@ -748,7 +782,9 @@ var HttpClient = class {
748
782
  config: this.resolveRetryConfig(method, options),
749
783
  signal: options.signal,
750
784
  onRetry: (attempt, error, delayMs) => {
751
- 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
+ });
752
788
  }
753
789
  });
754
790
  }
@@ -760,7 +796,9 @@ var HttpClient = class {
760
796
  if (!options.headers) {
761
797
  return false;
762
798
  }
763
- 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
+ );
764
802
  }
765
803
  /**
766
804
  * Resolve the retry configuration for a request.
@@ -798,28 +836,43 @@ var HttpClient = class {
798
836
  * Make a POST request
799
837
  */
800
838
  async post(path, body, options) {
801
- 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
+ });
802
844
  return response.data;
803
845
  }
804
846
  /**
805
847
  * Make a PUT request
806
848
  */
807
849
  async put(path, body, options) {
808
- 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
+ });
809
855
  return response.data;
810
856
  }
811
857
  /**
812
858
  * Make a PATCH request
813
859
  */
814
860
  async patch(path, body, options) {
815
- 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
+ });
816
866
  return response.data;
817
867
  }
818
868
  /**
819
869
  * Make a DELETE request
820
870
  */
821
871
  async delete(path, options) {
822
- const response = await this.request(path, { ...options, method: "DELETE" });
872
+ const response = await this.request(path, {
873
+ ...options,
874
+ method: "DELETE"
875
+ });
823
876
  return response.data;
824
877
  }
825
878
  };
@@ -1013,7 +1066,10 @@ var AuthResource = class {
1013
1066
  */
1014
1067
  async logout(refreshToken) {
1015
1068
  const token = refreshToken ?? this.getRefreshToken?.();
1016
- 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
+ );
1017
1073
  }
1018
1074
  /**
1019
1075
  * Get current user/session info
@@ -1031,7 +1087,10 @@ var AuthResource = class {
1031
1087
  * Start email-based login flow (sends magic link)
1032
1088
  */
1033
1089
  async startEmailLogin(email) {
1034
- return this.http.post("/auth/email/start", { email });
1090
+ return this.http.post(
1091
+ "/auth/email/start",
1092
+ { email }
1093
+ );
1035
1094
  }
1036
1095
  /**
1037
1096
  * Check if an email address exists in the system
@@ -1072,7 +1131,9 @@ var JobsResource = class {
1072
1131
  * List jobs with optional filtering
1073
1132
  */
1074
1133
  async list(params) {
1075
- return this.http.get("/jobs", { params });
1134
+ return this.http.get("/jobs", {
1135
+ params
1136
+ });
1076
1137
  }
1077
1138
  /**
1078
1139
  * Get a job by ID
@@ -1096,7 +1157,9 @@ var JobsResource = class {
1096
1157
  * Boost job priority
1097
1158
  */
1098
1159
  async boostPriority(id, priority) {
1099
- return this.http.put(`/jobs/${id}/priority`, { priority });
1160
+ return this.http.put(`/jobs/${id}/priority`, {
1161
+ priority
1162
+ });
1100
1163
  }
1101
1164
  /**
1102
1165
  * Get job statistics
@@ -1141,17 +1204,25 @@ var JobsResource = class {
1141
1204
  * Fail a job (worker nack)
1142
1205
  */
1143
1206
  async fail(id, params) {
1144
- return this.http.post(`/jobs/${id}/fail`, params);
1207
+ return this.http.post(
1208
+ `/jobs/${id}/fail`,
1209
+ params
1210
+ );
1145
1211
  }
1146
1212
  /**
1147
1213
  * Extend job lease (heartbeat)
1148
1214
  */
1149
1215
  async heartbeat(id, params) {
1150
- return this.http.post(`/jobs/${id}/heartbeat`, params);
1216
+ return this.http.post(
1217
+ `/jobs/${id}/heartbeat`,
1218
+ params
1219
+ );
1151
1220
  }
1152
1221
  // DLQ operations (private implementations)
1153
1222
  async listDlq(params) {
1154
- return this.http.get("/jobs/dlq", { params });
1223
+ return this.http.get("/jobs/dlq", {
1224
+ params
1225
+ });
1155
1226
  }
1156
1227
  async retryDlq(params) {
1157
1228
  return this.http.post("/jobs/dlq/retry", params);
@@ -1182,13 +1253,18 @@ var QueuesResource = class {
1182
1253
  * Update queue configuration
1183
1254
  */
1184
1255
  async updateConfig(name, params) {
1185
- return this.http.put(`/queues/${encodeURIComponent(name)}/config`, params);
1256
+ return this.http.put(
1257
+ `/queues/${encodeURIComponent(name)}/config`,
1258
+ params
1259
+ );
1186
1260
  }
1187
1261
  /**
1188
1262
  * Get queue statistics
1189
1263
  */
1190
1264
  async getStats(name) {
1191
- return this.http.get(`/queues/${encodeURIComponent(name)}/stats`);
1265
+ return this.http.get(
1266
+ `/queues/${encodeURIComponent(name)}/stats`
1267
+ );
1192
1268
  }
1193
1269
  /**
1194
1270
  * Pause a queue
@@ -1203,7 +1279,9 @@ var QueuesResource = class {
1203
1279
  * Resume a paused queue
1204
1280
  */
1205
1281
  async resume(name) {
1206
- return this.http.post(`/queues/${encodeURIComponent(name)}/resume`);
1282
+ return this.http.post(
1283
+ `/queues/${encodeURIComponent(name)}/resume`
1284
+ );
1207
1285
  }
1208
1286
  /**
1209
1287
  * Delete a queue configuration
@@ -1259,7 +1337,9 @@ var SchedulesResource = class {
1259
1337
  * List all schedules
1260
1338
  */
1261
1339
  async list(params) {
1262
- return this.http.get("/schedules", { params });
1340
+ return this.http.get("/schedules", {
1341
+ params
1342
+ });
1263
1343
  }
1264
1344
  /**
1265
1345
  * Create a new schedule
@@ -1331,7 +1411,9 @@ var WorkflowsResource = class {
1331
1411
  * List all workflows
1332
1412
  */
1333
1413
  async list(params) {
1334
- return this.http.get("/workflows", { params });
1414
+ return this.http.get("/workflows", {
1415
+ params
1416
+ });
1335
1417
  }
1336
1418
  /**
1337
1419
  * Create a new workflow
@@ -1368,13 +1450,18 @@ var WorkflowsResource = class {
1368
1450
  return this.http.get(`/workflows/${workflowId}/jobs/${jobId}`);
1369
1451
  }
1370
1452
  async getWorkflowJobsStatus(workflowId) {
1371
- return this.http.get(`/workflows/${workflowId}/jobs/status`);
1453
+ return this.http.get(
1454
+ `/workflows/${workflowId}/jobs/status`
1455
+ );
1372
1456
  }
1373
1457
  async getJobDependencies(jobId) {
1374
1458
  return this.http.get(`/jobs/${jobId}/dependencies`);
1375
1459
  }
1376
1460
  async addJobDependencies(jobId, params) {
1377
- return this.http.post(`/jobs/${jobId}/dependencies`, params);
1461
+ return this.http.post(
1462
+ `/jobs/${jobId}/dependencies`,
1463
+ params
1464
+ );
1378
1465
  }
1379
1466
  };
1380
1467
 
@@ -1423,7 +1510,9 @@ var WebhooksResource = class {
1423
1510
  * Get webhook delivery history
1424
1511
  */
1425
1512
  async getDeliveries(id) {
1426
- return this.http.get(`/outgoing-webhooks/${id}/deliveries`);
1513
+ return this.http.get(
1514
+ `/outgoing-webhooks/${id}/deliveries`
1515
+ );
1427
1516
  }
1428
1517
  /**
1429
1518
  * Retry a specific webhook delivery
@@ -1531,7 +1620,9 @@ var OrganizationsResource = class {
1531
1620
  * Generate a unique slug from an organization name
1532
1621
  */
1533
1622
  async generateSlug(name) {
1534
- return this.http.post("/organizations/generate-slug", { name });
1623
+ return this.http.post("/organizations/generate-slug", {
1624
+ name
1625
+ });
1535
1626
  }
1536
1627
  /**
1537
1628
  * Get the webhook token for the current organization.
@@ -1561,7 +1652,9 @@ var OrganizationsResource = class {
1561
1652
  * ```
1562
1653
  */
1563
1654
  async regenerateWebhookToken() {
1564
- return this.http.post("/organizations/webhook-token/regenerate");
1655
+ return this.http.post(
1656
+ "/organizations/webhook-token/regenerate"
1657
+ );
1565
1658
  }
1566
1659
  /**
1567
1660
  * Clear/delete the webhook token for the current organization.
@@ -1598,7 +1691,10 @@ var BillingResource = class {
1598
1691
  * POST /api/v1/billing/portal
1599
1692
  */
1600
1693
  async createPortal(params) {
1601
- return this.http.post("/billing/portal", params);
1694
+ return this.http.post(
1695
+ "/billing/portal",
1696
+ params
1697
+ );
1602
1698
  }
1603
1699
  };
1604
1700
 
@@ -1693,7 +1789,9 @@ var AdminResource = class {
1693
1789
  }
1694
1790
  headers() {
1695
1791
  if (!this.adminKey) {
1696
- 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
+ );
1697
1795
  }
1698
1796
  return { "X-Admin-Key": this.adminKey };
1699
1797
  }
@@ -1714,15 +1812,22 @@ var AdminResource = class {
1714
1812
  }
1715
1813
  /** POST /api/v1/admin/organizations */
1716
1814
  async createOrganization(params) {
1717
- return this.http.post("/admin/organizations", params, {
1718
- headers: this.headers()
1719
- });
1815
+ return this.http.post(
1816
+ "/admin/organizations",
1817
+ params,
1818
+ {
1819
+ headers: this.headers()
1820
+ }
1821
+ );
1720
1822
  }
1721
1823
  /** GET /api/v1/admin/organizations/{id} */
1722
1824
  async getOrganization(id) {
1723
- return this.http.get(`/admin/organizations/${id}`, {
1724
- headers: this.headers()
1725
- });
1825
+ return this.http.get(
1826
+ `/admin/organizations/${id}`,
1827
+ {
1828
+ headers: this.headers()
1829
+ }
1830
+ );
1726
1831
  }
1727
1832
  /** PATCH /api/v1/admin/organizations/{id} */
1728
1833
  async updateOrganization(id, params) {
@@ -1740,15 +1845,23 @@ var AdminResource = class {
1740
1845
  }
1741
1846
  /** POST /api/v1/admin/organizations/{id}/api-keys */
1742
1847
  async createApiKey(orgId, params) {
1743
- return this.http.post(`/admin/organizations/${orgId}/api-keys`, params, {
1744
- headers: this.headers()
1745
- });
1848
+ return this.http.post(
1849
+ `/admin/organizations/${orgId}/api-keys`,
1850
+ params,
1851
+ {
1852
+ headers: this.headers()
1853
+ }
1854
+ );
1746
1855
  }
1747
1856
  /** POST /api/v1/admin/organizations/{id}/reset-usage */
1748
1857
  async resetUsage(orgId) {
1749
- await this.http.post(`/admin/organizations/${orgId}/reset-usage`, void 0, {
1750
- headers: this.headers()
1751
- });
1858
+ await this.http.post(
1859
+ `/admin/organizations/${orgId}/reset-usage`,
1860
+ void 0,
1861
+ {
1862
+ headers: this.headers()
1863
+ }
1864
+ );
1752
1865
  }
1753
1866
  /** GET /api/v1/admin/stats */
1754
1867
  async getStats() {
@@ -1794,7 +1907,9 @@ var WebhookIngestionResource = class {
1794
1907
  const bytes = toBytes(body);
1795
1908
  const signature = options.signature ?? (options.secret ? githubSignature(options.secret, bytes) : void 0);
1796
1909
  if (!signature) {
1797
- 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
+ );
1798
1913
  }
1799
1914
  const headers = {
1800
1915
  "Content-Type": "application/json",
@@ -1825,7 +1940,9 @@ var WebhookIngestionResource = class {
1825
1940
  const timestamp = options.timestamp ?? Math.floor(Date.now() / 1e3);
1826
1941
  const signature = options.signature ?? (options.secret ? stripeSignature(options.secret, bytes, timestamp) : void 0);
1827
1942
  if (!signature) {
1828
- 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
+ );
1829
1946
  }
1830
1947
  const headers = {
1831
1948
  "Content-Type": "application/json",
@@ -1962,14 +2079,20 @@ var WebSocketRealtimeClient = class {
1962
2079
  resolve();
1963
2080
  };
1964
2081
  this.ws.onmessage = (event) => {
1965
- 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
+ );
1966
2085
  };
1967
2086
  this.ws.onclose = (event) => {
1968
2087
  this.options.debug(`WebSocket closed: ${event.code} ${event.reason}`);
1969
2088
  const wasConnecting = this.state === "connecting";
1970
2089
  this.handleDisconnect();
1971
2090
  if (wasConnecting) {
1972
- 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
+ );
1973
2096
  }
1974
2097
  };
1975
2098
  this.ws.onerror = (event) => {
@@ -2124,7 +2247,9 @@ var WebSocketRealtimeClient = class {
2124
2247
  this.options.maxReconnectDelay
2125
2248
  );
2126
2249
  const delay = Math.floor(baseDelay + Math.random() * baseDelay * 0.25);
2127
- 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
+ );
2128
2253
  this.reconnectTimer = setTimeout(async () => {
2129
2254
  try {
2130
2255
  await this.connect();
@@ -2177,11 +2302,13 @@ function isAuthFailureEvent(event) {
2177
2302
  if (!event) {
2178
2303
  return false;
2179
2304
  }
2180
- const status = event.status ?? event.statusCode ?? event.error?.status;
2181
- if (status === 401 || status === 403) {
2305
+ const status2 = event.status ?? event.statusCode ?? event.error?.status;
2306
+ if (status2 === 401 || status2 === 403) {
2182
2307
  return true;
2183
2308
  }
2184
- const text = String(event.message ?? event.error?.message ?? event.reason ?? "");
2309
+ const text = String(
2310
+ event.message ?? event.error?.message ?? event.reason ?? ""
2311
+ );
2185
2312
  return /\b(401|403)\b/.test(text);
2186
2313
  }
2187
2314
 
@@ -2244,7 +2371,9 @@ var SseRealtimeClient = class {
2244
2371
  EventSourceImpl = mod.default || mod.EventSource || mod;
2245
2372
  } catch {
2246
2373
  this.setState("disconnected");
2247
- 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
+ );
2248
2377
  }
2249
2378
  }
2250
2379
  let token;
@@ -2413,7 +2542,9 @@ var SseRealtimeClient = class {
2413
2542
  this.options.maxReconnectDelay
2414
2543
  );
2415
2544
  const delay = Math.floor(baseDelay + Math.random() * baseDelay * 0.25);
2416
- 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
+ );
2417
2548
  this.reconnectTimer = setTimeout(async () => {
2418
2549
  try {
2419
2550
  await this.connect(this.filter || void 0);
@@ -2559,20 +2690,40 @@ var PROTO_LOADER_OPTIONS = {
2559
2690
  // Support oneofs
2560
2691
  };
2561
2692
  var cachedDefinition = null;
2562
- function getProtoPath() {
2693
+ function getModuleDir() {
2563
2694
  if (typeof (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) !== "undefined") {
2564
- const __filename = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
2565
- const __dirname2 = path.dirname(__filename);
2566
- return path.join(__dirname2, "../../proto/spooled.proto");
2695
+ return path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
2567
2696
  }
2568
- return path.join(__dirname, "../../proto/spooled.proto");
2697
+ return __dirname;
2698
+ }
2699
+ function getProtoPath() {
2700
+ const moduleDir = getModuleDir();
2701
+ const candidates = [
2702
+ // Bundled ESM/CJS: dist/index.js → ../proto/spooled.proto
2703
+ path.join(moduleDir, "../proto/spooled.proto"),
2704
+ // Source / unbundled: src/grpc/loader.ts → ../../proto/spooled.proto
2705
+ path.join(moduleDir, "../../proto/spooled.proto"),
2706
+ // Extra safety if moduleDir is package root
2707
+ path.join(moduleDir, "proto/spooled.proto")
2708
+ ];
2709
+ for (const candidate of candidates) {
2710
+ if (fs.existsSync(candidate)) {
2711
+ return candidate;
2712
+ }
2713
+ }
2714
+ throw new Error(
2715
+ `Spooled gRPC proto not found. Tried: ${candidates.join(", ")}. Ensure the package's proto/spooled.proto is installed next to dist/.`
2716
+ );
2569
2717
  }
2570
2718
  function loadProtoDefinition() {
2571
2719
  if (cachedDefinition) {
2572
2720
  return cachedDefinition;
2573
2721
  }
2574
2722
  const protoPath = getProtoPath();
2575
- const packageDefinition = protoLoader__namespace.loadSync(protoPath, PROTO_LOADER_OPTIONS);
2723
+ const packageDefinition = protoLoader__namespace.loadSync(
2724
+ protoPath,
2725
+ PROTO_LOADER_OPTIONS
2726
+ );
2576
2727
  const grpcObject = grpc2__namespace.loadPackageDefinition(packageDefinition);
2577
2728
  cachedDefinition = grpcObject;
2578
2729
  return cachedDefinition;
@@ -2582,7 +2733,10 @@ async function loadProtoDefinitionAsync() {
2582
2733
  return cachedDefinition;
2583
2734
  }
2584
2735
  const protoPath = getProtoPath();
2585
- const packageDefinition = await protoLoader__namespace.load(protoPath, PROTO_LOADER_OPTIONS);
2736
+ const packageDefinition = await protoLoader__namespace.load(
2737
+ protoPath,
2738
+ PROTO_LOADER_OPTIONS
2739
+ );
2586
2740
  const grpcObject = grpc2__namespace.loadPackageDefinition(packageDefinition);
2587
2741
  cachedDefinition = grpcObject;
2588
2742
  return cachedDefinition;
@@ -2599,7 +2753,11 @@ async function* asyncIterableFromStream(stream, options) {
2599
2753
  stream.cancel();
2600
2754
  });
2601
2755
  }
2602
- return yield* new AsyncIterableStream(stream, { onConnected, onError, onEnd });
2756
+ return yield* new AsyncIterableStream(stream, {
2757
+ onConnected,
2758
+ onError,
2759
+ onEnd
2760
+ });
2603
2761
  }
2604
2762
  var AsyncIterableStream = class {
2605
2763
  stream;
@@ -2628,8 +2786,8 @@ var AsyncIterableStream = class {
2628
2786
  onEnd?.();
2629
2787
  resolve?.();
2630
2788
  });
2631
- this.stream.on("status", (status) => {
2632
- if (status.code === 0) {
2789
+ this.stream.on("status", (status2) => {
2790
+ if (status2.code === 0) {
2633
2791
  onConnected?.();
2634
2792
  }
2635
2793
  });
@@ -2730,7 +2888,9 @@ function decodeStruct(value) {
2730
2888
  return value;
2731
2889
  }
2732
2890
  const fields = value.fields ?? {};
2733
- 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
+ );
2734
2894
  }
2735
2895
  function decodeValue(value) {
2736
2896
  if ("nullValue" in value) {
@@ -2779,7 +2939,10 @@ function encodeEnqueueRequest(params) {
2779
2939
  return encoded;
2780
2940
  }
2781
2941
  function encodeCompleteRequest(params) {
2782
- return { ...params, result: encodeStruct(params.result) };
2942
+ return {
2943
+ ...params,
2944
+ result: encodeStruct(params.result)
2945
+ };
2783
2946
  }
2784
2947
  function decodeJob(job) {
2785
2948
  return {
@@ -2788,17 +2951,60 @@ function decodeJob(job) {
2788
2951
  result: decodeStruct(job.result)
2789
2952
  };
2790
2953
  }
2791
- function promisify(client, method, request, metadata) {
2954
+ function promisify(client, method, request, metadata, timeoutMs) {
2792
2955
  return new Promise((resolve, reject) => {
2793
- method.call(client, request, metadata, (error, response) => {
2956
+ const callback = (error, response) => {
2794
2957
  if (error) {
2795
- reject(error);
2958
+ reject(mapGrpcError(error, timeoutMs));
2796
2959
  } else {
2797
2960
  resolve(response);
2798
2961
  }
2799
- });
2962
+ };
2963
+ method.call(
2964
+ client,
2965
+ request,
2966
+ metadata,
2967
+ createUnaryCallOptions(timeoutMs),
2968
+ callback
2969
+ );
2800
2970
  });
2801
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
+ }
2802
3008
  function shouldUseTls(address, explicitTls) {
2803
3009
  if (explicitTls !== void 0) {
2804
3010
  return explicitTls;
@@ -2807,53 +3013,99 @@ function shouldUseTls(address, explicitTls) {
2807
3013
  return host !== "localhost" && host !== "127.0.0.1" && !host.startsWith("[::1]");
2808
3014
  }
2809
3015
  var GrpcQueueOperations = class {
2810
- constructor(client, metadata) {
3016
+ constructor(client, metadata, timeoutMs) {
2811
3017
  this.client = client;
2812
3018
  this.metadata = metadata;
3019
+ this.timeoutMs = timeoutMs;
2813
3020
  }
2814
3021
  /**
2815
3022
  * Enqueue a new job
2816
3023
  */
2817
3024
  async enqueue(params) {
2818
- 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
+ );
2819
3032
  }
2820
3033
  /**
2821
3034
  * Dequeue a job (for workers)
2822
3035
  */
2823
3036
  async dequeue(params) {
2824
- 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
+ );
2825
3044
  return { ...response, jobs: response.jobs.map(decodeJob) };
2826
3045
  }
2827
3046
  /**
2828
3047
  * Complete a job successfully
2829
3048
  */
2830
3049
  async complete(params) {
2831
- 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
+ );
2832
3057
  }
2833
3058
  /**
2834
3059
  * Fail a job
2835
3060
  */
2836
3061
  async fail(params) {
2837
- 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
+ );
2838
3069
  }
2839
3070
  /**
2840
3071
  * Renew a job's lease
2841
3072
  */
2842
3073
  async renewLease(params) {
2843
- 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
+ );
2844
3081
  }
2845
3082
  /**
2846
3083
  * Get a job by ID
2847
3084
  */
2848
3085
  async getJob(jobId) {
2849
- const response = await promisify(this.client, this.client.GetJob, { jobId }, this.metadata);
2850
- 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
+ };
2851
3097
  }
2852
3098
  /**
2853
3099
  * Get queue statistics
2854
3100
  */
2855
3101
  async getQueueStats(queueName) {
2856
- 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
+ );
2857
3109
  }
2858
3110
  /**
2859
3111
  * Stream jobs from a queue (server-side streaming)
@@ -2901,27 +3153,46 @@ var GrpcQueueOperations = class {
2901
3153
  }
2902
3154
  };
2903
3155
  var GrpcWorkerOperations = class {
2904
- constructor(client, metadata) {
3156
+ constructor(client, metadata, timeoutMs) {
2905
3157
  this.client = client;
2906
3158
  this.metadata = metadata;
3159
+ this.timeoutMs = timeoutMs;
2907
3160
  }
2908
3161
  /**
2909
3162
  * Register a new worker
2910
3163
  */
2911
3164
  async register(params) {
2912
- 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
+ );
2913
3172
  }
2914
3173
  /**
2915
3174
  * Send heartbeat
2916
3175
  */
2917
3176
  async heartbeat(params) {
2918
- 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
+ );
2919
3184
  }
2920
3185
  /**
2921
3186
  * Deregister a worker
2922
3187
  */
2923
3188
  async deregister(workerId) {
2924
- 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
+ );
2925
3196
  }
2926
3197
  };
2927
3198
  var SpooledGrpcClient = class {
@@ -2933,7 +3204,7 @@ var SpooledGrpcClient = class {
2933
3204
  /** Worker operations */
2934
3205
  workers;
2935
3206
  constructor(options) {
2936
- const { address, apiKey, useTls, credentials: credentials2, channelOptions } = options;
3207
+ const { address, apiKey, useTls, credentials: credentials2, channelOptions, timeout } = options;
2937
3208
  const creds = credentials2 ?? (shouldUseTls(address, useTls) ? grpc2__namespace.credentials.createSsl() : grpc2__namespace.credentials.createInsecure());
2938
3209
  this.metadata = new grpc2__namespace.Metadata();
2939
3210
  this.metadata.add("x-api-key", apiKey);
@@ -2948,8 +3219,16 @@ var SpooledGrpcClient = class {
2948
3219
  creds,
2949
3220
  channelOptions
2950
3221
  );
2951
- this.queue = new GrpcQueueOperations(this.queueClient, this.metadata);
2952
- 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
+ );
2953
3232
  }
2954
3233
  /**
2955
3234
  * Close all gRPC connections
@@ -3098,7 +3377,8 @@ var SpooledClient = class _SpooledClient {
3098
3377
  }
3099
3378
  this._grpc = new SpooledGrpcClient({
3100
3379
  address: grpcAddress,
3101
- apiKey
3380
+ apiKey,
3381
+ timeout: this.config.timeout
3102
3382
  });
3103
3383
  }
3104
3384
  return this._grpc;
@@ -3165,7 +3445,9 @@ var SpooledClient = class _SpooledClient {
3165
3445
  }
3166
3446
  return this.loginForJwt();
3167
3447
  }
3168
- throw new AuthenticationError("No authentication method available for realtime connection");
3448
+ throw new AuthenticationError(
3449
+ "No authentication method available for realtime connection"
3450
+ );
3169
3451
  }
3170
3452
  /**
3171
3453
  * Exchange the API key for a JWT via /auth/login and cache the result.
@@ -3462,7 +3744,10 @@ var SpooledWorker = class {
3462
3744
  this.sendWorkerHeartbeat();
3463
3745
  }, heartbeatInterval);
3464
3746
  this.state = "running";
3465
- this.emit("started", { workerId: this.workerId, queueName: this.options.queueName });
3747
+ this.emit("started", {
3748
+ workerId: this.workerId,
3749
+ queueName: this.options.queueName
3750
+ });
3466
3751
  this.schedulePoll();
3467
3752
  } catch (error) {
3468
3753
  this.state = "error";
@@ -3498,14 +3783,18 @@ var SpooledWorker = class {
3498
3783
  active.abortController.abort();
3499
3784
  }
3500
3785
  if (this.activeJobs.size > 0) {
3501
- this.debug(`Waiting for ${this.activeJobs.size} active jobs to complete...`);
3786
+ this.debug(
3787
+ `Waiting for ${this.activeJobs.size} active jobs to complete...`
3788
+ );
3502
3789
  await Promise.race([
3503
3790
  this.waitForActiveJobs(),
3504
3791
  this.sleep(this.options.shutdownTimeout)
3505
3792
  ]);
3506
3793
  const remaining = Array.from(this.activeJobs.values());
3507
3794
  for (const active of remaining) {
3508
- this.debug(`Force-failing job ${active.job.id} due to shutdown timeout`);
3795
+ this.debug(
3796
+ `Force-failing job ${active.job.id} due to shutdown timeout`
3797
+ );
3509
3798
  await this.failJob(active.job, "Worker shutdown timeout");
3510
3799
  this.cleanupJob(active);
3511
3800
  }
@@ -3613,7 +3902,9 @@ var SpooledWorker = class {
3613
3902
  retryCount: job.retryCount,
3614
3903
  maxRetries: job.maxRetries,
3615
3904
  signal,
3616
- progress: async (_percent, _message) => {
3905
+ progress: async (percent, message) => {
3906
+ const boundedPercent = Math.max(0, Math.min(100, percent));
3907
+ this.debug(`[${job.id}] progress ${boundedPercent}%`, message);
3617
3908
  },
3618
3909
  log: (level, message, meta) => {
3619
3910
  this.debug(`[${job.id}] [${level}] ${message}`, meta);
@@ -3776,6 +4067,7 @@ exports.createPaginatedResponse = createPaginatedResponse;
3776
4067
  exports.createProcessJobsStream = createProcessJobsStream;
3777
4068
  exports.createRetryWrapper = createRetryWrapper;
3778
4069
  exports.dateToTimestamp = dateToTimestamp;
4070
+ exports.getProtoPath = getProtoPath;
3779
4071
  exports.isRetryableError = isRetryableError;
3780
4072
  exports.isSpooledError = isSpooledError;
3781
4073
  exports.loadProtoDefinition = loadProtoDefinition;