@sparkvault/sdk-mobile 5.2.1 → 5.2.3

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/dist/config.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { FetchLike, MobileFileDownloader, MobileFileReader, SparkVaultLogger, TokenStorageAdapter } from './types.js';
1
+ import type { FetchLike, MobileBackgroundTransfer, MobileBackgroundUploader, MobileFileDownloader, MobileFileReader, SparkVaultLogger, TokenStorageAdapter } from './types.js';
2
2
  export interface SparkVaultMobileConfig {
3
3
  tokenStorage: TokenStorageAdapter;
4
4
  apiBaseUrl?: string;
@@ -9,10 +9,27 @@ export interface SparkVaultMobileConfig {
9
9
  timeoutMs?: number;
10
10
  fileTransferTimeoutMs?: number;
11
11
  tusPostTimeoutMs?: number;
12
+ /** Stall watchdog per tus chunk PATCH: aborts after this long with NO upload progress (not an absolute deadline). */
12
13
  tusChunkTimeoutMs?: number;
13
14
  fetch?: FetchLike;
14
15
  fileReader?: MobileFileReader;
15
16
  fileDownloader?: MobileFileDownloader;
17
+ /**
18
+ * OS-driven background transfer engine (iOS background URLSession with
19
+ * native chunk chaining). Used by uploads that ask for it
20
+ * (`transport: 'background'`) for the whole remaining range of any file,
21
+ * created or resumed, because the OS keeps chaining chunks while the app
22
+ * is suspended or killed; a terminate of the session also cancels its OS
23
+ * job. Takes precedence over `backgroundUploader` when both are set.
24
+ */
25
+ backgroundTransfer?: MobileBackgroundTransfer;
26
+ /**
27
+ * Background-URLSession whole-file PATCH adapter, the fallback when no
28
+ * `backgroundTransfer` engine is configured. Used only by uploads that ask
29
+ * for it (`transport: 'background'`) and fit one chunk; every other
30
+ * upload, and every upload when this is unset, streams over XHR.
31
+ */
32
+ backgroundUploader?: MobileBackgroundUploader;
16
33
  logger?: Partial<SparkVaultLogger>;
17
34
  allowedDownloadHostPatterns?: RegExp[];
18
35
  }
@@ -30,6 +47,8 @@ export interface ResolvedMobileConfig {
30
47
  fetch: FetchLike;
31
48
  fileReader?: MobileFileReader;
32
49
  fileDownloader?: MobileFileDownloader;
50
+ backgroundTransfer?: MobileBackgroundTransfer;
51
+ backgroundUploader?: MobileBackgroundUploader;
33
52
  logger: SparkVaultLogger;
34
53
  allowedDownloadHostPatterns: RegExp[];
35
54
  }
package/dist/config.js CHANGED
@@ -80,6 +80,8 @@ export function resolveMobileConfig(config) {
80
80
  fetch: config.fetch ?? defaultFetch,
81
81
  fileReader: config.fileReader,
82
82
  fileDownloader: config.fileDownloader,
83
+ backgroundTransfer: config.backgroundTransfer,
84
+ backgroundUploader: config.backgroundUploader,
83
85
  logger: { ...noopLogger, ...config.logger },
84
86
  allowedDownloadHostPatterns: config.allowedDownloadHostPatterns ?? DEFAULT_ALLOWED_DOWNLOAD_HOST_PATTERNS,
85
87
  };
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/errors.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ApiErrorBody, ApiMeta } from './types.js';
1
+ import type { ApiErrorBody, ApiMeta, BackgroundTransferFailureKind } from './types.js';
2
2
  export declare class SparkVaultMobileError extends Error {
3
3
  readonly code: string | number;
4
4
  readonly statusCode?: number;
@@ -69,12 +69,19 @@ export declare class TusUploadError extends SparkVaultMobileError {
69
69
  readonly filename: string | null;
70
70
  readonly fileSize: number | null;
71
71
  readonly phase: 'create' | 'upload' | 'network' | 'timeout' | 'cancelled' | 'stalled' | 'unknown';
72
+ /**
73
+ * True when Forge no longer has the session being resumed (HEAD answered
74
+ * 404/410: it expired or was terminated). The persisted session is
75
+ * worthless, so the caller starts a fresh upload instead of retrying it.
76
+ */
77
+ readonly sessionLost: boolean;
72
78
  constructor(message: string, options?: {
73
79
  cause?: Error | null;
74
80
  httpStatus?: number | null;
75
81
  filename?: string | null;
76
82
  fileSize?: number | null;
77
83
  phase?: TusUploadError['phase'];
84
+ sessionLost?: boolean;
78
85
  });
79
86
  static fromError(error: Error, context: {
80
87
  filename?: string;
@@ -82,6 +89,30 @@ export declare class TusUploadError extends SparkVaultMobileError {
82
89
  phase?: TusUploadError['phase'];
83
90
  }): TusUploadError;
84
91
  }
92
+ /**
93
+ * How the app's OS transfer adapter (`MobileBackgroundTransfer`) reports a
94
+ * job that ended short of the final offset. Deliberately a plain Error and
95
+ * not a `SparkVaultMobileError`: it is the adapter's wire format INTO the
96
+ * SDK, which the tus uploader translates into the one `TusUploadError` /
97
+ * gate-error taxonomy every caller already handles. App code that keys on
98
+ * this class directly would be routing around that translation. `offset` is
99
+ * the last server-confirmed offset, so a caller that must persist progress
100
+ * can, but the SDK's resume path asks Forge (HEAD) rather than trust it.
101
+ */
102
+ export declare class BackgroundTransferError extends Error {
103
+ readonly kind: BackgroundTransferFailureKind;
104
+ readonly offset: number;
105
+ readonly httpStatus?: number;
106
+ readonly body?: string;
107
+ readonly cause: Error | null;
108
+ constructor(message: string, options: {
109
+ kind: BackgroundTransferFailureKind;
110
+ offset: number;
111
+ httpStatus?: number;
112
+ body?: string;
113
+ cause?: Error | null;
114
+ });
115
+ }
85
116
  /**
86
117
  * True for a connectivity failure — no usable HTTP response reached the
87
118
  * client (offline, DNS failure, timeout, or a tus transfer that died on the
@@ -89,10 +120,32 @@ export declare class TusUploadError extends SparkVaultMobileError {
89
120
  */
90
121
  export declare function isNetworkError(err: unknown): boolean;
91
122
  /**
92
- * True for an HTTP auth failure (401/403) — a rejected/expired credential
93
- * (e.g. an expired vault VAT, or a rejected key) that a plain retry won't fix.
123
+ * True for an HTTP auth failure (401/403) from the API — a rejected/expired
124
+ * credential (e.g. an expired vault VAT, or a rejected key) that a plain retry
125
+ * won't fix.
126
+ *
127
+ * Never true for a `TusUploadError`: a Forge 401/403 rejects the ISTK — the
128
+ * per-upload token minted with the ingot (expired, consumed, or bound to a
129
+ * different session) — not the vault session. Re-unsealing the vault cannot
130
+ * fix it; the caller creates a new upload session (`POST /ingots`) instead.
131
+ * Use `isVatExpiredError` / `isReadOnlyVaultError` to tell the API's VAT
132
+ * rejections apart.
94
133
  */
95
134
  export declare function isAuthError(err: unknown): boolean;
135
+ /**
136
+ * True when the API rejected the vault access token because it has expired
137
+ * (`details.reason === 'VAT_EXPIRED'` on a 401 or 403 — an expired VAT
138
+ * answers 403 for its first hour, then 401). Re-unsealing the vault mints a
139
+ * fresh VAT and the same request can be retried as-is.
140
+ */
141
+ export declare function isVatExpiredError(err: unknown): err is SparkVaultMobileError;
142
+ /**
143
+ * True when the vault access token is valid but read-only
144
+ * (`details.reason === 'VAT_READ_ONLY'` on a 403): the session was unsealed
145
+ * with a read-only access level, so a write cannot succeed by retrying or by
146
+ * re-unsealing the same way — the caller needs write access to the vault.
147
+ */
148
+ export declare function isReadOnlyVaultError(err: unknown): err is SparkVaultMobileError;
96
149
  /** True for a 429 rate-limit response — retry after a cooldown, never immediately. */
97
150
  export declare function isRateLimitError(err: unknown): boolean;
98
151
  /** Type guard for the still-processing-ingot download failure. */
package/dist/errors.js CHANGED
@@ -107,6 +107,7 @@ export class TusUploadError extends SparkVaultMobileError {
107
107
  this.filename = options.filename ?? null;
108
108
  this.fileSize = options.fileSize ?? null;
109
109
  this.phase = options.phase ?? 'unknown';
110
+ this.sessionLost = options.sessionLost ?? false;
110
111
  Object.setPrototypeOf(this, TusUploadError.prototype);
111
112
  }
112
113
  static fromError(error, context) {
@@ -121,6 +122,28 @@ export class TusUploadError extends SparkVaultMobileError {
121
122
  });
122
123
  }
123
124
  }
125
+ /**
126
+ * How the app's OS transfer adapter (`MobileBackgroundTransfer`) reports a
127
+ * job that ended short of the final offset. Deliberately a plain Error and
128
+ * not a `SparkVaultMobileError`: it is the adapter's wire format INTO the
129
+ * SDK, which the tus uploader translates into the one `TusUploadError` /
130
+ * gate-error taxonomy every caller already handles. App code that keys on
131
+ * this class directly would be routing around that translation. `offset` is
132
+ * the last server-confirmed offset, so a caller that must persist progress
133
+ * can, but the SDK's resume path asks Forge (HEAD) rather than trust it.
134
+ */
135
+ export class BackgroundTransferError extends Error {
136
+ constructor(message, options) {
137
+ super(message);
138
+ this.name = 'BackgroundTransferError';
139
+ this.kind = options.kind;
140
+ this.offset = options.offset;
141
+ this.httpStatus = options.httpStatus;
142
+ this.body = options.body;
143
+ this.cause = options.cause ?? null;
144
+ Object.setPrototypeOf(this, BackgroundTransferError.prototype);
145
+ }
146
+ }
124
147
  /**
125
148
  * True for a connectivity failure — no usable HTTP response reached the
126
149
  * client (offline, DNS failure, timeout, or a tus transfer that died on the
@@ -136,13 +159,44 @@ export function isNetworkError(err) {
136
159
  return false;
137
160
  }
138
161
  /**
139
- * True for an HTTP auth failure (401/403) — a rejected/expired credential
140
- * (e.g. an expired vault VAT, or a rejected key) that a plain retry won't fix.
162
+ * True for an HTTP auth failure (401/403) from the API — a rejected/expired
163
+ * credential (e.g. an expired vault VAT, or a rejected key) that a plain retry
164
+ * won't fix.
165
+ *
166
+ * Never true for a `TusUploadError`: a Forge 401/403 rejects the ISTK — the
167
+ * per-upload token minted with the ingot (expired, consumed, or bound to a
168
+ * different session) — not the vault session. Re-unsealing the vault cannot
169
+ * fix it; the caller creates a new upload session (`POST /ingots`) instead.
170
+ * Use `isVatExpiredError` / `isReadOnlyVaultError` to tell the API's VAT
171
+ * rejections apart.
141
172
  */
142
173
  export function isAuthError(err) {
143
174
  return (err instanceof SparkVaultMobileError &&
175
+ !(err instanceof TusUploadError) &&
144
176
  (err.statusCode === 401 || err.statusCode === 403));
145
177
  }
178
+ /**
179
+ * True when the API rejected the vault access token because it has expired
180
+ * (`details.reason === 'VAT_EXPIRED'` on a 401 or 403 — an expired VAT
181
+ * answers 403 for its first hour, then 401). Re-unsealing the vault mints a
182
+ * fresh VAT and the same request can be retried as-is.
183
+ */
184
+ export function isVatExpiredError(err) {
185
+ return (err instanceof SparkVaultMobileError &&
186
+ (err.statusCode === 401 || err.statusCode === 403) &&
187
+ err.details?.reason === 'VAT_EXPIRED');
188
+ }
189
+ /**
190
+ * True when the vault access token is valid but read-only
191
+ * (`details.reason === 'VAT_READ_ONLY'` on a 403): the session was unsealed
192
+ * with a read-only access level, so a write cannot succeed by retrying or by
193
+ * re-unsealing the same way — the caller needs write access to the vault.
194
+ */
195
+ export function isReadOnlyVaultError(err) {
196
+ return (err instanceof SparkVaultMobileError &&
197
+ err.statusCode === 403 &&
198
+ err.details?.reason === 'VAT_READ_ONLY');
199
+ }
146
200
  /** True for a 429 rate-limit response — retry after a cooldown, never immediately. */
147
201
  export function isRateLimitError(err) {
148
202
  return (err instanceof SparkVaultMobileError &&
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAMA,SAAS,eAAe,CAAC,KAAc;IACrC,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,WAAW,CAAC;AACtD,CAAC;AAED,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAM9C,YACE,OAAe,EACf,UAKI,EAAE;QAEN,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,kBAAkB,CAAC;QAC/C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC/D,CAAC;CACF;AAED,MAAM,OAAO,yBAA0B,SAAQ,qBAAqB;IAClE,YAAY,OAAe,EAAE,OAAiC;QAC5D,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;QACvE,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;QACxC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,yBAAyB,CAAC,SAAS,CAAC,CAAC;IACnE,CAAC;CACF;AAED,MAAM,OAAO,6BAA8B,SAAQ,qBAAqB;IACtE,YAAY,OAAO,GAAG,yBAAyB,EAAE,OAAiC;QAChF,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,sBAAsB,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3E,IAAI,CAAC,IAAI,GAAG,+BAA+B,CAAC;QAC5C,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,6BAA6B,CAAC,SAAS,CAAC,CAAC;IACvE,CAAC;CACF;AAED,MAAM,OAAO,4BAA6B,SAAQ,qBAAqB;IACrE,YAAY,OAAO,GAAG,WAAW,EAAE,OAAiC;QAClE,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;QAC3C,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,4BAA4B,CAAC,SAAS,CAAC,CAAC;IACtE,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,iBAAkB,SAAQ,qBAAqB;IAC1D,YAAY,OAAO,GAAG,4CAA4C,EAAE,UAGhE,EAAE;QACJ,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QACzG,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC3D,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,kBAAmB,SAAQ,qBAAqB;IAG3D,YACE,QAAwC,EACxC,OAAgB,EAChB,UAAiE,EAAE;QAEnE,KAAK,CAAC,OAAO,IAAI,GAAG,QAAQ,iCAAiC,EAAE;YAC7D,IAAI,EAAE,gBAAgB;YACtB,UAAU,EAAE,GAAG;YACf,OAAO,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE;YACjD,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC5D,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,qBAAqB;IAC/D,YAAY,OAAO,GAAG,wBAAwB,EAAE,OAAiC;QAC/E,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAChE,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,qBAAqB;IAC/D,YAAY,OAAO,GAAG,mBAAmB;QACvC,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAChE,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,OAAO,kBAAmB,SAAQ,qBAAqB;IAI3D,YACE,OAAO,GAAG,qCAAqC,EAC/C,UAA8F,EAAE;QAEhG,KAAK,CAAC,OAAO,EAAE;YACb,IAAI,EAAE,iBAAiB;YACvB,UAAU,EAAE,GAAG;YACf,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC;QAC/C,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC5D,CAAC;CACF;AAED,MAAM,OAAO,cAAe,SAAQ,qBAAqB;IAMvD,YACE,OAAe,EACf,UAMI,EAAE;QAEN,KAAK,CAAC,OAAO,EAAE;YACb,IAAI,EAAE,OAAO,CAAC,UAAU,IAAI,kBAAkB;YAC9C,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,SAAS;SAC5C,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC;QACnC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC;QACzC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC;QACzC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,SAAS,CAAC;QACxC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,CAAC,SAAS,CACd,KAAY,EACZ,OAAkF;QAElF,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACnD,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACrE,OAAO,IAAI,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE;YACvC,KAAK,EAAE,KAAK;YACZ,UAAU;YACV,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,SAAS;SAClC,CAAC,CAAC;IACL,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,GAAY;IACzC,IAAI,GAAG,YAAY,sBAAsB,IAAI,GAAG,YAAY,sBAAsB,EAAE,CAAC;QACnF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,GAAG,YAAY,cAAc,EAAE,CAAC;QAClC,OAAO,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC;IACvF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,OAAO,CACL,GAAG,YAAY,qBAAqB;QACpC,CAAC,GAAG,CAAC,UAAU,KAAK,GAAG,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,CAAC,CACnD,CAAC;AACJ,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,OAAO,CACL,GAAG,YAAY,qBAAqB;QACpC,CAAC,GAAG,CAAC,UAAU,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,qBAAqB,CAAC,CAC/D,CAAC;AACJ,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,oBAAoB,CAAC,GAAY;IAC/C,OAAO,GAAG,YAAY,kBAAkB,CAAC;AAC3C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAY;IACnD,IAAI,GAAG,YAAY,yBAAyB;QAAE,OAAO,KAAK,CAAC;IAC3D,MAAM,MAAM,GAAG,GAAG,YAAY,qBAAqB,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IACjF,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACpG,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAC/B,UAAkB,EAClB,SAAuB;IAEvB,IAAI,UAAU,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAEpC,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC;IACnC,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,EAAE,OAAO,IAAI,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC;IAC3E,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC;IACzC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC;IAE5B,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;QAC7B,OAAO,IAAI,iBAAiB,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,IAAI,KAAK,gBAAgB,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,CAAC;QACnC,IAAI,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,OAAO,IAAI,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAMA,SAAS,eAAe,CAAC,KAAc;IACrC,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,WAAW,CAAC;AACtD,CAAC;AAED,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAM9C,YACE,OAAe,EACf,UAKI,EAAE;QAEN,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,kBAAkB,CAAC;QAC/C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC/D,CAAC;CACF;AAED,MAAM,OAAO,yBAA0B,SAAQ,qBAAqB;IAClE,YAAY,OAAe,EAAE,OAAiC;QAC5D,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;QACvE,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;QACxC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,yBAAyB,CAAC,SAAS,CAAC,CAAC;IACnE,CAAC;CACF;AAED,MAAM,OAAO,6BAA8B,SAAQ,qBAAqB;IACtE,YAAY,OAAO,GAAG,yBAAyB,EAAE,OAAiC;QAChF,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,sBAAsB,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3E,IAAI,CAAC,IAAI,GAAG,+BAA+B,CAAC;QAC5C,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,6BAA6B,CAAC,SAAS,CAAC,CAAC;IACvE,CAAC;CACF;AAED,MAAM,OAAO,4BAA6B,SAAQ,qBAAqB;IACrE,YAAY,OAAO,GAAG,WAAW,EAAE,OAAiC;QAClE,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;QAC3C,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,4BAA4B,CAAC,SAAS,CAAC,CAAC;IACtE,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,iBAAkB,SAAQ,qBAAqB;IAC1D,YAAY,OAAO,GAAG,4CAA4C,EAAE,UAGhE,EAAE;QACJ,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QACzG,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC3D,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,kBAAmB,SAAQ,qBAAqB;IAG3D,YACE,QAAwC,EACxC,OAAgB,EAChB,UAAiE,EAAE;QAEnE,KAAK,CAAC,OAAO,IAAI,GAAG,QAAQ,iCAAiC,EAAE;YAC7D,IAAI,EAAE,gBAAgB;YACtB,UAAU,EAAE,GAAG;YACf,OAAO,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE;YACjD,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC5D,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,qBAAqB;IAC/D,YAAY,OAAO,GAAG,wBAAwB,EAAE,OAAiC;QAC/E,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAChE,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,qBAAqB;IAC/D,YAAY,OAAO,GAAG,mBAAmB;QACvC,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAChE,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,OAAO,kBAAmB,SAAQ,qBAAqB;IAI3D,YACE,OAAO,GAAG,qCAAqC,EAC/C,UAA8F,EAAE;QAEhG,KAAK,CAAC,OAAO,EAAE;YACb,IAAI,EAAE,iBAAiB;YACvB,UAAU,EAAE,GAAG;YACf,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC;QAC/C,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC5D,CAAC;CACF;AAED,MAAM,OAAO,cAAe,SAAQ,qBAAqB;IAYvD,YACE,OAAe,EACf,UAOI,EAAE;QAEN,KAAK,CAAC,OAAO,EAAE;YACb,IAAI,EAAE,OAAO,CAAC,UAAU,IAAI,kBAAkB;YAC9C,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,SAAS;SAC5C,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC;QACnC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC;QACzC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC;QACzC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,SAAS,CAAC;QACxC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,KAAK,CAAC;QAChD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,CAAC,SAAS,CACd,KAAY,EACZ,OAAkF;QAElF,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACnD,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACrE,OAAO,IAAI,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE;YACvC,KAAK,EAAE,KAAK;YACZ,UAAU;YACV,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,SAAS;SAClC,CAAC,CAAC;IACL,CAAC;CACF;AAED;;;;;;;;;GASG;AACH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAOhD,YACE,OAAe,EACf,OAMC;QAED,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;QACtC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC;QACnC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,uBAAuB,CAAC,SAAS,CAAC,CAAC;IACjE,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,GAAY;IACzC,IAAI,GAAG,YAAY,sBAAsB,IAAI,GAAG,YAAY,sBAAsB,EAAE,CAAC;QACnF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,GAAG,YAAY,cAAc,EAAE,CAAC;QAClC,OAAO,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC;IACvF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,OAAO,CACL,GAAG,YAAY,qBAAqB;QACpC,CAAC,CAAC,GAAG,YAAY,cAAc,CAAC;QAChC,CAAC,GAAG,CAAC,UAAU,KAAK,GAAG,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,CAAC,CACnD,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAY;IAC5C,OAAO,CACL,GAAG,YAAY,qBAAqB;QACpC,CAAC,GAAG,CAAC,UAAU,KAAK,GAAG,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,CAAC;QAClD,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,aAAa,CACtC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAY;IAC/C,OAAO,CACL,GAAG,YAAY,qBAAqB;QACpC,GAAG,CAAC,UAAU,KAAK,GAAG;QACtB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,eAAe,CACxC,CAAC;AACJ,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,OAAO,CACL,GAAG,YAAY,qBAAqB;QACpC,CAAC,GAAG,CAAC,UAAU,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,qBAAqB,CAAC,CAC/D,CAAC;AACJ,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,oBAAoB,CAAC,GAAY;IAC/C,OAAO,GAAG,YAAY,kBAAkB,CAAC;AAC3C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAY;IACnD,IAAI,GAAG,YAAY,yBAAyB;QAAE,OAAO,KAAK,CAAC;IAC3D,MAAM,MAAM,GAAG,GAAG,YAAY,qBAAqB,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IACjF,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACpG,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAC/B,UAAkB,EAClB,SAAuB;IAEvB,IAAI,UAAU,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAEpC,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC;IACnC,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,EAAE,OAAO,IAAI,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC;IAC3E,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC;IACzC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC;IAE5B,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;QAC7B,OAAO,IAAI,iBAAiB,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,IAAI,KAAK,gBAAgB,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,CAAC;QACnC,IAAI,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,OAAO,IAAI,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ export { MobileAccountClient, } from './account.js';
6
6
  export { MobileVaultsClient, } from './vaults.js';
7
7
  export { MobileFoldersClient, } from './folders.js';
8
8
  export { MobileIngotsClient, } from './ingots.js';
9
- export type { DownloadToFileOptions, DownloadToFileResult, UploadOptions, UploadFromUriOptions, ReplaceFromUriOptions, CreateUploadOptions, CreateUploadResult, CreateDownloadLinkOptions, CreateDownloadLinkResult, } from './ingots.js';
9
+ export type { DownloadToFileOptions, DownloadToFileResult, UploadOptions, UploadFromUriOptions, ReplaceFromUriOptions, ResumeUploadOptions, CreateUploadOptions, CreateUploadResult, CreateDownloadLinkOptions, CreateDownloadLinkResult, } from './ingots.js';
10
10
  export { MobileHealthClient, } from './health.js';
11
11
  export type { HealthCheckOptions, HealthCheckResult, } from './health.js';
12
12
  export { MobileSparksClient, } from './sparks.js';
@@ -19,8 +19,8 @@ export { MobileBillingClient, } from './billing.js';
19
19
  export type { PortalSession, } from './billing.js';
20
20
  export { MobileUsersClient, } from './users.js';
21
21
  export { MobileTusUploader, parseForgeUrl, } from './tus.js';
22
- export type { TusUploadFromUriOptions, ParsedForgeUrl, } from './tus.js';
22
+ export type { TusUploadFromUriOptions, TusResumeSession, TusSessionProbe, ParsedForgeUrl, } from './tus.js';
23
23
  export { base64EncodeUtf8, base64UrlToBytes, base64UrlToUtf8, bytesToUtf8, base64ToBytes, bytesToBase64, utf8ToBytes, } from './encoding.js';
24
- export { gateErrorFromBody, IngotNotReadyError, isAuthError, isIngotNotReadyError, isNetworkError, isRateLimitError, isRetryableDownloadError, PlanRequiredError, QuotaExceededError, SparkVaultAuthenticationError, SparkVaultAuthorizationError, SparkVaultMobileError, SparkVaultNetworkError, SparkVaultTimeoutError, SparkVaultValidationError, TusUploadError, } from './errors.js';
24
+ export { BackgroundTransferError, gateErrorFromBody, IngotNotReadyError, isAuthError, isIngotNotReadyError, isNetworkError, isRateLimitError, isReadOnlyVaultError, isRetryableDownloadError, isVatExpiredError, PlanRequiredError, QuotaExceededError, SparkVaultAuthenticationError, SparkVaultAuthorizationError, SparkVaultMobileError, SparkVaultNetworkError, SparkVaultTimeoutError, SparkVaultValidationError, TusUploadError, } from './errors.js';
25
25
  export { isSafeId, sanitizeFilename, validateFolderId, validateIngotId, validateVaultId, } from './validation.js';
26
26
  export type * from './types.js';
package/dist/index.js CHANGED
@@ -12,6 +12,6 @@ export { MobileBillingClient, } from './billing.js';
12
12
  export { MobileUsersClient, } from './users.js';
13
13
  export { MobileTusUploader, parseForgeUrl, } from './tus.js';
14
14
  export { base64EncodeUtf8, base64UrlToBytes, base64UrlToUtf8, bytesToUtf8, base64ToBytes, bytesToBase64, utf8ToBytes, } from './encoding.js';
15
- export { gateErrorFromBody, IngotNotReadyError, isAuthError, isIngotNotReadyError, isNetworkError, isRateLimitError, isRetryableDownloadError, PlanRequiredError, QuotaExceededError, SparkVaultAuthenticationError, SparkVaultAuthorizationError, SparkVaultMobileError, SparkVaultNetworkError, SparkVaultTimeoutError, SparkVaultValidationError, TusUploadError, } from './errors.js';
15
+ export { BackgroundTransferError, gateErrorFromBody, IngotNotReadyError, isAuthError, isIngotNotReadyError, isNetworkError, isRateLimitError, isReadOnlyVaultError, isRetryableDownloadError, isVatExpiredError, PlanRequiredError, QuotaExceededError, SparkVaultAuthenticationError, SparkVaultAuthorizationError, SparkVaultMobileError, SparkVaultNetworkError, SparkVaultTimeoutError, SparkVaultValidationError, TusUploadError, } from './errors.js';
16
16
  export { isSafeId, sanitizeFilename, validateFolderId, validateIngotId, validateVaultId, } from './validation.js';
17
17
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,4BAA4B,GAC7B,MAAM,aAAa,CAAC;AASrB,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAYrB,OAAO,EACL,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAKrB,OAAO,EACL,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAUrB,OAAO,EACL,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAMtB,OAAO,EACL,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAgBrB,OAAO,EACL,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAItB,OAAO,EACL,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,aAAa,GACd,MAAM,UAAU,CAAC;AAKlB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,aAAa,EACb,aAAa,EACb,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,wBAAwB,EACxB,iBAAiB,EACjB,kBAAkB,EAClB,6BAA6B,EAC7B,4BAA4B,EAC5B,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,yBAAyB,EACzB,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,4BAA4B,GAC7B,MAAM,aAAa,CAAC;AASrB,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAarB,OAAO,EACL,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAKrB,OAAO,EACL,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAUrB,OAAO,EACL,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAMtB,OAAO,EACL,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAgBrB,OAAO,EACL,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAItB,OAAO,EACL,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,aAAa,GACd,MAAM,UAAU,CAAC;AAOlB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,aAAa,EACb,aAAa,EACb,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,6BAA6B,EAC7B,4BAA4B,EAC5B,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,yBAAyB,EACzB,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,iBAAiB,CAAC"}
package/dist/ingots.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { ResolvedMobileConfig } from './config.js';
2
2
  import type { MobileHttpClient } from './http.js';
3
- import type { DebugLogger, HashCheckResponse, Ingot, IngotAccessLog, IngotAccessStats, IngotInvite, IngotSharingConfig, ListIngotsResponse, UploadProgressCallback, UploadResult } from './types.js';
3
+ import type { DebugLogger, HashCheckResponse, Ingot, IngotAccessLog, IngotAccessStats, IngotInvite, IngotSharingConfig, ListIngotsResponse, IngotUploadSession, UploadAbortBehavior, UploadProgressCallback, UploadResult, UploadTransferPolicy, UploadTransport } from './types.js';
4
4
  export interface UploadOptions {
5
5
  vaultId: string;
6
6
  vat: string;
@@ -29,11 +29,69 @@ export interface UploadOptions {
29
29
  export interface UploadFromUriOptions extends UploadOptions {
30
30
  fileUri: string;
31
31
  fileSize: number;
32
+ /**
33
+ * Invoked once the API has minted (or, via its same-name upsert, handed
34
+ * back) the ingot and before any Forge session exists. `created: false`
35
+ * means the bytes about to be sent will REPLACE an existing ingot's content
36
+ * once they finalize; a caller that must never overwrite throws here, and
37
+ * the throw propagates as-is with nothing sent and no session to clean up.
38
+ */
39
+ onIngotCreated?: (ingot: {
40
+ ingotId: string;
41
+ created: boolean;
42
+ }) => void;
43
+ /**
44
+ * Invoked once the Forge tus session exists and before the first byte is
45
+ * sent, so the caller can persist it and `resumeUpload` after a failed
46
+ * attempt or a relaunch instead of re-sending bytes Forge already holds.
47
+ */
48
+ onSessionCreated?: (session: IngotUploadSession) => void;
49
+ /** See `UploadTransport`; default `foreground`. */
50
+ transport?: UploadTransport;
51
+ /** Network policy for the OS transfer engine; see `UploadTransferPolicy`. Default: both allowed. */
52
+ transferPolicy?: UploadTransferPolicy;
53
+ /**
54
+ * What an abort does to the Forge session; see `UploadAbortBehavior`.
55
+ * Default `terminate` (a user cancel: Forge drops the chunks). `keep`
56
+ * (a pause) leaves the session on Forge so the caller can `resumeUpload`
57
+ * the one it was handed via `onSessionCreated` from the server offset;
58
+ * the caller then owns that session and is expected to resume or
59
+ * terminate it. The error is the same either way: the pre-flight
60
+ * `upload_cancelled` or the tus `cancelled`-phase `TusUploadError`.
61
+ */
62
+ abortBehavior?: UploadAbortBehavior;
32
63
  debug?: DebugLogger;
33
64
  }
34
65
  export interface ReplaceFromUriOptions extends UploadFromUriOptions {
35
66
  ingotId: string;
36
67
  }
68
+ export interface ResumeUploadOptions {
69
+ vaultId: string;
70
+ vat: string;
71
+ /** The ingot the session was created for (`UploadResult.ingot_id`). */
72
+ ingotId: string;
73
+ fileUri: string;
74
+ fileSize: number;
75
+ name: string;
76
+ contentType: string;
77
+ /** The persisted tus session handed to `onSessionCreated`. */
78
+ session: {
79
+ uploadUrl: string;
80
+ istk: string;
81
+ };
82
+ /** See `UploadTransport`; default `foreground`. */
83
+ transport?: UploadTransport;
84
+ /** Network policy for the OS transfer engine; see `UploadTransferPolicy`. Default: both allowed. */
85
+ transferPolicy?: UploadTransferPolicy;
86
+ onProgress?: UploadProgressCallback;
87
+ abortSignal?: AbortSignal;
88
+ /**
89
+ * See `UploadFromUriOptions.abortBehavior`. `keep` leaves `session` on
90
+ * Forge across the abort so the same session can be resumed again later.
91
+ */
92
+ abortBehavior?: UploadAbortBehavior;
93
+ debug?: DebugLogger;
94
+ }
37
95
  export interface DownloadToFileOptions {
38
96
  vaultId: string;
39
97
  ingotId: string;
@@ -81,6 +139,11 @@ export interface CreateUploadResult {
81
139
  forgeUrl: string;
82
140
  name?: string;
83
141
  sizeBytes?: number;
142
+ /**
143
+ * Whether the server minted this ingot (true) or handed back an existing
144
+ * row via its same-name upsert (false). Absent when the server did not say.
145
+ */
146
+ created?: boolean;
84
147
  }
85
148
  export interface CreateDownloadLinkOptions {
86
149
  vaultId: string;
@@ -102,6 +165,43 @@ export declare class MobileIngotsClient {
102
165
  private readonly tus;
103
166
  constructor(config: ResolvedMobileConfig, http: MobileHttpClient);
104
167
  uploadFromUri(options: UploadFromUriOptions): Promise<UploadResult>;
168
+ /**
169
+ * Continue an interrupted upload from the tus session `uploadFromUri`
170
+ * handed to `onSessionCreated`. No ingot is created: the record already
171
+ * exists from the original call, and the session is bound to it — Forge is
172
+ * asked (HEAD) where the transfer stands and only the missing bytes are
173
+ * sent, then the ingot is verified active exactly as a fresh upload is.
174
+ *
175
+ * A `TusUploadError` with `sessionLost` means Forge no longer has the
176
+ * session (expired or terminated); the caller drops it and starts over
177
+ * with `uploadFromUri`.
178
+ */
179
+ resumeUpload(options: ResumeUploadOptions): Promise<UploadResult>;
180
+ /**
181
+ * Where a persisted tus session stands on Forge, or `null` when Forge no
182
+ * longer has it. Lets a caller reconcile sessions on relaunch — a session
183
+ * at `offset === length` only needs its ingot verified, a lost one needs a
184
+ * fresh `uploadFromUri` — without spending an upload slot to find out.
185
+ */
186
+ probeUploadSession(session: {
187
+ uploadUrl: string;
188
+ istk: string;
189
+ }): Promise<{
190
+ offset: number;
191
+ length: number;
192
+ chunkSize: number;
193
+ } | null>;
194
+ /**
195
+ * Release a tus session the caller kept on Forge (`abortBehavior: 'keep'`)
196
+ * and has since abandoned, so Forge drops the chunks it holds and settles
197
+ * the partial transfer's billing instead of waiting for expiry. Resolves
198
+ * once the session is gone, including when Forge no longer had it
199
+ * (404/410); any other failure rejects so the caller can retry later.
200
+ */
201
+ terminateUploadSession(session: {
202
+ uploadUrl: string;
203
+ istk: string;
204
+ }): Promise<void>;
105
205
  /**
106
206
  * Initialize an ingot record and obtain the Forge resumable-upload endpoint
107
207
  * (with its upload-session token). The canonical first step of every upload;
package/dist/ingots.js CHANGED
@@ -40,24 +40,96 @@ export class MobileIngotsClient {
40
40
  folderId: options.folderId,
41
41
  contentHmac: options.contentHmac,
42
42
  });
43
+ const created = init.created ?? true;
44
+ // The ingot row exists but no Forge session does: a caller that refuses
45
+ // to replace an existing ingot (created: false) aborts here, before a
46
+ // byte moves, and its error reaches it untouched.
47
+ options.onIngotCreated?.({ ingotId: init.ingotId, created });
43
48
  await this.tus.uploadFromUri({
44
49
  fileUri: options.fileUri,
45
50
  fileSize: options.fileSize,
46
51
  filename: options.name,
47
52
  contentType: options.contentType,
48
53
  forgeUrl: init.forgeUrl,
54
+ onSessionCreated: session => options.onSessionCreated?.({ ...session, ingotId: init.ingotId, created }),
55
+ transport: options.transport,
56
+ transferPolicy: options.transferPolicy,
49
57
  onProgress: options.onProgress,
50
58
  abortSignal: options.abortSignal,
59
+ abortBehavior: options.abortBehavior,
51
60
  debug: options.debug,
52
61
  });
53
- await this.verifyIngotActive(options.vaultId, init.ingotId, options.vat, options.debug);
62
+ const ingot = await this.verifyIngotActive(options.vaultId, init.ingotId, options.vat, options.debug);
54
63
  return {
55
64
  ingot_id: init.ingotId,
56
65
  name: init.name ?? options.name,
57
66
  size_bytes: init.sizeBytes ?? options.fileSize,
58
67
  storage_location: 's3',
68
+ // A server that does not report it minted the row (the upsert is the
69
+ // only other outcome, and it always says so).
70
+ created,
71
+ status: ingot.status,
59
72
  };
60
73
  }
74
+ /**
75
+ * Continue an interrupted upload from the tus session `uploadFromUri`
76
+ * handed to `onSessionCreated`. No ingot is created: the record already
77
+ * exists from the original call, and the session is bound to it — Forge is
78
+ * asked (HEAD) where the transfer stands and only the missing bytes are
79
+ * sent, then the ingot is verified active exactly as a fresh upload is.
80
+ *
81
+ * A `TusUploadError` with `sessionLost` means Forge no longer has the
82
+ * session (expired or terminated); the caller drops it and starts over
83
+ * with `uploadFromUri`.
84
+ */
85
+ async resumeUpload(options) {
86
+ validateVaultId(options.vaultId);
87
+ validateIngotId(options.ingotId);
88
+ if (options.abortSignal?.aborted) {
89
+ throw new SparkVaultMobileError('Upload cancelled', { code: 'upload_cancelled' });
90
+ }
91
+ await this.tus.uploadFromUri({
92
+ fileUri: options.fileUri,
93
+ fileSize: options.fileSize,
94
+ filename: options.name,
95
+ contentType: options.contentType,
96
+ resume: options.session,
97
+ transport: options.transport,
98
+ transferPolicy: options.transferPolicy,
99
+ onProgress: options.onProgress,
100
+ abortSignal: options.abortSignal,
101
+ abortBehavior: options.abortBehavior,
102
+ debug: options.debug,
103
+ });
104
+ const ingot = await this.verifyIngotActive(options.vaultId, options.ingotId, options.vat, options.debug);
105
+ return {
106
+ ingot_id: options.ingotId,
107
+ name: options.name,
108
+ size_bytes: options.fileSize,
109
+ storage_location: 's3',
110
+ created: false,
111
+ status: ingot.status,
112
+ };
113
+ }
114
+ /**
115
+ * Where a persisted tus session stands on Forge, or `null` when Forge no
116
+ * longer has it. Lets a caller reconcile sessions on relaunch — a session
117
+ * at `offset === length` only needs its ingot verified, a lost one needs a
118
+ * fresh `uploadFromUri` — without spending an upload slot to find out.
119
+ */
120
+ probeUploadSession(session) {
121
+ return this.tus.probeSession(session);
122
+ }
123
+ /**
124
+ * Release a tus session the caller kept on Forge (`abortBehavior: 'keep'`)
125
+ * and has since abandoned, so Forge drops the chunks it holds and settles
126
+ * the partial transfer's billing instead of waiting for expiry. Resolves
127
+ * once the session is gone, including when Forge no longer had it
128
+ * (404/410); any other failure rejects so the caller can retry later.
129
+ */
130
+ terminateUploadSession(session) {
131
+ return this.tus.terminateSession(session);
132
+ }
61
133
  /**
62
134
  * Initialize an ingot record and obtain the Forge resumable-upload endpoint
63
135
  * (with its upload-session token). The canonical first step of every upload;
@@ -90,6 +162,7 @@ export class MobileIngotsClient {
90
162
  forgeUrl: response.data.forge_url,
91
163
  name: response.data.name,
92
164
  sizeBytes: response.data.size_bytes,
165
+ created: typeof response.data.created === 'boolean' ? response.data.created : undefined,
93
166
  };
94
167
  }
95
168
  async replaceFromUri(options) {
@@ -114,16 +187,24 @@ export class MobileIngotsClient {
114
187
  filename: name,
115
188
  contentType,
116
189
  forgeUrl: response.data.forge_url,
190
+ // A replace never mints an ingot: the row is the caller's existing one.
191
+ onSessionCreated: session => options.onSessionCreated?.({ ...session, ingotId, created: false }),
192
+ transport: options.transport,
193
+ transferPolicy: options.transferPolicy,
117
194
  onProgress,
118
195
  abortSignal,
196
+ abortBehavior: options.abortBehavior,
119
197
  debug,
120
198
  });
121
- await this.verifyIngotActive(vaultId, response.data.ingot_id, vat, debug);
199
+ const ingot = await this.verifyIngotActive(vaultId, response.data.ingot_id, vat, debug);
122
200
  return {
123
201
  ingot_id: response.data.ingot_id,
124
202
  name,
125
203
  size_bytes: fileSize,
126
204
  storage_location: 's3',
205
+ // A replace never mints an ingot: the row is the caller's existing one.
206
+ created: false,
207
+ status: ingot.status,
127
208
  };
128
209
  }
129
210
  /**