@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,459 @@
1
+ /**
2
+ * The direct-upload driver.
3
+ *
4
+ * Bytes never pass through Rails. The flow the backend implements, and the one
5
+ * this module follows exactly:
6
+ *
7
+ * 1. `POST /fs_nodes/batch_upload_urls` with a manifest of up to 100 files
8
+ * (`client_id`, `name`, `size`, and optionally `relative_path`,
9
+ * `content_type`, `checksum`). ONE request does directory resolution, one
10
+ * quota reservation under a single lock, node creation and plan minting.
11
+ * 2. Per file the server answers a {@link UploadPlan}:
12
+ * - `strategy: "direct"` below 32 MiB: PUT the whole body to a presigned URL
13
+ * with the exact headers returned, then remember `blob_signed_id`;
14
+ * - `strategy: "multipart"` at or above 32 MiB: drive
15
+ * `/fs_nodes/:id/multipart/{start,part_urls,complete}` in 32 MiB parts.
16
+ * 3. `POST /fs_nodes/batch_attach_blobs` binds the direct-tier blobs to their
17
+ * nodes. Multipart finalises itself at `multipart/complete`.
18
+ *
19
+ * Two rules that are easy to get wrong and expensive to get wrong:
20
+ *
21
+ * - The direct tier REQUIRES a base64 MD5 `checksum`. It is bound into the
22
+ * presigned signature as `Content-MD5`, so a wrong or missing one makes the
23
+ * PUT fail at the object store with a signature error that says nothing
24
+ * about checksums. WebCrypto has no MD5, so {@link md5Base64} carries a
25
+ * self-contained implementation - see the note there.
26
+ * - The presigned PUTs go to the object store, NOT to the API. They must be
27
+ * sent with the injected fetch but WITHOUT the `Authorization` header, or
28
+ * MinIO rejects the request for having two authentication schemes.
29
+ *
30
+ * A node whose bytes never landed is adopted on the next attempt rather than
31
+ * failing, so a torn batch is simply retried with the same manifest.
32
+ *
33
+ * Multipart is not an optimisation. The object store sits behind Cloudflare on
34
+ * a plan that caps a request body at roughly 100 MB, so anything larger has no
35
+ * other way in.
36
+ */
37
+ import { Resource, type ApiClient } from "../../http";
38
+ import { type FetchLike, type FileInput, type Id, type OperationOptions, type ProgressCallback, type RequestOptions } from "../../types";
39
+ import type { FsNode } from "../storage";
40
+ /** Files at or above this size take the multipart path. Mirrors the backend. */
41
+ export declare const MULTIPART_THRESHOLD: number;
42
+ /**
43
+ * Part size for the multipart path. Mirrors the backend, but it is only a
44
+ * fallback: the real part size is whatever `multipart/start` answered, and that
45
+ * is the number the driver slices with.
46
+ */
47
+ export declare const MULTIPART_PART_SIZE: number;
48
+ /** Maximum files in one `batch_upload_urls` call. Mirrors the backend. */
49
+ export declare const MAX_BATCH = 100;
50
+ /**
51
+ * Files sent in one `batch_upload_urls` call by default.
52
+ *
53
+ * Deliberately half of {@link MAX_BATCH}: a batch is one quota reservation
54
+ * under one row lock, and a smaller batch narrows the window in which a torn
55
+ * run leaves reserved-but-unused bytes behind.
56
+ */
57
+ export declare const DEFAULT_BATCH_SIZE = 50;
58
+ /** Maximum part URLs requested in one call. Mirrors the backend. */
59
+ export declare const MAX_PART_URLS = 100;
60
+ /** Parts one multipart upload may have. Mirrors the backend's `MAX_PARTS`. */
61
+ export declare const MAX_PARTS = 10000;
62
+ /** Part URLs asked for in one round trip. Below {@link MAX_PART_URLS} on purpose. */
63
+ export declare const PART_URL_WINDOW = 32;
64
+ /** Parallel presigned PUTs in flight by default. The store is a Pi behind Cloudflare. */
65
+ export declare const DEFAULT_UPLOAD_CONCURRENCY = 4;
66
+ /**
67
+ * The `fs_upload/authed` throttle: `batch_upload_urls`, `batch_attach_blobs`
68
+ * and every `multipart/*` call share 300 requests a minute, keyed by session.
69
+ * {@link UploadManager} paces itself against this so a large run degrades into
70
+ * waiting rather than into a wall of 429s.
71
+ */
72
+ export declare const FS_UPLOAD_RATE_LIMIT = 300;
73
+ /**
74
+ * The `fs_bulk_job/authed` throttle: `copy`, `create_directories`,
75
+ * `empty_trash` and `move_to_trash` share TWELVE requests a minute. It is the
76
+ * tightest limit in the API and the easiest one to trip by looping.
77
+ */
78
+ export declare const FS_BULK_JOB_RATE_LIMIT = 12;
79
+ /** One entry of the manifest sent to `batch_upload_urls`. */
80
+ export interface UploadManifestEntry {
81
+ /** Caller-chosen correlation key. The response is matched back on this. */
82
+ readonly client_id: string;
83
+ readonly name: string;
84
+ readonly size: number;
85
+ /**
86
+ * Path INCLUDING the filename, e.g. `"fotos/2024/praia.jpg"`. Everything
87
+ * before the last segment becomes directories. A `..` segment is rejected.
88
+ */
89
+ readonly relative_path?: string;
90
+ readonly content_type?: string;
91
+ /** Base64 MD5 of the whole file. Required below {@link MULTIPART_THRESHOLD}. */
92
+ readonly checksum?: string;
93
+ }
94
+ /** The presigned PUT for a small file. */
95
+ export interface DirectUploadTarget {
96
+ readonly url: string;
97
+ /** Send these verbatim. They include the `Content-MD5` the signature covers. */
98
+ readonly headers: Record<string, string>;
99
+ /** Hand this back to `batch_attach_blobs` once the PUT succeeds. */
100
+ readonly blob_signed_id: string;
101
+ }
102
+ /** What the server decided for one manifest entry. */
103
+ export interface UploadPlan {
104
+ readonly client_id: string;
105
+ readonly fs_node_id: Id;
106
+ readonly strategy: "direct" | "multipart";
107
+ /** Present only for `strategy: "direct"`. */
108
+ readonly upload?: DirectUploadTarget;
109
+ }
110
+ /**
111
+ * One entry the server refused, with a machine-readable reason.
112
+ *
113
+ * The server names the entry by `client_id` only - it never echoes the
114
+ * filename back - so correlate on that and look the name up yourself.
115
+ */
116
+ export interface UploadPlanError {
117
+ readonly client_id: string;
118
+ /** `"quota_exceeded"`, `"invalid"`, `"mint_failed"`. */
119
+ readonly code: string;
120
+ readonly message: string;
121
+ }
122
+ /** The answer to `POST /fs_nodes/batch_upload_urls`. */
123
+ export interface UploadBatch {
124
+ readonly parent_id: Id;
125
+ /** Directories resolved or created while walking the relative paths. */
126
+ readonly directories: Array<{
127
+ readonly path: string;
128
+ readonly id: Id;
129
+ }>;
130
+ readonly results: UploadPlan[];
131
+ readonly errors: UploadPlanError[];
132
+ }
133
+ /** One entry of the answer to `POST /fs_nodes/batch_attach_blobs`. */
134
+ export interface AttachBlobResult {
135
+ readonly fs_node_id: Id;
136
+ readonly attached: boolean;
137
+ /** `"not_found"`, `"already_attached"`, `"blob_not_found"`, `"attach_failed"`. */
138
+ readonly code?: string;
139
+ readonly message?: string;
140
+ }
141
+ /** The answer to `POST /fs_nodes/:id/multipart/start`. */
142
+ export interface MultipartSession {
143
+ /** Signed `(key, upload_id)` pair. The server keeps no upload state. */
144
+ readonly upload_token: string;
145
+ readonly part_size: number;
146
+ readonly part_count: number;
147
+ }
148
+ /** A presigned PUT for one part. */
149
+ export interface MultipartPartUrl {
150
+ readonly part_number: number;
151
+ readonly url: string;
152
+ }
153
+ /** A part that has landed, identified by the ETag the store returned. */
154
+ export interface MultipartPart {
155
+ readonly part_number: number;
156
+ readonly etag: string;
157
+ }
158
+ /** The answer to `POST /fs_nodes/:id/multipart/complete`. */
159
+ export interface MultipartCompletion {
160
+ readonly attached: boolean;
161
+ /** Size the store measured. Authoritative: the declared size is reconciled to it. */
162
+ readonly byte_size: number;
163
+ }
164
+ /**
165
+ * Computes the base64 MD5 of a blob.
166
+ *
167
+ * Supply one to replace the built-in {@link md5Base64} with a faster native or
168
+ * WASM digest, on a host that has one.
169
+ */
170
+ export type Md5Base64Fn = (data: Blob) => Promise<string>;
171
+ /** Arguments for {@link UploadManager.upload}. */
172
+ export interface UploadInput {
173
+ /** Directory to upload into. */
174
+ readonly parentId: Id;
175
+ readonly files: FileInput[];
176
+ /**
177
+ * Relative path per file, parallel to `files`, when uploading a folder. Use
178
+ * {@link FileInput.filename} alone for a flat upload.
179
+ *
180
+ * The path INCLUDES the filename (`"fotos/2024/praia.jpg"`); everything
181
+ * before the last segment is created as directories in the same call, so a
182
+ * folder upload costs no extra round trips. A `..` segment is a 400.
183
+ */
184
+ readonly relativePaths?: string[];
185
+ /** Parallel PUTs in flight. Keep it modest: the store is a Pi behind Cloudflare. */
186
+ readonly concurrency?: number;
187
+ /** Files per `batch_upload_urls` call. Clamped to {@link MAX_BATCH}. */
188
+ readonly batchSize?: number;
189
+ /**
190
+ * Read the finished nodes back with one extra listing per batch. On by
191
+ * default, and worth it: a normal listing hides nodes whose bytes never
192
+ * landed, so a node coming back at all is proof the blob is bound.
193
+ */
194
+ readonly verify?: boolean;
195
+ /** Replacement for the built-in MD5. See {@link Md5Base64Fn}. */
196
+ readonly md5?: Md5Base64Fn;
197
+ }
198
+ /** What one file's upload ended as. */
199
+ export interface UploadResult {
200
+ readonly client_id: string;
201
+ /** Name that was uploaded, echoed back so a caller can report on it. */
202
+ readonly filename: string;
203
+ /** The node the bytes belong to. `null` when the file never got a plan. */
204
+ readonly fs_node_id: Id | null;
205
+ /**
206
+ * The finished node, when it was read back (see {@link UploadInput.verify}).
207
+ * `null` when verification was off or the node did not come back.
208
+ */
209
+ readonly node: FsNode | null;
210
+ /** Set when this file failed while the rest of the batch succeeded. */
211
+ readonly error?: UploadPlanError;
212
+ }
213
+ /**
214
+ * A sliding-window pacer for one throttle bucket.
215
+ *
216
+ * The API throttles by session, not by connection, so parallelism inside the
217
+ * SDK is exactly what trips it. Requests wait their turn here instead of
218
+ * racing into a 429 and paying the server's `Retry-After` afterwards.
219
+ *
220
+ * Isolate-safe: `Date.now` and `setTimeout`, nothing else. Per instance, so two
221
+ * clients in the same isolate do not share (and must not share) a window.
222
+ */
223
+ export declare class StorageRateGate {
224
+ private readonly limit;
225
+ private readonly windowMs;
226
+ private readonly headroom;
227
+ private readonly hits;
228
+ /**
229
+ * @param limit Requests allowed inside the window.
230
+ * @param windowMs Length of the window in milliseconds.
231
+ * @param headroom Fraction of the limit actually used, so the SDK is never
232
+ * the request that trips the bucket. `0.9` spends 270 of 300.
233
+ */
234
+ constructor(limit: number, windowMs?: number, headroom?: number);
235
+ /** Resolves when another request may be sent. */
236
+ wait(signal?: AbortSignal): Promise<void>;
237
+ }
238
+ /** Constructor options for {@link UploadManager}. */
239
+ export interface UploadManagerOptions {
240
+ /**
241
+ * The fetch used for the presigned PUTs.
242
+ *
243
+ * Defaults to the one the {@link ApiClient} was built with, so bytes travel
244
+ * on the same transport as everything else. Pass one only to send the object
245
+ * store's traffic somewhere different from the API's.
246
+ */
247
+ readonly fetch?: FetchLike;
248
+ /** Replacement for the built-in MD5. See {@link Md5Base64Fn}. */
249
+ readonly md5?: Md5Base64Fn;
250
+ }
251
+ /**
252
+ * Drives presigned uploads into the virtual filesystem.
253
+ *
254
+ * Reached as `oms.storage.uploads`, and wrapped by `oms.storage.upload()` for
255
+ * the common case. Instantiate it directly only when you need the low-level
256
+ * steps, for instance to resume a torn multipart.
257
+ */
258
+ export declare class UploadManager extends Resource {
259
+ /** Paces every control-plane call against the `fs_upload` throttle. */
260
+ readonly gate: StorageRateGate;
261
+ private readonly transport;
262
+ private readonly md5;
263
+ constructor(http: ApiClient, options?: UploadManagerOptions);
264
+ /**
265
+ * Uploads files end to end: manifest, presign, transfer, bind, verify.
266
+ *
267
+ * Splits into batches of {@link UploadInput.batchSize}, picks the strategy
268
+ * per file from the server's plan, and reports bytes moved through
269
+ * `onProgress`.
270
+ *
271
+ * Per-file failures do NOT abort the run: they come back as
272
+ * {@link UploadResult.error} with the rest of the batch intact, exactly as
273
+ * the server reports them. Only a structural failure (bad parent, empty or
274
+ * oversized batch) throws.
275
+ *
276
+ * Progress granularity is a whole direct PUT or a whole multipart part, not
277
+ * a byte counter. `fetch` has no upload-progress event and a streamed request
278
+ * body is neither universally supported nor usable against a presigned PUT,
279
+ * which needs a `Content-Length`.
280
+ *
281
+ * Every multipart session this call opened is aborted before the error
282
+ * leaves, so a torn run does not strand parts (and reserved quota) on the
283
+ * store.
284
+ */
285
+ upload(input: UploadInput, options?: OperationOptions): Promise<UploadResult[]>;
286
+ /**
287
+ * `POST /fs_nodes/batch_upload_urls` - step 1 on its own.
288
+ *
289
+ * One request buys directory resolution, a single quota reservation under one
290
+ * lock, node creation and plan minting for the whole manifest. Per-file
291
+ * rejections come back in `errors` with a 200; only a structural problem
292
+ * (invalid parent, empty batch, over {@link MAX_BATCH}, a `..` segment)
293
+ * is a 400.
294
+ *
295
+ * @throws {OmsApiError} 400 on a structural problem, 404 when the parent is
296
+ * not a directory the caller may write to.
297
+ */
298
+ createBatch(input: {
299
+ parentId: Id;
300
+ files: UploadManifestEntry[];
301
+ }, options?: RequestOptions): Promise<UploadBatch>;
302
+ /**
303
+ * PUTs one whole body to a presigned URL.
304
+ *
305
+ * Sends the plan's headers verbatim and NO `Authorization`: this request goes
306
+ * to the object store, not to the API. MinIO refuses a request that carries
307
+ * both a presigned signature and a bearer header.
308
+ *
309
+ * Retries a network fault or a 5xx from the store. A 4xx is never retried: an
310
+ * expired signature or a checksum mismatch is deterministic and a retry only
311
+ * costs the bytes again.
312
+ *
313
+ * @returns The ETag the store reported, or `null` when it was not readable -
314
+ * which happens in a browser whose bucket CORS policy does not expose
315
+ * `ETag`. The direct tier does not need it; multipart does.
316
+ */
317
+ putDirect(target: DirectUploadTarget, body: Blob, options?: RequestOptions & {
318
+ onProgress?: ProgressCallback;
319
+ }): Promise<string | null>;
320
+ /**
321
+ * `POST /fs_nodes/batch_attach_blobs` - binds finished direct uploads to
322
+ * their nodes. Until this lands the node exists with no bytes and is hidden
323
+ * from every listing.
324
+ *
325
+ * Answers 200 with per-item results even when every item failed. Read
326
+ * `attached` on each one; do not infer success from the status.
327
+ */
328
+ attachBlobs(attachments: Array<{
329
+ fs_node_id: Id;
330
+ blob_signed_id: string;
331
+ }>, options?: RequestOptions): Promise<AttachBlobResult[]>;
332
+ /**
333
+ * `POST /fs_nodes/:id/multipart/start`.
334
+ *
335
+ * The server keeps no upload state: the `(key, upload_id)` pair lives signed
336
+ * inside `upload_token`, which is good for 48 hours. Persist it and a torn
337
+ * upload can be resumed later with {@link uploadMultipart}'s `resume`.
338
+ *
339
+ * @throws {OmsApiError} 400 when the node already has data, is a directory,
340
+ * or has no size set.
341
+ */
342
+ multipartStart(fsNodeId: Id, options?: RequestOptions): Promise<MultipartSession>;
343
+ /**
344
+ * `POST /fs_nodes/:id/multipart/part_urls` - presigns up to
345
+ * {@link MAX_PART_URLS} parts at a time. The URLs live one hour.
346
+ */
347
+ multipartPartUrls(fsNodeId: Id, input: {
348
+ uploadToken: string;
349
+ partNumbers: number[];
350
+ }, options?: RequestOptions): Promise<MultipartPartUrl[]>;
351
+ /**
352
+ * `POST /fs_nodes/:id/multipart/complete` - assembles the parts. This is what
353
+ * creates the blob row; there is no separate attach step.
354
+ *
355
+ * `byte_size` is what the store measured, and it wins: the node's declared
356
+ * size and the root's quota counter are reconciled to it server-side.
357
+ */
358
+ multipartComplete(fsNodeId: Id, input: {
359
+ uploadToken: string;
360
+ parts: MultipartPart[];
361
+ }, options?: RequestOptions): Promise<MultipartCompletion>;
362
+ /**
363
+ * `POST /fs_nodes/:id/multipart/abort` - releases the parts already stored
364
+ * and, when no blob was ever bound, destroys the node and refunds its quota
365
+ * reservation.
366
+ *
367
+ * Call it whenever an upload fails or is cancelled. {@link upload} does; a
368
+ * caller driving the steps by hand must too. Skipping it leaves the bytes
369
+ * charged against the quota until a server-side reaper notices, which is a
370
+ * schedule, not a promise.
371
+ */
372
+ multipartAbort(fsNodeId: Id, input: {
373
+ uploadToken: string;
374
+ }, options?: RequestOptions): Promise<void>;
375
+ /**
376
+ * Drives one file through the multipart flow: start, presign a window of
377
+ * parts, PUT them in parallel, complete.
378
+ *
379
+ * Parts are sliced with the size the SERVER reported, never with
380
+ * {@link MULTIPART_PART_SIZE}, so a change on the backend does not silently
381
+ * corrupt an upload here.
382
+ *
383
+ * @param options.resume A session from a previous attempt plus the parts that
384
+ * already landed. The matching parts are skipped.
385
+ * @param options.onSession Called with the upload token as soon as there is
386
+ * one, so a caller can persist it and abort later.
387
+ * @param options.onPart Called with the byte count of each finished part.
388
+ */
389
+ uploadMultipart(fsNodeId: Id, body: Blob, options?: RequestOptions & {
390
+ concurrency?: number;
391
+ resume?: {
392
+ uploadToken: string;
393
+ partSize: number;
394
+ parts: MultipartPart[];
395
+ };
396
+ onSession?: (uploadToken: string, partSize: number) => void;
397
+ onPart?: (bytes: number, part: MultipartPart) => void;
398
+ }): Promise<MultipartCompletion>;
399
+ /**
400
+ * PUTs bytes at the object store on the injected transport, with no
401
+ * `Authorization` header and a bounded retry.
402
+ */
403
+ private putBytes;
404
+ /**
405
+ * Reads finished nodes back in ONE listing.
406
+ *
407
+ * A normal listing hides files whose bytes never landed, so a node coming
408
+ * back is proof the blob is bound - which is why this is a verification and
409
+ * not just a convenience.
410
+ */
411
+ private readBack;
412
+ /** Best-effort abort of every session still open. Never throws. */
413
+ private abortAll;
414
+ }
415
+ /**
416
+ * Base64 MD5 of a blob, in the exact form `Content-MD5` wants.
417
+ *
418
+ * The algorithm is not negotiable: the backend binds this digest into the
419
+ * presigned PUT signature as `Content-MD5`, so anything else makes the object
420
+ * store reject the upload with a signature error that never mentions checksums.
421
+ *
422
+ * WebCrypto implements SHA only, so this cannot be done with platform APIs, and
423
+ * the core carries no runtime dependencies - it has to run in an isolate where
424
+ * a Node-flavoured hashing package is not an option. Hence the implementation
425
+ * below: small, streaming, and self-contained. A host that already owns a
426
+ * faster digest can replace it through {@link UploadManagerOptions.md5} or
427
+ * {@link UploadInput.md5}.
428
+ *
429
+ * Streams the blob rather than materialising it, so hashing a 31 MiB file costs
430
+ * one 64 KiB window and not 31 MiB of extra heap.
431
+ */
432
+ export declare function md5Base64(data: Blob): Promise<string>;
433
+ /**
434
+ * Builds a manifest entry from a {@link FileInput}, computing the checksum only
435
+ * when the file is below {@link MULTIPART_THRESHOLD} and therefore needs one.
436
+ *
437
+ * Buffers a `ReadableStream` input: the manifest needs an exact byte count up
438
+ * front, and the direct tier needs a digest of the whole body. Prefer handing
439
+ * in a `Blob` when you have one.
440
+ */
441
+ export declare function manifestEntryFor(file: FileInput, input: {
442
+ clientId: string;
443
+ relativePath?: string;
444
+ md5?: Md5Base64Fn;
445
+ }): Promise<UploadManifestEntry>;
446
+ /**
447
+ * The `fetch` a request to the object store should travel on.
448
+ *
449
+ * It has to be the one the host injected into the {@link ApiClient} - a
450
+ * Worker's proxy, a test double, a policy wrapper - or the bytes silently
451
+ * escape whatever the host put around its network. `ApiClient` keeps that
452
+ * function private and `http.ts` is shared, so it is read here through a
453
+ * defensive probe instead of by growing the transport a new public member.
454
+ *
455
+ * When the probe finds nothing (a hand-built stub, a future refactor) the
456
+ * ambient `fetch` is used, and when there is none either the failure is loud
457
+ * rather than an upload sent through something unexpected.
458
+ */
459
+ export declare function objectStoreFetch(http: ApiClient): FetchLike;