@hostr/upload 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.
- package/README.md +298 -0
- package/dist/api/client.d.ts +71 -0
- package/dist/browser/index.d.ts +50 -0
- package/dist/browser.js +1061 -0
- package/dist/core/engine.d.ts +150 -0
- package/dist/core/events.d.ts +33 -0
- package/dist/core/retry.d.ts +20 -0
- package/dist/core/session.d.ts +28 -0
- package/dist/core/types.d.ts +44 -0
- package/dist/core/upload.d.ts +32 -0
- package/dist/hash/composite.d.ts +2 -0
- package/dist/hash/hasher.d.ts +2 -0
- package/dist/hash/hex.d.ts +2 -0
- package/dist/hash/md5.d.ts +24 -0
- package/dist/hash/sha256.d.ts +25 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +1012 -0
- package/dist/node/hasher.d.ts +2 -0
- package/dist/node/index.d.ts +43 -0
- package/dist/node.js +1089 -0
- package/dist/react-native/background-plan.d.ts +57 -0
- package/dist/react-native/background.d.ts +126 -0
- package/dist/react-native/fs.d.ts +28 -0
- package/dist/react-native/index.d.ts +69 -0
- package/dist/react-native/native-upload.d.ts +76 -0
- package/dist/react-native.js +896 -0
- package/package.json +48 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** Pure planner for the iOS background transport. Mirrors `UploadEngine`'s
|
|
2
|
+
* `reconcile()` philosophy exactly (see `../core/engine.ts`): an md5-vs-etag
|
|
3
|
+
* match is what promotes a part to "done" — never bookkeeping (a staged
|
|
4
|
+
* record, a completion callback) taken on its own. This function does no
|
|
5
|
+
* I/O; it only decides, given four facts —
|
|
6
|
+
* - `session`: the persisted record, whose `done` map is trusted
|
|
7
|
+
* (verified when written, by the engine or by a background completion
|
|
8
|
+
* callback that already checked md5-vs-etag itself);
|
|
9
|
+
* - `remote`: what R2 actually holds right now (`GET /v1/uploads/:id`);
|
|
10
|
+
* - `staged`: part slices currently sitting on disk, each carrying the
|
|
11
|
+
* md5 computed at staging time and the process epoch that staged it;
|
|
12
|
+
* - `currentEpoch`: this process's own epoch, so a staged record left by
|
|
13
|
+
* a process that no longer exists can be told apart from one this
|
|
14
|
+
* process itself just wrote —
|
|
15
|
+
* what the executor (`background.ts`) should do next: which parts to slice
|
|
16
|
+
* and hand to the OS, which staged files are safe to delete, and which
|
|
17
|
+
* landed parts can be adopted as done without re-uploading.
|
|
18
|
+
*
|
|
19
|
+
* Deliberate asymmetry with `UploadEngine.reconcile`: that function actively
|
|
20
|
+
* invalidates `session.done` entries R2 no longer backs (see its comment).
|
|
21
|
+
* This planner never does — `session.done` is trusted outright, full stop,
|
|
22
|
+
* everywhere below. A background completion callback and the engine's own
|
|
23
|
+
* writes are the only ways to set it, and both already checked md5-vs-etag
|
|
24
|
+
* before doing so, so re-checking here on every reconcile would just be
|
|
25
|
+
* spending a file read + hash for no new information. The cost of that
|
|
26
|
+
* trust: if a `done` entry were ever wrong (storage corruption, manual
|
|
27
|
+
* tampering), this planner would not catch it — the composite-etag check in
|
|
28
|
+
* `background.ts`'s completion path would, but only at the very end, not on
|
|
29
|
+
* the reconcile that produced the bad entry. */
|
|
30
|
+
import type { SessionRecord } from '../core/session.js';
|
|
31
|
+
import type { RemotePart } from '../api/client.js';
|
|
32
|
+
export type StagedPart = {
|
|
33
|
+
partNumber: number;
|
|
34
|
+
fileUri: string;
|
|
35
|
+
md5: string;
|
|
36
|
+
/** The staging process's epoch (see `currentEpoch` below) at the moment
|
|
37
|
+
* this part was staged. A force-kill cancels the OS's in-flight
|
|
38
|
+
* transfers, so a staged record surviving from a *different* (and
|
|
39
|
+
* therefore dead) epoch means no transfer for it is actually running
|
|
40
|
+
* anymore, however untouched the record looks. */
|
|
41
|
+
launchEpoch: string;
|
|
42
|
+
};
|
|
43
|
+
export type BackgroundWork = {
|
|
44
|
+
/** Parts to slice to disk and hand to the OS as a new background upload task. */
|
|
45
|
+
toStage: number[];
|
|
46
|
+
/** Staged file URIs whose parts have landed (or are already done) — safe to delete. */
|
|
47
|
+
toCleanup: string[];
|
|
48
|
+
/** partNumber -> md5, for remote parts whose etag matches a staged md5. */
|
|
49
|
+
adopt: Record<number, string>;
|
|
50
|
+
};
|
|
51
|
+
export declare function planBackgroundWork(i: {
|
|
52
|
+
session: SessionRecord;
|
|
53
|
+
remote: RemotePart[];
|
|
54
|
+
staged: StagedPart[];
|
|
55
|
+
/** This process's own epoch — see `StagedPart.launchEpoch`. */
|
|
56
|
+
currentEpoch: string;
|
|
57
|
+
}): BackgroundWork;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/** Thin executor over expo-file-system's iOS background upload tasks.
|
|
2
|
+
* Deliberately not unit-tested — everything that decides is
|
|
3
|
+
* `planBackgroundWork` (pure, tested in `test/background-plan.test.ts`);
|
|
4
|
+
* this file only does I/O and delegates every decision to it. Exercised
|
|
5
|
+
* on-device via `apps/expo-playground` (see the checklist at the top of
|
|
6
|
+
* its `App.tsx`).
|
|
7
|
+
*
|
|
8
|
+
* The OS carries the parts once a background upload task is launched — it
|
|
9
|
+
* keeps running (subject to the OS's own scheduling) even if the app is
|
|
10
|
+
* backgrounded or the JS runtime is suspended. But a transfer can still die
|
|
11
|
+
* two different ways, and this file recovers from both:
|
|
12
|
+
* - a force-kill cancels the OS's transfers outright (documented iOS
|
|
13
|
+
* behavior for `URLSessionUploadTask` background sessions with no
|
|
14
|
+
* persisted `URLSession` delegate reattachment on our side) — the
|
|
15
|
+
* process that launched them is simply gone, so `LAUNCH_EPOCH` (below)
|
|
16
|
+
* is what `planBackgroundWork` uses to recognize their staged records
|
|
17
|
+
* as dead and re-stage them;
|
|
18
|
+
* - a transfer can also die *in-process* — a presign expiring while iOS
|
|
19
|
+
* suspends rather than kills the app (a background URLSession task can
|
|
20
|
+
* sit deferred for well over the entry token's window), or the OS
|
|
21
|
+
* abandoning it during a long airplane-mode stretch — surfacing as a
|
|
22
|
+
* non-2xx `uploadAsync()` result or a rejected one. Its staged record's
|
|
23
|
+
* `launchEpoch` is still *this* process's live epoch, so the planner
|
|
24
|
+
* alone can't tell it apart from one genuinely still in flight; the
|
|
25
|
+
* completion handler below (`stagePart`) is what closes that gap, by
|
|
26
|
+
* dropping the staged record itself the moment it sees the failure —
|
|
27
|
+
* after that, the planner sees nothing staged for the part at all, the
|
|
28
|
+
* same as a part that was never staged in the first place.
|
|
29
|
+
* Either way, nothing here is treated as a fatal error: `reconcile()`
|
|
30
|
+
* re-derives truth from `ListParts` (`GET /v1/uploads/:id`) every time, so
|
|
31
|
+
* a lost or 403'd transfer really is simply re-staged with a fresh presign
|
|
32
|
+
* on the next call. Completion callbacks attached to a task while the app
|
|
33
|
+
* is alive are an optimization (mark done sooner, skip a network round
|
|
34
|
+
* trip, clear a dead record sooner, and — when the part they verify is the
|
|
35
|
+
* last one outstanding — trigger the completing reconcile automatically) —
|
|
36
|
+
* never the only path to correctness.
|
|
37
|
+
*
|
|
38
|
+
* `LAUNCH_EPOCH` is this process's boot id, generated once when this
|
|
39
|
+
* module is first loaded. It's what lets `planBackgroundWork` tell a
|
|
40
|
+
* staged-but-unlanded part apart from a *live* one: a staged record from a
|
|
41
|
+
* process that has since force-killed and relaunched — a new epoch — no
|
|
42
|
+
* longer describes a real OS transfer, however untouched the file and its
|
|
43
|
+
* record look, and gets re-staged; a record from *this* epoch is left
|
|
44
|
+
* alone because its OS task is presumably still in flight (or, if it isn't
|
|
45
|
+
* anymore, its own completion handler is the one responsible for saying
|
|
46
|
+
* so — see above). */
|
|
47
|
+
import type { StateStore } from '../core/types.js';
|
|
48
|
+
type BgFile = {
|
|
49
|
+
uri: string;
|
|
50
|
+
size: number;
|
|
51
|
+
modificationTime?: number;
|
|
52
|
+
};
|
|
53
|
+
/** Optional per-upload event sink, threaded from `startBackgroundUpload`
|
|
54
|
+
* down to each part's OS task. Everything here is advisory-grade, exactly
|
|
55
|
+
* like the completion handlers that fire it: events only flow while the JS
|
|
56
|
+
* runtime is alive, and nothing that matters for correctness depends on
|
|
57
|
+
* them — they exist so a foreground UI (`createUpload`'s `NativeUpload`
|
|
58
|
+
* wrapper) can show progress and settle its `done` promise without
|
|
59
|
+
* polling. */
|
|
60
|
+
export type BackgroundObserver = {
|
|
61
|
+
/** OS task progress for one part (bytes sent so far of that part). */
|
|
62
|
+
onPartProgress?: (partNumber: number, bytesSent: number) => void;
|
|
63
|
+
/** A part's etag verified and recorded in `session.done`. */
|
|
64
|
+
onPartLanded?: (partNumber: number) => void;
|
|
65
|
+
/** A part's task failed or its landed bytes didn't verify; its staged
|
|
66
|
+
* record was dropped and the part needs a re-stage (next reconcile). */
|
|
67
|
+
onPartFailed?: (partNumber: number) => void;
|
|
68
|
+
/** CompleteMultipartUpload succeeded and the composite etag verified. */
|
|
69
|
+
onComplete?: (info: {
|
|
70
|
+
assetId: string;
|
|
71
|
+
etag: string;
|
|
72
|
+
bytes: number;
|
|
73
|
+
}) => void;
|
|
74
|
+
};
|
|
75
|
+
/** Cancels every live OS task for one asset. Returns how many were told to
|
|
76
|
+
* cancel — already-settled tasks are unaffected (their `cancel()` is a
|
|
77
|
+
* documented no-op). */
|
|
78
|
+
export declare function cancelBackgroundTasks(assetId: string): number;
|
|
79
|
+
export declare function startBackgroundUpload(opts: {
|
|
80
|
+
endpoint: string;
|
|
81
|
+
uploadToken: string;
|
|
82
|
+
assetId: string;
|
|
83
|
+
file: BgFile;
|
|
84
|
+
store: StateStore;
|
|
85
|
+
onTokenExpired?: (assetId: string) => Promise<string>;
|
|
86
|
+
/** Called with the result of reconciles this module runs on its own — the
|
|
87
|
+
* automatic pass triggered when the last outstanding part lands, which is
|
|
88
|
+
* what finishes the multipart upload without a manual `reconcile()`. A
|
|
89
|
+
* caller-driven `reconcile()` reports through its return value instead. */
|
|
90
|
+
onAutoReconcile?: (result: 'complete' | 'in-progress') => void;
|
|
91
|
+
observer?: BackgroundObserver;
|
|
92
|
+
}): Promise<BackgroundHandle>;
|
|
93
|
+
export type BackgroundHandle = {
|
|
94
|
+
/** One reconcile pass: adopt landed parts, re-stage what died, complete
|
|
95
|
+
* when nothing remains. Safe to call any time; serialized per asset. */
|
|
96
|
+
reconcile(): Promise<'complete' | 'in-progress'>;
|
|
97
|
+
/** Cancels the live OS tasks (returns how many). Their staged records
|
|
98
|
+
* self-clean as the cancellations settle; nothing else is touched, so a
|
|
99
|
+
* later `reconcile()` resumes by re-staging what hadn't landed. */
|
|
100
|
+
pause(): number;
|
|
101
|
+
/** Cancels live tasks, aborts the multipart upload server-side (best
|
|
102
|
+
* effort), and deletes all local state — staged files and records, the
|
|
103
|
+
* session, the launch-recovery pointers. Not resumable. */
|
|
104
|
+
abort(): Promise<void>;
|
|
105
|
+
readonly assetId: string;
|
|
106
|
+
readonly partSize: number;
|
|
107
|
+
readonly expectedParts: number;
|
|
108
|
+
readonly declaredSize: number;
|
|
109
|
+
/** Part numbers verified so far, per the in-memory session record. */
|
|
110
|
+
donePartNumbers(): number[];
|
|
111
|
+
};
|
|
112
|
+
/** Call once on app launch (a `useEffect` with an empty dependency array is
|
|
113
|
+
* the natural fit). Finds every fileId with background work outstanding —
|
|
114
|
+
* from a previous `startBackgroundUpload` call, however long ago, possibly
|
|
115
|
+
* a prior app install's session if storage survived — and reconciles each:
|
|
116
|
+
* a force-kill mid-upload lands here with the OS having cancelled its
|
|
117
|
+
* transfers, and `ListParts` (not any surviving OS session, and not
|
|
118
|
+
* `launchEpoch` matches from the dead process) tells this function what
|
|
119
|
+
* actually made it to R2 and what needs re-staging. No `onTokenExpired`
|
|
120
|
+
* hook is available at this call site (the app hasn't picked a file or
|
|
121
|
+
* shown UI yet); an expired upload token here is a benign no-op — the next
|
|
122
|
+
* `startBackgroundUpload` for the same asset carries its own refresh. */
|
|
123
|
+
export declare function reconcileOnLaunch(store: StateStore, opts: {
|
|
124
|
+
endpoint: string;
|
|
125
|
+
}): Promise<void>;
|
|
126
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Lazily loads the modern `expo-file-system` File/Directory API — the
|
|
2
|
+
* surface this package's RN adapter and background transport are built on:
|
|
3
|
+
* `FileHandle` byte-window reads, `File.write(Uint8Array)` staging writes,
|
|
4
|
+
* the native `md5` property, and `File.createUploadTask` background
|
|
5
|
+
* transfers.
|
|
6
|
+
*
|
|
7
|
+
* Minimum peer: expo-file-system 57 (Expo SDK 57), and deliberately no
|
|
8
|
+
* fallback to the `expo-file-system/legacy` subpath this package used to
|
|
9
|
+
* ride. The modern API only gained upload support (`UploadTask`,
|
|
10
|
+
* `sessionType`) in v57 — on 54–56 the legacy subpath was the only option —
|
|
11
|
+
* but keeping both paths alive would mean every change to `background.ts`
|
|
12
|
+
* doubles the on-device test matrix (real iPhone, multi-GB files, force-kill
|
|
13
|
+
* runs), for SDK versions no consumer of this package uses: the playground
|
|
14
|
+
* is on 57 and Tinglebay isn't integrated yet. Older SDKs can pin an older
|
|
15
|
+
* version of this package.
|
|
16
|
+
*
|
|
17
|
+
* The import stays lazy (inside a function, not top-level) so this package
|
|
18
|
+
* loads fine in Node tests where expo-file-system isn't installed — only
|
|
19
|
+
* actually reading/staging/uploading in an RN runtime pulls it in. */
|
|
20
|
+
export declare function loadFileSystem(): Promise<{
|
|
21
|
+
FileHandle: typeof import("expo-file-system").FileHandle;
|
|
22
|
+
UploadType: typeof import("expo-file-system").UploadType;
|
|
23
|
+
UploadTask: typeof import("expo-file-system").UploadTask;
|
|
24
|
+
Directory: typeof import("expo-file-system").Directory;
|
|
25
|
+
File: typeof import("expo-file-system").File;
|
|
26
|
+
Paths: typeof import("expo-file-system").Paths;
|
|
27
|
+
default: typeof import("expo-file-system");
|
|
28
|
+
}>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** React Native adapter. This is a subpath entry (`@hostr/upload/react-native`),
|
|
2
|
+
* never re-exported from the package root — it pulls in a lazy
|
|
3
|
+
* `expo-file-system` dependency (declared locally in `./expo-fs.d.ts`, not
|
|
4
|
+
* installed as a real package here) that neither Node nor browser builds
|
|
5
|
+
* should see.
|
|
6
|
+
*
|
|
7
|
+
* There is ONE upload path on RN: staged parts + OS-carried native upload
|
|
8
|
+
* tasks (`background.ts`), wrapped in the engine-shaped `Upload` surface by
|
|
9
|
+
* `NativeUpload`. The browser-derived engine path (XHR transport, bytes
|
|
10
|
+
* read into JS, pure-JS hashing) used to live here as "foreground mode" —
|
|
11
|
+
* it was removed because every byte crossed the Hermes bridge two to three
|
|
12
|
+
* times and got hashed in interpreted JS, capping throughput far below the
|
|
13
|
+
* network, and because a native background-session task does everything it
|
|
14
|
+
* did (progress callbacks, cancellation) while also surviving the app
|
|
15
|
+
* being suspended. "Background" is not a mode anymore; it is a property
|
|
16
|
+
* every RN upload has. */
|
|
17
|
+
import type { StateStore } from '../core/types.js';
|
|
18
|
+
import { NativeUpload } from './native-upload.js';
|
|
19
|
+
export { NetworkError, ApiError } from '../api/client.js';
|
|
20
|
+
export { IntegrityError } from '../core/engine.js';
|
|
21
|
+
export type { CompleteInfo } from '../core/engine.js';
|
|
22
|
+
export type { Progress } from '../core/events.js';
|
|
23
|
+
/** Headless surface (no events, no `done` promise): launch, reconcile, and
|
|
24
|
+
* recover uploads directly. `createUpload` below is this plus liveness;
|
|
25
|
+
* `reconcileOnLaunch` is the mandatory app-launch recovery hook either
|
|
26
|
+
* way. `planBackgroundWork`, the pure decision core, lives at
|
|
27
|
+
* `./background-plan.ts` and is imported directly by its tests. */
|
|
28
|
+
export { startBackgroundUpload, reconcileOnLaunch, cancelBackgroundTasks, type BackgroundHandle, type BackgroundObserver, } from './background.js';
|
|
29
|
+
export { NativeUpload } from './native-upload.js';
|
|
30
|
+
/** The engine-compatible public surface (`core/upload.ts`'s `Upload` is the
|
|
31
|
+
* same pick over the engine class). `pause`/`abort` are synchronous here —
|
|
32
|
+
* cancellation of OS tasks doesn't need awaiting — where the engine's are
|
|
33
|
+
* async; callers that ignore the return value (all known ones) see no
|
|
34
|
+
* difference. */
|
|
35
|
+
export type Upload = Pick<NativeUpload, 'done' | 'state' | 'on' | 'start' | 'pause' | 'resume' | 'abort'>;
|
|
36
|
+
/** Wraps an AsyncStorage-like backend (`@react-native-async-storage/async-storage`,
|
|
37
|
+
* or any object with the same three methods) into a `StateStore`. RN has no
|
|
38
|
+
* default persisted store the way the browser adapter defaults to
|
|
39
|
+
* `localStorage` — the host app always supplies one. */
|
|
40
|
+
export declare function asyncStorageStore(as: {
|
|
41
|
+
getItem(k: string): Promise<string | null>;
|
|
42
|
+
setItem(k: string, v: string): Promise<void>;
|
|
43
|
+
removeItem(k: string): Promise<void>;
|
|
44
|
+
}): StateStore;
|
|
45
|
+
export type CreateUploadOptions = {
|
|
46
|
+
endpoint: string;
|
|
47
|
+
uploadToken: string;
|
|
48
|
+
assetId: string;
|
|
49
|
+
/** `uri`/`size` (and optional `modificationTime`) as returned by
|
|
50
|
+
* `expo-image-picker` / `expo-document-picker`. Identity for resume is
|
|
51
|
+
* `uri:size:modificationTime`. */
|
|
52
|
+
file: {
|
|
53
|
+
uri: string;
|
|
54
|
+
size: number;
|
|
55
|
+
modificationTime?: number;
|
|
56
|
+
};
|
|
57
|
+
/** REQUIRED — RN has no default persisted store the way the browser
|
|
58
|
+
* adapter defaults to `localStorage`. Wrap AsyncStorage (or an
|
|
59
|
+
* equivalent) with `asyncStorageStore`. */
|
|
60
|
+
store: StateStore;
|
|
61
|
+
onTokenExpired?: (assetId: string) => Promise<string>;
|
|
62
|
+
};
|
|
63
|
+
/** One upload, engine-shaped: `start()`, listen to `progress`/`statechange`/
|
|
64
|
+
* `error`, await `done`. Bytes move via OS background-session tasks, so the
|
|
65
|
+
* transfer continues if the app is backgrounded mid-upload; a force-kill is
|
|
66
|
+
* recovered by `reconcileOnLaunch` on the next launch, not by this object.
|
|
67
|
+
* `async` only for signature stability — construction itself is cheap and
|
|
68
|
+
* the real work starts at `start()`. */
|
|
69
|
+
export declare function createUpload(opts: CreateUploadOptions): Promise<Upload>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** `Upload`-shaped wrapper over the staged-parts + OS-task transport
|
|
2
|
+
* (`background.ts`). This is what makes RN's `createUpload` a single path:
|
|
3
|
+
* the same `done`/`state`/`on`/`start`/`pause`/`resume`/`abort` surface the
|
|
4
|
+
* browser/Node engine exposes, but with every byte moved natively — no
|
|
5
|
+
* bytes through the JS interpreter, transfers that survive the app being
|
|
6
|
+
* backgrounded, and completion driven by the all-parts-landed
|
|
7
|
+
* auto-reconcile.
|
|
8
|
+
*
|
|
9
|
+
* Division of labor: `background.ts` owns correctness (staging, ListParts
|
|
10
|
+
* reconciliation, completion); this class owns *liveness* — UI events, the
|
|
11
|
+
* `done` promise, and re-driving reconciles while the JS runtime is awake
|
|
12
|
+
* so a failed part doesn't sit dead until the next app launch. Everything
|
|
13
|
+
* here degrades to the background story when JS goes away: events stop,
|
|
14
|
+
* the retry timer dies, and `reconcileOnLaunch`/`reconcile()` pick up the
|
|
15
|
+
* truth later. That asymmetry is deliberate; nothing in this file may
|
|
16
|
+
* become a correctness dependency.
|
|
17
|
+
*
|
|
18
|
+
* Semantics that differ from the engine, on purpose:
|
|
19
|
+
* - `pause()` cancels the live OS tasks. Their unlanded staged bytes are
|
|
20
|
+
* discarded (records self-clean) and `resume()` re-stages what's still
|
|
21
|
+
* missing — cheap now that staging is native. Landed parts are never
|
|
22
|
+
* re-sent.
|
|
23
|
+
* - No `checksum` option and no whole-file sha256: the composite etag
|
|
24
|
+
* (verified part-by-part and again at complete) is the integrity check,
|
|
25
|
+
* which is also all Mux ever provided. `CompleteInfo.sha256` is
|
|
26
|
+
* therefore always undefined here.
|
|
27
|
+
* - Part upload failures never exhaust a retry budget; they re-stage on a
|
|
28
|
+
* timer while running (and on every explicit `resume()`/`reconcile()`),
|
|
29
|
+
* because the OS may hold a transfer in limbo for arbitrarily long and a
|
|
30
|
+
* "budget" would misfire on healthy-but-slow links. */
|
|
31
|
+
import type { SessionStatus } from '../core/session.js';
|
|
32
|
+
import type { CompleteInfo } from '../core/engine.js';
|
|
33
|
+
import { type Progress } from '../core/events.js';
|
|
34
|
+
type Events = {
|
|
35
|
+
progress: Progress;
|
|
36
|
+
statechange: SessionStatus;
|
|
37
|
+
error: Error;
|
|
38
|
+
};
|
|
39
|
+
export declare class NativeUpload {
|
|
40
|
+
readonly done: Promise<CompleteInfo>;
|
|
41
|
+
private resolveDone;
|
|
42
|
+
private rejectDone;
|
|
43
|
+
private emitter;
|
|
44
|
+
private handle;
|
|
45
|
+
private status;
|
|
46
|
+
private doneParts;
|
|
47
|
+
private inflight;
|
|
48
|
+
private confirmedBytes;
|
|
49
|
+
private confirmedAtStart;
|
|
50
|
+
private startedAtMs;
|
|
51
|
+
private lastProgressEmit;
|
|
52
|
+
private retryTimer;
|
|
53
|
+
private opts;
|
|
54
|
+
constructor(opts: NativeUpload['opts']);
|
|
55
|
+
get state(): SessionStatus;
|
|
56
|
+
on<K extends keyof Events>(ev: K, cb: (v: Events[K]) => void): () => void;
|
|
57
|
+
/** Kicks the upload off. Synchronous by signature (matching the engine);
|
|
58
|
+
* the launch itself is async and reports failures via the 'error' event
|
|
59
|
+
* and the `done` promise. Calling it twice is a no-op. */
|
|
60
|
+
start(): void;
|
|
61
|
+
pause(): void;
|
|
62
|
+
resume(): void;
|
|
63
|
+
abort(): void;
|
|
64
|
+
private launch;
|
|
65
|
+
/** A reconcile whose failure must not kill the upload: log-free retry via
|
|
66
|
+
* the timer, because reconcile failures here are transient by
|
|
67
|
+
* construction (offline, a token mid-refresh) and the store still holds
|
|
68
|
+
* everything needed to finish later. */
|
|
69
|
+
private reconcileQuietly;
|
|
70
|
+
private scheduleRetry;
|
|
71
|
+
private clearRetry;
|
|
72
|
+
private partSizeOf;
|
|
73
|
+
private setStatus;
|
|
74
|
+
private emitProgress;
|
|
75
|
+
}
|
|
76
|
+
export {};
|