@spooled/sdk 1.1.0 → 1.2.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
@@ -50,10 +50,10 @@ var DEFAULT_CONFIG = {
50
50
  successThreshold: 3,
51
51
  timeout: 3e4
52
52
  },
53
- userAgent: "@spooled/sdk-nodejs/1.1.0",
53
+ userAgent: "@spooled/sdk-nodejs/1.2.1",
54
54
  autoRefreshToken: true
55
55
  };
56
- var SDK_VERSION = "1.1.0";
56
+ var SDK_VERSION = "1.2.1";
57
57
  var API_VERSION = "v1";
58
58
  var API_BASE_PATH = `/api/${API_VERSION}`;
59
59
  function normalizeCredential(value) {
@@ -1079,21 +1079,28 @@ var AuthResource = class {
1079
1079
  }
1080
1080
  /**
1081
1081
  * Validate a token
1082
+ *
1083
+ * POST /auth/validate is `{ valid, error?, claims? }`. Claims use `org_id`
1084
+ * (camelCased `orgId`), not `organizationId`. Invalid tokens send `error`,
1085
+ * not `message`.
1082
1086
  */
1083
1087
  async validate(params) {
1084
- return this.http.post("/auth/validate", params);
1088
+ const raw = await this.http.post(
1089
+ "/auth/validate",
1090
+ params
1091
+ );
1092
+ return mapValidate(raw);
1085
1093
  }
1086
1094
  /**
1087
- * Start email-based login flow (sends magic link)
1095
+ * Start email-based login flow (sends a 6-digit code)
1088
1096
  */
1089
1097
  async startEmailLogin(email) {
1090
- return this.http.post(
1091
- "/auth/email/start",
1092
- { email }
1093
- );
1098
+ return this.http.post("/auth/email/start", {
1099
+ email
1100
+ });
1094
1101
  }
1095
1102
  /**
1096
- * Check if an email address exists in the system
1103
+ * Check whether an email is registered (GET /auth/check-email)
1097
1104
  */
1098
1105
  async checkEmail(email) {
1099
1106
  return this.http.get("/auth/check-email", {
@@ -1101,6 +1108,23 @@ var AuthResource = class {
1101
1108
  });
1102
1109
  }
1103
1110
  };
1111
+ function mapValidate(raw) {
1112
+ const claims = raw.claims;
1113
+ const organizationId = claims?.organizationId ?? claims?.orgId;
1114
+ return {
1115
+ valid: raw.valid,
1116
+ message: raw.message ?? raw.error,
1117
+ error: raw.error ?? raw.message,
1118
+ claims: claims ? {
1119
+ organizationId: organizationId ?? "",
1120
+ orgId: claims.orgId ?? organizationId,
1121
+ apiKeyId: claims.apiKeyId ?? "",
1122
+ queues: claims.queues ?? [],
1123
+ exp: claims.exp ?? 0,
1124
+ iat: claims.iat ?? 0
1125
+ } : void 0
1126
+ };
1127
+ }
1104
1128
 
1105
1129
  // src/resources/jobs.ts
1106
1130
  var JobsResource = class {
@@ -1131,15 +1155,20 @@ var JobsResource = class {
1131
1155
  * List jobs with optional filtering
1132
1156
  */
1133
1157
  async list(params) {
1134
- return this.http.get("/jobs", {
1158
+ const rows = await this.http.get("/jobs", {
1135
1159
  params
1136
1160
  });
1161
+ return rows.map((row) => ({
1162
+ ...row,
1163
+ jobType: row.jobType ?? jobTypeFromPayload(row)
1164
+ }));
1137
1165
  }
1138
1166
  /**
1139
1167
  * Get a job by ID
1140
1168
  */
1141
1169
  async get(id) {
1142
- return this.http.get(`/jobs/${id}`);
1170
+ const job = await this.http.get(`/jobs/${id}`);
1171
+ return withJobType(job);
1143
1172
  }
1144
1173
  /**
1145
1174
  * Cancel a pending or scheduled job
@@ -1151,7 +1180,8 @@ var JobsResource = class {
1151
1180
  * Retry a failed or dead-lettered job
1152
1181
  */
1153
1182
  async retry(id) {
1154
- return this.http.post(`/jobs/${id}/retry`);
1183
+ const job = await this.http.post(`/jobs/${id}/retry`);
1184
+ return withJobType(job);
1155
1185
  }
1156
1186
  /**
1157
1187
  * Boost job priority
@@ -1220,9 +1250,13 @@ var JobsResource = class {
1220
1250
  }
1221
1251
  // DLQ operations (private implementations)
1222
1252
  async listDlq(params) {
1223
- return this.http.get("/jobs/dlq", {
1253
+ const rows = await this.http.get("/jobs/dlq", {
1224
1254
  params
1225
1255
  });
1256
+ return rows.map((row) => ({
1257
+ ...row,
1258
+ jobType: row.jobType ?? jobTypeFromPayload(row)
1259
+ }));
1226
1260
  }
1227
1261
  async retryDlq(params) {
1228
1262
  return this.http.post("/jobs/dlq/retry", params);
@@ -1231,6 +1265,28 @@ var JobsResource = class {
1231
1265
  return this.http.post("/jobs/dlq/purge", params);
1232
1266
  }
1233
1267
  };
1268
+ function withJobType(job) {
1269
+ return {
1270
+ ...job,
1271
+ jobType: job.jobType ?? jobTypeFromPayload(job) ?? void 0
1272
+ };
1273
+ }
1274
+ function jobTypeFromPayload(row) {
1275
+ if (typeof row.jobType === "string" && row.jobType !== "") {
1276
+ return row.jobType;
1277
+ }
1278
+ const payload = row.payload;
1279
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
1280
+ return void 0;
1281
+ }
1282
+ const rec = payload;
1283
+ for (const value of [rec.jobType, rec.job_type]) {
1284
+ if (typeof value === "string" && value !== "") {
1285
+ return value;
1286
+ }
1287
+ }
1288
+ return void 0;
1289
+ }
1234
1290
 
1235
1291
  // src/resources/queues.ts
1236
1292
  var QueuesResource = class {
@@ -1425,7 +1481,8 @@ var WorkflowsResource = class {
1425
1481
  * Get a workflow by ID
1426
1482
  */
1427
1483
  async get(id) {
1428
- return this.http.get(`/workflows/${id}`);
1484
+ const raw = await this.http.get(`/workflows/${id}`);
1485
+ return mapWorkflowGetResponse(raw);
1429
1486
  }
1430
1487
  /**
1431
1488
  * Cancel a workflow
@@ -1443,27 +1500,104 @@ var WorkflowsResource = class {
1443
1500
  return this.http.post(`/workflows/${id}/retry`);
1444
1501
  }
1445
1502
  // Workflow job operations (private implementations)
1503
+ //
1504
+ // The backend has no /workflows/{id}/jobs routes. Job rows and dependency
1505
+ // edges are on GET /workflows/{id} (WorkflowDetailResponse).
1446
1506
  async listWorkflowJobs(workflowId) {
1447
- return this.http.get(`/workflows/${workflowId}/jobs`);
1507
+ const detail = await this.http.get(
1508
+ `/workflows/${workflowId}`
1509
+ );
1510
+ return mapWorkflowDetailJobs(detail);
1448
1511
  }
1449
1512
  async getWorkflowJob(workflowId, jobId) {
1450
- return this.http.get(`/workflows/${workflowId}/jobs/${jobId}`);
1513
+ const jobs = await this.listWorkflowJobs(workflowId);
1514
+ const job = jobs.find((item) => item.id === jobId);
1515
+ if (!job) {
1516
+ throw new NotFoundError(
1517
+ `Job ${jobId} not found in workflow ${workflowId}`
1518
+ );
1519
+ }
1520
+ return job;
1451
1521
  }
1452
1522
  async getWorkflowJobsStatus(workflowId) {
1453
- return this.http.get(
1454
- `/workflows/${workflowId}/jobs/status`
1455
- );
1523
+ const jobs = await this.listWorkflowJobs(workflowId);
1524
+ return jobs.map((job) => ({
1525
+ jobId: job.id,
1526
+ key: job.key,
1527
+ status: job.status
1528
+ }));
1456
1529
  }
1457
1530
  async getJobDependencies(jobId) {
1458
1531
  return this.http.get(`/jobs/${jobId}/dependencies`);
1459
1532
  }
1460
1533
  async addJobDependencies(jobId, params) {
1461
- return this.http.post(
1534
+ const dependsOn = params.dependsOn ?? params.dependsOnJobIds ?? [];
1535
+ const mode = params.dependencyMode ?? (params.dependencyType === "any" || params.dependencyType === "all" ? params.dependencyType : void 0);
1536
+ const raw = await this.http.post(
1462
1537
  `/jobs/${jobId}/dependencies`,
1463
- params
1538
+ {
1539
+ depends_on: dependsOn,
1540
+ ...mode ? { dependency_mode: mode } : {}
1541
+ },
1542
+ { skipRequestConversion: true }
1464
1543
  );
1544
+ return {
1545
+ added: raw.dependenciesAdded ?? 0,
1546
+ dependenciesMet: raw.dependenciesMet ?? false,
1547
+ dependencies: []
1548
+ };
1465
1549
  }
1466
1550
  };
1551
+ function mapWorkflowGetResponse(raw) {
1552
+ const totalJobs = raw.totalJobs ?? raw.progress?.total ?? 0;
1553
+ const completedJobs = raw.completedJobs ?? raw.progress?.completed ?? 0;
1554
+ const failedJobs = raw.failedJobs ?? raw.progress?.failed ?? 0;
1555
+ const progressPercent = raw.progressPercent ?? (totalJobs > 0 ? completedJobs / totalJobs * 100 : 0);
1556
+ return {
1557
+ id: String(raw.id ?? ""),
1558
+ name: String(raw.name ?? ""),
1559
+ status: raw.status ?? "pending",
1560
+ totalJobs,
1561
+ completedJobs,
1562
+ failedJobs,
1563
+ progressPercent,
1564
+ createdAt: String(raw.createdAt ?? ""),
1565
+ completedAt: raw.completedAt
1566
+ };
1567
+ }
1568
+ function mapWorkflowDetailJobs(detail) {
1569
+ const workflowId = String(detail.id ?? "");
1570
+ const deps = detail.dependencies ?? [];
1571
+ return (detail.jobs ?? []).map((job) => {
1572
+ const id = String(job.id ?? "");
1573
+ const timeoutMs = typeof job.timeoutMs === "number" ? job.timeoutMs : void 0;
1574
+ const errorObj = job.error;
1575
+ let error;
1576
+ if (typeof errorObj === "string") {
1577
+ error = errorObj;
1578
+ } else if (errorObj && typeof errorObj === "object" && "message" in errorObj) {
1579
+ error = String(errorObj.message);
1580
+ }
1581
+ return {
1582
+ id,
1583
+ workflowId: String(job.workflowId ?? workflowId),
1584
+ key: String(job.key ?? ""),
1585
+ queueName: String(job.queueName ?? job.queue ?? ""),
1586
+ status: job.status,
1587
+ payload: job.payload ?? {},
1588
+ result: job.result ?? void 0,
1589
+ error,
1590
+ dependsOn: deps.filter((dep) => dep.childJobId === id).map((dep) => String(dep.parentJobId ?? "")),
1591
+ priority: Number(job.priority ?? 0),
1592
+ maxRetries: Number(job.maxRetries ?? 0),
1593
+ attempt: Number(job.attempt ?? 0),
1594
+ timeoutSeconds: timeoutMs != null ? Math.max(1, Math.floor(timeoutMs / 1e3)) : void 0,
1595
+ createdAt: String(job.createdAt ?? ""),
1596
+ startedAt: job.startedAt ?? void 0,
1597
+ completedAt: job.completedAt ?? void 0
1598
+ };
1599
+ });
1600
+ }
1467
1601
 
1468
1602
  // src/resources/webhooks.ts
1469
1603
  var WebhooksResource = class {
@@ -1632,8 +1766,8 @@ var OrganizationsResource = class {
1632
1766
  *
1633
1767
  * @example
1634
1768
  * ```typescript
1635
- * const { token } = await client.organizations.getWebhookToken();
1636
- * console.log('Webhook token:', token);
1769
+ * const { webhookToken, webhookUrl } = await client.organizations.getWebhookToken();
1770
+ * console.log('Webhook token:', webhookToken);
1637
1771
  * ```
1638
1772
  */
1639
1773
  async getWebhookToken() {
@@ -1647,7 +1781,7 @@ var OrganizationsResource = class {
1647
1781
  *
1648
1782
  * @example
1649
1783
  * ```typescript
1650
- * const { token: newToken } = await client.organizations.regenerateWebhookToken();
1784
+ * const { webhookToken: newToken } = await client.organizations.regenerateWebhookToken();
1651
1785
  * console.log('New webhook token:', newToken);
1652
1786
  * ```
1653
1787
  */
@@ -1659,8 +1793,9 @@ var OrganizationsResource = class {
1659
1793
  /**
1660
1794
  * Clear/delete the webhook token for the current organization.
1661
1795
  *
1662
- * After this, webhook signature verification will fail until a new
1663
- * token is generated via regenerateWebhookToken().
1796
+ * POST /organizations/webhook-token/clear requires `{ confirm: true }`.
1797
+ * An empty body 422s. The API currently rejects the operation and tells
1798
+ * callers to use regenerateWebhookToken() instead.
1664
1799
  *
1665
1800
  * @example
1666
1801
  * ```typescript
@@ -1668,7 +1803,9 @@ var OrganizationsResource = class {
1668
1803
  * ```
1669
1804
  */
1670
1805
  async clearWebhookToken() {
1671
- await this.http.post("/organizations/webhook-token/clear");
1806
+ await this.http.post("/organizations/webhook-token/clear", {
1807
+ confirm: true
1808
+ });
1672
1809
  }
1673
1810
  };
1674
1811
 
@@ -1965,16 +2102,27 @@ var WebhookIngestionResource = class {
1965
2102
  /**
1966
2103
  * Ingest a custom webhook.
1967
2104
  *
1968
- * POST /api/v1/webhooks/{org_id}/custom
2105
+ * POST /api/v1/webhooks/{org_id}/custom returns `{ jobId, queueName, status }`.
2106
+ * An older empty 200 maps to an empty object rather than throwing.
1969
2107
  */
1970
2108
  async custom(orgId, params, opts) {
1971
2109
  const headers = {
1972
2110
  ...opts?.webhookToken ? { "X-Webhook-Token": opts.webhookToken } : {},
1973
2111
  ...opts?.forwardedProto ? { "X-Forwarded-Proto": opts.forwardedProto } : {}
1974
2112
  };
1975
- await this.http.post(`/webhooks/${orgId}/custom`, params, {
1976
- headers
1977
- });
2113
+ const raw = await this.http.post(
2114
+ `/webhooks/${orgId}/custom`,
2115
+ params,
2116
+ { headers }
2117
+ );
2118
+ if (!raw || typeof raw === "string") {
2119
+ return {};
2120
+ }
2121
+ return {
2122
+ jobId: raw.jobId,
2123
+ queueName: raw.queueName,
2124
+ status: raw.status
2125
+ };
1978
2126
  }
1979
2127
  };
1980
2128
 
@@ -2313,6 +2461,19 @@ function isAuthFailureEvent(event) {
2313
2461
  }
2314
2462
 
2315
2463
  // src/realtime/sse.ts
2464
+ var SSE_EVENT_NAMES = [
2465
+ "job.created",
2466
+ "job.status",
2467
+ "job.completed",
2468
+ "job.failed",
2469
+ "queue.stats",
2470
+ "worker.heartbeat",
2471
+ "worker.registered",
2472
+ "worker.deregistered",
2473
+ "system.health",
2474
+ "ping",
2475
+ "error"
2476
+ ];
2316
2477
  var SseRealtimeClient = class {
2317
2478
  options;
2318
2479
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -2404,6 +2565,11 @@ var SseRealtimeClient = class {
2404
2565
  this.eventSource.onmessage = (event) => {
2405
2566
  this.handleMessage(event.data);
2406
2567
  };
2568
+ for (const name of SSE_EVENT_NAMES) {
2569
+ this.eventSource.addEventListener(name, (event) => {
2570
+ if (typeof event?.data === "string") this.handleMessage(event.data);
2571
+ });
2572
+ }
2407
2573
  this.eventSource.onerror = () => {
2408
2574
  this.options.debug("SSE error");
2409
2575
  if (this.state === "connecting") {
@@ -2484,7 +2650,7 @@ var SseRealtimeClient = class {
2484
2650
  return `${this.options.baseUrl}/api/v1/events/jobs/${encodeURIComponent(filter.jobId)}`;
2485
2651
  }
2486
2652
  if (filter?.queueName) {
2487
- return `${this.options.baseUrl}/api/v1/events/queues/${encodeURIComponent(filter.queueName)}`;
2653
+ return `${this.options.baseUrl}/api/v1/events?queue=${encodeURIComponent(filter.queueName)}`;
2488
2654
  }
2489
2655
  return `${this.options.baseUrl}/api/v1/events`;
2490
2656
  }