@omelhorsite/sdk 0.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.
Files changed (36) hide show
  1. package/README.md +321 -0
  2. package/dist/index.js +11589 -0
  3. package/dist/types/auth/device.d.ts +156 -0
  4. package/dist/types/auth/index.d.ts +127 -0
  5. package/dist/types/auth/tokens.d.ts +356 -0
  6. package/dist/types/client.d.ts +133 -0
  7. package/dist/types/errors.d.ts +202 -0
  8. package/dist/types/http.d.ts +204 -0
  9. package/dist/types/index.d.ts +33 -0
  10. package/dist/types/local/index.d.ts +42 -0
  11. package/dist/types/local/password.d.ts +169 -0
  12. package/dist/types/local/qr.d.ts +127 -0
  13. package/dist/types/local/wordlist.d.ts +26 -0
  14. package/dist/types/resources/account.d.ts +296 -0
  15. package/dist/types/resources/chests.d.ts +194 -0
  16. package/dist/types/resources/dynamicQrs.d.ts +172 -0
  17. package/dist/types/resources/forms.d.ts +331 -0
  18. package/dist/types/resources/index.d.ts +30 -0
  19. package/dist/types/resources/ipLookup.d.ts +63 -0
  20. package/dist/types/resources/jobs.d.ts +233 -0
  21. package/dist/types/resources/linkTrees.d.ts +249 -0
  22. package/dist/types/resources/notepads.d.ts +96 -0
  23. package/dist/types/resources/shortLinks.d.ts +248 -0
  24. package/dist/types/resources/storage/upload.d.ts +459 -0
  25. package/dist/types/resources/storage.d.ts +527 -0
  26. package/dist/types/resources/tickets.d.ts +236 -0
  27. package/dist/types/resources/tools/backgroundRemoval.d.ts +99 -0
  28. package/dist/types/resources/tools/captions.d.ts +318 -0
  29. package/dist/types/resources/tools/downloader.d.ts +397 -0
  30. package/dist/types/resources/tools/index.d.ts +215 -0
  31. package/dist/types/resources/tools/jumpstyle.d.ts +194 -0
  32. package/dist/types/resources/tools/transcription.d.ts +178 -0
  33. package/dist/types/resources/tools/upscale.d.ts +94 -0
  34. package/dist/types/resources/tools/vocalSeparation.d.ts +183 -0
  35. package/dist/types/types.d.ts +245 -0
  36. package/package.json +37 -0
@@ -0,0 +1,331 @@
1
+ /**
2
+ * The `forms` namespace: hosted forms and the submissions they collect.
3
+ *
4
+ * A form has an owner-facing side (create, edit the schema, read submissions)
5
+ * and a public side addressed by ENDPOINT rather than by id
6
+ * (`GET /forms/by_endpoint/:endpoint`, `POST /form_submissions`), which is what
7
+ * an anonymous respondent hits. The SDK exposes both; the public calls work
8
+ * without a credential unless the form turns `settings.require_login` on.
9
+ *
10
+ * The endpoint is not a column on the form: it lives on a short link paired
11
+ * with it, which is why renaming it is a real operation and why availability
12
+ * has its own lookup.
13
+ */
14
+ import { type ApiClient, Resource } from "../http";
15
+ import type { BaseRecord, FileInput, Id, Json, RequestOptions, Timestamp } from "../types";
16
+ /**
17
+ * Publication state. Only `"published"` answers on the public side; `"draft"`
18
+ * and `"archived"` both 404 there.
19
+ */
20
+ export type FormStatus = "draft" | "published" | "archived";
21
+ /** Kinds of field the builder understands. Anything else is dropped. */
22
+ export type FormSchemaFieldType = "short_text" | "long_text" | "email" | "number" | "single_choice" | "multi_choice" | "dropdown" | "statement" | "image";
23
+ /** One choice of a `single_choice`, `multi_choice` or `dropdown` field. */
24
+ export interface FormSchemaFieldOption {
25
+ /** Stable within the form. The server mints one when you leave it out. */
26
+ readonly id?: string;
27
+ readonly label: string;
28
+ }
29
+ /**
30
+ * One field of a form.
31
+ *
32
+ * `id` is the key answers are filed under - NOT the label. Leave it out on
33
+ * create and the server mints a UUID, which you then have to read back before
34
+ * you can submit anything.
35
+ */
36
+ export interface FormSchemaField {
37
+ readonly id?: string;
38
+ readonly type: FormSchemaFieldType;
39
+ readonly label: string;
40
+ readonly description?: string;
41
+ readonly required?: boolean;
42
+ readonly placeholder?: string;
43
+ /** Only meaningful on the three choice types. */
44
+ readonly options?: FormSchemaFieldOption[];
45
+ /** Only meaningful on `number`. */
46
+ readonly min?: number;
47
+ /** Only meaningful on `number`. */
48
+ readonly max?: number;
49
+ }
50
+ /**
51
+ * The field definition of a form. The server rebuilds this from scratch on
52
+ * every write, keeping only the keys above, so anything extra is lost without
53
+ * a word.
54
+ */
55
+ export interface FormSchema {
56
+ readonly fields: FormSchemaField[];
57
+ }
58
+ /**
59
+ * Styling of a form. Colours must be `#RRGGBB`; images must be `data:image/`
60
+ * URIs under 2 MB. Anything else in here is dropped silently.
61
+ */
62
+ export interface FormTheme {
63
+ readonly bg_color?: string;
64
+ readonly fg_color?: string;
65
+ readonly accent_color?: string;
66
+ readonly card_color?: string;
67
+ readonly mode?: "light" | "dark" | "custom";
68
+ readonly font?: "inter" | "cantarell" | "system";
69
+ /** `data:image/...` or `null` to clear. */
70
+ readonly bg_image?: string | null;
71
+ /** `data:image/...` or `null` to clear. */
72
+ readonly logo_image?: string | null;
73
+ }
74
+ /** Behaviour switches. Like the theme, unknown keys are dropped. */
75
+ export interface FormSettings {
76
+ readonly layout?: "one_per_screen" | "single_page";
77
+ /** Makes the public form and its submissions require a credential. */
78
+ readonly require_login?: boolean;
79
+ readonly show_progress?: boolean;
80
+ readonly collect_email?: boolean;
81
+ /** These five are truncated to 500 characters. */
82
+ readonly welcome_title?: string;
83
+ readonly welcome_subtitle?: string;
84
+ readonly submit_label?: string;
85
+ readonly thank_you_title?: string;
86
+ readonly thank_you_subtitle?: string;
87
+ }
88
+ /** A hosted form, owner view. */
89
+ export interface Form extends BaseRecord {
90
+ readonly user_id: Id;
91
+ readonly title: string;
92
+ readonly status: FormStatus;
93
+ readonly schema: FormSchema;
94
+ readonly theme: FormTheme;
95
+ readonly settings: FormSettings;
96
+ /** Public path segment, e.g. `"contacto"`. `null` if the pairing is broken. */
97
+ readonly endpoint: string | null;
98
+ /** Shareable short URL, or `null` while there is no endpoint. */
99
+ readonly published_url: string | null;
100
+ /** Set the first time the form is published, and never cleared. */
101
+ readonly published_at?: Timestamp | null;
102
+ readonly views_count: number;
103
+ readonly submissions_count: number;
104
+ }
105
+ /** The reduced form a public respondent is allowed to see. */
106
+ export interface PublicForm {
107
+ readonly id: Id;
108
+ readonly title: string;
109
+ readonly schema: FormSchema;
110
+ readonly theme: FormTheme;
111
+ readonly settings: FormSettings;
112
+ readonly endpoint: string;
113
+ readonly require_login: boolean;
114
+ }
115
+ /**
116
+ * One answered form.
117
+ *
118
+ * Deliberately has no `updated_at`: a submission is never edited, and the
119
+ * blueprint leaves the field out rather than render a lie.
120
+ */
121
+ export interface FormSubmission {
122
+ readonly id: Id;
123
+ readonly form_id: Id;
124
+ /** The respondent, when they were signed in. */
125
+ readonly user_id?: Id | null;
126
+ /**
127
+ * Answers keyed by {@link FormSchemaField.id}. An `image` answer is
128
+ * `{ attachment_id, filename }`; a `number` is a float; a `multi_choice` is
129
+ * an array of strings; everything else is a string.
130
+ */
131
+ readonly answers: Record<string, Json>;
132
+ /** Resolved from the respondent's IP. */
133
+ readonly country?: string | null;
134
+ /** Parsed from the respondent's user agent. */
135
+ readonly device_name?: string | null;
136
+ readonly completed_at?: Timestamp | null;
137
+ readonly created_at: Timestamp;
138
+ }
139
+ /** What `POST /form_attachments` answers with. */
140
+ export interface FormAttachment {
141
+ readonly id: Id;
142
+ readonly filename: string;
143
+ readonly content_type: string;
144
+ /** Absolute URL that serves the bytes inline, no credential required. */
145
+ readonly url: string;
146
+ }
147
+ /**
148
+ * `GET /forms/endpoint_availability`. Read `available`; `suggestions` is only
149
+ * present when the endpoint is well formed but taken.
150
+ */
151
+ export interface FormEndpointAvailability {
152
+ readonly endpoint: string;
153
+ /** Whether it matches the format and is not reserved. */
154
+ readonly valid: boolean;
155
+ readonly available: boolean;
156
+ /** `"invalid"` when the format or the reserved list rejected it. */
157
+ readonly reason?: string;
158
+ readonly suggestions?: string[];
159
+ }
160
+ /**
161
+ * Arguments for creating a form.
162
+ *
163
+ * The form always starts as a `"draft"`: `status` is not writable here, only
164
+ * through {@link UpdateFormInput}. `require_login` lives in `settings`.
165
+ */
166
+ export interface CreateFormInput {
167
+ readonly title: string;
168
+ /**
169
+ * Public path segment. Lowercased, 1 to 64 characters of `[a-z0-9_-]`
170
+ * starting and ending alphanumeric, and never one of `new create index
171
+ * admin api login signup help`.
172
+ */
173
+ readonly endpoint: string;
174
+ readonly schema?: FormSchema;
175
+ readonly theme?: FormTheme;
176
+ readonly settings?: FormSettings;
177
+ }
178
+ /**
179
+ * Fields that can change afterwards. A true PATCH: only the keys you pass are
180
+ * touched, and each of `schema`, `theme` and `settings` is replaced wholesale
181
+ * rather than merged.
182
+ */
183
+ export interface UpdateFormInput {
184
+ readonly title?: string;
185
+ /** Renames the paired short link, so the old public URL stops working. */
186
+ readonly endpoint?: string;
187
+ readonly schema?: FormSchema;
188
+ readonly theme?: FormTheme;
189
+ readonly settings?: FormSettings;
190
+ /**
191
+ * An unrecognised status is IGNORED in silence and still answers 200 with
192
+ * the old value, so read the returned form rather than trusting the code.
193
+ */
194
+ readonly status?: FormStatus;
195
+ }
196
+ /** Arguments for answering a form. */
197
+ export interface SubmitFormInput {
198
+ /** The form's public endpoint, not its id. */
199
+ readonly endpoint: string;
200
+ /**
201
+ * Answers keyed by {@link FormSchemaField.id}. Keys that are not in the
202
+ * schema are discarded in silence, so send ids, never labels.
203
+ */
204
+ readonly answers: Record<string, Json>;
205
+ /**
206
+ * Files for `image` fields, keyed by the same field id. The SDK uploads each
207
+ * one to `POST /form_attachments` first and folds the resulting
208
+ * `{ attachment_id }` into `answers`, which is the shape the endpoint reads.
209
+ *
210
+ * Authenticated callers, in practice. Each of those uploads is a separate
211
+ * request an anonymous caller has to pass a captcha for, and a Turnstile
212
+ * token is single-use: one token cannot cover both an upload and the
213
+ * submission. An anonymous caller uploads each file through
214
+ * {@link FormSubmissionsNamespace.uploadAttachment} with its own fresh
215
+ * token, then writes `{ attachment_id }` into `answers` by hand.
216
+ */
217
+ readonly files?: Record<string, FileInput>;
218
+ /**
219
+ * Turnstile token. Required while the caller is anonymous, ignored
220
+ * otherwise. Single-use: one token pays for one request.
221
+ */
222
+ readonly captchaToken?: string;
223
+ }
224
+ /** Arguments for staging one file against a public form. */
225
+ export interface UploadFormAttachmentInput {
226
+ /** The form's public endpoint. */
227
+ readonly endpoint: string;
228
+ /** JPEG, PNG, WebP, GIF or HEIC, up to 8 MB. */
229
+ readonly file: FileInput;
230
+ /**
231
+ * Turnstile token. Required while the caller is anonymous, ignored
232
+ * otherwise. Single-use: one token pays for one upload.
233
+ */
234
+ readonly captchaToken?: string;
235
+ }
236
+ /** Filters for {@link FormSubmissionsNamespace.list}. */
237
+ export interface ListFormSubmissionsParams {
238
+ readonly formId: Id;
239
+ }
240
+ /** Submissions of a form, reachable as `oms.forms.submissions`. */
241
+ export declare class FormSubmissionsNamespace extends Resource {
242
+ /**
243
+ * `GET /form_submissions?form_id=...` - the answers, newest first. Owner
244
+ * only.
245
+ *
246
+ * Not paginated: the endpoint takes no page modifier and hard-caps itself at
247
+ * the 500 most recent, so a busy form silently loses its tail. That is why
248
+ * this returns an array rather than a `Paginated` that could never advance.
249
+ */
250
+ list(params: ListFormSubmissionsParams, options?: RequestOptions): Promise<FormSubmission[]>;
251
+ /**
252
+ * `POST /form_submissions` - answers a published form.
253
+ *
254
+ * Anonymous unless the form sets `settings.require_login`, in which case an
255
+ * anonymous caller gets 401. Answers are coerced to the type each field
256
+ * declares and the whole payload is capped at 200 000 bytes of JSON.
257
+ *
258
+ * Answers only the new submission's id: the server does not echo the record
259
+ * back, and the respondent has no permission to read it afterwards.
260
+ *
261
+ * Not retried by default: a replayed submission would be recorded twice.
262
+ */
263
+ create(input: SubmitFormInput, options?: RequestOptions): Promise<{
264
+ id: Id;
265
+ }>;
266
+ /**
267
+ * `POST /form_attachments` - stages one image against a published form and
268
+ * hands back the id an `image` answer refers to.
269
+ *
270
+ * Called for you by {@link create} when you pass `files`; reach for it
271
+ * directly only to upload before the rest of the answers are ready.
272
+ * Throttled to 30 an hour per IP for anonymous callers.
273
+ */
274
+ uploadAttachment(input: UploadFormAttachmentInput, options?: RequestOptions): Promise<FormAttachment>;
275
+ }
276
+ /** The `forms` namespace, reachable as `oms.forms`. */
277
+ export declare class FormsNamespace extends Resource {
278
+ /** Answers to a form, and the files staged against one. */
279
+ readonly submissions: FormSubmissionsNamespace;
280
+ constructor(http: ApiClient);
281
+ /**
282
+ * `GET /forms` - the forms you own, most recently edited first, each with
283
+ * its submission count.
284
+ *
285
+ * Not paginated and not filterable: the endpoint takes no modifiers and
286
+ * answers with the whole set.
287
+ */
288
+ list(options?: RequestOptions): Promise<Form[]>;
289
+ /** `GET /forms/:id` - the full definition, owner view. */
290
+ get(id: Id, options?: RequestOptions): Promise<Form>;
291
+ /**
292
+ * `GET /forms/by_endpoint/:endpoint` - the respondent's view. Answers only
293
+ * for a published form; a draft is a 404.
294
+ *
295
+ * Every call INCREMENTS the form's `views_count`. Do not use it to poll for
296
+ * changes, and do not call it on behalf of the owner: use {@link get}.
297
+ *
298
+ * @throws {OmsAuthError} 401 when the form requires a login and there is none.
299
+ */
300
+ getPublic(endpoint: string, options?: RequestOptions): Promise<PublicForm>;
301
+ /**
302
+ * `GET /forms/endpoint_availability` - whether an endpoint is free, with
303
+ * alternatives when it is not. The server is still the authority: create can
304
+ * lose a race and answer 400.
305
+ */
306
+ endpointAvailability(endpoint: string, options?: RequestOptions): Promise<FormEndpointAvailability>;
307
+ /** {@link endpointAvailability} reduced to its verdict. */
308
+ endpointAvailable(endpoint: string, options?: RequestOptions): Promise<boolean>;
309
+ /**
310
+ * `POST /forms` - creates the form and reserves its endpoint in one
311
+ * transaction. The form starts as a draft; publish it with
312
+ * {@link update}.
313
+ *
314
+ * Not retried by default: a replayed create would fail on the endpoint being
315
+ * taken by its own first attempt.
316
+ */
317
+ create(input: CreateFormInput, options?: RequestOptions): Promise<Form>;
318
+ /** `PATCH /forms/:id`. Only the keys you pass are touched. */
319
+ update(id: Id, input: UpdateFormInput, options?: RequestOptions): Promise<Form>;
320
+ /** Convenience over {@link update} with `status: "published"`. */
321
+ publish(id: Id, options?: RequestOptions): Promise<Form>;
322
+ /** `DELETE /forms/:id`. Takes the submissions and the endpoint with it. */
323
+ delete(id: Id, options?: RequestOptions): Promise<void>;
324
+ /**
325
+ * `GET /form_attachments/:id` - one file a respondent uploaded.
326
+ *
327
+ * Served inline with `X-Content-Type-Options: nosniff`, and readable by
328
+ * anyone holding the id.
329
+ */
330
+ attachment(attachmentId: Id, options?: RequestOptions): Promise<Blob>;
331
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Every resource namespace, in one place.
3
+ *
4
+ * `client.ts` imports the namespace classes from here indirectly (it imports
5
+ * the modules themselves, so a namespace can be dropped in without editing an
6
+ * aggregator), and consumers import the record and input types from here.
7
+ *
8
+ * Re-exports every sibling module so nobody has to touch this file again: a new
9
+ * resource means one new file plus one line in {@link Oms}, and no edit to a
10
+ * file another agent may also be holding.
11
+ *
12
+ * Naming rule that keeps `export *` unambiguous: every exported name is
13
+ * prefixed by its domain (`ShortLink`, `ShortLinkStats`, `CreateShortLinkInput`).
14
+ * A bare `Stats` or `CreateInput` would collide the moment a second resource
15
+ * wanted one, and TypeScript reports that as an error at this file, not at the
16
+ * file that caused it.
17
+ */
18
+ export * from "./account";
19
+ export * from "./chests";
20
+ export * from "./dynamicQrs";
21
+ export * from "./forms";
22
+ export * from "./ipLookup";
23
+ export * from "./jobs";
24
+ export * from "./linkTrees";
25
+ export * from "./notepads";
26
+ export * from "./shortLinks";
27
+ export * from "./storage";
28
+ export * from "./storage/upload";
29
+ export * from "./tickets";
30
+ export * from "./tools/index";
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The `ipLookup` namespace: network metadata for an IP address.
3
+ *
4
+ * Works anonymously. The special argument `"mine"` asks the backend what IP it
5
+ * sees the caller coming from, which is the only way for a client behind NAT
6
+ * or a proxy to learn its own public address.
7
+ *
8
+ * The name promises more than the service delivers, and it is worth knowing
9
+ * before you build a UI on it: the database behind `IpLookuper` is built by
10
+ * `backend/bin/create_mmdb` from the iptoasn.com IP-to-ASN table, NOT from a
11
+ * GeoIP City database. It knows the country, the autonomous system and the
12
+ * network - it does not know the city, the coordinates or the timezone, and no
13
+ * amount of asking will make those appear.
14
+ */
15
+ import { Resource } from "../http";
16
+ import type { RequestOptions } from "../types";
17
+ /**
18
+ * What the lookup service knows about an address.
19
+ *
20
+ * Every field comes straight out of the MMDB record, so the shape is exactly
21
+ * what the backend read: no field is computed and none is filled in for a
22
+ * missing row - a miss is a 400, not a half-empty object.
23
+ */
24
+ export interface IpLookupResult {
25
+ /** The address that was looked up, echoed back. For `"mine"`, the resolved one. */
26
+ readonly ip: string;
27
+ /**
28
+ * ISO 3166-1 alpha-2, **lowercase** (`"pt"`, `"us"`), or the literal
29
+ * `"unknown"` when the source table had no country for the range. Uppercase
30
+ * it yourself before feeding it to a flag or a locale lookup.
31
+ */
32
+ readonly country: string;
33
+ /** Autonomous system number, or `0` when the range is not announced. */
34
+ readonly asn: number;
35
+ /** Network operator name as the AS registry spells it, e.g. `"GOOGLE"`. */
36
+ readonly organization: string;
37
+ /** The CIDR block the address fell in, e.g. `"8.8.8.0/24"`. */
38
+ readonly network: string;
39
+ }
40
+ /** The `ipLookup` namespace, reachable as `oms.ipLookup`. */
41
+ export declare class IpLookupNamespace extends Resource {
42
+ /**
43
+ * `GET /ip_lookup/:ip` - looks up any IPv4 or IPv6 address.
44
+ *
45
+ * Anonymous. Counts against the general anonymous ceiling (120/min per IP)
46
+ * or the authenticated one (600/min per session) when a token is set.
47
+ *
48
+ * @param ip The address, or the literal `"mine"` (prefer {@link mine} for
49
+ * that, it reads better at the call site).
50
+ * @throws {OmsApiError} 400 `Invalid IP address` when the argument does not
51
+ * parse as an address, and also when it parses but the database has no row
52
+ * for it. The two cases are not distinguishable from the response.
53
+ */
54
+ get(ip: string, options?: RequestOptions): Promise<IpLookupResult>;
55
+ /**
56
+ * `GET /ip_lookup/mine` - the public address the backend sees this client
57
+ * arriving from.
58
+ *
59
+ * Behind Cloudflare the backend reads `CF-Connecting-IP`, so this is the
60
+ * client's real address, not the edge's.
61
+ */
62
+ mine(options?: RequestOptions): Promise<IpLookupResult>;
63
+ }
@@ -0,0 +1,233 @@
1
+ /**
2
+ * The `jobs` namespace: the one place a background job is polled.
3
+ *
4
+ * Every asynchronous tool (upscale, background removal, transcription, vocal
5
+ * separation, captions, jumpstyle) enqueues a job and hands back a `job_id`
6
+ * plus, for an anonymous caller, a `watch_token`. That token grants read access
7
+ * to exactly one job and is the reason `GET /jobs/:id` is reachable without a
8
+ * credential.
9
+ *
10
+ * Every tool namespace that returns a job delegates its `wait()` to
11
+ * {@link JobsNamespace.wait}, so the polling policy lives here once. Do not
12
+ * write a second polling loop inside a tool module.
13
+ *
14
+ * Only two tools enqueue through the generic `jobs` table - background removal
15
+ * and upscale. The other five are polled by re-reading their own row, which is
16
+ * still not a reason to write a loop there: {@link pollUntilTerminal} is the
17
+ * same engine with a different `poll` function, and that is what those modules
18
+ * call.
19
+ *
20
+ * The loop is deliberately dumb and bounded:
21
+ *
22
+ * - the first poll happens immediately, with no initial pause;
23
+ * - each pause is {@link POLL_BACKOFF_FACTOR} times the last, capped at
24
+ * {@link MAX_POLL_INTERVAL_MS}, so a five-second job costs a handful of
25
+ * requests and a ninety-minute one does not cost eleven thousand;
26
+ * - there is no jitter. A poll loop is one client watching one job; the place a
27
+ * herd actually forms is a 429, and `Retry-After` is honoured by the
28
+ * transport, which is where that belongs;
29
+ * - `waitTimeoutMs` is the caller's deadline and has NO default. A job that the
30
+ * server never terminalises would otherwise hang an isolate forever, so pass
31
+ * one - or a `signal` - whenever the caller is not a person watching a
32
+ * terminal.
33
+ *
34
+ * Three outcomes, and they are different types on purpose:
35
+ *
36
+ * | What happened | How it arrives |
37
+ * |---|---|
38
+ * | The work finished, well or badly | resolves with a {@link Job}; read `status` |
39
+ * | The client gave up first | throws {@link OmsTimeoutError} with `code: "timeout"` |
40
+ * | The caller aborted | throws {@link OmsTimeoutError} with `code: "aborted"` |
41
+ *
42
+ * A job that ends `"failed"` is an ANSWER, not a transport error: the request
43
+ * cycle worked perfectly and the work did not. Only the caller knows whether
44
+ * that deserves an exception.
45
+ */
46
+ import { Resource } from "../http";
47
+ import type { BaseRecord, Id, Json, JobStatus, Paginated, PageParams, Progress, RequestOptions, Timestamp, WaitOptions } from "../types";
48
+ /**
49
+ * The five status strings, spelled the way the backend spells them.
50
+ *
51
+ * `complete`, not `completed`. `canceled`, one L. Reach for this object instead
52
+ * of typing the literal: a loop that waits for `"completed"` waits forever.
53
+ */
54
+ export declare const JOB_STATUS: Readonly<{
55
+ readonly pending: "pending";
56
+ readonly processing: "processing";
57
+ readonly complete: "complete";
58
+ readonly failed: "failed";
59
+ readonly canceled: "canceled";
60
+ }>;
61
+ /**
62
+ * Statuses a job never leaves.
63
+ *
64
+ * Note `canceled` is here and has no equivalent on a tool row: a job can be
65
+ * cancelled out from under a tool whose own row is still `"pending"`.
66
+ */
67
+ export declare const JOB_TERMINAL_STATUSES: readonly JobStatus[];
68
+ /** True once this status can never change again. */
69
+ export declare function isJobTerminal(status: string): boolean;
70
+ /** A background job. */
71
+ export interface Job extends BaseRecord {
72
+ readonly status: JobStatus;
73
+ /**
74
+ * Feature-level kind of the run. Today the backend only ever writes
75
+ * `"omsvs"` (vocal separation) or `"unknown"`, which is the column default
76
+ * every generic enqueue gets - including the upscale and background-removal
77
+ * proxies. It is NOT the worker's class name.
78
+ */
79
+ readonly job_type: string;
80
+ /** Enqueue-time arguments, when the enqueuer wrote any. Shape depends on `job_type`. */
81
+ readonly payload?: Json;
82
+ /** Set when a worker claimed the job. */
83
+ readonly started_at?: Timestamp | null;
84
+ readonly finished_at?: Timestamp | null;
85
+ /**
86
+ * Percentage, an integer in `[0, 100]`. The column is `NOT NULL DEFAULT 0`,
87
+ * so it is a real number from the moment the row exists and 0 means "not
88
+ * started", never "unknown".
89
+ */
90
+ readonly progress?: number | null;
91
+ /** Failure message, set once `status === "failed"` (or a cancellation reason). */
92
+ readonly error?: string | null;
93
+ /** Worker-specific payload; shape depends on `job_type`. */
94
+ readonly result?: Json;
95
+ /** Who enqueued it. `null` for a job with no owner, e.g. an anonymous tool run. */
96
+ readonly creator_id?: Id | null;
97
+ /** Which worker claimed it. */
98
+ readonly worker_id?: string | null;
99
+ }
100
+ /**
101
+ * How to address a job.
102
+ *
103
+ * An authenticated caller needs only the id. An anonymous one must also present
104
+ * the `watchToken` the tool handed back at enqueue time.
105
+ */
106
+ export interface JobRef {
107
+ readonly id: Id;
108
+ /** Signed token scoped to this one job. Required when anonymous. */
109
+ readonly watchToken?: string;
110
+ }
111
+ /** Filters for {@link JobsNamespace.list}. */
112
+ export interface ListJobsParams extends PageParams {
113
+ readonly status?: JobStatus | JobStatus[];
114
+ readonly jobType?: string | string[];
115
+ }
116
+ /** Pause before the second poll, in milliseconds. */
117
+ export declare const DEFAULT_POLL_INTERVAL_MS = 1000;
118
+ /** Ceiling for one pause, in milliseconds. */
119
+ export declare const MAX_POLL_INTERVAL_MS = 15000;
120
+ /** Each pause is this many times the last one, until the ceiling. */
121
+ export declare const POLL_BACKOFF_FACTOR = 1.5;
122
+ /**
123
+ * Everything {@link pollUntilTerminal} needs to watch something finish.
124
+ *
125
+ * Generic over the record because the seven tools are polled two different
126
+ * ways - two through `GET /jobs/:id`, five through their own `show` - and only
127
+ * `poll` and `terminal` differ between them. Nothing else about the loop does,
128
+ * which is exactly why there is one loop.
129
+ */
130
+ export interface PollUntilTerminalOptions<T> extends WaitOptions {
131
+ /**
132
+ * Reads the current state. Called once immediately, then again after every
133
+ * pause. It is handed the request-shaped half of these options (signal,
134
+ * per-request timeout, headers, retry), never the wait-shaped half.
135
+ */
136
+ readonly poll: (options: RequestOptions) => Promise<T>;
137
+ /** True once `state` can never change again. */
138
+ readonly terminal: (state: T) => boolean;
139
+ /**
140
+ * Maps a state onto a {@link Progress} for `onProgress`. Its `status` is also
141
+ * what the timeout message quotes, so a run that gave up says what it was
142
+ * last doing.
143
+ */
144
+ readonly progress?: (state: T) => Progress;
145
+ /** What is being waited on, for the timeout message: `"job 3f2a"`. */
146
+ readonly label?: string;
147
+ }
148
+ /**
149
+ * Polls until `terminal` says so, then returns the final state.
150
+ *
151
+ * This is THE polling loop of the SDK. A resource module supplies `poll` and
152
+ * `terminal`; it does not supply a `while`.
153
+ *
154
+ * @throws {OmsTimeoutError} `code: "timeout"` when `waitTimeoutMs` elapsed,
155
+ * `code: "aborted"` when the caller's signal fired.
156
+ * @throws {OmsApiError} whatever `poll` throws - a 404 once a finished job's
157
+ * 24-hour retention expires, a 401 for an anonymous caller with no watch
158
+ * token. A polling loop is not the place to swallow those.
159
+ */
160
+ export declare function pollUntilTerminal<T>(options: PollUntilTerminalOptions<T>): Promise<T>;
161
+ /**
162
+ * Same loop as {@link pollUntilTerminal}, but yields every state it observes -
163
+ * including the terminal one, which is also the generator's return value.
164
+ *
165
+ * For a host that would rather render each step than take a callback. Breaking
166
+ * out of the `for await` stops the loop; nothing is left running.
167
+ */
168
+ export declare function watchUntilTerminal<T>(options: PollUntilTerminalOptions<T>): AsyncGenerator<T, T, undefined>;
169
+ /** The `jobs` namespace, reachable as `oms.jobs`. */
170
+ export declare class JobsNamespace extends Resource {
171
+ /**
172
+ * `GET /jobs` - your jobs. Requires a credential; a watch token cannot list.
173
+ *
174
+ * An admin sees every job, a signed-in user sees the ones they created, and
175
+ * an anonymous caller sees an empty page - never a 401, because the scope is
176
+ * empty rather than forbidden.
177
+ *
178
+ * @throws {OmsApiError} 400 naming the key when a filter is not on the
179
+ * controller's allowlist (`id`, `job_type`, `status`, `created_at`,
180
+ * `updated_at`, `finished_at`).
181
+ */
182
+ list(params?: ListJobsParams, options?: RequestOptions): Promise<Paginated<Job>>;
183
+ /**
184
+ * `GET /jobs/:id` - one poll, no waiting.
185
+ *
186
+ * A `watchToken` is sent as `?watch_token=`, which is the only way an
187
+ * anonymous caller reaches a job. The server checks the signature resolves to
188
+ * the job named in the path, so the token cannot be pointed at another id.
189
+ *
190
+ * @throws {OmsApiError} 404 when the job is gone, which for a finished job
191
+ * also happens once its retention window expires. A wrong, expired or
192
+ * missing watch token is the same 404, not a 401: the controller never says
193
+ * whether the id exists.
194
+ */
195
+ get(ref: JobRef | Id, options?: RequestOptions): Promise<Job>;
196
+ /**
197
+ * Polls until the job reaches a terminal state, then returns it.
198
+ *
199
+ * The polling policy belongs here, not in the callers: start at
200
+ * `pollIntervalMs`, back off towards a ceiling, stop at `waitTimeoutMs`, and
201
+ * abort immediately if the caller's `signal` fires. `onProgress` is called on
202
+ * every poll so a host can drive a spinner.
203
+ *
204
+ * Resolves for `"complete"`, `"failed"` AND `"canceled"` - a job that ended
205
+ * badly is an answer, not a transport error. Check `job.status` before
206
+ * reading `job.result`.
207
+ *
208
+ * @throws {OmsTimeoutError} `code: "timeout"` when `waitTimeoutMs` elapses
209
+ * first, `code: "aborted"` when the caller's signal fires.
210
+ */
211
+ wait(ref: JobRef | Id, options?: WaitOptions): Promise<Job>;
212
+ /**
213
+ * Yields the job's state on every poll until it finishes, for a host that
214
+ * wants to render each step rather than take a callback.
215
+ *
216
+ * The terminal state is both the last value yielded and the generator's
217
+ * return value, so neither `for await` nor a manual `next()` loop can miss
218
+ * it.
219
+ */
220
+ watch(ref: JobRef | Id, options?: WaitOptions): AsyncGenerator<Job, Job, undefined>;
221
+ /** The one description of "watching a job", shared by `wait` and `watch`. */
222
+ private pollPlan;
223
+ }
224
+ /** Normalises the two ways a job can be addressed into the object form. */
225
+ export declare function jobRef(ref: JobRef | Id): JobRef;
226
+ /**
227
+ * Renders a job as a {@link Progress}.
228
+ *
229
+ * `total` is 100 rather than `undefined` because `progress` is a percentage the
230
+ * server always has: the column is `NOT NULL DEFAULT 0`, so there is no
231
+ * "unknown" to be honest about.
232
+ */
233
+ export declare function jobProgress(job: Job): Progress;