@rendobar/sdk 5.1.0 → 5.2.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/LICENSE +21 -0
- package/README.md +155 -223
- package/dist/index.cjs +20 -0
- package/dist/index.d.cts +117 -44
- package/dist/index.d.mts +117 -44
- package/dist/index.mjs +20 -0
- package/package.json +24 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rendobar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,110 +1,84 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://rendobar.com">
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.rendobar.com/assets/brand/logo-mark.svg">
|
|
5
|
+
<img alt="Rendobar" src="https://cdn.rendobar.com/assets/brand/logo-mark-black.svg" width="80">
|
|
6
|
+
</picture>
|
|
7
|
+
</a>
|
|
8
|
+
</p>
|
|
2
9
|
|
|
3
|
-
|
|
10
|
+
<h1 align="center">@rendobar/sdk</h1>
|
|
4
11
|
|
|
5
|
-
|
|
12
|
+
<p align="center">
|
|
13
|
+
<strong>TypeScript client for the Rendobar media processing API.</strong>
|
|
14
|
+
</p>
|
|
6
15
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
16
|
+
<p align="center">
|
|
17
|
+
<a href="https://rendobar.com/docs/sdk">Docs</a> ·
|
|
18
|
+
<a href="https://rendobar.com/docs/quickstart">Quickstart</a> ·
|
|
19
|
+
<a href="https://www.npmjs.com/package/@rendobar/sdk">npm</a> ·
|
|
20
|
+
<a href="https://discord.gg/kAGqjBzx8N">Discord</a>
|
|
21
|
+
</p>
|
|
10
22
|
|
|
11
|
-
|
|
23
|
+
<p align="center">
|
|
24
|
+
<a href="https://www.npmjs.com/package/@rendobar/sdk"><img src="https://img.shields.io/npm/v/@rendobar/sdk?style=flat-square&color=059669&label=npm" alt="npm version"></a>
|
|
25
|
+
<a href="https://www.npmjs.com/package/@rendobar/sdk"><img src="https://img.shields.io/npm/dm/@rendobar/sdk?style=flat-square&color=059669" alt="npm downloads"></a>
|
|
26
|
+
<img src="https://img.shields.io/node/v/@rendobar/sdk?style=flat-square&color=059669" alt="Node version">
|
|
27
|
+
</p>
|
|
12
28
|
|
|
13
|
-
|
|
14
|
-
import { createClient } from "@rendobar/sdk";
|
|
29
|
+
`@rendobar/sdk` is the official TypeScript client for [Rendobar](https://rendobar.com), a serverless media processing API. You submit a job, the SDK polls it to completion, and you get back a hosted URL. It ships ESM and CJS builds with full types, retries 429 and 5xx automatically, auto-paginates, and accepts an `AbortSignal` on every method.
|
|
15
30
|
|
|
16
|
-
|
|
31
|
+
## Requirements
|
|
17
32
|
|
|
18
|
-
|
|
19
|
-
const job = await client.jobs.create({
|
|
20
|
-
type: "watermark.apply",
|
|
21
|
-
inputs: { source: "https://example.com/video.mp4" },
|
|
22
|
-
params: { text: "PREVIEW", position: "center", opacity: 0.3 },
|
|
23
|
-
});
|
|
33
|
+
Node 18 or later. Works in any runtime with `fetch`, including Cloudflare Workers, Deno, and Bun.
|
|
24
34
|
|
|
25
|
-
|
|
26
|
-
import { outputUrl } from "@rendobar/sdk";
|
|
35
|
+
## Install
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
```bash
|
|
38
|
+
npm install @rendobar/sdk
|
|
30
39
|
```
|
|
31
40
|
|
|
32
|
-
##
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
// API key (external users, CLI, scripts)
|
|
36
|
-
const client = createClient({ apiKey: "rb_..." });
|
|
37
|
-
|
|
38
|
-
// Session cookie (internal dashboard)
|
|
39
|
-
const client = createClient({
|
|
40
|
-
baseUrl: "https://api.rendobar.com",
|
|
41
|
-
credentials: "include",
|
|
42
|
-
});
|
|
43
|
-
```
|
|
41
|
+
## Quickstart
|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
Run an FFmpeg command against a remote file and wait for the result. Put input
|
|
44
|
+
URLs directly in `-i` positions; the SDK stages them before execution.
|
|
46
45
|
|
|
47
46
|
```typescript
|
|
48
|
-
|
|
49
|
-
apiKey: "rb_...",
|
|
50
|
-
baseUrl: "https://api.rendobar.com", // default
|
|
51
|
-
timeout: 30_000, // default: 30s
|
|
52
|
-
maxRetries: 2, // default: 2 (retries 429, 5xx)
|
|
53
|
-
orgId: "org_abc", // X-Org-Id header
|
|
54
|
-
debug: false, // log request metadata
|
|
55
|
-
fetch: customFetch, // custom fetch for testing
|
|
56
|
-
});
|
|
57
|
-
```
|
|
47
|
+
import { createClient, outputUrl } from "@rendobar/sdk";
|
|
58
48
|
|
|
59
|
-
|
|
49
|
+
const client = createClient({ apiKey: process.env.RENDOBAR_API_KEY });
|
|
60
50
|
|
|
61
|
-
```typescript
|
|
62
|
-
// Create
|
|
63
51
|
const job = await client.jobs.create({
|
|
64
|
-
type: "
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
52
|
+
type: "ffmpeg",
|
|
53
|
+
params: {
|
|
54
|
+
command:
|
|
55
|
+
"ffmpeg -i https://cdn.example.com/source.mp4 " +
|
|
56
|
+
"-vf scale=1280:720 -c:v libx264 -crf 23 output.mp4",
|
|
57
|
+
},
|
|
68
58
|
});
|
|
69
59
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// List (paginated — works with React Query)
|
|
74
|
-
const page = await client.jobs.list({ status: "complete", limit: 20 });
|
|
75
|
-
// page.data: Job[], page.meta: { total, page, limit }
|
|
60
|
+
const done = await client.jobs.wait(job.id);
|
|
61
|
+
console.log(outputUrl(done));
|
|
62
|
+
```
|
|
76
63
|
|
|
77
|
-
|
|
78
|
-
for await (const job of client.jobs.listAll({ status: "complete" })) {
|
|
79
|
-
console.log(job.id);
|
|
80
|
-
}
|
|
64
|
+
Prefer named inputs? Pass them in `inputs` and reference the keys in the command:
|
|
81
65
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
signal: abortController.signal,
|
|
66
|
+
```typescript
|
|
67
|
+
await client.jobs.create({
|
|
68
|
+
type: "ffmpeg",
|
|
69
|
+
inputs: { source: "https://cdn.example.com/source.mp4" },
|
|
70
|
+
params: { command: "ffmpeg -i source -vf scale=1280:720 output.mp4" },
|
|
88
71
|
});
|
|
89
|
-
|
|
90
|
-
// Cancel
|
|
91
|
-
await client.jobs.cancel("job_abc");
|
|
92
|
-
|
|
93
|
-
// Download output (returns raw Response)
|
|
94
|
-
const response = await client.jobs.download("job_abc");
|
|
95
|
-
const blob = await response.blob();
|
|
96
|
-
|
|
97
|
-
// Execution logs
|
|
98
|
-
const logs = await client.jobs.logs("job_abc");
|
|
99
|
-
|
|
100
|
-
// Available job types
|
|
101
|
-
const types = await client.jobs.types();
|
|
102
72
|
```
|
|
103
73
|
|
|
104
|
-
|
|
74
|
+
`ffmpeg` is one of several job types. Composition, compression to a size budget,
|
|
75
|
+
subtitle burn-in, animated captions, and media inspection each have their own.
|
|
76
|
+
Browse them at [rendobar.com/docs/jobs](https://rendobar.com/docs/jobs), or call
|
|
77
|
+
`client.jobs.types()` to read the live registry.
|
|
78
|
+
|
|
79
|
+
## Reading a result
|
|
105
80
|
|
|
106
|
-
Every job type returns the same output shape
|
|
107
|
-
`output` with four fields:
|
|
81
|
+
Every job type returns the same output shape, so you write this handling once.
|
|
108
82
|
|
|
109
83
|
```typescript
|
|
110
84
|
type Output = {
|
|
@@ -123,219 +97,177 @@ type OutputFile = {
|
|
|
123
97
|
};
|
|
124
98
|
```
|
|
125
99
|
|
|
126
|
-
`output` exists only
|
|
127
|
-
|
|
100
|
+
`output` exists only when `status === "complete"`, so narrow on status before
|
|
101
|
+
reading it.
|
|
128
102
|
|
|
129
103
|
```typescript
|
|
130
104
|
import { outputUrl, jobData } from "@rendobar/sdk";
|
|
131
105
|
|
|
132
106
|
const job = await client.jobs.wait("job_abc");
|
|
133
107
|
|
|
134
|
-
// Headline URL: the single file, or a stream manifest (.m3u8/.mpd).
|
|
135
|
-
//
|
|
108
|
+
// Headline URL: the single file, or a stream manifest (.m3u8/.mpd). Undefined
|
|
109
|
+
// for data-only jobs and for pure file sets with no single headline.
|
|
136
110
|
const url = outputUrl(job);
|
|
137
111
|
|
|
138
|
-
// Or read it yourself after a status check
|
|
139
112
|
if (job.status === "complete") {
|
|
140
|
-
console.log(job.output.file?.url);
|
|
141
|
-
|
|
142
|
-
// Iterate every produced file (frame extraction, HLS segments, sprite sets)
|
|
143
113
|
for (const f of job.output.files) {
|
|
144
114
|
console.log(f.type, f.path, f.size, f.url);
|
|
145
115
|
}
|
|
146
116
|
}
|
|
147
117
|
|
|
148
|
-
// A failed job carries a structured error instead
|
|
149
118
|
if (job.status === "failed") {
|
|
150
119
|
console.log(job.error.code, job.error.message, job.error.detail);
|
|
151
120
|
}
|
|
152
121
|
```
|
|
153
122
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
read `job.output.data` and narrow it yourself:
|
|
123
|
+
Inspection jobs put their answer in `output.data`, which is `unknown` at the
|
|
124
|
+
contract level because its shape depends on the job type. `jobData<T>(job)`
|
|
125
|
+
reads it as your expected type:
|
|
158
126
|
|
|
159
127
|
```typescript
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
type Metadata = { format: string; durationMs: number; width: number; height: number };
|
|
163
|
-
|
|
164
|
-
const job = await client.jobs.wait("job_abc");
|
|
165
|
-
const meta = jobData<Metadata>(job); // Metadata | null
|
|
128
|
+
type Probe = { format: string; durationMs: number; width: number; height: number };
|
|
166
129
|
|
|
167
|
-
|
|
168
|
-
console.log(meta.format, meta.durationMs);
|
|
169
|
-
}
|
|
130
|
+
const probe = jobData<Probe>(await client.jobs.wait("job_abc")); // Probe | null
|
|
170
131
|
```
|
|
171
132
|
|
|
172
|
-
`T` is your claim about the shape
|
|
173
|
-
|
|
174
|
-
type.
|
|
175
|
-
|
|
176
|
-
## Raw FFmpeg
|
|
177
|
-
|
|
178
|
-
Run arbitrary FFmpeg commands in a sandboxed container. Commands are parsed, sanitized (protocol whitelist/blacklist flags blocked), and executed with a clean process environment.
|
|
179
|
-
|
|
180
|
-
Put input URLs directly in `-i` positions — they're extracted automatically and staged via presigned R2 URLs before execution:
|
|
133
|
+
`T` is your claim about the shape, not a guarantee. If the input is untrusted,
|
|
134
|
+
validate `output.data` with Zod rather than asserting.
|
|
181
135
|
|
|
182
|
-
|
|
183
|
-
import { createClient } from "@rendobar/sdk";
|
|
136
|
+
## Errors
|
|
184
137
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const job = await client.jobs.create({
|
|
188
|
-
type: "ffmpeg",
|
|
189
|
-
params: {
|
|
190
|
-
command:
|
|
191
|
-
"ffmpeg -i https://cdn.example.com/source.mp4 " +
|
|
192
|
-
"-vf scale=1280:720 -c:v libx264 -crf 23 output.mp4",
|
|
193
|
-
},
|
|
194
|
-
});
|
|
138
|
+
```typescript
|
|
139
|
+
import { isApiError } from "@rendobar/sdk";
|
|
195
140
|
|
|
196
|
-
|
|
197
|
-
|
|
141
|
+
try {
|
|
142
|
+
await client.jobs.create({ type: "ffmpeg", params: { command } });
|
|
143
|
+
} catch (err) {
|
|
144
|
+
if (isApiError(err)) {
|
|
145
|
+
console.log(err.code); // "INSUFFICIENT_CREDITS"
|
|
146
|
+
console.log(err.statusCode); // 402
|
|
147
|
+
console.log(err.retryAfter); // seconds, on 429s
|
|
148
|
+
}
|
|
149
|
+
}
|
|
198
150
|
```
|
|
199
151
|
|
|
200
|
-
|
|
152
|
+
Codes: `UNAUTHORIZED`, `FORBIDDEN`, `VALIDATION_ERROR`, `INSUFFICIENT_CREDITS`,
|
|
153
|
+
`RATE_LIMITED`, `NOT_FOUND`, `CONFLICT`, `INTERNAL_ERROR`. Full reference at
|
|
154
|
+
[rendobar.com/docs/support/errors](https://rendobar.com/docs/support/errors).
|
|
201
155
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
type: "ffmpeg",
|
|
205
|
-
inputs: { source: "https://cdn.example.com/source.mp4" },
|
|
206
|
-
params: { command: "ffmpeg -i source -vf scale=1280:720 output.mp4" },
|
|
207
|
-
});
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
Need a typed params object? Import `FfmpegParams` — optional fields (`timeout`, `compute`) have server-side defaults. The output format comes from the command's output filename.
|
|
156
|
+
429 and 5xx retry automatically with exponential backoff (500ms, then 1s), twice
|
|
157
|
+
by default, respecting `Retry-After`. 400, 401, 403 and 404 throw immediately.
|
|
211
158
|
|
|
212
|
-
|
|
159
|
+
## The rest of the API
|
|
213
160
|
|
|
214
|
-
|
|
161
|
+
<details>
|
|
162
|
+
<summary><strong>Jobs</strong></summary>
|
|
215
163
|
|
|
216
164
|
```typescript
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
165
|
+
await client.jobs.create({ type, inputs, params, idempotencyKey });
|
|
166
|
+
await client.jobs.get("job_abc");
|
|
167
|
+
await client.jobs.cancel("job_abc");
|
|
168
|
+
await client.jobs.logs("job_abc");
|
|
169
|
+
await client.jobs.getTimings("job_abc");
|
|
170
|
+
await client.jobs.types(); // live job registry
|
|
221
171
|
|
|
222
|
-
|
|
172
|
+
const page = await client.jobs.list({ status: "complete", limit: 20 });
|
|
173
|
+
// page.data: Job[], page.meta: { total, page, limit }
|
|
223
174
|
|
|
224
|
-
|
|
225
|
-
// Upload a file, get a URL to use as job input
|
|
226
|
-
const { downloadUrl } = await client.uploads.upload(file, { filename: "input.mp4" });
|
|
175
|
+
for await (const job of client.jobs.listAll({ status: "complete" })) { /* ... */ }
|
|
227
176
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
177
|
+
await client.jobs.wait("job_abc", {
|
|
178
|
+
timeout: 300_000,
|
|
179
|
+
interval: 2_000,
|
|
180
|
+
onProgress: (job) => console.log(job.status),
|
|
181
|
+
signal: controller.signal,
|
|
232
182
|
});
|
|
183
|
+
|
|
184
|
+
const response = await client.jobs.download("job_abc"); // raw Response
|
|
233
185
|
```
|
|
186
|
+
</details>
|
|
234
187
|
|
|
235
|
-
|
|
188
|
+
<details>
|
|
189
|
+
<summary><strong>Uploads, batches, billing</strong></summary>
|
|
236
190
|
|
|
237
191
|
```typescript
|
|
238
|
-
const
|
|
192
|
+
const { downloadUrl } = await client.uploads.upload(file, { filename: "input.mp4" });
|
|
193
|
+
|
|
194
|
+
await client.batches.create({
|
|
239
195
|
jobs: [
|
|
240
|
-
{ type: "
|
|
241
|
-
{ type: "
|
|
196
|
+
{ type: "ffmpeg", inputs: { source: url1 }, params: { command } },
|
|
197
|
+
{ type: "ffmpeg", inputs: { source: url2 }, params: { command } },
|
|
242
198
|
],
|
|
243
199
|
});
|
|
200
|
+
|
|
201
|
+
await client.billing.state();
|
|
202
|
+
await client.billing.usage({ start: "2026-01-01", end: "2026-03-29" });
|
|
203
|
+
await client.billing.transactions({ page: 1, limit: 50 });
|
|
244
204
|
```
|
|
205
|
+
</details>
|
|
245
206
|
|
|
246
|
-
|
|
207
|
+
<details>
|
|
208
|
+
<summary><strong>Webhooks</strong></summary>
|
|
247
209
|
|
|
248
210
|
```typescript
|
|
249
|
-
|
|
250
|
-
const endpoint = await client.webhooks.create({
|
|
211
|
+
await client.webhooks.create({
|
|
251
212
|
name: "My Webhook",
|
|
252
213
|
url: "https://example.com/webhook",
|
|
253
214
|
subscribedEvents: ["job.completed", "job.failed"],
|
|
254
215
|
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Verify a delivery with the **raw body**, not parsed JSON. `verifyWebhook` reads
|
|
219
|
+
the signature and timestamp headers, rebuilds the signed string, checks the
|
|
220
|
+
HMAC, rejects stale deliveries, and handles secret rotation.
|
|
255
221
|
|
|
256
|
-
|
|
257
|
-
// parsed JSON) and the request headers. verifyWebhook reads the signature and
|
|
258
|
-
// timestamp headers, rebuilds the signed string, checks the HMAC, rejects stale
|
|
259
|
-
// deliveries (replay protection), and handles secret rotation.
|
|
222
|
+
```typescript
|
|
260
223
|
import { verifyWebhook } from "@rendobar/sdk/webhooks";
|
|
261
224
|
|
|
262
|
-
|
|
263
|
-
|
|
225
|
+
if (!(await verifyWebhook(rawBody, request.headers, signingSecret))) {
|
|
226
|
+
throw new Error("invalid signature");
|
|
227
|
+
}
|
|
264
228
|
```
|
|
265
229
|
|
|
266
|
-
|
|
230
|
+
See the [webhooks guide](https://rendobar.com/docs/guides/webhooks).
|
|
231
|
+
</details>
|
|
232
|
+
|
|
233
|
+
<details>
|
|
234
|
+
<summary><strong>Realtime and client configuration</strong></summary>
|
|
235
|
+
|
|
236
|
+
Realtime needs session cookie auth. On an API key, use `jobs.wait()` instead.
|
|
267
237
|
|
|
268
238
|
```typescript
|
|
269
|
-
// Org-wide event stream
|
|
270
239
|
const connection = client.realtime.connect({
|
|
271
|
-
onEvent: (
|
|
272
|
-
onLive: () => console.log("
|
|
273
|
-
onResync: () => console.log("
|
|
240
|
+
onEvent: (e) => console.log(e.type, e),
|
|
241
|
+
onLive: () => console.log("replay complete, now live"),
|
|
242
|
+
onResync: () => console.log("buffer overflow, refresh data"),
|
|
274
243
|
});
|
|
275
244
|
|
|
276
|
-
// Per-job subscription
|
|
277
245
|
const sub = client.realtime.subscribeJob("job_abc", {
|
|
278
246
|
onProgress: (e) => console.log(`${e.progress}%`),
|
|
279
|
-
|
|
280
|
-
onComplete: (e) => console.log("Done!", e.status),
|
|
247
|
+
onComplete: (e) => console.log("done", e.status),
|
|
281
248
|
});
|
|
282
249
|
|
|
283
|
-
// Cleanup
|
|
284
250
|
connection.disconnect();
|
|
285
251
|
sub.unsubscribe();
|
|
286
252
|
```
|
|
287
253
|
|
|
288
|
-
> **Note:** Realtime requires session cookie auth. API key users should use `jobs.wait()` for polling.
|
|
289
|
-
|
|
290
|
-
## Error Handling
|
|
291
|
-
|
|
292
254
|
```typescript
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
console.log(err.retryAfter); // seconds (for 429s)
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
Error codes: `UNAUTHORIZED`, `FORBIDDEN`, `VALIDATION_ERROR`, `INSUFFICIENT_CREDITS`, `RATE_LIMITED`, `NOT_FOUND`, `CONFLICT`, `INTERNAL_ERROR`.
|
|
308
|
-
|
|
309
|
-
## Retries
|
|
310
|
-
|
|
311
|
-
The SDK automatically retries:
|
|
312
|
-
- **429** (rate limited) — respects `Retry-After` header
|
|
313
|
-
- **500, 502, 503, 504** — server errors
|
|
314
|
-
|
|
315
|
-
Retries use exponential backoff (500ms, 1s). Max 2 retries by default. Non-retryable errors (400, 401, 403, 404) throw immediately.
|
|
316
|
-
|
|
317
|
-
## AbortSignal
|
|
318
|
-
|
|
319
|
-
Every method accepts an optional `signal` for cancellation:
|
|
320
|
-
|
|
321
|
-
```typescript
|
|
322
|
-
const controller = new AbortController();
|
|
323
|
-
const job = await client.jobs.get("job_abc", { signal: controller.signal });
|
|
255
|
+
const client = createClient({
|
|
256
|
+
apiKey: "rb_...",
|
|
257
|
+
baseUrl: "https://api.rendobar.com", // default
|
|
258
|
+
timeout: 30_000, // default 30s
|
|
259
|
+
maxRetries: 2, // default 2
|
|
260
|
+
orgId: "org_abc", // X-Org-Id header
|
|
261
|
+
debug: false, // log request metadata
|
|
262
|
+
fetch: customFetch, // inject a fetch, for tests
|
|
263
|
+
});
|
|
324
264
|
|
|
325
|
-
//
|
|
326
|
-
|
|
265
|
+
// Browser session cookie instead of a key
|
|
266
|
+
createClient({ baseUrl: "https://api.rendobar.com", credentials: "include" });
|
|
327
267
|
```
|
|
268
|
+
</details>
|
|
328
269
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
```typescript
|
|
334
|
-
import type {
|
|
335
|
-
Job, JobStep, Output, OutputFile, FileType, JobError, Cost, JobType, JobCreatedResponse,
|
|
336
|
-
BillingState, Transaction, UsageSummary,
|
|
337
|
-
WebhookEndpoint, WebhookDelivery,
|
|
338
|
-
Organization, ApiKey, PaginationMeta,
|
|
339
|
-
BillingInvoice, PaymentMethod, LogEntryData,
|
|
340
|
-
} from "@rendobar/sdk";
|
|
341
|
-
```
|
|
270
|
+
Response types are all exported: `Job`, `JobStep`, `Output`, `OutputFile`,
|
|
271
|
+
`FileType`, `JobError`, `Cost`, `JobType`, `BillingState`, `Transaction`,
|
|
272
|
+
`UsageSummary`, `WebhookEndpoint`, `WebhookDelivery`, `Organization`, `ApiKey`,
|
|
273
|
+
and `PaginationMeta`.
|
package/dist/index.cjs
CHANGED
|
@@ -209,6 +209,25 @@ function createJobsResource(request) {
|
|
|
209
209
|
signal: options?.signal
|
|
210
210
|
});
|
|
211
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Create a job and wait for it to reach a terminal state, returning the
|
|
214
|
+
* finished job. A convenience wrapper over `create()` + `wait()` for
|
|
215
|
+
* callers who just want the result without juggling two calls — works for
|
|
216
|
+
* any job type, not just a specific product.
|
|
217
|
+
*
|
|
218
|
+
* Unlike `wait()` (which defaults `throwOnFailure` to `false` to preserve
|
|
219
|
+
* its existing behavior), `run()` defaults `throwOnFailure` to `true`: a
|
|
220
|
+
* job that ends "failed" or "cancelled" throws `JobFailedError` unless the
|
|
221
|
+
* caller explicitly opts out with `throwOnFailure: false`.
|
|
222
|
+
*/
|
|
223
|
+
async function run(params, options = {}) {
|
|
224
|
+
const { signal, throwOnFailure = true, ...waitOptions } = options;
|
|
225
|
+
return wait((await create(params, { signal })).id, {
|
|
226
|
+
...waitOptions,
|
|
227
|
+
signal,
|
|
228
|
+
throwOnFailure
|
|
229
|
+
});
|
|
230
|
+
}
|
|
212
231
|
async function get(id, options) {
|
|
213
232
|
return request(`/jobs/${id}`, { signal: options?.signal });
|
|
214
233
|
}
|
|
@@ -265,6 +284,7 @@ function createJobsResource(request) {
|
|
|
265
284
|
list,
|
|
266
285
|
listAll,
|
|
267
286
|
wait,
|
|
287
|
+
run,
|
|
268
288
|
cancel,
|
|
269
289
|
download,
|
|
270
290
|
logs,
|