@xformmedia/sdk 0.5.3
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/README.md +501 -0
- package/dist/admin-client.d.ts +115 -0
- package/dist/admin-client.d.ts.map +1 -0
- package/dist/admin-client.js +187 -0
- package/dist/client.d.ts +100 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +194 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +10 -0
- package/dist/event-stream.d.ts +7 -0
- package/dist/event-stream.d.ts.map +1 -0
- package/dist/event-stream.js +86 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/types.d.ts +265 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/package.json +22 -0
package/README.md
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
# @xformmedia/sdk
|
|
2
|
+
|
|
3
|
+
Official SDK for the [xform.media](https://xform.media) service.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @xformmedia/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
The SDK ships two clients:
|
|
16
|
+
|
|
17
|
+
| Client | Key type | Use for |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `XformClient` | Source key | Video ingest, upload, and playback operations on a single source |
|
|
20
|
+
| `XformAdminClient` | Organization key | Programmatic source management + all video operations (enterprise / server-to-server) |
|
|
21
|
+
|
|
22
|
+
API keys are created in the xform admin:
|
|
23
|
+
- **Source keys** — Source detail page → API Keys tab
|
|
24
|
+
- **Organization keys** — Org settings → API Keys
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## `XformClient` — Source-scoped client
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { XformClient } from '@xformmedia/sdk'
|
|
32
|
+
|
|
33
|
+
const client = new XformClient({
|
|
34
|
+
sourceId: 'your-source-id',
|
|
35
|
+
organizationId: 'your-org-id',
|
|
36
|
+
apiKey: 'xfm_your_source_key',
|
|
37
|
+
baseUrl: 'https://admin.xform.media',
|
|
38
|
+
})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### `client.ingest(options)`
|
|
42
|
+
|
|
43
|
+
Trigger transcoding for a video already in your connected S3/R2 bucket. Call this after your own upload completes.
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
const result = await client.ingest({
|
|
47
|
+
key: 'recordings/standup.mp4',
|
|
48
|
+
callbackUrl: 'https://your-app.com/webhooks/xform', // optional
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
console.log(result.videoKey) // "recordings-standup"
|
|
52
|
+
console.log(result.transcodeStatus) // "pending"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### `client.createUploadUrl(options)`
|
|
56
|
+
|
|
57
|
+
Get a presigned URL to upload a file directly to xform's storage, bypassing your own servers.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
const { uploadUrl, key } = await client.createUploadUrl({
|
|
61
|
+
filename: 'recording.mp4',
|
|
62
|
+
contentType: 'video/mp4',
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
// Upload directly from browser or server
|
|
66
|
+
await fetch(uploadUrl, {
|
|
67
|
+
method: 'PUT',
|
|
68
|
+
body: fileBlob,
|
|
69
|
+
headers: { 'Content-Type': 'video/mp4' },
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// Then trigger transcoding
|
|
73
|
+
const result = await client.ingest({ key })
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### `client.events()`
|
|
77
|
+
|
|
78
|
+
Open an SSE connection to receive real-time video status updates. Events are emitted as videos transition through: `pending` → `processing` → `ready` / `failed`.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const stream = client.events()
|
|
82
|
+
|
|
83
|
+
stream.on('video.ready', (video) => {
|
|
84
|
+
console.log(`${video.videoKey} is ready (${video.duration}s)`)
|
|
85
|
+
console.log(`Stream: https://${subdomain}.xform.media${video.streamUrl}`)
|
|
86
|
+
console.log(`Download: https://${subdomain}.xform.media${video.downloadUrl}`)
|
|
87
|
+
console.log(`Thumbnail: https://${subdomain}.xform.media${video.thumbnailUrl}`)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
stream.on('video.failed', (video) => {
|
|
91
|
+
console.error(`${video.videoKey} failed: ${video.errorMessage}`)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
// Listen to all events
|
|
95
|
+
stream.on('*', (video) => {
|
|
96
|
+
console.log(video.videoKey, video.transcodeStatus)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
// Close when done
|
|
100
|
+
stream.close()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
This is an alternative to polling `client.videos.get()` or providing a `callbackUrl`. The stream uses Server-Sent Events over a persistent HTTP connection — no webhook endpoint needed.
|
|
104
|
+
|
|
105
|
+
**Ingest + wait for ready:**
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
const stream = client.events()
|
|
109
|
+
|
|
110
|
+
stream.on('video.ready', (video) => {
|
|
111
|
+
console.log(`Stream: https://${subdomain}.xform.media${video.streamUrl}`)
|
|
112
|
+
console.log(`Download: https://${subdomain}.xform.media${video.downloadUrl}`)
|
|
113
|
+
stream.close()
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
stream.on('video.failed', (video) => {
|
|
117
|
+
console.error(video.errorMessage)
|
|
118
|
+
stream.close()
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
await client.ingest({ key: 'recordings/standup.mp4' })
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### `client.videos.get(videoKey)`
|
|
125
|
+
|
|
126
|
+
Get the current status and metadata of a video.
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
const video = await client.videos.get('recordings-standup')
|
|
130
|
+
|
|
131
|
+
if (video.transcodeStatus === 'ready') {
|
|
132
|
+
const subdomain = 'acme' // your source subdomain
|
|
133
|
+
console.log(`Stream: https://${subdomain}.xform.media/v/${video.videoKey}.m3u8`)
|
|
134
|
+
console.log(`Download: https://${subdomain}.xform.media/d/${video.videoKey}`)
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### `client.videos.delete(videoId)`
|
|
139
|
+
|
|
140
|
+
Delete a video and all its renditions by database ID. This action is irreversible.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const result = await client.ingest({ key: 'recordings/standup.mp4' })
|
|
144
|
+
|
|
145
|
+
// Later...
|
|
146
|
+
await client.videos.delete(result.id)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### `client.videos.list(options?)`
|
|
150
|
+
|
|
151
|
+
List all videos for this source, newest first.
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
const { data } = await client.videos.list({ limit: 20, skip: 0 })
|
|
155
|
+
|
|
156
|
+
for (const video of data) {
|
|
157
|
+
console.log(video.videoKey, video.transcodeStatus, video.duration)
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### `client.usage()`
|
|
162
|
+
|
|
163
|
+
Get storage and bandwidth usage for this source.
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
const usage = await client.usage()
|
|
167
|
+
console.log(usage.totalVideos)
|
|
168
|
+
console.log(usage.totalDurationSeconds)
|
|
169
|
+
console.log(usage.totalFileSizeBytes)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### `client.updatePlan(input)`
|
|
173
|
+
|
|
174
|
+
Change the source's plan, billing cycle, or add-ons. All fields are optional — omit any to leave it unchanged.
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
// Upgrade to Pro (prorated immediately)
|
|
178
|
+
await client.updatePlan({ name: 'Pro', billingCycle: 'monthly' })
|
|
179
|
+
|
|
180
|
+
// Switch to annual billing
|
|
181
|
+
await client.updatePlan({ billingCycle: 'annual' })
|
|
182
|
+
|
|
183
|
+
// Enable Video add-on on Starter plan
|
|
184
|
+
await client.updatePlan({ addOns: { video: true } })
|
|
185
|
+
|
|
186
|
+
// Downgrade to Starter with Smart Crop (no credit issued, takes effect next cycle)
|
|
187
|
+
const billing = await client.updatePlan({
|
|
188
|
+
name: 'Starter',
|
|
189
|
+
billingCycle: 'monthly',
|
|
190
|
+
addOns: { smartCrop: true },
|
|
191
|
+
})
|
|
192
|
+
console.log(billing.status) // "active"
|
|
193
|
+
console.log(billing.plan.name) // "Starter"
|
|
194
|
+
console.log(billing.plan.billingCycle) // "monthly"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Behaviour:**
|
|
198
|
+
- Upgrades are prorated and charged immediately
|
|
199
|
+
- Downgrades take effect at the next billing cycle — no credit is issued
|
|
200
|
+
- Downgrading Pro → Starter automatically removes any attached custom domain
|
|
201
|
+
- Upgrading Starter → Pro automatically removes the Smart Crop add-on (Pro includes it)
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## `XformAdminClient` — Organization-scoped client
|
|
206
|
+
|
|
207
|
+
For enterprise integrations that need to create and manage sources programmatically. Requires an organization-level API key.
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import { createXformAdminClient } from '@xformmedia/sdk'
|
|
211
|
+
|
|
212
|
+
const admin = createXformAdminClient({
|
|
213
|
+
organizationId: 'your-org-id',
|
|
214
|
+
apiKey: 'xfm_your_org_key',
|
|
215
|
+
baseUrl: 'https://admin.xform.media',
|
|
216
|
+
})
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### `admin.sources.checkSubdomain(subdomain)`
|
|
220
|
+
|
|
221
|
+
Check if a subdomain is available before creating a source.
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
const { available } = await admin.sources.checkSubdomain('my-app')
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### `admin.sources.create(input)`
|
|
228
|
+
|
|
229
|
+
Create a new source. This provisions a subdomain, Cloudflare DNS record, and Stripe subscription item.
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
const source = await admin.sources.create({
|
|
233
|
+
name: 'My App Media',
|
|
234
|
+
subdomain: 'my-app',
|
|
235
|
+
provider: 'cloudflare-r2',
|
|
236
|
+
credentials: {
|
|
237
|
+
bucket: 'my-bucket',
|
|
238
|
+
endpoint: 'https://accountid.r2.cloudflarestorage.com',
|
|
239
|
+
accessKeyId: 'your-r2-key-id',
|
|
240
|
+
secretAccessKey: 'your-r2-secret',
|
|
241
|
+
},
|
|
242
|
+
plan: {
|
|
243
|
+
name: 'Starter',
|
|
244
|
+
billingCycle: 'monthly',
|
|
245
|
+
},
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
console.log(source.id) // "64a1b2c3..."
|
|
249
|
+
console.log(source.subdomain) // "my-app"
|
|
250
|
+
// Media now served at: https://my-app.xform.media/
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Supported providers: `'aws'`, `'cloudflare-r2'`, `'public-web'`
|
|
254
|
+
|
|
255
|
+
### `admin.sources.list()`
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
const sources = await admin.sources.list()
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### `admin.sources.get(sourceId)`
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
const source = await admin.sources.get('source-id')
|
|
265
|
+
console.log(source.subdomain, source.status)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### `admin.sources.update(sourceId, input)`
|
|
269
|
+
|
|
270
|
+
Update a source's name or credentials.
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
await admin.sources.update('source-id', {
|
|
274
|
+
name: 'New Name',
|
|
275
|
+
credentials: {
|
|
276
|
+
accessKeyId: 'rotated-key-id',
|
|
277
|
+
secretAccessKey: 'rotated-secret',
|
|
278
|
+
},
|
|
279
|
+
})
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### `admin.source(sourceId)` — per-source operations
|
|
283
|
+
|
|
284
|
+
All video operations, event streaming, and plan management are available via `admin.source(id)`:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
const src = admin.source(sourceId)
|
|
288
|
+
|
|
289
|
+
// Real-time events
|
|
290
|
+
const stream = src.events()
|
|
291
|
+
stream.on('video.ready', (video) => console.log(video.videoKey, 'ready'))
|
|
292
|
+
|
|
293
|
+
// Plan management
|
|
294
|
+
await src.updatePlan({ name: 'Pro' })
|
|
295
|
+
await src.updatePlan({ addOns: { video: true } })
|
|
296
|
+
|
|
297
|
+
// Video ingest
|
|
298
|
+
const { uploadUrl, key } = await src.createUploadUrl({ filename: 'clip.mp4', contentType: 'video/mp4' })
|
|
299
|
+
await fetch(uploadUrl, { method: 'PUT', body: blob, headers: { 'Content-Type': 'video/mp4' } })
|
|
300
|
+
const job = await src.ingest({ key })
|
|
301
|
+
|
|
302
|
+
// Video status
|
|
303
|
+
const video = await src.videos.get(job.videoKey)
|
|
304
|
+
const { data } = await src.videos.list({ limit: 50 })
|
|
305
|
+
|
|
306
|
+
// Delete a video by ID
|
|
307
|
+
await src.videos.delete(job.id)
|
|
308
|
+
|
|
309
|
+
// Usage
|
|
310
|
+
const usage = await src.usage()
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### `admin.source(sourceId).webhook` — Webhook configuration
|
|
314
|
+
|
|
315
|
+
Configure a webhook to receive HTTP POST notifications when video events occur. All payloads are signed with HMAC-SHA256 for verification.
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
const src = admin.source(sourceId)
|
|
319
|
+
|
|
320
|
+
// Set a webhook (subscribes to all events by default)
|
|
321
|
+
await src.webhook.set({
|
|
322
|
+
url: 'https://your-app.com/webhooks/xform',
|
|
323
|
+
secret: 'whsec_your_signing_secret',
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
// Or subscribe to specific events only
|
|
327
|
+
await src.webhook.set({
|
|
328
|
+
url: 'https://your-app.com/webhooks/xform',
|
|
329
|
+
secret: 'whsec_your_signing_secret',
|
|
330
|
+
events: ['video.ready', 'video.failed'],
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
// Get current config (secret is masked)
|
|
334
|
+
const config = await src.webhook.get()
|
|
335
|
+
// { url: "https://...", secret: "whse••••••••", events: [...] }
|
|
336
|
+
|
|
337
|
+
// Remove webhook
|
|
338
|
+
await src.webhook.delete()
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Webhook events:**
|
|
342
|
+
|
|
343
|
+
| Event | Trigger | Payload `data` fields |
|
|
344
|
+
|---|---|---|
|
|
345
|
+
| `video.ready` | Transcode completed | `streamUrl`, `downloadUrl`, `duration`, `thumbnailUrl`, `width`, `height` |
|
|
346
|
+
| `video.failed` | Transcode failed (after retries) | `errorMessage` |
|
|
347
|
+
| `video.renamed` | Video display name changed | `displayName` |
|
|
348
|
+
|
|
349
|
+
**Payload format:**
|
|
350
|
+
|
|
351
|
+
```json
|
|
352
|
+
{
|
|
353
|
+
"event": "video.ready",
|
|
354
|
+
"sourceId": "64a1b2c3...",
|
|
355
|
+
"videoKey": "recordings-standup",
|
|
356
|
+
"timestamp": "2026-03-06T12:00:00.000Z",
|
|
357
|
+
"data": {
|
|
358
|
+
"streamUrl": "/v/recordings-standup.m3u8",
|
|
359
|
+
"downloadUrl": "/d/recordings-standup",
|
|
360
|
+
"duration": 42.5,
|
|
361
|
+
"thumbnailUrl": "/v/recordings-standup/thumbnail.jpg",
|
|
362
|
+
"width": 1920,
|
|
363
|
+
"height": 1080
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
**Verifying signatures:**
|
|
369
|
+
|
|
370
|
+
```ts
|
|
371
|
+
import { createHmac } from 'crypto'
|
|
372
|
+
|
|
373
|
+
function verifyWebhook(body: string, signature: string, secret: string): boolean {
|
|
374
|
+
const expected = createHmac('sha256', secret).update(body).digest('hex')
|
|
375
|
+
return signature === expected
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// In your webhook handler:
|
|
379
|
+
app.post('/webhooks/xform', (req, res) => {
|
|
380
|
+
const signature = req.headers['x-webhook-signature']
|
|
381
|
+
const event = req.headers['x-webhook-event']
|
|
382
|
+
|
|
383
|
+
if (!verifyWebhook(JSON.stringify(req.body), signature, 'whsec_your_secret')) {
|
|
384
|
+
return res.status(401).send('Invalid signature')
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Process event...
|
|
388
|
+
res.sendStatus(200)
|
|
389
|
+
})
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
Webhooks are retried up to 3 times with exponential backoff if your endpoint returns a non-2xx status.
|
|
393
|
+
|
|
394
|
+
### Full end-to-end example
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
// 1. Create source
|
|
398
|
+
const { available } = await admin.sources.checkSubdomain('pixelfilm-user-123')
|
|
399
|
+
if (!available) throw new Error('Subdomain taken')
|
|
400
|
+
|
|
401
|
+
const source = await admin.sources.create({
|
|
402
|
+
name: 'User 123',
|
|
403
|
+
subdomain: 'pixelfilm-user-123',
|
|
404
|
+
provider: 'cloudflare-r2',
|
|
405
|
+
credentials: { bucket, endpoint, accessKeyId, secretAccessKey },
|
|
406
|
+
plan: { name: 'Starter', billingCycle: 'monthly' },
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
// 2. Upload and ingest with real-time status
|
|
410
|
+
const src = admin.source(source.id)
|
|
411
|
+
const stream = src.events()
|
|
412
|
+
|
|
413
|
+
stream.on('video.ready', (video) => {
|
|
414
|
+
console.log(`https://${source.subdomain}.xform.media/v/${video.videoKey}.m3u8`)
|
|
415
|
+
stream.close()
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
stream.on('video.failed', (video) => {
|
|
419
|
+
console.error(video.errorMessage)
|
|
420
|
+
stream.close()
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
const { uploadUrl, key } = await src.createUploadUrl({
|
|
424
|
+
filename: 'recording.mp4',
|
|
425
|
+
contentType: 'video/mp4',
|
|
426
|
+
})
|
|
427
|
+
await fetch(uploadUrl, { method: 'PUT', body: blob, headers: { 'Content-Type': 'video/mp4' } })
|
|
428
|
+
await src.ingest({ key })
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## Error Handling
|
|
434
|
+
|
|
435
|
+
All methods throw `XformError` on API failures.
|
|
436
|
+
|
|
437
|
+
```ts
|
|
438
|
+
import { XformError } from '@xformmedia/sdk'
|
|
439
|
+
|
|
440
|
+
try {
|
|
441
|
+
await admin.sources.create({ ... })
|
|
442
|
+
} catch (err) {
|
|
443
|
+
if (err instanceof XformError) {
|
|
444
|
+
console.error(err.statusCode, err.message)
|
|
445
|
+
console.error(err.body) // full response body
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
| Status | Meaning |
|
|
451
|
+
|---|---|
|
|
452
|
+
| `400` | Invalid input — missing fields, bad subdomain format, etc. |
|
|
453
|
+
| `401` | Invalid or expired API key |
|
|
454
|
+
| `403` | Key scope mismatch — wrong source, or source key used on org endpoint |
|
|
455
|
+
| `404` | Source or video not found |
|
|
456
|
+
| `409` | Subdomain already taken |
|
|
457
|
+
| `500` | Server error — retry with backoff |
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## TypeScript
|
|
462
|
+
|
|
463
|
+
All types are exported:
|
|
464
|
+
|
|
465
|
+
```ts
|
|
466
|
+
import type {
|
|
467
|
+
// Clients
|
|
468
|
+
XformClientOptions,
|
|
469
|
+
XformAdminClientOptions,
|
|
470
|
+
// Sources
|
|
471
|
+
Source,
|
|
472
|
+
SourceBilling,
|
|
473
|
+
SourcePlan,
|
|
474
|
+
SourceCredentials,
|
|
475
|
+
SourceProvider,
|
|
476
|
+
SourceStatus,
|
|
477
|
+
CreateSourceInput,
|
|
478
|
+
UpdateSourceInput,
|
|
479
|
+
UpdatePlanInput,
|
|
480
|
+
BillingCycle,
|
|
481
|
+
BillingStatus,
|
|
482
|
+
PlanName,
|
|
483
|
+
CheckSubdomainResult,
|
|
484
|
+
// Webhooks
|
|
485
|
+
WebhookEventType,
|
|
486
|
+
WebhookConfig,
|
|
487
|
+
SetWebhookInput,
|
|
488
|
+
// Videos
|
|
489
|
+
Video,
|
|
490
|
+
VideoEvent,
|
|
491
|
+
VideoEventType,
|
|
492
|
+
VideoEventStream,
|
|
493
|
+
VideoRendition,
|
|
494
|
+
VideoTranscodeStatus,
|
|
495
|
+
VideoRenditionQuality,
|
|
496
|
+
IngestResult,
|
|
497
|
+
UploadUrlResult,
|
|
498
|
+
ListVideosResult,
|
|
499
|
+
VideoUsageResult,
|
|
500
|
+
} from '@xformmedia/sdk'
|
|
501
|
+
```
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { XformAdminClientOptions, Source, SourceBilling, VideoEventStream, CreateSourceInput, UpdateSourceInput, UpdatePlanInput, CheckSubdomainResult, Video, IngestResult, UploadUrlResult, ListVideosResult, VideoUsageResult, WebhookConfig, SetWebhookInput } from './types.js';
|
|
2
|
+
export declare class XformAdminClient {
|
|
3
|
+
private readonly organizationId;
|
|
4
|
+
private readonly apiKey;
|
|
5
|
+
private readonly baseUrl;
|
|
6
|
+
constructor(options: XformAdminClientOptions);
|
|
7
|
+
private get headers();
|
|
8
|
+
private request;
|
|
9
|
+
/** Source management operations (requires org-scoped API key) */
|
|
10
|
+
readonly sources: {
|
|
11
|
+
/**
|
|
12
|
+
* List all sources for this organization.
|
|
13
|
+
*/
|
|
14
|
+
list: () => Promise<Source[]>;
|
|
15
|
+
/**
|
|
16
|
+
* Get a specific source by ID.
|
|
17
|
+
*/
|
|
18
|
+
get: (sourceId: string) => Promise<Source>;
|
|
19
|
+
/**
|
|
20
|
+
* Create a new source. This provisions a subdomain, Cloudflare DNS record,
|
|
21
|
+
* and Stripe subscription item.
|
|
22
|
+
*/
|
|
23
|
+
create: (input: CreateSourceInput) => Promise<Source>;
|
|
24
|
+
/**
|
|
25
|
+
* Update an existing source's settings.
|
|
26
|
+
*/
|
|
27
|
+
update: (sourceId: string, input: UpdateSourceInput) => Promise<Source>;
|
|
28
|
+
/**
|
|
29
|
+
* Check if a subdomain is available before creating a source.
|
|
30
|
+
*/
|
|
31
|
+
checkSubdomain: (subdomain: string) => Promise<CheckSubdomainResult>;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Returns a set of video operations scoped to a specific source.
|
|
35
|
+
* Use this after creating or looking up a source.
|
|
36
|
+
*/
|
|
37
|
+
source(sourceId: string): {
|
|
38
|
+
/**
|
|
39
|
+
* Open an SSE connection to receive real-time video status updates for this source.
|
|
40
|
+
*/
|
|
41
|
+
events(): VideoEventStream;
|
|
42
|
+
/**
|
|
43
|
+
* Ingest a video from the source's connected S3/R2 bucket.
|
|
44
|
+
* Call this after uploading the file to the bucket.
|
|
45
|
+
*/
|
|
46
|
+
ingest(options: {
|
|
47
|
+
key: string;
|
|
48
|
+
callbackUrl?: string;
|
|
49
|
+
}): Promise<IngestResult>;
|
|
50
|
+
/**
|
|
51
|
+
* Create a presigned URL for uploading a file directly to xform's R2 storage.
|
|
52
|
+
* After uploading, call `ingest()` with the returned `key`.
|
|
53
|
+
*/
|
|
54
|
+
createUploadUrl(options: {
|
|
55
|
+
filename: string;
|
|
56
|
+
contentType: string;
|
|
57
|
+
}): Promise<UploadUrlResult>;
|
|
58
|
+
/**
|
|
59
|
+
* Get storage and bandwidth usage for this source.
|
|
60
|
+
*/
|
|
61
|
+
usage(): Promise<VideoUsageResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Change the source's plan, billing cycle, or add-ons.
|
|
64
|
+
* Omit any field to leave it unchanged.
|
|
65
|
+
*
|
|
66
|
+
* Upgrades are prorated immediately.
|
|
67
|
+
* Downgrades take effect at the next billing cycle (no credit issued).
|
|
68
|
+
* Downgrading from Pro to Starter automatically removes any custom domain.
|
|
69
|
+
*/
|
|
70
|
+
updatePlan(input: UpdatePlanInput): Promise<SourceBilling>;
|
|
71
|
+
/** Video operations for this source */
|
|
72
|
+
videos: {
|
|
73
|
+
/**
|
|
74
|
+
* List all videos for this source.
|
|
75
|
+
*/
|
|
76
|
+
list(options?: {
|
|
77
|
+
limit?: number;
|
|
78
|
+
skip?: number;
|
|
79
|
+
}): Promise<ListVideosResult>;
|
|
80
|
+
/**
|
|
81
|
+
* Get the status and metadata of a specific video.
|
|
82
|
+
*/
|
|
83
|
+
get(videoKey: string): Promise<Video>;
|
|
84
|
+
};
|
|
85
|
+
/** Webhook configuration for this source */
|
|
86
|
+
webhook: {
|
|
87
|
+
/**
|
|
88
|
+
* Get the current webhook configuration (secret is masked).
|
|
89
|
+
* Returns null if no webhook is configured.
|
|
90
|
+
*/
|
|
91
|
+
get(): Promise<WebhookConfig | null>;
|
|
92
|
+
/**
|
|
93
|
+
* Set or update the webhook configuration.
|
|
94
|
+
* If `events` is omitted, all event types are subscribed.
|
|
95
|
+
*/
|
|
96
|
+
set(input: SetWebhookInput): Promise<WebhookConfig>;
|
|
97
|
+
/**
|
|
98
|
+
* Remove the webhook configuration. No more webhooks will be sent.
|
|
99
|
+
*/
|
|
100
|
+
delete(): Promise<void>;
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Create an organization-scoped xform client.
|
|
106
|
+
* Use this for programmatic source management (enterprise / server-to-server).
|
|
107
|
+
* Requires an organization-level API key.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* const admin = createXformAdminClient({ organizationId, apiKey, baseUrl })
|
|
111
|
+
* const source = await admin.sources.create({ name: 'My Source', ... })
|
|
112
|
+
* const { uploadUrl, key } = await admin.source(source.id).createUploadUrl({ ... })
|
|
113
|
+
*/
|
|
114
|
+
export declare function createXformAdminClient(options: XformAdminClientOptions): XformAdminClient;
|
|
115
|
+
//# sourceMappingURL=admin-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-client.d.ts","sourceRoot":"","sources":["../src/admin-client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,uBAAuB,EACvB,MAAM,EACN,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,oBAAoB,EACpB,KAAK,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,eAAe,EAChB,MAAM,YAAY,CAAA;AAEnB,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;gBAEpB,OAAO,EAAE,uBAAuB;IAM5C,OAAO,KAAK,OAAO,GAMlB;YAEa,OAAO;IAuBrB,iEAAiE;IACjE,QAAQ,CAAC,OAAO;QACd;;WAEG;oBACO,OAAO,CAAC,MAAM,EAAE,CAAC;QAI3B;;WAEG;wBACa,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;QAIxC;;;WAGG;wBACa,iBAAiB,KAAG,OAAO,CAAC,MAAM,CAAC;QAInD;;WAEG;2BACgB,MAAM,SAAS,iBAAiB,KAAG,OAAO,CAAC,MAAM,CAAC;QAIrE;;WAEG;oCACyB,MAAM,KAAG,OAAO,CAAC,oBAAoB,CAAC;MAInE;IAED;;;OAGG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM;QAOnB;;WAEG;kBACO,gBAAgB;QAQ1B;;;WAGG;wBACmB;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,YAAY,CAAC;QASnF;;;WAGG;iCAC4B;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,eAAe,CAAC;QASnG;;WAEG;iBACM,OAAO,CAAC,gBAAgB,CAAC;QAIlC;;;;;;;WAOG;0BACe,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC;QAQ1D,uCAAuC;;YAErC;;eAEG;2BACW;gBAAE,KAAK,CAAC,EAAE,MAAM,CAAC;gBAAC,IAAI,CAAC,EAAE,MAAM,CAAA;aAAE,GAAQ,OAAO,CAAC,gBAAgB,CAAC;YAQhF;;eAEG;0BACW,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;;QAQvC,4CAA4C;;YAE1C;;;eAGG;mBACI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;YAOpC;;;eAGG;uBACQ,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC;YAQnD;;eAEG;sBACO,OAAO,CAAC,IAAI,CAAC;;;CAM9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,gBAAgB,CAEzF"}
|