@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
package/dist/types/client.d.ts
CHANGED
|
@@ -14,15 +14,25 @@ import type { TokenSet } from "./auth/tokens";
|
|
|
14
14
|
import { ApiClient, type TokenProvider } from "./http";
|
|
15
15
|
import { type LocalNamespace } from "./local/index";
|
|
16
16
|
import { AccountNamespace } from "./resources/account";
|
|
17
|
+
import { AdminNamespace } from "./resources/admin";
|
|
18
|
+
import { PasskeysNamespace } from "./resources/auth/passkeys";
|
|
19
|
+
import { AuthSessionsNamespace } from "./resources/auth/sessions";
|
|
17
20
|
import { ChestsNamespace } from "./resources/chests";
|
|
21
|
+
import { ContentNamespace } from "./resources/content";
|
|
18
22
|
import { DynamicQrsNamespace } from "./resources/dynamicQrs";
|
|
19
23
|
import { FormsNamespace } from "./resources/forms";
|
|
20
24
|
import { IpLookupNamespace } from "./resources/ipLookup";
|
|
21
25
|
import { JobsNamespace } from "./resources/jobs";
|
|
26
|
+
import { LibraryNamespace } from "./resources/library";
|
|
22
27
|
import { LinkTreesNamespace } from "./resources/linkTrees";
|
|
28
|
+
import { MediaNamespace } from "./resources/media";
|
|
29
|
+
import { MoviesNamespace } from "./resources/movies";
|
|
30
|
+
import { MusicNamespace } from "./resources/music/index";
|
|
23
31
|
import { NotepadsNamespace } from "./resources/notepads";
|
|
24
32
|
import { QuotasNamespace } from "./resources/quotas";
|
|
33
|
+
import { RealtimeNamespace } from "./resources/realtime";
|
|
25
34
|
import { ShortLinksNamespace } from "./resources/shortLinks";
|
|
35
|
+
import { SocialNamespace } from "./resources/social";
|
|
26
36
|
import { StorageNamespace } from "./resources/storage";
|
|
27
37
|
import { TicketsNamespace } from "./resources/tickets";
|
|
28
38
|
import { ToolsNamespace } from "./resources/tools/index";
|
|
@@ -87,10 +97,32 @@ export interface OmsOptions {
|
|
|
87
97
|
* const oms = new Oms({ token: "..." });
|
|
88
98
|
* const me = await oms.account.me();
|
|
89
99
|
* const link = await oms.shortLinks.create({ url: "https://example.com" });
|
|
100
|
+
* const songs = await oms.music.songs.list({ artist: "Nina Simone" });
|
|
101
|
+
* const cable = oms.realtime.connect({ token, socket: (u) => new WebSocket(u) });
|
|
90
102
|
* ```
|
|
91
103
|
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
104
|
+
* ## Two credentials, two namespaces
|
|
105
|
+
*
|
|
106
|
+
* `oms.sessions` mints the opaque `Session` token the website and the mobile
|
|
107
|
+
* apps use, which carries the whole account. `oms.auth` runs OAuth, whose
|
|
108
|
+
* tokens are scoped and, because `enforce_oauth_scope!` denies by omission,
|
|
109
|
+
* cannot reach most of the API at all. `oms.realtime` accepts only the first
|
|
110
|
+
* kind. Picking the wrong one shows up as a `403 insufficient_scope`, or on the
|
|
111
|
+
* cable as a connection that is silently anonymous.
|
|
112
|
+
*
|
|
113
|
+
* ## Isolate-safe
|
|
114
|
+
*
|
|
115
|
+
* No `node:*`, no environment, no stdout, and `fetch` is injectable. A file is
|
|
116
|
+
* normally a VALUE (`Blob` / `Uint8Array` / `ReadableStream`), never a path,
|
|
117
|
+
* because turning a path into bytes is the host's job.
|
|
118
|
+
*
|
|
119
|
+
* The one exception is React Native, which cannot produce a usable `Blob` from
|
|
120
|
+
* a picked file: there, pass the picker's `{ uri, name, type }`
|
|
121
|
+
* ({@link NativeFile}) straight into a form bag and the transport appends it
|
|
122
|
+
* verbatim for RN's networking layer to stream off disk. That works on
|
|
123
|
+
* multipart endpoints only - the storage direct-upload path has to read and MD5
|
|
124
|
+
* the bytes, so it rejects a `NativeFile` with a message saying so rather than
|
|
125
|
+
* failing at the object store.
|
|
94
126
|
*/
|
|
95
127
|
export declare class Oms {
|
|
96
128
|
/**
|
|
@@ -99,8 +131,12 @@ export declare class Oms {
|
|
|
99
131
|
* the SDK to add one call.
|
|
100
132
|
*/
|
|
101
133
|
readonly http: ApiClient;
|
|
102
|
-
/**
|
|
134
|
+
/** OAuth: refreshing, revoking, the RFC 8628 device grant, OIDC discovery. */
|
|
103
135
|
readonly auth: AuthNamespace;
|
|
136
|
+
/** Password sign-in, sign-up, and the emailed six-digit ceremonies. */
|
|
137
|
+
readonly sessions: AuthSessionsNamespace;
|
|
138
|
+
/** WebAuthn credentials: register one, sign in with one, list, revoke. */
|
|
139
|
+
readonly passkeys: PasskeysNamespace;
|
|
104
140
|
/** The signed-in user, their profile, and their usage report. */
|
|
105
141
|
readonly account: AccountNamespace;
|
|
106
142
|
/** Support tickets and their message threads. */
|
|
@@ -127,6 +163,27 @@ export declare class Oms {
|
|
|
127
163
|
readonly quotas: QuotasNamespace;
|
|
128
164
|
/** The metered media tools, each with its own daily quota. */
|
|
129
165
|
readonly tools: ToolsNamespace;
|
|
166
|
+
/** Songs, artists, playlists, imports, jams and the music assistant. */
|
|
167
|
+
readonly music: MusicNamespace;
|
|
168
|
+
/** The canonical bytes of the music library, by media id. */
|
|
169
|
+
readonly media: MediaNamespace;
|
|
170
|
+
/** Direct messages, friendships and blocks, group chats. */
|
|
171
|
+
readonly social: SocialNamespace;
|
|
172
|
+
/** The book library: uploads, shelves, annotations, the study assistant. */
|
|
173
|
+
readonly library: LibraryNamespace;
|
|
174
|
+
/** Stremio addons, collections and watch progress. Session credential only. */
|
|
175
|
+
readonly movies: MoviesNamespace;
|
|
176
|
+
/** Blogs, notifications, feedback, jokes, site config and the status page. */
|
|
177
|
+
readonly content: ContentNamespace;
|
|
178
|
+
/** OAuth client registration for anyone, plus the `/admin/*` routes. */
|
|
179
|
+
readonly admin: AdminNamespace;
|
|
180
|
+
/**
|
|
181
|
+
* The ActionCable connection: playback handoff, jams, notifications, job
|
|
182
|
+
* progress. Opens nothing until {@link RealtimeNamespace.connect} is called,
|
|
183
|
+
* and wants a `Session` token rather than the client's own credential - see
|
|
184
|
+
* that method for why.
|
|
185
|
+
*/
|
|
186
|
+
readonly realtime: RealtimeNamespace;
|
|
130
187
|
/**
|
|
131
188
|
* Pure client-side helpers that touch no network and need no credential
|
|
132
189
|
* (password generation, QR encoding). Also exported standalone as `local`,
|
package/dist/types/http.d.ts
CHANGED
|
@@ -5,10 +5,122 @@
|
|
|
5
5
|
* calls `fetch`, `AbortController`, `FormData`, `Blob` and `setTimeout` only -
|
|
6
6
|
* all of which a Cloudflare Worker provides. `fetch` itself is injected, so a
|
|
7
7
|
* host can wrap it (proxy, cache, test double) without patching a global.
|
|
8
|
+
*
|
|
9
|
+
* ## Three runtimes, and where they stop agreeing
|
|
10
|
+
*
|
|
11
|
+
* The same transport runs in a browser (the Next.js frontend), in React Native
|
|
12
|
+
* (the Expo app) and in Bun (the CLI and the MCP server). They agree on
|
|
13
|
+
* `fetch`, `AbortController`, `Headers`, `FormData` and `setTimeout`. They do
|
|
14
|
+
* NOT agree on three things, each of which has a named capability check here
|
|
15
|
+
* rather than a `try` and a shrug:
|
|
16
|
+
*
|
|
17
|
+
* | capability | browser | React Native | Worker / Bun |
|
|
18
|
+
* | --------------------------------- | ------- | ------------ | ------------ |
|
|
19
|
+
* | `supportsResponseStreaming()` | yes | **no** | yes |
|
|
20
|
+
* | `supportsUploadProgress()` (XHR) | yes | yes | **no** |
|
|
21
|
+
* | `supportsNativeFormDataFiles()` | no | **yes** | no |
|
|
22
|
+
*
|
|
23
|
+
* Read them at the point of use, not once at module load: a host may install a
|
|
24
|
+
* polyfill after the SDK is imported, and in RN the fetch implementation is
|
|
25
|
+
* swapped by libraries often enough that a cached answer goes stale.
|
|
8
26
|
*/
|
|
9
|
-
import { type FetchLike, type FileInput, type FileOutput, type QueryParams, type RequestOptions, type ResolvedRetry, type RetryOptions } from "./types";
|
|
27
|
+
import { type FetchLike, type FileInput, type FileOutput, type NativeFile, type QueryParams, type RequestOptions, type ResolvedRetry, type RetryOptions } from "./types";
|
|
10
28
|
/** Production API root. Override only for a local backend or a test double. */
|
|
11
29
|
export declare const DEFAULT_BASE_URL = "https://backend.omelhorsite.pt";
|
|
30
|
+
/**
|
|
31
|
+
* What the runtime this code woke up in can actually do.
|
|
32
|
+
*
|
|
33
|
+
* Answered by probing the globals, never by sniffing a platform name, and
|
|
34
|
+
* recomputed on every call so a polyfill installed after import is seen. See
|
|
35
|
+
* the table in the module note for how the three clients score.
|
|
36
|
+
*/
|
|
37
|
+
export interface TransportCapabilities {
|
|
38
|
+
/** `fetch` hands back a readable body that can be consumed as it arrives. */
|
|
39
|
+
readonly responseStreaming: boolean;
|
|
40
|
+
/** `XMLHttpRequest` is present, so byte-level upload progress is reachable. */
|
|
41
|
+
readonly uploadProgress: boolean;
|
|
42
|
+
/** `FormData` is React Native's, so a {@link NativeFile} can be appended. */
|
|
43
|
+
readonly nativeFormDataFiles: boolean;
|
|
44
|
+
}
|
|
45
|
+
/** All three capability probes at once. */
|
|
46
|
+
export declare function transportCapabilities(): TransportCapabilities;
|
|
47
|
+
/**
|
|
48
|
+
* Whether a response body can be read INCREMENTALLY on this runtime.
|
|
49
|
+
*
|
|
50
|
+
* This is the question a caller has to answer before it offers a token-by-token
|
|
51
|
+
* UI, and it has a hard "no" on React Native. RN's `fetch` is its
|
|
52
|
+
* `XMLHttpRequest` with a whatwg-fetch shim over it: the whole body is
|
|
53
|
+
* accumulated by the native layer and handed over at the end, `response.body`
|
|
54
|
+
* is `undefined`, and there is no `ReadableStream` in the runtime at all -
|
|
55
|
+
* confirmed by grep over `oms-music/src`, which mentions neither
|
|
56
|
+
* `ReadableStream` nor `getReader` anywhere.
|
|
57
|
+
*
|
|
58
|
+
* That is not a gap to polyfill. Nothing that runs in JS can make the native
|
|
59
|
+
* networking layer emit partial bodies, and the shims that claim to (RN's
|
|
60
|
+
* `textStreaming` blob response type, `react-native-fetch-api`) either need
|
|
61
|
+
* `XMLHttpRequest`'s `onprogress` plumbed by hand or a different fetch
|
|
62
|
+
* altogether. Which is why this is a capability check and not a feature.
|
|
63
|
+
*
|
|
64
|
+
* ## What a caller does with the answer
|
|
65
|
+
*
|
|
66
|
+
* Pick a PRESENTATION, not a protocol. Both paths hit the same endpoint and
|
|
67
|
+
* read the same bytes; the difference is whether they arrive in pieces:
|
|
68
|
+
*
|
|
69
|
+
* ```ts
|
|
70
|
+
* if (supportsResponseStreaming()) {
|
|
71
|
+
* for await (const chunk of oms.http.streamText("POST", path, { body })) render(chunk);
|
|
72
|
+
* } else {
|
|
73
|
+
* const whole = await oms.http.raw("POST", path, { body }).then((r) => r.text());
|
|
74
|
+
* render(whole); // one paint, after the server is done
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* {@link ApiClient.streamText} already contains that fork, so the usual answer
|
|
79
|
+
* is to call it and use this only to decide what the UI PROMISES: a typing
|
|
80
|
+
* indicator that never types is worse than a spinner that admits it is waiting.
|
|
81
|
+
*
|
|
82
|
+
* The consumer this was designed against is the frontend's `BookChatService`,
|
|
83
|
+
* which reads an SSE-shaped body (`data: {"delta": "..."}` lines) off
|
|
84
|
+
* `POST /books/:id/chat`. Framing the lines is the domain's job; this and
|
|
85
|
+
* `streamText` only promise decoded text in order.
|
|
86
|
+
*
|
|
87
|
+
* The three probes are all needed. `ReadableStream` existing does not mean
|
|
88
|
+
* `fetch` produces one (a polyfilled global over RN's fetch is exactly that
|
|
89
|
+
* case), `Response.prototype` having `body` does not mean it is non-null on a
|
|
90
|
+
* given response - `streamText` still checks the instance - and `TextDecoder`
|
|
91
|
+
* is what turns the chunks into text on the way out.
|
|
92
|
+
*/
|
|
93
|
+
export declare function supportsResponseStreaming(): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Whether `XMLHttpRequest` is available, which is the only route to byte-level
|
|
96
|
+
* UPLOAD progress in any of the three clients.
|
|
97
|
+
*
|
|
98
|
+
* True in a browser and in React Native (RN's networking is XHR underneath and
|
|
99
|
+
* `xhr.upload.onprogress` fires there); false in a Worker-class isolate and in
|
|
100
|
+
* Bun's server runtime. See {@link Progress} for the whole argument and
|
|
101
|
+
* `UploadManagerOptions.fetch` for the recipe - this only reports whether that
|
|
102
|
+
* recipe can be used here, so a UI can decide between a real bar and a
|
|
103
|
+
* per-file tick.
|
|
104
|
+
*
|
|
105
|
+
* The SDK never uses XHR itself. It cannot: the core has to load in an isolate.
|
|
106
|
+
*/
|
|
107
|
+
export declare function supportsUploadProgress(): boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Whether this runtime's `FormData` accepts a {@link NativeFile} descriptor.
|
|
110
|
+
*
|
|
111
|
+
* React Native's `FormData` is not the web one. `append(name, value)` stores
|
|
112
|
+
* the value as it is, and `getParts()` later reads `value.uri`, `value.name`
|
|
113
|
+
* and `value.type` off it to build a file part that the native layer streams
|
|
114
|
+
* from disk. The web `FormData` instead coerces any non-`Blob` to a string, so
|
|
115
|
+
* the same object silently becomes the literal text `"[object Object]"` in a
|
|
116
|
+
* text field - a 200 response and an upload that never happened.
|
|
117
|
+
*
|
|
118
|
+
* Detected by the presence of `getParts` on the prototype, with
|
|
119
|
+
* `navigator.product === "ReactNative"` as the second opinion. Not by a
|
|
120
|
+
* `Platform.OS` import, which would drag `react-native` into a package that has
|
|
121
|
+
* to load in a Worker.
|
|
122
|
+
*/
|
|
123
|
+
export declare function supportsNativeFormDataFiles(): boolean;
|
|
12
124
|
/**
|
|
13
125
|
* Supplies the bearer token for each request.
|
|
14
126
|
*
|
|
@@ -59,9 +171,23 @@ export interface ApiClientOptions {
|
|
|
59
171
|
readonly sessionCookie?: boolean;
|
|
60
172
|
/** Headers merged into every request, below per-call headers. */
|
|
61
173
|
readonly headers?: Record<string, string>;
|
|
62
|
-
/**
|
|
174
|
+
/**
|
|
175
|
+
* Default deadline for ONE ATTEMPT, not for the whole call. `0` disables it.
|
|
176
|
+
*
|
|
177
|
+
* Each attempt gets a fresh one, so a call that retries can take up to
|
|
178
|
+
* `maxAttempts` times this plus the backoff. It also stops at the response
|
|
179
|
+
* headers rather than at the last byte, which is what lets `raw()` and
|
|
180
|
+
* `streamText()` hold a body open for longer. See {@link RequestOptions.timeoutMs}.
|
|
181
|
+
*/
|
|
63
182
|
readonly timeoutMs?: number;
|
|
64
|
-
/**
|
|
183
|
+
/**
|
|
184
|
+
* Shape of the default backoff: how many attempts, how long between them.
|
|
185
|
+
*
|
|
186
|
+
* It does not widen WHICH requests are eligible - that stays "safe methods,
|
|
187
|
+
* plus a 429 on anything". Only a per-call `retry` can put a mutator in
|
|
188
|
+
* scope. `false` turns retrying off completely, for every method and every
|
|
189
|
+
* status. See the retry policy on {@link ApiClient}.
|
|
190
|
+
*/
|
|
65
191
|
readonly retry?: RetryOptions | false;
|
|
66
192
|
/**
|
|
67
193
|
* Value for the `X-Oms-Client` header, e.g. `"oms-cli/0.3.1"`. The SDK does
|
|
@@ -72,8 +198,15 @@ export interface ApiClientOptions {
|
|
|
72
198
|
}
|
|
73
199
|
/** Body accepted by `post`/`patch`. `undefined` sends no body at all. */
|
|
74
200
|
export type JsonBody = unknown;
|
|
75
|
-
/**
|
|
76
|
-
|
|
201
|
+
/**
|
|
202
|
+
* One field of a multipart form.
|
|
203
|
+
*
|
|
204
|
+
* {@link NativeFile} is here so that a React Native caller can pass the object
|
|
205
|
+
* its picker returned straight through - `form.image = picked` - without
|
|
206
|
+
* wrapping it. It is appended verbatim on RN and rejected loudly elsewhere; see
|
|
207
|
+
* {@link buildFormData}.
|
|
208
|
+
*/
|
|
209
|
+
export type FormFieldValue = string | number | boolean | FileInput | NativeFile | null | undefined;
|
|
77
210
|
/**
|
|
78
211
|
* Fields of a multipart form. An array value is appended once per entry with a
|
|
79
212
|
* `[]` suffix, which is how Rails reads a list (`clips[]`).
|
|
@@ -86,16 +219,88 @@ export interface GetOptions extends RequestOptions {
|
|
|
86
219
|
/**
|
|
87
220
|
* HTTP client for the omelhorsite API.
|
|
88
221
|
*
|
|
89
|
-
* Retry policy
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
222
|
+
* ## Retry policy
|
|
223
|
+
*
|
|
224
|
+
* What may be retried depends on WHY the attempt failed and on WHICH method
|
|
225
|
+
* made it, because those two together decide whether a replay can duplicate
|
|
226
|
+
* work the server already did:
|
|
227
|
+
*
|
|
228
|
+
* | outcome | GET / HEAD | POST / PATCH / PUT / DELETE |
|
|
229
|
+
* | ------------------------------------ | ---------- | --------------------------- |
|
|
230
|
+
* | `fetch` rejection (DNS, TLS, reset) | retried | **not** retried |
|
|
231
|
+
* | `5xx` | retried | **not** retried |
|
|
232
|
+
* | `429` | retried | retried |
|
|
233
|
+
* | every other `4xx` | fails | fails |
|
|
234
|
+
*
|
|
235
|
+
* The first two rows are ambiguous: a reset connection and a 502 from a proxy
|
|
236
|
+
* both mean "no usable answer", never "nothing happened". A `POST` that the
|
|
237
|
+
* server committed before the response was lost is indistinguishable from one
|
|
238
|
+
* it never saw, so replaying it is how one `create` call becomes two records.
|
|
239
|
+
* Safe methods carry no such risk, which is the whole reason the split exists.
|
|
240
|
+
*
|
|
241
|
+
* `429` is the exception, and it is safe on every method because of how this
|
|
242
|
+
* particular backend produces one. It comes either from `Rack::Attack`, which
|
|
243
|
+
* answers from middleware before the router is reached, or from a
|
|
244
|
+
* `too_many_requests!` guard that every controller places BEFORE the write it
|
|
245
|
+
* protects. A 429 is therefore proof the request was refused rather than
|
|
246
|
+
* performed, and waiting out `Retry-After` and trying again is exactly right.
|
|
247
|
+
*
|
|
248
|
+
* ## Opting a mutator back in
|
|
249
|
+
*
|
|
250
|
+
* Pass a `retry` object AT THE CALL SITE and it applies whatever the method:
|
|
251
|
+
*
|
|
252
|
+
* ```ts
|
|
253
|
+
* await oms.tools.downloader.create(input, { retry: {} }); // POST, retried
|
|
254
|
+
* ```
|
|
255
|
+
*
|
|
256
|
+
* A client-wide `new Oms({ retry })` deliberately does NOT do this. It sets the
|
|
257
|
+
* SHAPE of the backoff (attempts, delays, jitter) for whatever is eligible; it
|
|
258
|
+
* is not a statement about any one endpoint, and "every POST in this process
|
|
259
|
+
* may be replayed" is not a decision anybody makes correctly in a constructor.
|
|
260
|
+
* `retry: false` still disables everything, `429` included, and stays the right
|
|
261
|
+
* answer for a call whose failure mode is minting something (a short link, a
|
|
262
|
+
* notepad) under a fresh random identifier on each attempt.
|
|
263
|
+
*
|
|
264
|
+
* This is narrower than the SDK's 0.2.0 behaviour, which retried 5xx on every
|
|
265
|
+
* verb. Nothing in `resources/` relied on that: every call site that mentions
|
|
266
|
+
* retry is turning it OFF.
|
|
267
|
+
*
|
|
268
|
+
* ## Why this and not the mobile app's rule
|
|
269
|
+
*
|
|
270
|
+
* `oms-music/src/api/retryPolicy.ts` states the rule the app arrived at after
|
|
271
|
+
* the empty-Home bug: **retry for TIME, never for a RESPONSE**. A transport
|
|
272
|
+
* failure (a `fetch` that throws, DNS, the iOS radio still waking) is time and
|
|
273
|
+
* is worth asking again; a `4xx` is an answer and asking again does not change
|
|
274
|
+
* it; a `5xx` gets one extra go for a deploy or a blinking proxy; `429` gets
|
|
275
|
+
* none. Two attempts, `error.status >= 500` as the whole predicate.
|
|
276
|
+
*
|
|
277
|
+
* That rule is right for what it governs and wrong to copy here, for one
|
|
278
|
+
* reason: it sits under react-query, where every retried operation is a QUERY.
|
|
279
|
+
* "Repeat any transport error" is safe when the thing being repeated is a read.
|
|
280
|
+
* This SDK carries the writes too, and the app's own mutations do NOT go
|
|
281
|
+
* through that policy. Applying it verbatim would replay a `POST /songs` whose
|
|
282
|
+
* answer was lost on a lift ride and mint the record twice, which is precisely
|
|
283
|
+
* the failure the method split above exists to prevent. So the SDK keeps the
|
|
284
|
+
* axis the app does not have to think about (the METHOD) and agrees with it on
|
|
285
|
+
* the axis it does: a `4xx` is never retried anywhere.
|
|
286
|
+
*
|
|
287
|
+
* On `429` the two genuinely disagree, and the disagreement is deliberate on
|
|
288
|
+
* both sides. The app declines because its ceiling-aware screens would rather
|
|
289
|
+
* show "slow down" at once than sit on a hidden `Retry-After` sleep, and
|
|
290
|
+
* because react-query re-throws a parked error before the network is even
|
|
291
|
+
* consulted. The SDK retries because it also serves the CLI and the MCP server,
|
|
292
|
+
* where the right move on a rate limit is to wait the header out and continue,
|
|
293
|
+
* and because this backend's `429` provably precedes the write (Rack::Attack in
|
|
294
|
+
* middleware, `too_many_requests!` ahead of every guarded action). A mobile
|
|
295
|
+
* host that wants the app's behaviour asks for it per call - `retry: false` -
|
|
296
|
+
* or, at the client, gets the closest match with `new Oms({ retry: { maxAttempts: 2 } })`:
|
|
297
|
+
* one extra attempt, safe methods only, exactly the app's shape.
|
|
94
298
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
299
|
+
* One consequence to design around rather than discover: a retried `429` obeys
|
|
300
|
+
* `Retry-After`, this API sets it from a one-minute window, and `timeoutMs`
|
|
301
|
+
* bounds ONE attempt rather than the call. A rate-limited call can therefore
|
|
302
|
+
* take minutes of wall clock. Anything with a user watching it should pass a
|
|
303
|
+
* `signal` it can abort, or turn retrying off.
|
|
99
304
|
*/
|
|
100
305
|
export declare class ApiClient {
|
|
101
306
|
/** API root with no trailing slash. */
|
|
@@ -137,13 +342,79 @@ export declare class ApiClient {
|
|
|
137
342
|
* media file or an SSE endpoint. Retry and auth still apply.
|
|
138
343
|
*
|
|
139
344
|
* The caller owns the body and must consume or cancel it.
|
|
345
|
+
*
|
|
346
|
+
* `response.body` is NOT a promise this method can make: React Native has no
|
|
347
|
+
* `ReadableStream` and leaves it undefined, so any code reaching for
|
|
348
|
+
* `.body.getReader()` here works in the browser and the CLI and throws on a
|
|
349
|
+
* phone. Test with {@link supportsResponseStreaming} first, or use
|
|
350
|
+
* {@link ApiClient.streamText}, which contains that fork already.
|
|
140
351
|
*/
|
|
141
352
|
raw(method: string, path: string, options?: GetOptions & {
|
|
142
353
|
body?: BodyInit;
|
|
143
354
|
}): Promise<Response>;
|
|
355
|
+
/**
|
|
356
|
+
* Reads a response body as text, in pieces where the runtime allows it and in
|
|
357
|
+
* one piece where it does not.
|
|
358
|
+
*
|
|
359
|
+
* This is the primitive a streaming endpoint is built on. It promises exactly
|
|
360
|
+
* two things - decoded text, in order, with nothing lost - and deliberately
|
|
361
|
+
* promises nothing about chunk boundaries, because they are not the same on
|
|
362
|
+
* the three clients:
|
|
363
|
+
*
|
|
364
|
+
* - browser and Bun: one yield per network chunk, as it lands;
|
|
365
|
+
* - React Native: ONE yield, containing everything, after the server has
|
|
366
|
+
* finished. `fetch` there cannot do better - see
|
|
367
|
+
* {@link supportsResponseStreaming} - and pretending otherwise by slicing
|
|
368
|
+
* the finished body into fake chunks would only make a UI claim to be live.
|
|
369
|
+
*
|
|
370
|
+
* So a caller must not assume a chunk is a frame, a line, or a whole
|
|
371
|
+
* anything: a `data:` line can arrive split across two chunks, and on RN a
|
|
372
|
+
* hundred of them arrive as one string. Buffer, then split on your own
|
|
373
|
+
* delimiter. `BookChatService` in the frontend is the reference reader (SSE
|
|
374
|
+
* `data:` lines carrying `{ delta, done, error }`) and shows the shape of it;
|
|
375
|
+
* framing belongs to the domain module, not here.
|
|
376
|
+
*
|
|
377
|
+
* ## The silence limit exists because of an outage
|
|
378
|
+
*
|
|
379
|
+
* A stalled sidecar answered `200` and then said nothing for two minutes at a
|
|
380
|
+
* time, and `await reader.read()` has no deadline of its own, so the chat
|
|
381
|
+
* panel span for as long as the tab stayed open. `timeoutMs` does not help:
|
|
382
|
+
* it is disposed once the headers arrive (it has to be, or no stream could
|
|
383
|
+
* outlive it). `silenceTimeoutMs` bounds the gap BETWEEN chunks instead and
|
|
384
|
+
* raises {@link OmsTimeoutError} when nothing arrives for that long. It has
|
|
385
|
+
* to sit well clear of a cold model's first token; 45s is the number the
|
|
386
|
+
* frontend settled on and the default here.
|
|
387
|
+
*
|
|
388
|
+
* Pass `0` to disable it, and mean it: an unbounded read is a spinner with no
|
|
389
|
+
* way out. It does not apply on the buffered path, where the single `text()`
|
|
390
|
+
* read is covered by the caller's `signal`.
|
|
391
|
+
*
|
|
392
|
+
* The body is always released: the reader is cancelled on every exit,
|
|
393
|
+
* including an abandoned `for await` (a `break` runs the generator's
|
|
394
|
+
* `finally`). An abandoned reader holds the connection open.
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* ```ts
|
|
398
|
+
* let buffer = "";
|
|
399
|
+
* for await (const chunk of oms.http.streamText("POST", `/books/${id}/chat`, { body })) {
|
|
400
|
+
* buffer += chunk;
|
|
401
|
+
* // ... pull whole lines out of `buffer`, leave the partial one behind
|
|
402
|
+
* }
|
|
403
|
+
* ```
|
|
404
|
+
*/
|
|
405
|
+
streamText(method: string, path: string, options?: GetOptions & {
|
|
406
|
+
body?: JsonBody;
|
|
407
|
+
silenceTimeoutMs?: number;
|
|
408
|
+
}): AsyncGenerator<string, void, undefined>;
|
|
144
409
|
/**
|
|
145
410
|
* `GET` that reads the whole response as a {@link FileOutput}, taking the
|
|
146
411
|
* filename from `Content-Disposition` when the server sent one.
|
|
412
|
+
*
|
|
413
|
+
* Reads the body into memory in every runtime, React Native included, where
|
|
414
|
+
* `Response#blob()` needs RN's `BlobModule` and produces a Blob whose bytes
|
|
415
|
+
* live on the native side. For a large media file on a phone that is the
|
|
416
|
+
* wrong tool: ask for a signed URL and hand it to the player or to a native
|
|
417
|
+
* downloader instead of pulling it through JavaScript.
|
|
147
418
|
*/
|
|
148
419
|
download(path: string, options?: GetOptions): Promise<FileOutput>;
|
|
149
420
|
/** Absolute URL for a path, with the query string applied. */
|
|
@@ -165,24 +436,167 @@ export declare abstract class Resource {
|
|
|
165
436
|
protected readonly http: ApiClient;
|
|
166
437
|
constructor(http: ApiClient);
|
|
167
438
|
}
|
|
439
|
+
/**
|
|
440
|
+
* The backend's null sentinel: a single backspace character, `U+0008`.
|
|
441
|
+
*
|
|
442
|
+
* A query string has no way to say `null`. `?parent_id=` is the empty string,
|
|
443
|
+
* `?parent_id=null` is the four-letter word "null", and omitting the key
|
|
444
|
+
* entirely says something else again. So the API picked a character no real
|
|
445
|
+
* value ever contains and decodes it back to `nil` on arrival:
|
|
446
|
+
* `CrudActions#define_option_param_getter` runs
|
|
447
|
+
* `value.transform_values! { |v| v == "\b" ? nil : v }` over every filter
|
|
448
|
+
* bucket, and `GroupChatsController`, `GroupChatMessagesController` and
|
|
449
|
+
* `BookServices::Creator` each repeat the same test on the fields they read by
|
|
450
|
+
* hand.
|
|
451
|
+
*
|
|
452
|
+
* Where that `nil` lands is what makes it worth having. `Searchable.exact_search`
|
|
453
|
+
* is `where(params)`, so `exact_search[parent_id]` set to the sentinel becomes
|
|
454
|
+
* `WHERE parent_id IS NULL` - the only way to ask for the storage root nodes,
|
|
455
|
+
* for a comment with no parent, for anything unassigned.
|
|
456
|
+
*
|
|
457
|
+
* Exported because it is part of the wire format, not because you should need
|
|
458
|
+
* it: {@link encodeQuery} writes it for you whenever a query value is `null`.
|
|
459
|
+
*/
|
|
460
|
+
export declare const NULL_SENTINEL = "\b";
|
|
168
461
|
/**
|
|
169
462
|
* Encodes query parameters the way Rails parses them.
|
|
170
463
|
*
|
|
171
464
|
* - `{ page: 2 }` -> `page=2`
|
|
172
465
|
* - `{ ids: ["a", "b"] }` -> `ids%5B%5D=a&ids%5B%5D=b`
|
|
173
466
|
* - `{ search: { status: "open" } }` -> `search%5Bstatus%5D=open`
|
|
174
|
-
* - `
|
|
467
|
+
* - `{ since: new Date(...) }` -> `since=2026-08-29T09%3A00%3A00.000Z`
|
|
468
|
+
* - `{ parent_id: null }` -> `parent_id=%08`, the null sentinel
|
|
469
|
+
* - `undefined` values are dropped entirely.
|
|
470
|
+
*
|
|
471
|
+
* ## Why `null` and `undefined` are not the same thing here
|
|
472
|
+
*
|
|
473
|
+
* They are the two answers to two different questions, and a query string can
|
|
474
|
+
* only express one of them without help:
|
|
475
|
+
*
|
|
476
|
+
* - `undefined` means "I am not filtering on this column". Dropping the key is
|
|
477
|
+
* the correct encoding.
|
|
478
|
+
* - `null` means "filter where this column IS NULL". There is no literal for
|
|
479
|
+
* that in a URL, so it goes out as {@link NULL_SENTINEL} and the backend
|
|
480
|
+
* turns it back into `nil`.
|
|
481
|
+
*
|
|
482
|
+
* Encoding `null` as "drop the key" - which this function used to do - is the
|
|
483
|
+
* dangerous direction. The filter simply vanishes and `Searchable.exact_search`
|
|
484
|
+
* returns early (`return self unless params.present?`), so the endpoint answers
|
|
485
|
+
* with the UNFILTERED set: a request for the root of somebody's drive comes
|
|
486
|
+
* back as their entire tree. That failure has bitten this API before, from the
|
|
487
|
+
* other end, and is why `CrudActions#reject_unknown_filter_keys!` now 400s on a
|
|
488
|
+
* key it does not recognise rather than quietly widening the query.
|
|
489
|
+
*
|
|
490
|
+
* The sentinel is written at every depth, even though the server only decodes
|
|
491
|
+
* it one level inside a filter bucket (`search`, `exact_search`, `modifiers`,
|
|
492
|
+
* `extra_options`). Anywhere else it stays the literal character and matches no
|
|
493
|
+
* row - an empty result, which is the safe way to be wrong. Compare that with
|
|
494
|
+
* dropping the key, which is an over-broad result nobody notices.
|
|
495
|
+
*
|
|
496
|
+
* ## Dates
|
|
497
|
+
*
|
|
498
|
+
* A `Date` reaches `typeof value === "object"` like anything else, and
|
|
499
|
+
* `Object.entries(new Date())` is `[]`, so before this branch existed a date
|
|
500
|
+
* filter did not merely arrive malformed - the key disappeared and the listing
|
|
501
|
+
* came back unfiltered. ISO-8601 is what the server reads:
|
|
502
|
+
* `QuerySearcher#date_search` runs `String#to_date_safe` (`Date.parse`) over
|
|
503
|
+
* the value and turns an unparseable one into `nil`, which silently drops that
|
|
504
|
+
* side of the range. Bodies need no equivalent branch, because `JSON.stringify`
|
|
505
|
+
* already calls `Date.prototype.toJSON` and emits the same string.
|
|
506
|
+
*
|
|
507
|
+
* ## The brackets are percent-encoded, and that is load-bearing
|
|
508
|
+
*
|
|
509
|
+
* `search[title]` goes out as `search%5Btitle%5D`, never as raw `[` and `]`.
|
|
510
|
+
* Both parse identically in Rails, so this looks like the kind of noise someone
|
|
511
|
+
* tidies away on a quiet afternoon. It is not, and the reason is iOS.
|
|
512
|
+
*
|
|
513
|
+
* `[` and `]` are not legal in a URI query (RFC 3986 reserves them for the host
|
|
514
|
+
* component). Every browser tolerates them; Apple's URL stack does not. Give
|
|
515
|
+
* `NSURL`/`URLComponents` a string with a raw bracket in the query and it
|
|
516
|
+
* re-percent-encodes THE WHOLE QUERY to make it valid - including the `%` signs
|
|
517
|
+
* of anything already encoded. `search[title]=Caf%C3%A9` comes back out as
|
|
518
|
+
* `search%5Btitle%5D=Caf%25C3%25A9`, and the server dutifully searches for the
|
|
519
|
+
* literal text `Caf%C3%A9`, which matches nothing. The failure is a silent
|
|
520
|
+
* empty list on one platform, which is the most expensive kind to find.
|
|
521
|
+
*
|
|
522
|
+
* `encodeURIComponent` handles this by encoding brackets already; the point of
|
|
523
|
+
* writing it down is that nothing here may "simplify" the encoder into
|
|
524
|
+
* `encodeURI`, a template literal, or a hand-built `key[sub]=value`, all of
|
|
525
|
+
* which emit raw brackets. `test/react-native.test.ts` pins it.
|
|
526
|
+
*
|
|
527
|
+
* @throws {TypeError} for an invalid `Date`. `toISOString()` would throw a bare
|
|
528
|
+
* `RangeError` from deep inside the transport; failing loudly at the boundary
|
|
529
|
+
* beats sending `Invalid Date` and getting an unfiltered page back.
|
|
175
530
|
*/
|
|
176
531
|
export declare function encodeQuery(params: QueryParams): string;
|
|
532
|
+
/**
|
|
533
|
+
* Methods whose replay cannot duplicate work, so a lost answer may simply be
|
|
534
|
+
* asked for again.
|
|
535
|
+
*
|
|
536
|
+
* `PUT` and `DELETE` are idempotent by RFC 9110 and are still absent, because
|
|
537
|
+
* idempotent is not the same as harmless to replay HERE. Rails' `destroy` is
|
|
538
|
+
* behind a `find_by` that answers `404` the second time, so a `DELETE` retried
|
|
539
|
+
* after a torn connection reports "not found" for a row it deleted perfectly
|
|
540
|
+
* well - a success turned into an error the caller then acts on.
|
|
541
|
+
*/
|
|
542
|
+
export declare const SAFE_METHODS: ReadonlySet<string>;
|
|
543
|
+
/** True when {@link SAFE_METHODS} contains `method`, case-insensitively. */
|
|
544
|
+
export declare function isSafeMethod(method: string): boolean;
|
|
177
545
|
/**
|
|
178
546
|
* Builds the `modifiers[page]` string the backend expects (`"2:100"`).
|
|
179
|
-
*
|
|
180
|
-
*
|
|
547
|
+
*
|
|
548
|
+
* Both halves go through `resolvePageSize` / `resolvePageNumber`, which is the
|
|
549
|
+
* same pair `createPage` uses. That is the point: the size on the wire and the
|
|
550
|
+
* size `Paginated.pageSize` reports have to be one number, or a caller who asks
|
|
551
|
+
* for 1200 gets the server's 500 rows measured against their own 1200 and is
|
|
552
|
+
* told `hasMore: false` while 700 rows sit behind it.
|
|
553
|
+
*
|
|
554
|
+
* @throws {TypeError} for a page size that is not a finite number of at least
|
|
555
|
+
* 1. `"1:NaN"` on the wire reads as size `0` in
|
|
556
|
+
* `QueryModifier#apply_pagination`, which skips `limit`/`offset` altogether
|
|
557
|
+
* and returns the entire table.
|
|
181
558
|
*/
|
|
182
559
|
export declare function pageModifier(page?: number, pageSize?: number): string;
|
|
183
|
-
/**
|
|
560
|
+
/**
|
|
561
|
+
* Turns a {@link FormFields} bag into a `FormData`, buffering any streams.
|
|
562
|
+
*
|
|
563
|
+
* `null` and `undefined` fields are OMITTED, which is the opposite of what
|
|
564
|
+
* {@link encodeQuery} does with `null` and is right for the same reason it is
|
|
565
|
+
* right there: what the receiver reads from an absent field. A multipart body
|
|
566
|
+
* only ever feeds a create here, and an absent field means `params[:x]` is
|
|
567
|
+
* `nil` - already the value a sentinel would decode to, so writing one would
|
|
568
|
+
* add a step that changes nothing. In a query the absent key means "no filter",
|
|
569
|
+
* which is a different answer entirely.
|
|
570
|
+
*
|
|
571
|
+
* The one thing this cannot express is clearing a column through a multipart
|
|
572
|
+
* UPDATE, where absent means "leave it alone". No endpoint takes one - the SDK
|
|
573
|
+
* sends multipart to the tools and to `POST /books` only - and if one appears,
|
|
574
|
+
* pass {@link NULL_SENTINEL} as the field value explicitly.
|
|
575
|
+
*
|
|
576
|
+
* ## React Native files
|
|
577
|
+
*
|
|
578
|
+
* A {@link NativeFile} - the `{ uri, name, type }` a picker returns - is
|
|
579
|
+
* appended VERBATIM, as the object it is. That is the whole trick: RN's
|
|
580
|
+
* `FormData` keeps the value untouched and its `getParts()` turns an entry with
|
|
581
|
+
* a `uri` into a file part that the native layer streams off disk. Converting
|
|
582
|
+
* it to anything first is what breaks it.
|
|
583
|
+
*
|
|
584
|
+
* On a runtime whose `FormData` is the web one, the same object would be
|
|
585
|
+
* coerced to the string `"[object Object]"` and uploaded as a text field: a
|
|
586
|
+
* 200, a stored record, and no file. So it throws there instead
|
|
587
|
+
* ({@link supportsNativeFormDataFiles}), and for the same reason any other
|
|
588
|
+
* unrecognised object throws rather than being stringified - a descriptor that
|
|
589
|
+
* lost its `name` in transit is a typo, not a form value.
|
|
590
|
+
*/
|
|
184
591
|
export declare function buildFormData(fields: FormFields): Promise<FormData>;
|
|
185
|
-
/**
|
|
592
|
+
/**
|
|
593
|
+
* Narrows an unknown form value to a {@link FileInput}.
|
|
594
|
+
*
|
|
595
|
+
* A {@link NativeFile} in `data` counts: it is a legitimate `FileInput` on RN,
|
|
596
|
+
* it just cannot be turned into bytes. Callers that need bytes should test with
|
|
597
|
+
* `isNativeFile(input.data)` rather than trusting this, or let
|
|
598
|
+
* `readFileInput` throw the message that explains the way out.
|
|
599
|
+
*/
|
|
186
600
|
export declare function isFileInput(value: unknown): value is FileInput;
|
|
187
601
|
/** Fills in whatever a partial {@link RetryOptions} left out. */
|
|
188
602
|
export declare function resolveRetry(options: RetryOptions | undefined): ResolvedRetry;
|
|
@@ -193,6 +607,17 @@ export declare function backoffDelay(attempt: number, retry: ResolvedRetry): num
|
|
|
193
607
|
* `setTimeout`, which a Worker isolate provides.
|
|
194
608
|
*/
|
|
195
609
|
export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
610
|
+
/**
|
|
611
|
+
* How long {@link ApiClient.streamText} will wait for the NEXT chunk before it
|
|
612
|
+
* gives up: 45 seconds, the number the frontend's `BookChatService` arrived at.
|
|
613
|
+
*
|
|
614
|
+
* It is a silence limit, not a total: a stream that keeps producing runs as
|
|
615
|
+
* long as it likes. The value has to clear a cold model's first token (the
|
|
616
|
+
* weights are paged in before anything is generated) while still being shorter
|
|
617
|
+
* than a user's patience. The proxy usually drops a silent stream first; this
|
|
618
|
+
* is the backstop for when nothing else closes the connection.
|
|
619
|
+
*/
|
|
620
|
+
export declare const DEFAULT_STREAM_SILENCE_MS = 45000;
|
|
196
621
|
/**
|
|
197
622
|
* Combines a deadline with the caller's signal into one signal.
|
|
198
623
|
*
|
package/dist/types/index.d.ts
CHANGED
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
* 1. It runs in a Cloudflare-Worker-class isolate. No `node:*`, no `process`,
|
|
15
15
|
* no filesystem, no stdout. `fetch` is injectable through the constructor.
|
|
16
16
|
* 2. Files are values - `Blob`, `Uint8Array`, `ReadableStream` - never paths.
|
|
17
|
-
* Turning a path into bytes is the host's job.
|
|
17
|
+
* Turning a path into bytes is the host's job. React Native is the single
|
|
18
|
+
* exception, and only on multipart endpoints: a picked `{ uri, name, type }`
|
|
19
|
+
* (`NativeFile`) goes to the wire verbatim because RN's `FormData` streams
|
|
20
|
+
* it off disk and a `Blob` there uploads truncated.
|
|
18
21
|
* 3. The TYPES are the public interface. In code mode a model reads the `.d.ts`
|
|
19
22
|
* and nothing else, so a name or a JSDoc line carries as much weight as the
|
|
20
23
|
* behaviour behind it.
|
|
@@ -26,6 +29,7 @@ export * from "./auth/index";
|
|
|
26
29
|
export * from "./client";
|
|
27
30
|
export * from "./errors";
|
|
28
31
|
export * from "./http";
|
|
32
|
+
export * from "./listing";
|
|
29
33
|
export * from "./local/index";
|
|
30
34
|
export * from "./resources/index";
|
|
31
35
|
export * from "./types";
|