@rendobar/sdk 3.0.0 → 3.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
@@ -307,22 +307,53 @@ function toBlob(file) {
307
307
  return file instanceof Blob ? file : new Blob([file]);
308
308
  }
309
309
  /**
310
+ * Per-attempt upload time budget. fetch() exposes no upload progress, so a
311
+ * stalled socket and a slow link are indistinguishable from the outside; a
312
+ * hard per-attempt budget is the only portable stall cap. The ladder escalates
313
+ * (healthy 500 KiB/s assumption first, then 100, then 50) so a genuinely slow
314
+ * connection that times out on an early attempt still completes on a later,
315
+ * more patient one — while a hung socket gets cut and retried on a fresh
316
+ * connection instead of waiting on the OS TCP timeout.
317
+ *
318
+ * @internal exported for tests
319
+ */
320
+ function attemptTimeoutMs(sizeBytes, attempt) {
321
+ const GRACE_MS = [
322
+ 1e4,
323
+ 3e4,
324
+ 6e4
325
+ ];
326
+ const RATE_BPS = [
327
+ 500 * 1024,
328
+ 100 * 1024,
329
+ 50 * 1024
330
+ ];
331
+ const i = Math.min(attempt, GRACE_MS.length - 1);
332
+ return GRACE_MS[i] + Math.ceil(sizeBytes / RATE_BPS[i] * 1e3);
333
+ }
334
+ /**
310
335
  * PUT a body to a presigned R2 url with bounded retry.
311
336
  *
312
337
  * Returns the ETag string when the bucket exposes it, or `null` when the
313
- * response header is absent (e.g. bucket CORS not exposing ETag). Only
314
- * network/HTTP failures trigger retries; a missing ETag is not retried because
315
- * it is a deterministic misconfiguration, not a transient error.
338
+ * response header is absent (e.g. bucket CORS not exposing ETag). Network/HTTP
339
+ * failures and per-attempt timeouts trigger retries; a caller abort and a
340
+ * missing ETag do not (the latter is a deterministic misconfiguration, not a
341
+ * transient error).
342
+ *
343
+ * @internal exported for tests
316
344
  */
317
- async function putWithRetry(url, body, signal, attempts = 3) {
345
+ async function putWithRetry(url, body, signal, sizeBytes, attempts = 3, attemptTimeouts) {
318
346
  let lastErr;
319
347
  for (let i = 0; i < attempts; i++) {
320
348
  if (signal?.aborted) throw new Error("Upload aborted");
349
+ const timeoutMs = attemptTimeouts?.[i] ?? attemptTimeoutMs(sizeBytes, i);
350
+ const timeout = AbortSignal.timeout(timeoutMs);
351
+ const attemptSignal = signal ? AbortSignal.any([signal, timeout]) : timeout;
321
352
  try {
322
353
  const res = await fetch(url, {
323
354
  method: "PUT",
324
355
  body,
325
- signal
356
+ signal: attemptSignal
326
357
  });
327
358
  if (!res.ok) throw new Error(`Upload PUT failed: ${res.status}`);
328
359
  return res.headers.get("etag");
@@ -378,7 +409,7 @@ function createUploadsResource(request) {
378
409
  return init.data;
379
410
  }
380
411
  if (init.status === "presigned") {
381
- await putWithRetry(init.upload.url, blob, options.signal);
412
+ await putWithRetry(init.upload.url, blob, options.signal, size);
382
413
  options.onProgress?.({
383
414
  loaded: size,
384
415
  total: size
@@ -399,7 +430,7 @@ function createUploadsResource(request) {
399
430
  const chunk = blob.slice(start, Math.min(start + partSize, size));
400
431
  let etag;
401
432
  try {
402
- etag = await putWithRetry(p.url, chunk, combinedSignal);
433
+ etag = await putWithRetry(p.url, chunk, combinedSignal, chunk.size);
403
434
  } catch (e) {
404
435
  internal.abort();
405
436
  partError = e instanceof Error ? e : new Error(String(e));
@@ -668,6 +699,8 @@ const KNOWN_EVENT_TYPES = new Set([
668
699
  "job.progress",
669
700
  "job.step",
670
701
  "job.log",
702
+ "job.metrics",
703
+ "job.context",
671
704
  "balance.updated",
672
705
  "subscription.updated",
673
706
  "notification",
@@ -756,6 +789,12 @@ function dispatchJobEvent(event, options) {
756
789
  case "job.result":
757
790
  options.onResult?.(event);
758
791
  break;
792
+ case "job.metrics":
793
+ options.onMetrics?.(event);
794
+ break;
795
+ case "job.context":
796
+ options.onContext?.(event);
797
+ break;
759
798
  }
760
799
  }
761
800
  //#endregion
package/dist/index.d.cts CHANGED
@@ -2269,6 +2269,7 @@ declare const jobResponseSchema: ZodDiscriminatedUnion<[ZodObject<{
2269
2269
  error: ZodOptional<ZodString>;
2270
2270
  }, $strip>>;
2271
2271
  logsAvailable: ZodBoolean;
2272
+ metricsAvailable: ZodBoolean;
2272
2273
  createdAt: ZodNumber;
2273
2274
  dispatchedAt: ZodNullable<ZodNumber>;
2274
2275
  startedAt: ZodNullable<ZodNumber>;
@@ -2305,6 +2306,7 @@ declare const jobResponseSchema: ZodDiscriminatedUnion<[ZodObject<{
2305
2306
  error: ZodOptional<ZodString>;
2306
2307
  }, $strip>>;
2307
2308
  logsAvailable: ZodBoolean;
2309
+ metricsAvailable: ZodBoolean;
2308
2310
  createdAt: ZodNumber;
2309
2311
  dispatchedAt: ZodNullable<ZodNumber>;
2310
2312
  startedAt: ZodNullable<ZodNumber>;
@@ -2343,6 +2345,7 @@ declare const jobResponseSchema: ZodDiscriminatedUnion<[ZodObject<{
2343
2345
  error: ZodOptional<ZodString>;
2344
2346
  }, $strip>>;
2345
2347
  logsAvailable: ZodBoolean;
2348
+ metricsAvailable: ZodBoolean;
2346
2349
  createdAt: ZodNumber;
2347
2350
  dispatchedAt: ZodNullable<ZodNumber>;
2348
2351
  startedAt: ZodNullable<ZodNumber>;
@@ -2381,6 +2384,7 @@ declare const jobResponseSchema: ZodDiscriminatedUnion<[ZodObject<{
2381
2384
  error: ZodOptional<ZodString>;
2382
2385
  }, $strip>>;
2383
2386
  logsAvailable: ZodBoolean;
2387
+ metricsAvailable: ZodBoolean;
2384
2388
  createdAt: ZodNumber;
2385
2389
  dispatchedAt: ZodNullable<ZodNumber>;
2386
2390
  startedAt: ZodNullable<ZodNumber>;
@@ -2461,6 +2465,7 @@ declare const jobResponseSchema: ZodDiscriminatedUnion<[ZodObject<{
2461
2465
  error: ZodOptional<ZodString>;
2462
2466
  }, $strip>>;
2463
2467
  logsAvailable: ZodBoolean;
2468
+ metricsAvailable: ZodBoolean;
2464
2469
  createdAt: ZodNumber;
2465
2470
  dispatchedAt: ZodNullable<ZodNumber>;
2466
2471
  startedAt: ZodNullable<ZodNumber>;
@@ -2503,6 +2508,7 @@ declare const jobResponseSchema: ZodDiscriminatedUnion<[ZodObject<{
2503
2508
  error: ZodOptional<ZodString>;
2504
2509
  }, $strip>>;
2505
2510
  logsAvailable: ZodBoolean;
2511
+ metricsAvailable: ZodBoolean;
2506
2512
  createdAt: ZodNumber;
2507
2513
  dispatchedAt: ZodNullable<ZodNumber>;
2508
2514
  startedAt: ZodNullable<ZodNumber>;
@@ -2520,7 +2526,7 @@ declare const jobTypeSchema: ZodObject<{
2520
2526
  summary: ZodString;
2521
2527
  needs: ZodArray<ZodString>;
2522
2528
  pattern: ZodNullable<ZodString>;
2523
- provider: ZodOptional<ZodObject<{
2529
+ runner: ZodOptional<ZodObject<{
2524
2530
  id: ZodString;
2525
2531
  resource: ZodString;
2526
2532
  }, $strip>>;
@@ -2866,6 +2872,19 @@ declare const orgEventSchema: ZodDiscriminatedUnion<[ZodObject<{
2866
2872
  stepProgress: ZodOptional<ZodNumber>;
2867
2873
  eta: ZodOptional<ZodNumber>;
2868
2874
  metrics: ZodOptional<ZodRecord<ZodString, ZodUnion<readonly [ZodNumber, ZodString]>>>;
2875
+ }, $strip>, ZodObject<{
2876
+ type: ZodLiteral<"job.metrics">;
2877
+ jobId: ZodString;
2878
+ timestamp: ZodNumber;
2879
+ cpuPct: ZodNumber;
2880
+ cpuCores: ZodNumber;
2881
+ allocatedCores: ZodOptional<ZodNumber>;
2882
+ memUsedMB: ZodNumber;
2883
+ memLimitMB: ZodNumber;
2884
+ diskRMBps: ZodOptional<ZodNumber>;
2885
+ diskWMBps: ZodOptional<ZodNumber>;
2886
+ netRxMBps: ZodOptional<ZodNumber>;
2887
+ netTxMBps: ZodOptional<ZodNumber>;
2869
2888
  }, $strip>, ZodObject<{
2870
2889
  type: ZodLiteral<"job.step">;
2871
2890
  jobId: ZodString;
@@ -2902,10 +2921,10 @@ declare const orgEventSchema: ZodDiscriminatedUnion<[ZodObject<{
2902
2921
  }, $strip>], "type">;
2903
2922
  type OrgEvent = output<typeof orgEventSchema>;
2904
2923
  /**
2905
- * Log entry data structure used by dashboard hooks and executor.
2924
+ * Log entry data structure used by dashboard hooks and runner.
2906
2925
  * Import from `@rendobar/shared/events/org-events`.
2907
2926
  *
2908
- * Matches executor's log event format.
2927
+ * Matches runner's log event format.
2909
2928
  */
2910
2929
  interface LogEntryData {
2911
2930
  timestamp: number;
@@ -2949,7 +2968,7 @@ declare function jobData<T>(job: JobResponse): T | null;
2949
2968
  * Per-job execution timing breakdown. Returned by `client.jobs.getTimings(id)`.
2950
2969
  *
2951
2970
  * All values are milliseconds, or `null` if the corresponding stage did not
2952
- * execute (e.g. a sync-completed job has no executor or queue timings).
2971
+ * execute (e.g. a sync-completed job has no runner or queue timings).
2953
2972
  */
2954
2973
  interface JobTimings {
2955
2974
  jobId: string;
@@ -3278,6 +3297,12 @@ interface JobSubscribeOptions {
3278
3297
  onComplete?: (event: Extract<OrgEvent, {
3279
3298
  type: "job.status";
3280
3299
  }>) => void;
3300
+ onMetrics?: (event: Extract<OrgEvent, {
3301
+ type: "job.metrics";
3302
+ }>) => void;
3303
+ onContext?: (event: Extract<OrgEvent, {
3304
+ type: "job.context";
3305
+ }>) => void;
3281
3306
  onError?: (error: Error) => void;
3282
3307
  }
3283
3308
  interface JobSubscription {
package/dist/index.d.mts CHANGED
@@ -2269,6 +2269,7 @@ declare const jobResponseSchema: ZodDiscriminatedUnion<[ZodObject<{
2269
2269
  error: ZodOptional<ZodString>;
2270
2270
  }, $strip>>;
2271
2271
  logsAvailable: ZodBoolean;
2272
+ metricsAvailable: ZodBoolean;
2272
2273
  createdAt: ZodNumber;
2273
2274
  dispatchedAt: ZodNullable<ZodNumber>;
2274
2275
  startedAt: ZodNullable<ZodNumber>;
@@ -2305,6 +2306,7 @@ declare const jobResponseSchema: ZodDiscriminatedUnion<[ZodObject<{
2305
2306
  error: ZodOptional<ZodString>;
2306
2307
  }, $strip>>;
2307
2308
  logsAvailable: ZodBoolean;
2309
+ metricsAvailable: ZodBoolean;
2308
2310
  createdAt: ZodNumber;
2309
2311
  dispatchedAt: ZodNullable<ZodNumber>;
2310
2312
  startedAt: ZodNullable<ZodNumber>;
@@ -2343,6 +2345,7 @@ declare const jobResponseSchema: ZodDiscriminatedUnion<[ZodObject<{
2343
2345
  error: ZodOptional<ZodString>;
2344
2346
  }, $strip>>;
2345
2347
  logsAvailable: ZodBoolean;
2348
+ metricsAvailable: ZodBoolean;
2346
2349
  createdAt: ZodNumber;
2347
2350
  dispatchedAt: ZodNullable<ZodNumber>;
2348
2351
  startedAt: ZodNullable<ZodNumber>;
@@ -2381,6 +2384,7 @@ declare const jobResponseSchema: ZodDiscriminatedUnion<[ZodObject<{
2381
2384
  error: ZodOptional<ZodString>;
2382
2385
  }, $strip>>;
2383
2386
  logsAvailable: ZodBoolean;
2387
+ metricsAvailable: ZodBoolean;
2384
2388
  createdAt: ZodNumber;
2385
2389
  dispatchedAt: ZodNullable<ZodNumber>;
2386
2390
  startedAt: ZodNullable<ZodNumber>;
@@ -2461,6 +2465,7 @@ declare const jobResponseSchema: ZodDiscriminatedUnion<[ZodObject<{
2461
2465
  error: ZodOptional<ZodString>;
2462
2466
  }, $strip>>;
2463
2467
  logsAvailable: ZodBoolean;
2468
+ metricsAvailable: ZodBoolean;
2464
2469
  createdAt: ZodNumber;
2465
2470
  dispatchedAt: ZodNullable<ZodNumber>;
2466
2471
  startedAt: ZodNullable<ZodNumber>;
@@ -2503,6 +2508,7 @@ declare const jobResponseSchema: ZodDiscriminatedUnion<[ZodObject<{
2503
2508
  error: ZodOptional<ZodString>;
2504
2509
  }, $strip>>;
2505
2510
  logsAvailable: ZodBoolean;
2511
+ metricsAvailable: ZodBoolean;
2506
2512
  createdAt: ZodNumber;
2507
2513
  dispatchedAt: ZodNullable<ZodNumber>;
2508
2514
  startedAt: ZodNullable<ZodNumber>;
@@ -2520,7 +2526,7 @@ declare const jobTypeSchema: ZodObject<{
2520
2526
  summary: ZodString;
2521
2527
  needs: ZodArray<ZodString>;
2522
2528
  pattern: ZodNullable<ZodString>;
2523
- provider: ZodOptional<ZodObject<{
2529
+ runner: ZodOptional<ZodObject<{
2524
2530
  id: ZodString;
2525
2531
  resource: ZodString;
2526
2532
  }, $strip>>;
@@ -2866,6 +2872,19 @@ declare const orgEventSchema: ZodDiscriminatedUnion<[ZodObject<{
2866
2872
  stepProgress: ZodOptional<ZodNumber>;
2867
2873
  eta: ZodOptional<ZodNumber>;
2868
2874
  metrics: ZodOptional<ZodRecord<ZodString, ZodUnion<readonly [ZodNumber, ZodString]>>>;
2875
+ }, $strip>, ZodObject<{
2876
+ type: ZodLiteral<"job.metrics">;
2877
+ jobId: ZodString;
2878
+ timestamp: ZodNumber;
2879
+ cpuPct: ZodNumber;
2880
+ cpuCores: ZodNumber;
2881
+ allocatedCores: ZodOptional<ZodNumber>;
2882
+ memUsedMB: ZodNumber;
2883
+ memLimitMB: ZodNumber;
2884
+ diskRMBps: ZodOptional<ZodNumber>;
2885
+ diskWMBps: ZodOptional<ZodNumber>;
2886
+ netRxMBps: ZodOptional<ZodNumber>;
2887
+ netTxMBps: ZodOptional<ZodNumber>;
2869
2888
  }, $strip>, ZodObject<{
2870
2889
  type: ZodLiteral<"job.step">;
2871
2890
  jobId: ZodString;
@@ -2902,10 +2921,10 @@ declare const orgEventSchema: ZodDiscriminatedUnion<[ZodObject<{
2902
2921
  }, $strip>], "type">;
2903
2922
  type OrgEvent = output<typeof orgEventSchema>;
2904
2923
  /**
2905
- * Log entry data structure used by dashboard hooks and executor.
2924
+ * Log entry data structure used by dashboard hooks and runner.
2906
2925
  * Import from `@rendobar/shared/events/org-events`.
2907
2926
  *
2908
- * Matches executor's log event format.
2927
+ * Matches runner's log event format.
2909
2928
  */
2910
2929
  interface LogEntryData {
2911
2930
  timestamp: number;
@@ -2949,7 +2968,7 @@ declare function jobData<T>(job: JobResponse): T | null;
2949
2968
  * Per-job execution timing breakdown. Returned by `client.jobs.getTimings(id)`.
2950
2969
  *
2951
2970
  * All values are milliseconds, or `null` if the corresponding stage did not
2952
- * execute (e.g. a sync-completed job has no executor or queue timings).
2971
+ * execute (e.g. a sync-completed job has no runner or queue timings).
2953
2972
  */
2954
2973
  interface JobTimings {
2955
2974
  jobId: string;
@@ -3278,6 +3297,12 @@ interface JobSubscribeOptions {
3278
3297
  onComplete?: (event: Extract<OrgEvent, {
3279
3298
  type: "job.status";
3280
3299
  }>) => void;
3300
+ onMetrics?: (event: Extract<OrgEvent, {
3301
+ type: "job.metrics";
3302
+ }>) => void;
3303
+ onContext?: (event: Extract<OrgEvent, {
3304
+ type: "job.context";
3305
+ }>) => void;
3281
3306
  onError?: (error: Error) => void;
3282
3307
  }
3283
3308
  interface JobSubscription {
package/dist/index.mjs CHANGED
@@ -306,22 +306,53 @@ function toBlob(file) {
306
306
  return file instanceof Blob ? file : new Blob([file]);
307
307
  }
308
308
  /**
309
+ * Per-attempt upload time budget. fetch() exposes no upload progress, so a
310
+ * stalled socket and a slow link are indistinguishable from the outside; a
311
+ * hard per-attempt budget is the only portable stall cap. The ladder escalates
312
+ * (healthy 500 KiB/s assumption first, then 100, then 50) so a genuinely slow
313
+ * connection that times out on an early attempt still completes on a later,
314
+ * more patient one — while a hung socket gets cut and retried on a fresh
315
+ * connection instead of waiting on the OS TCP timeout.
316
+ *
317
+ * @internal exported for tests
318
+ */
319
+ function attemptTimeoutMs(sizeBytes, attempt) {
320
+ const GRACE_MS = [
321
+ 1e4,
322
+ 3e4,
323
+ 6e4
324
+ ];
325
+ const RATE_BPS = [
326
+ 500 * 1024,
327
+ 100 * 1024,
328
+ 50 * 1024
329
+ ];
330
+ const i = Math.min(attempt, GRACE_MS.length - 1);
331
+ return GRACE_MS[i] + Math.ceil(sizeBytes / RATE_BPS[i] * 1e3);
332
+ }
333
+ /**
309
334
  * PUT a body to a presigned R2 url with bounded retry.
310
335
  *
311
336
  * Returns the ETag string when the bucket exposes it, or `null` when the
312
- * response header is absent (e.g. bucket CORS not exposing ETag). Only
313
- * network/HTTP failures trigger retries; a missing ETag is not retried because
314
- * it is a deterministic misconfiguration, not a transient error.
337
+ * response header is absent (e.g. bucket CORS not exposing ETag). Network/HTTP
338
+ * failures and per-attempt timeouts trigger retries; a caller abort and a
339
+ * missing ETag do not (the latter is a deterministic misconfiguration, not a
340
+ * transient error).
341
+ *
342
+ * @internal exported for tests
315
343
  */
316
- async function putWithRetry(url, body, signal, attempts = 3) {
344
+ async function putWithRetry(url, body, signal, sizeBytes, attempts = 3, attemptTimeouts) {
317
345
  let lastErr;
318
346
  for (let i = 0; i < attempts; i++) {
319
347
  if (signal?.aborted) throw new Error("Upload aborted");
348
+ const timeoutMs = attemptTimeouts?.[i] ?? attemptTimeoutMs(sizeBytes, i);
349
+ const timeout = AbortSignal.timeout(timeoutMs);
350
+ const attemptSignal = signal ? AbortSignal.any([signal, timeout]) : timeout;
320
351
  try {
321
352
  const res = await fetch(url, {
322
353
  method: "PUT",
323
354
  body,
324
- signal
355
+ signal: attemptSignal
325
356
  });
326
357
  if (!res.ok) throw new Error(`Upload PUT failed: ${res.status}`);
327
358
  return res.headers.get("etag");
@@ -377,7 +408,7 @@ function createUploadsResource(request) {
377
408
  return init.data;
378
409
  }
379
410
  if (init.status === "presigned") {
380
- await putWithRetry(init.upload.url, blob, options.signal);
411
+ await putWithRetry(init.upload.url, blob, options.signal, size);
381
412
  options.onProgress?.({
382
413
  loaded: size,
383
414
  total: size
@@ -398,7 +429,7 @@ function createUploadsResource(request) {
398
429
  const chunk = blob.slice(start, Math.min(start + partSize, size));
399
430
  let etag;
400
431
  try {
401
- etag = await putWithRetry(p.url, chunk, combinedSignal);
432
+ etag = await putWithRetry(p.url, chunk, combinedSignal, chunk.size);
402
433
  } catch (e) {
403
434
  internal.abort();
404
435
  partError = e instanceof Error ? e : new Error(String(e));
@@ -667,6 +698,8 @@ const KNOWN_EVENT_TYPES = new Set([
667
698
  "job.progress",
668
699
  "job.step",
669
700
  "job.log",
701
+ "job.metrics",
702
+ "job.context",
670
703
  "balance.updated",
671
704
  "subscription.updated",
672
705
  "notification",
@@ -755,6 +788,12 @@ function dispatchJobEvent(event, options) {
755
788
  case "job.result":
756
789
  options.onResult?.(event);
757
790
  break;
791
+ case "job.metrics":
792
+ options.onMetrics?.(event);
793
+ break;
794
+ case "job.context":
795
+ options.onContext?.(event);
796
+ break;
758
797
  }
759
798
  }
760
799
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rendobar/sdk",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "type": "module",
5
5
  "description": "TypeScript client for the Rendobar media processing API",
6
6
  "main": "./dist/index.cjs",