@omelhorsite/sdk 0.2.0 → 0.4.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.
Files changed (96) hide show
  1. package/README.md +112 -130
  2. package/dist/index.js +4800 -610
  3. package/dist/types/client.d.ts +60 -3
  4. package/dist/types/http.d.ts +444 -19
  5. package/dist/types/index.d.ts +5 -1
  6. package/dist/types/internal/attachments.d.ts +17 -0
  7. package/dist/types/internal/helpers.d.ts +9 -0
  8. package/dist/types/listing.d.ts +90 -0
  9. package/dist/types/resources/account.d.ts +71 -5
  10. package/dist/types/resources/admin/authorizedApplications.d.ts +133 -0
  11. package/dist/types/resources/admin/chests.d.ts +51 -0
  12. package/dist/types/resources/admin/eventAlerts.d.ts +60 -0
  13. package/dist/types/resources/admin/identities.d.ts +99 -0
  14. package/dist/types/resources/admin/index.d.ts +77 -0
  15. package/dist/types/resources/admin/jobs.d.ts +97 -0
  16. package/dist/types/resources/admin/myOauthApplications.d.ts +282 -0
  17. package/dist/types/resources/admin/notepads.d.ts +49 -0
  18. package/dist/types/resources/admin/oauthApplications.d.ts +309 -0
  19. package/dist/types/resources/admin/quotas.d.ts +114 -0
  20. package/dist/types/resources/admin/shortLinks.d.ts +201 -0
  21. package/dist/types/resources/admin/types.d.ts +222 -0
  22. package/dist/types/resources/admin/vocalSeparations.d.ts +98 -0
  23. package/dist/types/resources/admin.d.ts +1841 -0
  24. package/dist/types/resources/auth/index.d.ts +39 -0
  25. package/dist/types/resources/auth/passkeys.d.ts +652 -0
  26. package/dist/types/resources/auth/sessions.d.ts +844 -0
  27. package/dist/types/resources/chests.d.ts +54 -3
  28. package/dist/types/resources/content/analysis.d.ts +62 -0
  29. package/dist/types/resources/content/blogs.d.ts +471 -0
  30. package/dist/types/resources/content/feedbacks.d.ts +263 -0
  31. package/dist/types/resources/content/index.d.ts +52 -0
  32. package/dist/types/resources/content/intel/articles.d.ts +230 -0
  33. package/dist/types/resources/content/intel/config.d.ts +135 -0
  34. package/dist/types/resources/content/intel/index.d.ts +53 -0
  35. package/dist/types/resources/content/intel/items.d.ts +91 -0
  36. package/dist/types/resources/content/intel/reports.d.ts +108 -0
  37. package/dist/types/resources/content/intel/scripts.d.ts +152 -0
  38. package/dist/types/resources/content/intel/sources.d.ts +236 -0
  39. package/dist/types/resources/content/intel/stats.d.ts +105 -0
  40. package/dist/types/resources/content/intel/types.d.ts +86 -0
  41. package/dist/types/resources/content/jokes.d.ts +93 -0
  42. package/dist/types/resources/content/notifications.d.ts +147 -0
  43. package/dist/types/resources/content/serviceUsages.d.ts +73 -0
  44. package/dist/types/resources/content/servicesStatus.d.ts +194 -0
  45. package/dist/types/resources/content/siteConfig.d.ts +43 -0
  46. package/dist/types/resources/content/spaceInvaders.d.ts +154 -0
  47. package/dist/types/resources/content.d.ts +2775 -0
  48. package/dist/types/resources/dynamicQrs.d.ts +39 -3
  49. package/dist/types/resources/forms.d.ts +176 -35
  50. package/dist/types/resources/index.d.ts +19 -8
  51. package/dist/types/resources/ipLookup.d.ts +20 -4
  52. package/dist/types/resources/jobs.d.ts +69 -25
  53. package/dist/types/resources/library/annotations.d.ts +162 -0
  54. package/dist/types/resources/library/books.d.ts +534 -0
  55. package/dist/types/resources/library/chat.d.ts +259 -0
  56. package/dist/types/resources/library/index.d.ts +39 -0
  57. package/dist/types/resources/library/shelves.d.ts +237 -0
  58. package/dist/types/resources/library/types.d.ts +120 -0
  59. package/dist/types/resources/library.d.ts +1433 -0
  60. package/dist/types/resources/linkTrees.d.ts +142 -30
  61. package/dist/types/resources/media.d.ts +351 -0
  62. package/dist/types/resources/movies/addons.d.ts +388 -0
  63. package/dist/types/resources/movies/collections.d.ts +352 -0
  64. package/dist/types/resources/movies/index.d.ts +29 -0
  65. package/dist/types/resources/movies/types.d.ts +50 -0
  66. package/dist/types/resources/movies/watchProgress.d.ts +277 -0
  67. package/dist/types/resources/movies.d.ts +1160 -0
  68. package/dist/types/resources/music/artists.d.ts +1068 -0
  69. package/dist/types/resources/music/imports.d.ts +941 -0
  70. package/dist/types/resources/music/index.d.ts +61 -0
  71. package/dist/types/resources/music/playlists.d.ts +1027 -0
  72. package/dist/types/resources/music/social.d.ts +1132 -0
  73. package/dist/types/resources/music/songs.d.ts +1167 -0
  74. package/dist/types/resources/notepads.d.ts +4 -1
  75. package/dist/types/resources/quotas.d.ts +7 -1
  76. package/dist/types/resources/realtime.d.ts +855 -0
  77. package/dist/types/resources/shortLinks.d.ts +49 -5
  78. package/dist/types/resources/social/groupChats.d.ts +458 -0
  79. package/dist/types/resources/social/index.d.ts +19 -0
  80. package/dist/types/resources/social/messages.d.ts +424 -0
  81. package/dist/types/resources/social/relationships.d.ts +322 -0
  82. package/dist/types/resources/social/types.d.ts +37 -0
  83. package/dist/types/resources/social.d.ts +1348 -0
  84. package/dist/types/resources/storage/upload.d.ts +158 -11
  85. package/dist/types/resources/storage.d.ts +111 -27
  86. package/dist/types/resources/tickets.d.ts +101 -13
  87. package/dist/types/resources/tools/backgroundRemoval.d.ts +18 -3
  88. package/dist/types/resources/tools/captions.d.ts +448 -21
  89. package/dist/types/resources/tools/downloader.d.ts +21 -0
  90. package/dist/types/resources/tools/index.d.ts +57 -15
  91. package/dist/types/resources/tools/jumpstyle.d.ts +50 -17
  92. package/dist/types/resources/tools/transcription.d.ts +35 -13
  93. package/dist/types/resources/tools/upscale.d.ts +23 -3
  94. package/dist/types/resources/tools/vocalSeparation.d.ts +30 -13
  95. package/dist/types/types.d.ts +249 -17
  96. package/package.json +2 -1
@@ -37,9 +37,31 @@
37
37
  * Read {@link CaptionsNamespace.quota} before uploading, and again once the
38
38
  * window is known: the second check is the exact one, and the server refuses a
39
39
  * window that would cross the ceiling rather than truncating it.
40
+ *
41
+ * **The 250 MiB ceiling is not reachable through {@link CaptionsNamespace.create}.**
42
+ * Cloudflare rejects any request body over 100 MB with a 413 of its own before
43
+ * Rails ever sees it, so the backend also exposes a three-call chunked upload -
44
+ * `POST /caption_jobs/uploads` opens a session and returns a signed
45
+ * `upload_token` plus a `part_size` (32 MiB), `POST /caption_jobs/uploads/parts?offset=`
46
+ * streams each raw part under an `X-Upload-Token` header, and
47
+ * `POST /caption_jobs/uploads/finish` probes the assembled file and creates the
48
+ * row. {@link CaptionsNamespace.upload} drives whichever of the two a given
49
+ * file needs, so step 1 is one call again: under
50
+ * {@link CAPTION_CHUNKED_THRESHOLD} it IS `create`, above it it is
51
+ * {@link CaptionsNamespace.createChunked}, and both answer the same
52
+ * `"uploaded"` row. Reach for `create` directly only when the file is known to
53
+ * be small, and for the three methods under `upload` only to drive the parts
54
+ * yourself.
55
+ *
56
+ * One caveat that is the backend's and not the SDK's: the three chunked routes
57
+ * carry no `oauth_scope` declaration, and scopes there are default-deny, so an
58
+ * OAuth access token gets `403 insufficient_scope` on all three (and on
59
+ * `fonts`). A session or a first-party bearer token reaches them; a token
60
+ * minted through the OIDC provider does not, and for one of those only
61
+ * `create` is open.
40
62
  */
41
63
  import { Resource } from "../../http";
42
- import type { FileInput, Id, Progress, RequestOptions } from "../../types";
64
+ import type { FileInput, Id, OperationOptions, Progress, RequestOptions } from "../../types";
43
65
  import { type SecondsQuota, type ToolCaptcha, type ToolRecord, type ToolRunOptions } from "./index";
44
66
  /**
45
67
  * Lifecycle of a caption job. Wider than {@link ToolStatus} because the job
@@ -76,6 +98,13 @@ export interface CaptionWord {
76
98
  /** Seconds from the video start. Must be `>= t0`. */
77
99
  readonly t1: number;
78
100
  }
101
+ /**
102
+ * An RGB triple, each channel clamped to `[0, 255]` server-side.
103
+ *
104
+ * The controller reads the key only when the array has EXACTLY three entries;
105
+ * two or four is dropped in silence, which costs a whole render to discover.
106
+ */
107
+ export type CaptionRgb = readonly [number, number, number] | readonly number[];
79
108
  /**
80
109
  * Look of the burned-in captions.
81
110
  *
@@ -89,8 +118,22 @@ export interface CaptionWord {
89
118
  * back as the nearest end of it rather than as a 400.
90
119
  */
91
120
  export interface CaptionStyle {
121
+ /**
122
+ * A font KEY from {@link CaptionsNamespace.fonts}, never a path: the sidecar
123
+ * resolves the key against the faces installed in its image, so a caller
124
+ * cannot point the renderer at a file. Must match `/\A[a-z0-9_-]{1,40}\z/`;
125
+ * anything else is dropped and the renderer uses its own default.
126
+ */
127
+ readonly font?: string;
92
128
  /** Font size as a fraction of the video height. Clamped to `[0.03, 0.09]`. */
93
129
  readonly fontscale?: number;
130
+ /**
131
+ * Outline thickness as a fraction of the font size. Clamped to `[0, 0.3]`,
132
+ * and `0` is a legitimate choice meaning no outline at all - which is why
133
+ * the controller reads this key whenever it is PRESENT rather than when it
134
+ * is truthy, unlike every other numeric key here.
135
+ */
136
+ readonly stroke_factor?: number;
94
137
  /** Vertical placement, `0` top to `1` bottom. Clamped to `[0.3, 0.9]`. */
95
138
  readonly pos?: number;
96
139
  /** Words on screen at once. Clamped to `[1, 6]`. */
@@ -103,32 +146,64 @@ export interface CaptionStyle {
103
146
  readonly preset?: "veryfast" | "medium" | "slow";
104
147
  /**
105
148
  * Colour of the word currently being sung, as `[r, g, b]`, each clamped to
106
- * `[0, 255]`. Ignored unless it has exactly three entries.
149
+ * `[0, 255]`. Named after its default rather than after its job. Ignored
150
+ * unless the array has exactly three entries.
107
151
  */
108
- readonly yellow?: readonly [number, number, number] | readonly number[];
152
+ readonly yellow?: CaptionRgb;
153
+ /** Colour of the words that are not highlighted. Same three-entry rule. */
154
+ readonly white?: CaptionRgb;
155
+ /** Colour of the outline. Same three-entry rule. */
156
+ readonly stroke?: CaptionRgb;
109
157
  readonly [key: string]: unknown;
110
158
  }
111
- /** A caption job. */
159
+ /**
160
+ * A caption job.
161
+ *
162
+ * Every route that answers with one - create, the chunked upload's finish
163
+ * call, show, transcribe and render - renders the `:extended` view, so every
164
+ * key below is present on every response.
165
+ *
166
+ * Five of the columns are `NOT NULL` with a numeric default, so `width`,
167
+ * `height`, `fps`, `duration` and `transcribed_seconds` are always real
168
+ * numbers. On a row whose upload has only just been probed they are the probed
169
+ * values; the sidecar's precise metadata overwrites them a moment later.
170
+ */
112
171
  export interface CaptionJob extends ToolRecord {
113
172
  readonly status: CaptionStatus;
173
+ /** Original upload name. The controller substitutes `"video.mp4"` for a
174
+ * blank one, so this is never empty in practice. */
114
175
  readonly filename: string;
115
- readonly width?: number | null;
116
- readonly height?: number | null;
117
- readonly fps?: number | null;
118
- /** Seconds of video, probed at upload. */
119
- readonly duration?: number | null;
120
- readonly language?: string | null;
121
- /** The transcribed window, seconds from the video start. */
122
- readonly window_start?: number | null;
123
- readonly window_end?: number | null;
124
- /** Seconds charged against the quota so far. Accumulates across windows. */
125
- readonly transcribed_seconds?: number | null;
126
- /** Timed words. Present from `"transcribed"` onwards. */
127
- readonly words?: CaptionWord[] | null;
128
- /** What the renderer is doing right now. `null` unless rendering. */
129
- readonly render_stage?: string | null;
130
- /** The finished video, once the status is `"complete"`. */
131
- readonly output_url?: string | null;
176
+ /** Pixels. `NOT NULL DEFAULT 0`, so `0` means "not probed yet", not unknown. */
177
+ readonly width: number;
178
+ readonly height: number;
179
+ /** `NOT NULL DEFAULT 0.0`. */
180
+ readonly fps: number;
181
+ /** Seconds of video, probed at upload. `NOT NULL DEFAULT 0.0`. */
182
+ readonly duration: number;
183
+ /** Language of the last transcribe call, `"auto"` included. `null` before the first. */
184
+ readonly language: string | null;
185
+ /** The transcribed window, seconds from the video start. `null` before the first transcribe. */
186
+ readonly window_start: number | null;
187
+ readonly window_end: number | null;
188
+ /**
189
+ * Seconds charged against the quota so far, accumulated across every window
190
+ * transcribed on this job. `NOT NULL DEFAULT 0`.
191
+ */
192
+ readonly transcribed_seconds: number;
193
+ /**
194
+ * Timed words, from `"transcribed"` onwards.
195
+ *
196
+ * `null` and not `[]` when there are none: the column is `NOT NULL DEFAULT
197
+ * '[]'`, but the blueprint renders `words.presence`, and `[].presence` is
198
+ * `nil` in Ruby. So an empty list arrives as `null`, and the key is present
199
+ * either way.
200
+ */
201
+ readonly words: CaptionWord[] | null;
202
+ /** What the renderer is doing right now, read live off the sidecar. `null`
203
+ * unless the status is `"rendering"`. */
204
+ readonly render_stage: string | null;
205
+ /** Signed URL of the finished video once complete and attached, `null` otherwise. */
206
+ readonly output_url: string | null;
132
207
  }
133
208
  /** Arguments for uploading a video. */
134
209
  export interface CreateCaptionJobInput extends ToolCaptcha {
@@ -168,6 +243,190 @@ export interface RenderCaptionInput {
168
243
  * nothing.
169
244
  */
170
245
  export declare function captionProgress(record: CaptionJob): Progress;
246
+ /**
247
+ * Largest file the backend will accept, chunked or not: 250 MiB exactly.
248
+ *
249
+ * Mirrors `CaptionJobsController::MAX_FILE_SIZE`. Not checked client-side on
250
+ * purpose - the server owns the number, and the chunked path finds out in one
251
+ * cheap round trip because {@link CaptionsNamespace.startUpload} is given the
252
+ * size before a single byte moves.
253
+ */
254
+ export declare const CAPTION_MAX_UPLOAD_BYTES: number;
255
+ /**
256
+ * Where {@link CaptionsNamespace.upload} stops using one `POST` and starts
257
+ * using the three-call path: 64 MiB.
258
+ *
259
+ * This is a CLIENT-SIDE choice, not a protocol constant, and it is the same
260
+ * one the web tool makes (`frontend/services/CaptionsService.ts`). Cloudflare's
261
+ * ceiling is on the whole request, and a `multipart/form-data` envelope rides
262
+ * on top of the file's own bytes, so the switch sits well under the cap rather
263
+ * than at it. Below it a single request is one round trip and one probe; above
264
+ * it a single request is a 413 that says nothing useful.
265
+ *
266
+ * Override it per call with {@link UploadCaptionVideoInput.chunkedThreshold} -
267
+ * for instance `0`, to exercise the chunked path on a small file.
268
+ */
269
+ export declare const CAPTION_CHUNKED_THRESHOLD: number;
270
+ /**
271
+ * Part size, 32 MiB. The same number as the storage tier's, and by coincidence
272
+ * rather than by sharing: `CaptionJobsController::PART_SIZE` copied it.
273
+ *
274
+ * It is BOTH the size the server reports in the session and the ceiling the
275
+ * server validates each part against, and the controller validates against its
276
+ * own constant rather than against the session it minted. See
277
+ * {@link resolveCaptionPartSize} for what that means when the two disagree.
278
+ */
279
+ export declare const CAPTION_PART_SIZE: number;
280
+ /**
281
+ * Parts in flight at once by default.
282
+ *
283
+ * Three, as the web tool uses. A single sequential stream leaves a home
284
+ * connection idle between round trips; three fills it without holding an unfair
285
+ * number of the API's threads, and every part in flight parks one Rails thread
286
+ * for as long as it takes to arrive - these bytes pass THROUGH Rails on their
287
+ * way to the sidecar, unlike a storage upload.
288
+ */
289
+ export declare const CAPTION_UPLOAD_CONCURRENCY = 3;
290
+ /**
291
+ * How long an upload session stays usable: six hours, from
292
+ * `CaptionJobsController::UPLOAD_TOKEN_TTL`.
293
+ *
294
+ * The window for a resume. Past it the token verifies as expired and every
295
+ * call answers 400 "Invalid or expired upload session", parts already sent
296
+ * included - there is no way to re-mint a token for a half-uploaded file,
297
+ * because the new session would carry a new job id.
298
+ */
299
+ export declare const CAPTION_UPLOAD_TOKEN_TTL_MS: number;
300
+ /**
301
+ * What `POST /caption_jobs/uploads` answers.
302
+ *
303
+ * `upload_token` is a CREDENTIAL: a signed `(job_id, size, filename)` triple,
304
+ * and the only thing standing between a stranger and writing bytes into this
305
+ * job. It is not a job id and there is no job yet - the row is created by
306
+ * `finish`, out of the id sealed inside the token - so it cannot be handed to
307
+ * {@link CaptionsNamespace.get} and there is nothing to poll until then.
308
+ */
309
+ export interface CaptionUploadSession {
310
+ /** Signed session token. Goes in the `X-Upload-Token` header on both later calls. */
311
+ readonly upload_token: string;
312
+ /** Bytes per part, and the ceiling the server validates each part against. */
313
+ readonly part_size: number;
314
+ /** `ceil(size / part_size)`, so a caller can size a progress bar before slicing. */
315
+ readonly part_count: number;
316
+ }
317
+ /** Arguments for opening a chunked upload session. */
318
+ export interface StartCaptionUploadInput extends ToolCaptcha {
319
+ /**
320
+ * EXACT byte length of the file.
321
+ *
322
+ * Not an estimate and not a ceiling: the number is sealed into the token and
323
+ * every part is checked against it, so a declared size below the real one
324
+ * makes the last part fail with 400 "Part exceeds declared size", and a
325
+ * declared size above it leaves the sidecar waiting for bytes that never
326
+ * come and `finish` probing a truncated file.
327
+ */
328
+ readonly size: number;
329
+ /** Stored name. Trimmed to 255 characters, and `"video.mp4"` when blank. */
330
+ readonly filename?: string;
331
+ }
332
+ /**
333
+ * Arguments for {@link CaptionsNamespace.upload} and
334
+ * {@link CaptionsNamespace.createChunked}.
335
+ */
336
+ export interface UploadCaptionVideoInput extends ToolCaptcha {
337
+ /**
338
+ * The video.
339
+ *
340
+ * For the chunked path this has to be sliceable, which means real bytes: a
341
+ * `Blob`/`File` on the web or in Bun, a `Uint8Array` anywhere. A React
342
+ * Native `{ uri, name, type }` descriptor is NOT sliceable - see the note on
343
+ * {@link CaptionsNamespace.createChunked}.
344
+ */
345
+ readonly video: FileInput;
346
+ /** Overrides {@link CAPTION_CHUNKED_THRESHOLD} for this call only. */
347
+ readonly chunkedThreshold?: number;
348
+ /** Overrides {@link CAPTION_UPLOAD_CONCURRENCY} for this call only. */
349
+ readonly concurrency?: number;
350
+ /**
351
+ * Continues an upload that was cut off, rather than starting a new one.
352
+ *
353
+ * Present or not, this is what decides the path: a resume always goes
354
+ * chunked, whatever the file's size and threshold say. The session is only
355
+ * good for {@link CAPTION_UPLOAD_TOKEN_TTL_MS} from when it was minted.
356
+ */
357
+ readonly resume?: CaptionUploadResume;
358
+ }
359
+ /** A chunked upload picked up where it stopped. */
360
+ export interface CaptionUploadResume {
361
+ /**
362
+ * The session from the interrupted attempt, or just its `upload_token`.
363
+ *
364
+ * Keep the whole session where you can. A bare token has no `part_size`, so
365
+ * the driver slices with {@link CAPTION_PART_SIZE} instead - which is the
366
+ * server's own number today, and therefore the same offsets, but it stops
367
+ * being true the day the backend changes it.
368
+ */
369
+ readonly session: CaptionUploadSession | string;
370
+ /**
371
+ * Byte offsets already accepted, so their bytes are not sent twice.
372
+ *
373
+ * Offsets, not part numbers, and they must be multiples of the session's
374
+ * `part_size` - they are the same numbers the driver sliced with, which is
375
+ * what {@link UploadCaptionOptions.onPart} reports. Anything else is simply
376
+ * not in the list of offsets to skip and its part is sent again, which costs
377
+ * bandwidth and breaks nothing.
378
+ *
379
+ * Omit it to re-send every part. That is always correct, just slower: a part
380
+ * is written at its own offset, so sending it twice is idempotent.
381
+ */
382
+ readonly uploaded?: readonly number[];
383
+ }
384
+ /**
385
+ * Options for the two driving methods: everything a request takes, plus the
386
+ * two callbacks that make a 250 MiB upload watchable.
387
+ */
388
+ export interface UploadCaptionOptions extends OperationOptions {
389
+ /**
390
+ * Called after each part the server has ACCEPTED, with the offset it was
391
+ * written at and its length.
392
+ *
393
+ * The point of it is resuming: collect these offsets, and hand them back as
394
+ * {@link CaptionUploadResume.uploaded} if the run is cut off.
395
+ */
396
+ readonly onPart?: (offset: number, length: number) => void;
397
+ }
398
+ /**
399
+ * Byte length of a video about to be uploaded, or `undefined` when it cannot be
400
+ * known without reading it.
401
+ *
402
+ * `undefined` has exactly two causes, and both matter: a `ReadableStream`,
403
+ * which has no length until it is drained, and a React Native picker that
404
+ * reported no `size` for a `content://` URI. Neither can be sized cheaply, so
405
+ * {@link CaptionsNamespace.upload} sends them down the single-request path and
406
+ * lets the server judge - which is the right guess for a phone pick and the
407
+ * wrong one for a 200 MiB stream, so pass `video.size` when you know it.
408
+ */
409
+ export declare function captionUploadSize(video: FileInput): number | undefined;
410
+ /**
411
+ * The size to slice parts with, from what the session reported.
412
+ *
413
+ * Follows the storage driver's rule - slice with the number the SERVER named,
414
+ * never with the SDK's copy of it - but with a ceiling the storage tier does
415
+ * not need, because the two ends of this protocol read the number from
416
+ * different places. `start_upload` reports `PART_SIZE` from the session it is
417
+ * minting, while `upload_part` validates `length <= PART_SIZE` against the
418
+ * CONTROLLER CONSTANT. They are the same constant today and can only disagree
419
+ * mid-deploy, and the disagreement is one-sided: honouring a larger reported
420
+ * size would make every part a 400 "Invalid part", while capping at the size
421
+ * this SDK knows the validator uses only sends smaller parts, which is always
422
+ * legal. So the reported number wins downwards and loses upwards.
423
+ *
424
+ * A missing, zero or nonsense `part_size` falls back to
425
+ * {@link CAPTION_PART_SIZE}. Note the web tool falls back to its 64 MiB
426
+ * threshold instead, which is above the validator's ceiling - a fallback that
427
+ * would reject every part if it ever fired.
428
+ */
429
+ export declare function resolveCaptionPartSize(reported: number | undefined): number;
171
430
  /** The `captions` tool, reachable as `oms.tools.captions`. */
172
431
  export declare class CaptionsNamespace extends Resource {
173
432
  /**
@@ -184,6 +443,24 @@ export declare class CaptionsNamespace extends Resource {
184
443
  * ceiling rather than truncating it.
185
444
  */
186
445
  quota(options?: RequestOptions): Promise<SecondsQuota>;
446
+ /**
447
+ * `GET /caption_jobs/fonts` - the font KEYS the renderer has, for
448
+ * {@link CaptionStyle.font}.
449
+ *
450
+ * Plain strings, not objects and not display names: they are the keys the
451
+ * sidecar resolves against the faces installed in its image, which is the
452
+ * whole reason a caller cannot pass a path.
453
+ *
454
+ * The controller answers `{ fonts: [...] }`; this unwraps it. It also
455
+ * NEVER fails: a sidecar that is down or slow is caught and answered as an
456
+ * empty list with a 200, so `[]` means "could not ask right now" just as
457
+ * much as it means "no fonts", and the two are not distinguishable. Do not
458
+ * treat an empty answer as a reason to refuse a render - omitting `font`
459
+ * lets the renderer use its own default.
460
+ *
461
+ * Cached server-side for ten minutes, so polling it buys nothing.
462
+ */
463
+ fonts(options?: RequestOptions): Promise<string[]>;
187
464
  /**
188
465
  * `POST /caption_jobs` - step 1. Uploads the video and probes it.
189
466
  *
@@ -308,6 +585,156 @@ export declare class CaptionsNamespace extends Resource {
308
585
  * @throws {OmsError} explaining which of the three reasons there is no URL.
309
586
  */
310
587
  outputUrl(record: CaptionJob): string;
588
+ /**
589
+ * Step 1 for any video, picking the path by size. Prefer this over
590
+ * {@link create} unless you control what the caller can hand you.
591
+ *
592
+ * Under {@link CAPTION_CHUNKED_THRESHOLD} it is exactly {@link create}: one
593
+ * `POST`, one probe, one round trip. At or above it, it is
594
+ * {@link createChunked}. The answer is the same `"uploaded"` row either way,
595
+ * because `finish` and `create` render the same `:extended` view - so the
596
+ * caller's step 2 does not need to know which path ran.
597
+ *
598
+ * The size comes from {@link captionUploadSize}, which cannot always find
599
+ * one: a `ReadableStream` and some React Native picks have no length until
600
+ * they are read. Those go down the single-request path, which is right for a
601
+ * phone pick and wrong for a large stream, so pass `video.size` whenever you
602
+ * know it rather than letting a 413 teach you.
603
+ *
604
+ * `onProgress` is honest about which path ran and reports COMPLETED
605
+ * transfers, never bytes handed to the runtime - the whole file as one tick
606
+ * on the direct path, one tick per 32 MiB part on the chunked one. `fetch`
607
+ * has no upload-progress event in any of the three runtimes this SDK targets;
608
+ * see the module note in `storage/upload.ts` for why neither XHR nor a
609
+ * counting stream body is the answer inside the core.
610
+ *
611
+ * Rate limit: the direct path and the chunked path's opening call both count
612
+ * against the expensive-tools bucket, 20 a minute shared with every other
613
+ * tool. The parts and the finish do not.
614
+ */
615
+ upload(input: UploadCaptionVideoInput, options?: UploadCaptionOptions): Promise<CaptionJob>;
616
+ /**
617
+ * Step 1 in three calls, for a video too big for one request.
618
+ *
619
+ * Opens a session, sends the parts (three at a time by default, in whatever
620
+ * order the pool finishes them), then finishes - and `finish` is what probes
621
+ * the assembled file and creates the row, so this resolves with the same
622
+ * `"uploaded"` row {@link create} would have answered.
623
+ *
624
+ * Nothing is cleaned up on failure and nothing needs to be: the controller
625
+ * deletes the sidecar's copy itself when `finish` rejects the file, and a run
626
+ * abandoned before `finish` leaves only a partial file on the sidecar's disk
627
+ * with no row pointing at it. There is no client-visible way to abort a
628
+ * session - the backend has `CaptionsClient.abort_upload` but no route onto
629
+ * it - so an abandoned upload is simply abandoned.
630
+ *
631
+ * **React Native cannot take this path.** The bytes have to be sliced, and a
632
+ * picked `{ uri, name, type }` is a handle into the device that only a native
633
+ * module can open - so this throws for one rather than uploading an empty
634
+ * file with a 200 on it. What works on a phone is {@link create} (the
635
+ * transport hands the descriptor to RN's own `FormData` verbatim and the
636
+ * platform streams it off disk), which caps out at Cloudflare's ~100 MB. Past
637
+ * that the only ways through are reading the file into a `Uint8Array` first -
638
+ * Expo's `new File(uri).bytes()`, which means the whole video in the JS heap,
639
+ * so it is not a plan for 250 MiB - or shrinking the video on the device.
640
+ *
641
+ * Resuming: pass {@link CaptionUploadResume} with the earlier session and the
642
+ * offsets {@link UploadCaptionOptions.onPart} reported. Skipping is only an
643
+ * optimisation, because a part is written at its own offset and re-sending it
644
+ * is idempotent; the session itself expires after
645
+ * {@link CAPTION_UPLOAD_TOKEN_TTL_MS}.
646
+ *
647
+ * @throws {OmsError} `invalid_request` for a React Native descriptor, or for
648
+ * an empty file - which the server would answer as "Invalid size", after a
649
+ * round trip that had nothing to carry.
650
+ * @throws {OmsApiError} 413 over 250 MiB, raised by the opening call before
651
+ * any bytes move; 400 from `finish` when the assembled file does not decode
652
+ * or runs past 20 minutes; 400 "Invalid or expired upload session" once the
653
+ * token is six hours old.
654
+ * @throws {OmsAuthError} 401 when anonymous and the captcha is missing or
655
+ * bad. It is checked when the session is opened, not when the bytes land,
656
+ * so a bad token costs one round trip rather than the whole upload.
657
+ */
658
+ createChunked(input: UploadCaptionVideoInput, options?: UploadCaptionOptions): Promise<CaptionJob>;
659
+ /**
660
+ * `POST /caption_jobs/uploads` - opens a chunked upload session.
661
+ *
662
+ * Public so a caller can drive the three calls itself, which is the supported
663
+ * way to get byte-level progress: wrap XHR (or a native uploader) around
664
+ * {@link uploadPart}'s job and keep the session and the finish from here.
665
+ *
666
+ * Three things happen here and nowhere else in the flow: the captcha is
667
+ * verified, the 250 MiB ceiling is checked against the DECLARED size, and the
668
+ * job id is minted - early, because the sidecar needs an id to write parts
669
+ * under and the row cannot exist until the file is whole. So a session is not
670
+ * a job: nothing is in the database yet, and {@link get} has nothing to find
671
+ * until {@link finishUpload} returns.
672
+ *
673
+ * NOT retried by default. A replay opens a SECOND sidecar upload under a
674
+ * second job id and hands back a second token, and the first is then orphaned
675
+ * on the sidecar's disk with no route to abort it.
676
+ *
677
+ * Rate limit: 20 a minute, shared with every other expensive tool - the same
678
+ * bucket `POST /caption_jobs` counts against, since this is its twin.
679
+ *
680
+ * @throws {OmsApiError} 413 when `size` is over 250 MiB, 400 when it is not
681
+ * positive, 503 when the captions sidecar is down.
682
+ * @throws {OmsAuthError} 401 when anonymous and the captcha is missing or bad.
683
+ * @throws {OmsQuotaError} 429 from the expensive-tools throttle. No daily
684
+ * quota is spent by an upload - only a transcribed window is metered.
685
+ */
686
+ startUpload(input: StartCaptionUploadInput, options?: RequestOptions): Promise<CaptionUploadSession>;
687
+ /**
688
+ * `POST /caption_jobs/uploads/parts?offset=` - one part, as raw bytes.
689
+ *
690
+ * The body is the part itself, `application/octet-stream`, with no envelope:
691
+ * not a form field, not base64, not JSON. Rails never reads it into a string
692
+ * - it streams the request body straight through to the sidecar - so a 32 MiB
693
+ * part costs 32 MiB of socket rather than 32 MiB of Ruby heap, and that is
694
+ * why the endpoint takes bytes rather than a multipart part.
695
+ *
696
+ * RETRIED BY DEFAULT, which no other writing method in this SDK is. The
697
+ * server writes the part at the offset the query names, so a replay is the
698
+ * same bytes in the same place: there is no record to duplicate and no
699
+ * position to lose. Pass `retry: false` to opt out. For the same reason the
700
+ * parts may go in any order and in parallel, and a part sent twice is not an
701
+ * error.
702
+ *
703
+ * `part` must not be longer than the session's `part_size` (see
704
+ * {@link resolveCaptionPartSize}) and `offset + part.length` must not run
705
+ * past the size declared when the session was opened. Both are 400s whose
706
+ * text does not distinguish them from each other.
707
+ *
708
+ * @param session The session from {@link startUpload}, or its token.
709
+ * @param offset Byte offset of this part in the whole file, from `0`.
710
+ * @returns The byte count the sidecar acknowledged, from `{ received }`.
711
+ * @throws {OmsApiError} 400 for an over-long part, an offset past the
712
+ * declared size, or a token that has expired (six hours); 503 when the
713
+ * sidecar is down.
714
+ */
715
+ uploadPart(session: CaptionUploadSession | string, offset: number, part: Blob | Uint8Array, options?: RequestOptions): Promise<number>;
716
+ /**
717
+ * `POST /caption_jobs/uploads/finish` - assembles, probes and creates the row.
718
+ *
719
+ * This is where a chunked upload becomes a job: the sidecar joins the parts,
720
+ * ffprobe reads the result, and only then is a `CaptionJob` written, with the
721
+ * id that was sealed into the token at the start. The answer is the same
722
+ * `:extended` view {@link create} returns, in `"uploaded"`.
723
+ *
724
+ * It is also where a bad upload is caught. A file that does not decode, or
725
+ * one over 20 minutes, is a 400 - and the controller deletes the sidecar's
726
+ * copy before answering, so there is nothing to clean up and nothing to
727
+ * retry: the parts are gone with it.
728
+ *
729
+ * NOT retried by default. The row is created with a fixed id, so a replay
730
+ * that lands after a lost answer fails on the id already existing and reports
731
+ * a 400 for an upload that worked.
732
+ *
733
+ * @throws {OmsApiError} 400 when the assembled file cannot be read or runs
734
+ * past 20 minutes, 400 "Invalid or expired upload session" for a token that
735
+ * is over six hours old, 503 when the sidecar is down.
736
+ */
737
+ finishUpload(session: CaptionUploadSession | string, options?: RequestOptions): Promise<CaptionJob>;
311
738
  /**
312
739
  * Waits for a started step to settle, reusing the one polling loop.
313
740
  *
@@ -394,4 +394,25 @@ export declare class DownloaderNamespace extends Resource {
394
394
  * @throws {OmsApiError} exactly as {@link download}.
395
395
  */
396
396
  downloadFile(id: Id, options?: RequestOptions): Promise<FileOutput>;
397
+ /**
398
+ * The unauthenticated URL of the file endpoint. Pure string building, no
399
+ * request.
400
+ *
401
+ * Deliberately does NOT carry a credential, which makes it useless for
402
+ * handing to an `<a download>` or a `<video src>`: the route needs one, and
403
+ * an anonymous request to it is a 401. It is here so a caller can log or
404
+ * display the address, and so the shape of the endpoint is documented
405
+ * somewhere other than inside {@link downloadFile}.
406
+ *
407
+ * The web app builds a *usable* browser URL by appending its session token
408
+ * as a `?token=` query parameter. The SDK will not do that: the transport
409
+ * holds the token so it can put it in an `Authorization` header, and copying
410
+ * it into a URL puts it in browser history, in the Referer, and in every
411
+ * proxy log between here and the API. A host that genuinely needs a
412
+ * browser-followable link should call {@link downloadFile} and hand the
413
+ * blob to a local object URL instead.
414
+ *
415
+ * Remember the endpoint is ONE SHOT whichever way it is reached.
416
+ */
417
+ fileUrl(id: Id): string;
397
418
  }
@@ -57,30 +57,72 @@ export * from "./vocalSeparation";
57
57
  export type ToolStatus = "pending" | "processing" | "complete" | "failed";
58
58
  /** Terminal states. A row in one of these will never change again. */
59
59
  export declare const TOOL_TERMINAL_STATUSES: readonly ToolStatus[];
60
- /** Fields every tool row carries. */
60
+ /**
61
+ * Fields every tool row carries.
62
+ *
63
+ * All six blueprints in the family declare `status`, `error`, `finished_at`,
64
+ * `user_id` and `ip_address` in their DEFAULT view, and every route that
65
+ * answers with a tool row renders `:extended`, which inherits the default
66
+ * view's fields. So those five keys are always present; four of them hold a
67
+ * nullable column and are `null` rather than missing.
68
+ *
69
+ * `progress_percent` is the exception and is optional on purpose: it is an
70
+ * `:extended`-only field on FOUR of the six - transcription, vocal separation,
71
+ * captions and jumpstyle, the ones with a sidecar to ask - and does not exist
72
+ * at all on upscale or background removal, whose progress lives on the {@link
73
+ * Job} row instead. `toolProgress` reads it defensively for exactly that
74
+ * reason.
75
+ */
61
76
  export interface ToolRecord extends BaseRecord {
62
77
  readonly status: ToolStatus | string;
63
- /** Failure message, set once the status is `"failed"`. */
64
- readonly error?: string | null;
65
- readonly finished_at?: Timestamp | null;
66
- readonly user_id?: string | null;
67
- /** Set instead of `user_id` for an anonymous run. */
68
- readonly ip_address?: string | null;
69
- /** Percentage the sidecar reports while running. `null` when idle or done. */
78
+ /** Failure message, set once the status is `"failed"`. `null` otherwise. */
79
+ readonly error: string | null;
80
+ /** Set when the run reached a terminal state; `null` before that. */
81
+ readonly finished_at: Timestamp | null;
82
+ /** The owner, or `null` for an anonymous run. */
83
+ readonly user_id: string | null;
84
+ /**
85
+ * The address an anonymous run was started from, and `null` for a run with
86
+ * an owner - the controllers write one or the other, never both, and the
87
+ * models validate that at least one is set.
88
+ *
89
+ * It is also the credential for an anonymous run: `accessible_by?` matches
90
+ * this against the reader's own address, which is why picking up an
91
+ * anonymous run from a different network is a 401.
92
+ */
93
+ readonly ip_address: string | null;
94
+ /**
95
+ * Percentage the sidecar reports while running, `null` when idle or done -
96
+ * and absent entirely on upscale and background removal, which have no such
97
+ * field. See the note on this interface.
98
+ */
70
99
  readonly progress_percent?: number | null;
71
100
  }
72
101
  /**
73
102
  * Handle for polling an enqueued run.
74
103
  *
75
- * `job_id` and `watch_token` come back from the tools that enqueue through the
76
- * generic job table (upscale, background removal). The token is what lets an
77
- * anonymous caller poll `GET /jobs/:id`; the other tools are polled by reading
78
- * their own row instead.
104
+ * Both keys come back from the two tools that enqueue through the generic job
105
+ * table (upscale, background removal), and BOTH are always present on their
106
+ * create response: the controllers merge them onto the rendered row
107
+ * unconditionally. `watch_token` is nullable rather than absent, because
108
+ * `signed_id` is called on a tracking row that may not have been found.
109
+ *
110
+ * The token is what lets an anonymous caller poll `GET /jobs/:id`. The other
111
+ * five tools are polled by reading their own row and answer neither key, which
112
+ * is why this is a separate interface mixed into their `Created` types rather
113
+ * than part of {@link ToolRecord}.
79
114
  */
80
115
  export interface ToolJobHandle {
81
- readonly job_id?: string;
82
- /** Signed, scoped to this one job. Required when anonymous. */
83
- readonly watch_token?: string;
116
+ readonly job_id: string;
117
+ /**
118
+ * Signed and scoped to this one job, and expiring with the tool's retention
119
+ * window. Required when anonymous.
120
+ *
121
+ * `null` when the tracking row could not be read back after enqueue, which
122
+ * leaves an anonymous caller with no way to poll at all - the job id alone
123
+ * is a 404 without a credential.
124
+ */
125
+ readonly watch_token: string | null;
84
126
  }
85
127
  /** Mixed into every create input: the anonymous caller's captcha. */
86
128
  export interface ToolCaptcha {