@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.
- package/README.md +112 -130
- package/dist/index.js +4800 -610
- package/dist/types/client.d.ts +60 -3
- package/dist/types/http.d.ts +444 -19
- package/dist/types/index.d.ts +5 -1
- package/dist/types/internal/attachments.d.ts +17 -0
- package/dist/types/internal/helpers.d.ts +9 -0
- package/dist/types/listing.d.ts +90 -0
- package/dist/types/resources/account.d.ts +71 -5
- package/dist/types/resources/admin/authorizedApplications.d.ts +133 -0
- package/dist/types/resources/admin/chests.d.ts +51 -0
- package/dist/types/resources/admin/eventAlerts.d.ts +60 -0
- package/dist/types/resources/admin/identities.d.ts +99 -0
- package/dist/types/resources/admin/index.d.ts +77 -0
- package/dist/types/resources/admin/jobs.d.ts +97 -0
- package/dist/types/resources/admin/myOauthApplications.d.ts +282 -0
- package/dist/types/resources/admin/notepads.d.ts +49 -0
- package/dist/types/resources/admin/oauthApplications.d.ts +309 -0
- package/dist/types/resources/admin/quotas.d.ts +114 -0
- package/dist/types/resources/admin/shortLinks.d.ts +201 -0
- package/dist/types/resources/admin/types.d.ts +222 -0
- package/dist/types/resources/admin/vocalSeparations.d.ts +98 -0
- package/dist/types/resources/admin.d.ts +1841 -0
- package/dist/types/resources/auth/index.d.ts +39 -0
- package/dist/types/resources/auth/passkeys.d.ts +652 -0
- package/dist/types/resources/auth/sessions.d.ts +844 -0
- package/dist/types/resources/chests.d.ts +54 -3
- package/dist/types/resources/content/analysis.d.ts +62 -0
- package/dist/types/resources/content/blogs.d.ts +471 -0
- package/dist/types/resources/content/feedbacks.d.ts +263 -0
- package/dist/types/resources/content/index.d.ts +52 -0
- package/dist/types/resources/content/intel/articles.d.ts +230 -0
- package/dist/types/resources/content/intel/config.d.ts +135 -0
- package/dist/types/resources/content/intel/index.d.ts +53 -0
- package/dist/types/resources/content/intel/items.d.ts +91 -0
- package/dist/types/resources/content/intel/reports.d.ts +108 -0
- package/dist/types/resources/content/intel/scripts.d.ts +152 -0
- package/dist/types/resources/content/intel/sources.d.ts +236 -0
- package/dist/types/resources/content/intel/stats.d.ts +105 -0
- package/dist/types/resources/content/intel/types.d.ts +86 -0
- package/dist/types/resources/content/jokes.d.ts +93 -0
- package/dist/types/resources/content/notifications.d.ts +147 -0
- package/dist/types/resources/content/serviceUsages.d.ts +73 -0
- package/dist/types/resources/content/servicesStatus.d.ts +194 -0
- package/dist/types/resources/content/siteConfig.d.ts +43 -0
- package/dist/types/resources/content/spaceInvaders.d.ts +154 -0
- package/dist/types/resources/content.d.ts +2775 -0
- package/dist/types/resources/dynamicQrs.d.ts +39 -3
- package/dist/types/resources/forms.d.ts +176 -35
- package/dist/types/resources/index.d.ts +19 -8
- package/dist/types/resources/ipLookup.d.ts +20 -4
- package/dist/types/resources/jobs.d.ts +69 -25
- package/dist/types/resources/library/annotations.d.ts +162 -0
- package/dist/types/resources/library/books.d.ts +534 -0
- package/dist/types/resources/library/chat.d.ts +259 -0
- package/dist/types/resources/library/index.d.ts +39 -0
- package/dist/types/resources/library/shelves.d.ts +237 -0
- package/dist/types/resources/library/types.d.ts +120 -0
- package/dist/types/resources/library.d.ts +1433 -0
- package/dist/types/resources/linkTrees.d.ts +142 -30
- package/dist/types/resources/media.d.ts +351 -0
- package/dist/types/resources/movies/addons.d.ts +388 -0
- package/dist/types/resources/movies/collections.d.ts +352 -0
- package/dist/types/resources/movies/index.d.ts +29 -0
- package/dist/types/resources/movies/types.d.ts +50 -0
- package/dist/types/resources/movies/watchProgress.d.ts +277 -0
- package/dist/types/resources/movies.d.ts +1160 -0
- package/dist/types/resources/music/artists.d.ts +1068 -0
- package/dist/types/resources/music/imports.d.ts +941 -0
- package/dist/types/resources/music/index.d.ts +61 -0
- package/dist/types/resources/music/playlists.d.ts +1027 -0
- package/dist/types/resources/music/social.d.ts +1132 -0
- package/dist/types/resources/music/songs.d.ts +1167 -0
- package/dist/types/resources/notepads.d.ts +4 -1
- package/dist/types/resources/quotas.d.ts +7 -1
- package/dist/types/resources/realtime.d.ts +855 -0
- package/dist/types/resources/shortLinks.d.ts +49 -5
- package/dist/types/resources/social/groupChats.d.ts +458 -0
- package/dist/types/resources/social/index.d.ts +19 -0
- package/dist/types/resources/social/messages.d.ts +424 -0
- package/dist/types/resources/social/relationships.d.ts +322 -0
- package/dist/types/resources/social/types.d.ts +37 -0
- package/dist/types/resources/social.d.ts +1348 -0
- package/dist/types/resources/storage/upload.d.ts +158 -11
- package/dist/types/resources/storage.d.ts +111 -27
- package/dist/types/resources/tickets.d.ts +101 -13
- package/dist/types/resources/tools/backgroundRemoval.d.ts +18 -3
- package/dist/types/resources/tools/captions.d.ts +448 -21
- package/dist/types/resources/tools/downloader.d.ts +21 -0
- package/dist/types/resources/tools/index.d.ts +57 -15
- package/dist/types/resources/tools/jumpstyle.d.ts +50 -17
- package/dist/types/resources/tools/transcription.d.ts +35 -13
- package/dist/types/resources/tools/upscale.d.ts +23 -3
- package/dist/types/resources/tools/vocalSeparation.d.ts +30 -13
- package/dist/types/types.d.ts +249 -17
- package/package.json +2 -1
|
@@ -46,24 +46,57 @@ import { type EditsQuota, type ToolCaptcha, type ToolRecord, type ToolRunOptions
|
|
|
46
46
|
export type JumpstyleDensity = "chill" | "normal" | "hyper";
|
|
47
47
|
/** The three the backend accepts. Anything else is silently read as `"normal"`. */
|
|
48
48
|
export declare const JUMPSTYLE_DENSITIES: readonly JumpstyleDensity[];
|
|
49
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Lifecycle of a jumpstyle edit.
|
|
51
|
+
*
|
|
52
|
+
* Narrower than {@link ToolStatus}: `JumpstyleJob::STATUSES` has only three
|
|
53
|
+
* entries and `"pending"` is not one of them. The controller forwards the files
|
|
54
|
+
* to the sidecar inside the create request and saves the row already
|
|
55
|
+
* `"processing"`, so there is no state in which an edit exists but has not
|
|
56
|
+
* started.
|
|
57
|
+
*/
|
|
58
|
+
export type JumpstyleStatus = "processing" | "complete" | "failed";
|
|
59
|
+
/**
|
|
60
|
+
* A jumpstyle edit.
|
|
61
|
+
*
|
|
62
|
+
* Both routes that answer with one - `POST /jumpstyle_jobs` and
|
|
63
|
+
* `GET /jumpstyle_jobs/:id` - render the `:extended` view, so every key below
|
|
64
|
+
* is present on every response.
|
|
65
|
+
*
|
|
66
|
+
* The first four columns are nullable in the schema but written by the
|
|
67
|
+
* controller on every create, so a row the API can produce always carries
|
|
68
|
+
* them. `density` and `rapid_fire` are `NOT NULL` with defaults on top of that.
|
|
69
|
+
*/
|
|
50
70
|
export interface JumpstyleJob extends ToolRecord {
|
|
51
|
-
readonly
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
readonly
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
readonly status: JumpstyleStatus;
|
|
72
|
+
/** Uploaded track's name; `"track"` when the upload carried none. */
|
|
73
|
+
readonly track_filename: string;
|
|
74
|
+
/** How many clips were sent. At most 20. */
|
|
75
|
+
readonly n_clips: number;
|
|
76
|
+
/** Seconds of output, clamped by the server to `[10, 60]`. */
|
|
77
|
+
readonly duration: number;
|
|
78
|
+
/**
|
|
79
|
+
* The seed that produced this cut. Reuse it to reproduce the edit - it is
|
|
80
|
+
* the ONLY way to, and the server rolls a fresh one whenever it is not
|
|
81
|
+
* given one.
|
|
82
|
+
*/
|
|
83
|
+
readonly seed: number;
|
|
84
|
+
/** `NOT NULL DEFAULT 'normal'`. An unrecognised request value lands here as `"normal"`. */
|
|
85
|
+
readonly density: JumpstyleDensity | string;
|
|
86
|
+
/** `NOT NULL DEFAULT false`. */
|
|
87
|
+
readonly rapid_fire: boolean;
|
|
88
|
+
/** BPM that was forced at create time, or `null` when detection was left to run. */
|
|
89
|
+
readonly bpm: number | null;
|
|
90
|
+
/**
|
|
91
|
+
* While processing: the BPM the sidecar has detected, or `null` if it has not
|
|
92
|
+
* yet. Once the row settles: whatever `bpm` holds, forced or not. So this
|
|
93
|
+
* changes meaning at the moment the run ends.
|
|
94
|
+
*/
|
|
95
|
+
readonly detected_bpm: number | null;
|
|
96
|
+
/** What the sidecar is doing right now, read live. `null` unless processing. */
|
|
97
|
+
readonly stage: string | null;
|
|
98
|
+
/** Signed URL of the finished video once complete and attached, `null` otherwise. */
|
|
99
|
+
readonly output_url: string | null;
|
|
67
100
|
}
|
|
68
101
|
/** Arguments for starting an edit. */
|
|
69
102
|
export interface CreateJumpstyleInput extends ToolCaptcha {
|
|
@@ -36,22 +36,44 @@
|
|
|
36
36
|
import { Resource } from "../../http";
|
|
37
37
|
import type { FileInput, Id, RequestOptions } from "../../types";
|
|
38
38
|
import { type SecondsQuota, type ToolCaptcha, type ToolModel, type ToolRecord, type ToolRunOptions } from "./index";
|
|
39
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* A transcription run.
|
|
41
|
+
*
|
|
42
|
+
* Both routes that answer with one - `POST /transcriptions` and
|
|
43
|
+
* `GET /transcriptions/:id` - render the `:extended` view, so every key below
|
|
44
|
+
* is present on every response, the five `:extended` ones included. What
|
|
45
|
+
* changes as the run progresses is the VALUE, not the key set: polling a
|
|
46
|
+
* pending run and polling a finished one return the same shape.
|
|
47
|
+
*/
|
|
40
48
|
export interface Transcription extends ToolRecord {
|
|
49
|
+
/** Never `null`: the column is `NOT NULL` and the controller rejects an
|
|
50
|
+
* unknown model with a 400 before the row is built. */
|
|
41
51
|
readonly model_id: string;
|
|
42
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Audio duration charged against the quota, rounded UP at create time. Never
|
|
54
|
+
* `null` (`NOT NULL DEFAULT 0`), and it is what was spent even if the run
|
|
55
|
+
* later fails.
|
|
56
|
+
*/
|
|
43
57
|
readonly duration_seconds: number;
|
|
44
|
-
/** Language that was requested, or `null` when it was
|
|
45
|
-
readonly language
|
|
46
|
-
/** Language the model actually detected. */
|
|
47
|
-
readonly detected_language
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
readonly
|
|
53
|
-
/**
|
|
54
|
-
|
|
58
|
+
/** Language that was requested, or `null` when it was left to detection. */
|
|
59
|
+
readonly language: string | null;
|
|
60
|
+
/** Language the model actually detected, or `null` before it has. */
|
|
61
|
+
readonly detected_language: string | null;
|
|
62
|
+
/**
|
|
63
|
+
* Whether the uploaded audio is still attached. A real boolean, never
|
|
64
|
+
* `null`: the field is computed as `original_audio.attached?`.
|
|
65
|
+
*/
|
|
66
|
+
readonly has_original: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* The transcript, or `null`. Shipped ONLY once the status is `"complete"` -
|
|
69
|
+
* deliberately, so that polling a long run does not drag the whole text
|
|
70
|
+
* across the wire every few seconds.
|
|
71
|
+
*/
|
|
72
|
+
readonly text: string | null;
|
|
73
|
+
/** Signed SubRip URL once complete and attached, `null` otherwise. */
|
|
74
|
+
readonly srt_url: string | null;
|
|
75
|
+
/** Signed WebVTT URL once complete and attached, `null` otherwise. */
|
|
76
|
+
readonly vtt_url: string | null;
|
|
55
77
|
}
|
|
56
78
|
/** Arguments for starting a transcription. */
|
|
57
79
|
export interface CreateTranscriptionInput extends ToolCaptcha {
|
|
@@ -19,11 +19,31 @@ import { type ToolCaptcha, type ToolJobHandle, type ToolRecord, type ToolRunOpti
|
|
|
19
19
|
* allow-list of strings; `4` and `"4"` are not the same request.
|
|
20
20
|
*/
|
|
21
21
|
export type UpscaleScale = "2" | "3" | "4";
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* An upscale run.
|
|
24
|
+
*
|
|
25
|
+
* Both routes that answer with one - `POST /upscales` and `GET /upscales/:id` -
|
|
26
|
+
* render the `:extended` view, so `result_url` is always PRESENT and simply
|
|
27
|
+
* `null` until the run completes. There is no default-view variant of this
|
|
28
|
+
* record reachable through the API.
|
|
29
|
+
*
|
|
30
|
+
* `progress_percent`, inherited from {@link ToolRecord}, is never sent for this
|
|
31
|
+
* tool: `UpscaleBlueprint` has no such field. Progress for an upscale lives on
|
|
32
|
+
* the {@link Job} row that {@link UpscaleCreated.job_id} names.
|
|
33
|
+
*/
|
|
23
34
|
export interface Upscale extends ToolRecord {
|
|
35
|
+
/**
|
|
36
|
+
* One of `"2"`, `"3"`, `"4"` - a string, because the column is a string and
|
|
37
|
+
* the allow-list is `%w[2 3 4]`. Never `null`: `NOT NULL DEFAULT '4'`.
|
|
38
|
+
*/
|
|
24
39
|
readonly scale: string;
|
|
25
|
-
/**
|
|
26
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Signed URL of the enlarged PNG, or `null`. `null` covers three different
|
|
42
|
+
* situations - the run has not finished, it failed, or the 24-hour sweep
|
|
43
|
+
* took the attachment - which is why {@link UpscaleNamespace.resultUrl}
|
|
44
|
+
* exists rather than a bare read of this field.
|
|
45
|
+
*/
|
|
46
|
+
readonly result_url: string | null;
|
|
27
47
|
}
|
|
28
48
|
/** What `POST /upscales` answers with. */
|
|
29
49
|
export type UpscaleCreated = Upscale & ToolJobHandle;
|
|
@@ -35,28 +35,45 @@ import type { FileInput, Id, Progress, RequestOptions } from "../../types";
|
|
|
35
35
|
import { type SecondsQuota, type ToolCaptcha, type ToolModel, type ToolRecord, type ToolRunOptions } from "./index";
|
|
36
36
|
/** Which stem to fetch. */
|
|
37
37
|
export type VocalStem = "vocals" | "instrumental";
|
|
38
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* A vocal separation run.
|
|
40
|
+
*
|
|
41
|
+
* Both routes that answer with one - `POST /vocal_separations` and
|
|
42
|
+
* `GET /vocal_separations/:id` - render the `:extended` view, so every key
|
|
43
|
+
* below is present on every response. Only the values move.
|
|
44
|
+
*/
|
|
39
45
|
export interface VocalSeparation extends ToolRecord {
|
|
46
|
+
/** Never `null`: `NOT NULL`, and an unknown model is a 400 before the row exists. */
|
|
40
47
|
readonly model_id: string;
|
|
41
|
-
/** Audio duration charged against the quota. */
|
|
48
|
+
/** Audio duration charged against the quota, rounded up at create time. */
|
|
42
49
|
readonly duration_seconds: number;
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Set when the run came from the music library rather than an upload.
|
|
52
|
+
*
|
|
53
|
+
* A **number**, not a string. `vocal_separations.song_id` is a `bigint`
|
|
54
|
+
* pointing at `songs`, which is one of the few tables in this API that kept
|
|
55
|
+
* an integer primary key - so this is the one id in the tools family that is
|
|
56
|
+
* not an {@link Id}. Comparing it against a string never matches.
|
|
57
|
+
*/
|
|
58
|
+
readonly song_id: number | null;
|
|
59
|
+
/** Title of that song, or `null` for an uploaded run. */
|
|
60
|
+
readonly song_title: string | null;
|
|
61
|
+
/** Real booleans, never `null`: each is an `attached?` call on the record. */
|
|
62
|
+
readonly has_original: boolean;
|
|
63
|
+
readonly has_vocals: boolean;
|
|
64
|
+
readonly has_instrumental: boolean;
|
|
49
65
|
/**
|
|
50
66
|
* Live runs queued ahead of this one; `0` means next up. `null` once the run
|
|
51
67
|
* is processing or terminal.
|
|
52
68
|
*/
|
|
53
|
-
readonly queue_position
|
|
69
|
+
readonly queue_position: number | null;
|
|
54
70
|
/**
|
|
55
|
-
*
|
|
56
|
-
* whose stems
|
|
71
|
+
* Signed stem URLs once complete, `null` otherwise - and permanently `null`
|
|
72
|
+
* for a song-owned separation, whose stems are written onto the song as
|
|
73
|
+
* filesystem nodes and never attached to this row.
|
|
57
74
|
*/
|
|
58
|
-
readonly vocals_url
|
|
59
|
-
readonly instrumental_url
|
|
75
|
+
readonly vocals_url: string | null;
|
|
76
|
+
readonly instrumental_url: string | null;
|
|
60
77
|
}
|
|
61
78
|
/** Arguments for starting a separation. */
|
|
62
79
|
export interface CreateVocalSeparationInput extends ToolCaptcha {
|
package/dist/types/types.d.ts
CHANGED
|
@@ -2,8 +2,17 @@
|
|
|
2
2
|
* Primitives shared by every namespace of the SDK.
|
|
3
3
|
*
|
|
4
4
|
* Nothing here touches the platform: no `node:*`, no `process`, no `console`.
|
|
5
|
-
* Files are values (Blob / Uint8Array / ReadableStream), never paths -
|
|
6
|
-
* has no filesystem
|
|
5
|
+
* Files are normally values (Blob / Uint8Array / ReadableStream), never paths -
|
|
6
|
+
* the core has no filesystem, and turning a path into a {@link FileInput} is the
|
|
7
|
+
* CLI's job.
|
|
8
|
+
*
|
|
9
|
+
* React Native is the one exception, and it is the platform's exception rather
|
|
10
|
+
* than a relaxation of ours: a file the user picked there is a
|
|
11
|
+
* {@link NativeFile} descriptor `{ uri, name, type }` whose bytes live behind a
|
|
12
|
+
* `file://` / `content://` / `ph://` URI that only the RN runtime can resolve.
|
|
13
|
+
* It is still not a path the SDK reads - the SDK never reads it. It is handed
|
|
14
|
+
* back to RN's own `FormData`, which resolves it natively while building the
|
|
15
|
+
* multipart body. See {@link FileInput} and {@link NativeFile}.
|
|
7
16
|
*/
|
|
8
17
|
/** Any JSON value the API can send or receive. */
|
|
9
18
|
export type Json = string | number | boolean | null | Json[] | {
|
|
@@ -19,21 +28,86 @@ export type JsonObject = {
|
|
|
19
28
|
* behind an authenticating proxy.
|
|
20
29
|
*/
|
|
21
30
|
export type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
|
|
31
|
+
/**
|
|
32
|
+
* A file picked on React Native, exactly as the platform hands it over.
|
|
33
|
+
*
|
|
34
|
+
* This is the shape `expo-file-system`'s picker returns and the shape the app
|
|
35
|
+
* already builds by hand (`oms-music/src/features/settings/pickers.ts`,
|
|
36
|
+
* `features/playlist/artworkPicker.ts`), and it is a DESCRIPTOR, not bytes: the
|
|
37
|
+
* URI is a `file://`, `content://` or `ph://` handle into the device, and
|
|
38
|
+
* nothing in JavaScript can turn it into a `Blob` without a native module.
|
|
39
|
+
*
|
|
40
|
+
* The SDK therefore never reads it. It appends the object VERBATIM to a
|
|
41
|
+
* `FormData`, because RN's `FormData` is not the web one: `getParts()` sees an
|
|
42
|
+
* entry whose value is an object with a `uri` and emits a file part from it,
|
|
43
|
+
* and the native networking layer streams the file off disk while building the
|
|
44
|
+
* multipart body. Handing RN a `Blob` instead is the thing that does not work -
|
|
45
|
+
* on Android it silently uploads an empty or truncated part.
|
|
46
|
+
*
|
|
47
|
+
* The three fields are exactly the three RN reads. `size` is carried because
|
|
48
|
+
* pickers report it and the SDK's own ceilings want it, and it is harmless:
|
|
49
|
+
* RN spreads the object into the part and ignores what it does not know.
|
|
50
|
+
*
|
|
51
|
+
* ## Where it works, and where it does not
|
|
52
|
+
*
|
|
53
|
+
* - `postForm` / any multipart endpoint: yes, on RN. On a web or Bun runtime
|
|
54
|
+
* the same object is REJECTED with a `TypeError` rather than stringified into
|
|
55
|
+
* `"[object Object]"`, which is what a plain `FormData.append` would do to it.
|
|
56
|
+
* See `supportsNativeFormDataFiles` in `http.ts`.
|
|
57
|
+
* - Storage direct upload (`resources/storage/upload.ts`): NO. That path
|
|
58
|
+
* `PUT`s the bytes to a presigned URL and MD5s them first, and neither is
|
|
59
|
+
* possible without reading the file. On RN, read it into bytes first (Expo's
|
|
60
|
+
* `File#bytes()`), or use a native uploader. {@link readFileInput} throws a
|
|
61
|
+
* message saying so rather than failing at the object store.
|
|
62
|
+
*/
|
|
63
|
+
export interface NativeFile {
|
|
64
|
+
/** Platform handle to the bytes: `file://`, `content://`, `ph://`. */
|
|
65
|
+
readonly uri: string;
|
|
66
|
+
/** Filename the server should store. RN sends it as the part's `filename`. */
|
|
67
|
+
readonly name: string;
|
|
68
|
+
/**
|
|
69
|
+
* MIME type. Pickers report `""` for some `content://` URIs, which is why the
|
|
70
|
+
* app falls back to a per-kind constant before it gets here; do the same, as
|
|
71
|
+
* Rails infers the container format from the part's content type for several
|
|
72
|
+
* of the tools.
|
|
73
|
+
*/
|
|
74
|
+
readonly type?: string;
|
|
75
|
+
/** Byte length when the picker reported one. Ignored by RN, used by the SDK. */
|
|
76
|
+
readonly size?: number;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* True when `value` is a {@link NativeFile} descriptor.
|
|
80
|
+
*
|
|
81
|
+
* The test is `uri` and `name` both being strings, which no other form value
|
|
82
|
+
* satisfies: a {@link FileInput} carries `data` + `filename`, and everything
|
|
83
|
+
* else in a form bag is a primitive. Deliberately tolerant of a missing `type`,
|
|
84
|
+
* because a `content://` pick on Android genuinely arrives without one and
|
|
85
|
+
* refusing it would lose the upload over a field RN treats as optional.
|
|
86
|
+
*/
|
|
87
|
+
export declare function isNativeFile(value: unknown): value is NativeFile;
|
|
22
88
|
/**
|
|
23
89
|
* Bytes handed to the SDK for upload.
|
|
24
90
|
*
|
|
25
|
-
* `data` is a value, never a path
|
|
26
|
-
*
|
|
91
|
+
* `data` is a value, never a path - with the one platform exception,
|
|
92
|
+
* {@link NativeFile}, which is a descriptor RN resolves for us and which only
|
|
93
|
+
* works on a multipart endpoint. `filename` is required because the API derives
|
|
94
|
+
* the stored name and, for some tools, the container format from it.
|
|
27
95
|
*
|
|
28
96
|
* `ReadableStream` is accepted for symmetry with the platform, but note that
|
|
29
97
|
* multipart form bodies have to be materialised: {@link readFileInput} buffers
|
|
30
98
|
* a stream into a Blob before it can be appended to a `FormData`. For anything
|
|
31
99
|
* large, prefer the storage direct-upload path, which streams straight to the
|
|
32
100
|
* object store and never passes through Rails.
|
|
101
|
+
*
|
|
102
|
+
* On React Native there is no need to wrap a picked file in one of these at
|
|
103
|
+
* all: pass the picked `{ uri, name, type }` object straight into the form bag
|
|
104
|
+
* and the transport appends it verbatim. Wrapping it is for the case where the
|
|
105
|
+
* name the server should store differs from the name on the device, in which
|
|
106
|
+
* case `filename` wins and is what RN sends as the part's filename.
|
|
33
107
|
*/
|
|
34
108
|
export interface FileInput {
|
|
35
|
-
/** The bytes. */
|
|
36
|
-
readonly data: Blob | Uint8Array | ReadableStream<Uint8Array
|
|
109
|
+
/** The bytes, or a {@link NativeFile} descriptor on React Native. */
|
|
110
|
+
readonly data: Blob | Uint8Array | ReadableStream<Uint8Array> | NativeFile;
|
|
37
111
|
/** Name the server should store, e.g. `"take-3.wav"`. Required. */
|
|
38
112
|
readonly filename: string;
|
|
39
113
|
/** MIME type. Defaults to the Blob's own type, then `application/octet-stream`. */
|
|
@@ -60,6 +134,14 @@ export interface FileOutput {
|
|
|
60
134
|
*
|
|
61
135
|
* Buffers a `ReadableStream` fully - see the note on {@link FileInput}. Uses
|
|
62
136
|
* only platform APIs, so it runs in an isolate.
|
|
137
|
+
*
|
|
138
|
+
* @throws {TypeError} for a {@link NativeFile}. There is no honest Blob to
|
|
139
|
+
* return: the bytes are behind a device URI that only the RN runtime can
|
|
140
|
+
* open, and returning an empty Blob would upload an empty file with a 200 on
|
|
141
|
+
* it. Multipart endpoints never reach here - `buildFormData` appends a native
|
|
142
|
+
* descriptor verbatim instead - so the throw belongs to the byte-hungry
|
|
143
|
+
* callers (the storage direct-upload driver, which must also MD5 the body),
|
|
144
|
+
* and it names the way out rather than just refusing.
|
|
63
145
|
*/
|
|
64
146
|
export declare function readFileInput(input: FileInput): Promise<{
|
|
65
147
|
blob: Blob;
|
|
@@ -69,8 +151,13 @@ export declare function readFileInput(input: FileInput): Promise<{
|
|
|
69
151
|
/**
|
|
70
152
|
* Convenience constructor for a {@link FileInput}. Prefer it over an object
|
|
71
153
|
* literal so the `filename`-is-required rule stays visible at the call site.
|
|
154
|
+
*
|
|
155
|
+
* A {@link NativeFile} is accepted and needs this only when the server should
|
|
156
|
+
* store a different name than the device used; otherwise pass the picked object
|
|
157
|
+
* straight into the form bag. When wrapped, `filename` and `contentType` are
|
|
158
|
+
* what go on the wire - the descriptor's own `name` and `type` are overridden.
|
|
72
159
|
*/
|
|
73
|
-
export declare function file(data: Blob | Uint8Array | ReadableStream<Uint8Array
|
|
160
|
+
export declare function file(data: Blob | Uint8Array | ReadableStream<Uint8Array> | NativeFile, filename: string, options?: {
|
|
74
161
|
contentType?: string;
|
|
75
162
|
size?: number;
|
|
76
163
|
}): FileInput;
|
|
@@ -79,6 +166,37 @@ export declare function file(data: Blob | Uint8Array | ReadableStream<Uint8Array
|
|
|
79
166
|
*
|
|
80
167
|
* `total` is `undefined` whenever the size is genuinely unknown (a stream
|
|
81
168
|
* upload, a server-side render with no ETA). Do not fake it with a guess.
|
|
169
|
+
*
|
|
170
|
+
* ## What `phase: "upload"` can and cannot promise
|
|
171
|
+
*
|
|
172
|
+
* It ticks once per COMPLETED transfer, never per byte, and that is a property
|
|
173
|
+
* of `fetch` rather than a decision this SDK is free to revisit. No `fetch` -
|
|
174
|
+
* browser, React Native or Worker - exposes request-body progress. The web
|
|
175
|
+
* frontend gets a real byte counter in its 36 `onUploadProgress` call sites
|
|
176
|
+
* because axios is XHR underneath, and XHR is the only API that has ever
|
|
177
|
+
* reported bytes as they leave.
|
|
178
|
+
*
|
|
179
|
+
* There is therefore ONE mechanism in this SDK, not two: `resources/storage/upload.ts`
|
|
180
|
+
* ticks per finished transfer (one per 32 MiB part on the multipart tier, one
|
|
181
|
+
* per file on the direct tier), and a host that wants byte-level granularity
|
|
182
|
+
* hands an XHR-backed {@link FetchLike} to `UploadManagerOptions.fetch` and
|
|
183
|
+
* keeps the rest of the driver. That escape hatch is documented there with a
|
|
184
|
+
* working recipe, and it is available in exactly the runtimes that have XHR:
|
|
185
|
+
*
|
|
186
|
+
* - browser: yes, `xhr.upload.onprogress`;
|
|
187
|
+
* - React Native: yes. RN's own `fetch` is a thin layer over its `XMLHttpRequest`,
|
|
188
|
+
* and `xhr.upload.onprogress` fires there for both `Blob` and
|
|
189
|
+
* {@link NativeFile} parts - it is the same event `expo-file-system`'s
|
|
190
|
+
* uploader surfaces;
|
|
191
|
+
* - Worker isolate: no. There is no XHR, so per-transfer ticks are the ceiling.
|
|
192
|
+
*
|
|
193
|
+
* `supportsUploadProgress()` in `http.ts` answers that question at runtime so a
|
|
194
|
+
* caller can hide a byte-accurate bar rather than let it sit at 0% and jump.
|
|
195
|
+
*
|
|
196
|
+
* The thing not to do is count bytes as they are handed to the runtime: a
|
|
197
|
+
* stream request body reports what was buffered, not what was acknowledged,
|
|
198
|
+
* which is the lie that parks a bar at 100% for a minute. It also breaks the
|
|
199
|
+
* presigned PUTs outright - see the module note in `storage/upload.ts`.
|
|
82
200
|
*/
|
|
83
201
|
export interface Progress {
|
|
84
202
|
/** What is happening right now. */
|
|
@@ -100,15 +218,39 @@ export interface RequestOptions {
|
|
|
100
218
|
*/
|
|
101
219
|
readonly signal?: AbortSignal;
|
|
102
220
|
/**
|
|
103
|
-
* Deadline in milliseconds for
|
|
104
|
-
*
|
|
221
|
+
* Deadline in milliseconds for ONE ATTEMPT. Overrides the client default.
|
|
222
|
+
* `0` disables it.
|
|
223
|
+
*
|
|
224
|
+
* Not the deadline for the whole call: the transport starts a fresh one per
|
|
225
|
+
* attempt, so a call that retries can take up to `maxAttempts` times this
|
|
226
|
+
* plus the backoff between them (and a `429` waits out `Retry-After`, which
|
|
227
|
+
* this API sets from a one-minute window). To bound the wall clock, pass a
|
|
228
|
+
* {@link RequestOptions.signal} you abort yourself, or `retry: false`.
|
|
229
|
+
*
|
|
230
|
+
* The deadline covers getting a response, not draining it: it is disposed as
|
|
231
|
+
* soon as the headers arrive, which is what lets `raw()` and `streamText()`
|
|
232
|
+
* hold a stream open for longer than `timeoutMs`. A stream needs its own
|
|
233
|
+
* silence limit instead - `streamText` has one.
|
|
105
234
|
*/
|
|
106
235
|
readonly timeoutMs?: number;
|
|
107
236
|
/** Extra request headers. Merged over the client's, under `Authorization`. */
|
|
108
237
|
readonly headers?: Record<string, string>;
|
|
109
238
|
/**
|
|
110
|
-
* Per-call retry override
|
|
111
|
-
*
|
|
239
|
+
* Per-call retry override, and the ONLY way to put a mutating request in
|
|
240
|
+
* scope for a retry.
|
|
241
|
+
*
|
|
242
|
+
* By default only safe methods are replayed after an ambiguous failure (a
|
|
243
|
+
* torn connection, a 5xx); a `POST` is not, because a replay after a lost
|
|
244
|
+
* answer is how one create becomes two records. Passing an object here -
|
|
245
|
+
* `{}` is enough, it inherits {@link DEFAULT_RETRY} - says you have looked at
|
|
246
|
+
* this specific endpoint and decided a duplicate is acceptable.
|
|
247
|
+
*
|
|
248
|
+
* `false` disables retrying completely, `429` included. Pass it for a call
|
|
249
|
+
* that mints something under a fresh random identifier per attempt (a short
|
|
250
|
+
* link, a notepad, a chest): a retry there does not duplicate the answer, it
|
|
251
|
+
* leaves an orphan behind and hands you the wrong one.
|
|
252
|
+
*
|
|
253
|
+
* See the retry policy documented on `ApiClient`.
|
|
112
254
|
*/
|
|
113
255
|
readonly retry?: RetryOptions | false;
|
|
114
256
|
}
|
|
@@ -138,15 +280,75 @@ export interface ResolvedRetry {
|
|
|
138
280
|
/** Defaults applied when a caller says nothing about retrying. */
|
|
139
281
|
export declare const DEFAULT_RETRY: ResolvedRetry;
|
|
140
282
|
/**
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
283
|
+
* One value in a query string.
|
|
284
|
+
*
|
|
285
|
+
* Nested objects and arrays are encoded the way Rails reads them
|
|
286
|
+
* (`search[status]=open`, `ids[]=1&ids[]=2`). Three values do NOT encode
|
|
287
|
+
* literally, and `encodeQuery` in `http.ts` carries the full argument:
|
|
288
|
+
*
|
|
289
|
+
* - `undefined` is dropped. It means "I am not filtering on this column".
|
|
290
|
+
* - `null` is sent as the backend's `\b` null sentinel and comes back out of
|
|
291
|
+
* `CrudActions` as SQL `NULL`. It means "filter where this column IS NULL".
|
|
292
|
+
* The two are not interchangeable, and getting them the wrong way round is
|
|
293
|
+
* the difference between one folder and somebody's entire tree.
|
|
294
|
+
* - a `Date` is sent as its ISO-8601 string, which is the only shape the Rails
|
|
295
|
+
* date filters parse (`String#to_date_safe`).
|
|
144
296
|
*/
|
|
145
|
-
export type QueryValue = string | number | boolean | null | undefined | QueryValue[] | {
|
|
297
|
+
export type QueryValue = string | number | boolean | Date | null | undefined | QueryValue[] | {
|
|
146
298
|
[key: string]: QueryValue;
|
|
147
299
|
};
|
|
148
300
|
/** A bag of query parameters. */
|
|
149
301
|
export type QueryParams = Record<string, QueryValue>;
|
|
302
|
+
/**
|
|
303
|
+
* Hard ceiling the backend applies to a page size, mirroring
|
|
304
|
+
* `QueryModifier::MAX_PAGE_SIZE`.
|
|
305
|
+
*
|
|
306
|
+
* The server clamps silently - `size = [size, MAX_PAGE_SIZE].min` - so asking
|
|
307
|
+
* for 1200 returns 500 rows and no indication that a ceiling was hit. The SDK
|
|
308
|
+
* therefore clamps to the same number BEFORE the request, so that the size it
|
|
309
|
+
* reports back in {@link Paginated.pageSize} is the size the rows were actually
|
|
310
|
+
* counted against. See {@link resolvePageSize}.
|
|
311
|
+
*/
|
|
312
|
+
export declare const MAX_PAGE_SIZE = 500;
|
|
313
|
+
/**
|
|
314
|
+
* Page size the SDK asks for when the caller says nothing.
|
|
315
|
+
*
|
|
316
|
+
* Deliberately below {@link MAX_PAGE_SIZE}: a `list()` is usually the first
|
|
317
|
+
* screen of something, and 500 rows of expanded records is a slow first paint.
|
|
318
|
+
* Note this is NOT the server's own default - a request with no page modifier
|
|
319
|
+
* at all gets `QueryModifier::DEFAULT_PAGE_SIZE` (500) forced on it by
|
|
320
|
+
* `CrudActions#index_modifiers_params` - but the SDK always sends one.
|
|
321
|
+
*/
|
|
322
|
+
export declare const DEFAULT_PAGE_SIZE = 100;
|
|
323
|
+
/**
|
|
324
|
+
* Normalises a requested page size into the one the server will actually use.
|
|
325
|
+
*
|
|
326
|
+
* Two different failures are handled differently on purpose:
|
|
327
|
+
*
|
|
328
|
+
* - **above the ceiling** is clamped, not rejected. The request still succeeds
|
|
329
|
+
* and paging still reaches every row, so failing it would break a working
|
|
330
|
+
* call for nothing. What must not happen is the caller being told it got the
|
|
331
|
+
* size it asked for: that is how `pageSize: 1200` yielded 500 items and
|
|
332
|
+
* `hasMore: false`, dropping 700 rows without a word.
|
|
333
|
+
* - **not a usable size at all** (`NaN`, `Infinity`, zero, negative) throws.
|
|
334
|
+
* There is nothing sensible to clamp such a value to, and it is not merely
|
|
335
|
+
* wrong on the client: `pageModifier` would put `"1:NaN"` on the wire,
|
|
336
|
+
* `QueryModifier#apply_pagination` reads that as size `0`, bails out before
|
|
337
|
+
* `limit`/`offset` are applied, and the endpoint answers with the WHOLE
|
|
338
|
+
* table. A typo would turn a listing into an unbounded scan holding a Puma
|
|
339
|
+
* thread and a DB connection.
|
|
340
|
+
*
|
|
341
|
+
* @throws {TypeError} when `pageSize` is not a finite number of at least 1.
|
|
342
|
+
*/
|
|
343
|
+
export declare function resolvePageSize(pageSize?: number): number;
|
|
344
|
+
/**
|
|
345
|
+
* Normalises a requested page number. Pages are 1-based on the server
|
|
346
|
+
* (`offset = (number - 1) * size`), so page `0` is page 1 - and a
|
|
347
|
+
* {@link Paginated} that reported `page: 0` would fetch page 1 twice.
|
|
348
|
+
*
|
|
349
|
+
* @throws {TypeError} when `page` is not a finite number.
|
|
350
|
+
*/
|
|
351
|
+
export declare function resolvePageNumber(page?: number): number;
|
|
150
352
|
/**
|
|
151
353
|
* Paging arguments accepted by every `list()` method.
|
|
152
354
|
*
|
|
@@ -157,7 +359,26 @@ export type QueryParams = Record<string, QueryValue>;
|
|
|
157
359
|
export interface PageParams {
|
|
158
360
|
/** 1-based page number. Defaults to 1. */
|
|
159
361
|
readonly page?: number;
|
|
160
|
-
/**
|
|
362
|
+
/**
|
|
363
|
+
* Items per page. Defaults to {@link DEFAULT_PAGE_SIZE}.
|
|
364
|
+
*
|
|
365
|
+
* {@link MAX_PAGE_SIZE} is the ceiling and it is enforced on both sides: ask
|
|
366
|
+
* for more and you get the ceiling, with {@link Paginated.pageSize} reporting
|
|
367
|
+
* the size you actually got rather than the one you asked for. To read more
|
|
368
|
+
* than 500 rows, page through them - `collect` and `pages` do it for you.
|
|
369
|
+
*
|
|
370
|
+
* A size the server could not parse is NOT clamped, it throws: `0`, a
|
|
371
|
+
* negative, `NaN` and `Infinity` all raise a `TypeError` before the request
|
|
372
|
+
* is built. This is deliberate and it is not defensive tidiness. `"1:NaN"`
|
|
373
|
+
* on the wire makes `QueryModifier#apply_pagination` read the size as zero
|
|
374
|
+
* and bail out before `limit`/`offset` are applied, so the endpoint answers
|
|
375
|
+
* with the WHOLE table: one typo turns a listing into an unbounded scan
|
|
376
|
+
* holding a Puma thread and a database connection. Failing at the call site
|
|
377
|
+
* is the only place that mistake is still cheap.
|
|
378
|
+
*
|
|
379
|
+
* Narrower than 0.2.0, which clamped `0` to 1 and let `NaN` through onto
|
|
380
|
+
* the wire. See {@link resolvePageSize}.
|
|
381
|
+
*/
|
|
161
382
|
readonly pageSize?: number;
|
|
162
383
|
/** `"column:asc"` or `"column:desc"`, passed through as `modifiers[order]`. */
|
|
163
384
|
readonly order?: string;
|
|
@@ -176,7 +397,12 @@ export interface Paginated<T> {
|
|
|
176
397
|
readonly items: T[];
|
|
177
398
|
/** 1-based number of this page. */
|
|
178
399
|
readonly page: number;
|
|
179
|
-
/**
|
|
400
|
+
/**
|
|
401
|
+
* The page size the server actually applied, which is NOT necessarily the
|
|
402
|
+
* one that was requested: it is clamped to {@link MAX_PAGE_SIZE}. Read this
|
|
403
|
+
* rather than the number you passed in - they differ exactly when the
|
|
404
|
+
* difference matters.
|
|
405
|
+
*/
|
|
180
406
|
readonly pageSize: number;
|
|
181
407
|
/** True when this page came back full, so another page may exist. */
|
|
182
408
|
readonly hasMore: boolean;
|
|
@@ -189,6 +415,12 @@ export type PageLoader<T> = (params: Required<Pick<PageParams, "page" | "pageSiz
|
|
|
189
415
|
* Builds a {@link Paginated} from the raw array the API returned plus the
|
|
190
416
|
* loader that can fetch the next page. Resource modules use this instead of
|
|
191
417
|
* hand-rolling the shape.
|
|
418
|
+
*
|
|
419
|
+
* @throws {TypeError} when `pageSize` is not a finite number of at least 1, or
|
|
420
|
+
* `page` is not finite. Both go through {@link resolvePageSize} /
|
|
421
|
+
* {@link resolvePageNumber}, which reject rather than clamp - see
|
|
422
|
+
* {@link PageParams.pageSize} for why an unparseable size is dangerous
|
|
423
|
+
* enough to be worth a throw.
|
|
192
424
|
*/
|
|
193
425
|
export declare function createPage<T>(items: T[], page: number, pageSize: number, load: PageLoader<T>): Paginated<T>;
|
|
194
426
|
/** Walks every page of a listing, yielding one page at a time. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omelhorsite/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "TypeScript SDK for the omelhorsite API. Isolate-safe: no node builtins, no environment access, no stdout.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"typecheck:tests": "tsc --noEmit -p tsconfig.test.json"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
+
"@types/bun": "^1.4.0",
|
|
28
29
|
"typescript": "^5.9.3"
|
|
29
30
|
},
|
|
30
31
|
"dependencies": {
|