@sparkvault/sdk-mobile 6.0.2 → 7.0.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 +5 -5
- package/dist/account.d.ts +4 -3
- package/dist/account.js +16 -11
- package/dist/account.js.map +1 -1
- package/dist/app-sdk/email-address.d.ts +37 -0
- package/dist/app-sdk/email-address.js +91 -0
- package/dist/app-sdk/email-address.js.map +1 -0
- package/dist/app-sdk/encoding.d.ts +64 -0
- package/dist/app-sdk/encoding.js +245 -0
- package/dist/app-sdk/encoding.js.map +1 -0
- package/dist/app-sdk/entropy-contract.d.ts +33 -0
- package/dist/app-sdk/entropy-contract.js +51 -0
- package/dist/app-sdk/entropy-contract.js.map +1 -0
- package/dist/app-sdk/filename.d.ts +93 -0
- package/dist/app-sdk/filename.js +161 -0
- package/dist/app-sdk/filename.js.map +1 -0
- package/dist/app-sdk/forge-url.d.ts +107 -0
- package/dist/app-sdk/forge-url.js +222 -0
- package/dist/app-sdk/forge-url.js.map +1 -0
- package/dist/app-sdk/ids.d.ts +48 -0
- package/dist/app-sdk/ids.js +65 -0
- package/dist/app-sdk/ids.js.map +1 -0
- package/dist/app-sdk/ingot-access-log.d.ts +29 -0
- package/dist/app-sdk/ingot-access-log.js +49 -0
- package/dist/app-sdk/ingot-access-log.js.map +1 -0
- package/dist/app-sdk/phone.d.ts +37 -0
- package/dist/app-sdk/phone.js +134 -0
- package/dist/app-sdk/phone.js.map +1 -0
- package/dist/app-sdk/platform-domains.d.ts +41 -0
- package/dist/app-sdk/platform-domains.js +82 -0
- package/dist/app-sdk/platform-domains.js.map +1 -0
- package/dist/app-sdk/request-path.d.ts +49 -0
- package/dist/app-sdk/request-path.js +81 -0
- package/dist/app-sdk/request-path.js.map +1 -0
- package/dist/app-sdk/transient.d.ts +23 -0
- package/dist/app-sdk/transient.js +29 -0
- package/dist/app-sdk/transient.js.map +1 -0
- package/dist/app-sdk/vat-refusal.d.ts +66 -0
- package/dist/app-sdk/vat-refusal.js +69 -0
- package/dist/app-sdk/vat-refusal.js.map +1 -0
- package/dist/billing.d.ts +9 -17
- package/dist/billing.js +14 -17
- package/dist/billing.js.map +1 -1
- package/dist/config.d.ts +7 -1
- package/dist/config.js +10 -17
- package/dist/config.js.map +1 -1
- package/dist/encoding.d.ts +30 -4
- package/dist/encoding.js +89 -83
- package/dist/encoding.js.map +1 -1
- package/dist/entropy.d.ts +25 -4
- package/dist/entropy.js +62 -15
- package/dist/entropy.js.map +1 -1
- package/dist/entropy.test-d.d.ts +40 -0
- package/dist/entropy.test-d.js +12 -0
- package/dist/entropy.test-d.js.map +1 -0
- package/dist/errors.d.ts +67 -17
- package/dist/errors.js +70 -27
- package/dist/errors.js.map +1 -1
- package/dist/folders.js +8 -7
- package/dist/folders.js.map +1 -1
- package/dist/http.d.ts +57 -15
- package/dist/http.js +125 -138
- package/dist/http.js.map +1 -1
- package/dist/identity-dialog.js +4 -30
- package/dist/identity-dialog.js.map +1 -1
- package/dist/identity-input.d.ts +39 -0
- package/dist/identity-input.js +59 -0
- package/dist/identity-input.js.map +1 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/ingots.d.ts +51 -18
- package/dist/ingots.js +132 -122
- package/dist/ingots.js.map +1 -1
- package/dist/mutex.d.ts +1 -2
- package/dist/mutex.js +0 -6
- package/dist/mutex.js.map +1 -1
- package/dist/notify.d.ts +7 -2
- package/dist/notify.js +12 -15
- package/dist/notify.js.map +1 -1
- package/dist/notify.test-d.d.ts +3 -2
- package/dist/notify.test-d.js +1 -1
- package/dist/products/identity.d.ts +5 -0
- package/dist/products/identity.js +40 -35
- package/dist/products/identity.js.map +1 -1
- package/dist/request-path.d.ts +32 -0
- package/dist/request-path.js +35 -0
- package/dist/request-path.js.map +1 -0
- package/dist/sparks.js +16 -15
- package/dist/sparks.js.map +1 -1
- package/dist/tus.d.ts +19 -2
- package/dist/tus.js +86 -35
- package/dist/tus.js.map +1 -1
- package/dist/types.d.ts +167 -15
- package/dist/users.d.ts +12 -5
- package/dist/users.js +27 -19
- package/dist/users.js.map +1 -1
- package/dist/validation.d.ts +20 -5
- package/dist/validation.js +29 -27
- package/dist/validation.js.map +1 -1
- package/dist/vaults.d.ts +34 -83
- package/dist/vaults.js +64 -44
- package/dist/vaults.js.map +1 -1
- package/dist/vaults.test-d.d.ts +22 -0
- package/dist/vaults.test-d.js +14 -0
- package/dist/vaults.test-d.js.map +1 -0
- package/package.json +4 -2
- package/src/account.ts +16 -11
- package/src/app-sdk/email-address.js +94 -0
- package/src/app-sdk/encoding.js +250 -0
- package/src/app-sdk/entropy-contract.js +56 -0
- package/src/app-sdk/filename.js +174 -0
- package/src/app-sdk/forge-url.js +224 -0
- package/src/app-sdk/ids.js +71 -0
- package/src/app-sdk/ingot-access-log.js +51 -0
- package/src/app-sdk/phone.js +139 -0
- package/src/app-sdk/platform-domains.js +99 -0
- package/src/app-sdk/request-path.js +81 -0
- package/src/app-sdk/transient.js +30 -0
- package/src/app-sdk/vat-refusal.js +74 -0
- package/src/billing.ts +16 -25
- package/src/config.ts +16 -23
- package/src/encoding.ts +91 -103
- package/src/entropy.test-d.ts +47 -0
- package/src/entropy.ts +86 -20
- package/src/errors.ts +93 -33
- package/src/folders.ts +8 -7
- package/src/http.ts +145 -150
- package/src/identity-dialog.tsx +4 -37
- package/src/identity-input.ts +70 -0
- package/src/index.ts +11 -3
- package/src/ingots.ts +173 -159
- package/src/mutex.ts +1 -8
- package/src/notify.test-d.ts +3 -1
- package/src/notify.ts +19 -17
- package/src/products/identity.ts +45 -39
- package/src/request-path.ts +45 -0
- package/src/sparks.ts +16 -13
- package/src/tus.ts +90 -37
- package/src/types.ts +165 -15
- package/src/users.ts +27 -17
- package/src/validation.ts +30 -24
- package/src/vaults.test-d.ts +29 -0
- package/src/vaults.ts +87 -153
- package/dist/platform-domains.generated.d.ts +0 -2
- package/dist/platform-domains.generated.js +0 -16
- package/dist/platform-domains.generated.js.map +0 -1
- package/src/platform-domains.generated.ts +0 -17
package/src/errors.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { ApiErrorBody, ApiMeta, BackgroundTransferFailureKind } from './types.js';
|
|
2
|
+
import { isTransientHttpStatus } from './app-sdk/transient.js';
|
|
3
|
+
import { VAT_REFUSAL_REASONS, vatRefusalReason as readVatRefusalReason } from './app-sdk/vat-refusal.js';
|
|
2
4
|
|
|
3
|
-
// Only the byte pools hard-gate:
|
|
4
|
-
// Identity soft-gates through its own path.
|
|
5
|
+
// Only the byte pools hard-gate: licenses are assigned per member, never
|
|
6
|
+
// drawn from a pool, and Identity soft-gates through its own path.
|
|
5
7
|
type QuotaResource = 'storage' | 'bandwidth';
|
|
6
8
|
|
|
7
9
|
function isQuotaResource(value: unknown): value is QuotaResource {
|
|
@@ -115,10 +117,10 @@ export class SparkVaultTimeoutError extends SparkVaultMobileError {
|
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
/**
|
|
118
|
-
* Thrown when a download is requested against an ingot
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
120
|
+
* Thrown when a download is requested against an ingot whose upload Forge has
|
|
121
|
+
* not activated yet (status `uploading`). Transient by nature, but retrying
|
|
122
|
+
* immediately will not help — callers should back off until the ingot becomes
|
|
123
|
+
* active.
|
|
122
124
|
*/
|
|
123
125
|
export class IngotNotReadyError extends SparkVaultMobileError {
|
|
124
126
|
/** Server-reported ingot status at the time of the request, when known. */
|
|
@@ -140,6 +142,41 @@ export class IngotNotReadyError extends SparkVaultMobileError {
|
|
|
140
142
|
}
|
|
141
143
|
}
|
|
142
144
|
|
|
145
|
+
/** The ingot a create was refused over, as the server read it. */
|
|
146
|
+
export interface IngotNameHolder {
|
|
147
|
+
ingotId: string;
|
|
148
|
+
/**
|
|
149
|
+
* Lifecycle status of the holder: `active` when its upload finished,
|
|
150
|
+
* `uploading` when it has not (only a `refuse` create is refused over an
|
|
151
|
+
* unfinished holder; `complete_pending` completes one).
|
|
152
|
+
*/
|
|
153
|
+
status: string;
|
|
154
|
+
sizeBytes: number;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Thrown when a create is refused because an ingot already holds the name in
|
|
159
|
+
* the target folder (409 `details.reason === 'NAME_HELD'`): with
|
|
160
|
+
* `onNameConflict: 'complete_pending'` a published holder, with `'refuse'` any
|
|
161
|
+
* holder. The server decided before writing anything, so the holder is
|
|
162
|
+
* untouched; `holder` is the row it read, which lets the caller act on it
|
|
163
|
+
* (send its bytes into its own row by id, adopt it, or try another name)
|
|
164
|
+
* without a second read.
|
|
165
|
+
*/
|
|
166
|
+
export class IngotNameHeldError extends SparkVaultMobileError {
|
|
167
|
+
readonly holder: IngotNameHolder;
|
|
168
|
+
|
|
169
|
+
constructor(
|
|
170
|
+
message: string,
|
|
171
|
+
options: { holder: IngotNameHolder; details?: Record<string, unknown>; meta?: ApiMeta }
|
|
172
|
+
) {
|
|
173
|
+
super(message, { code: 'CONFLICT', statusCode: 409, details: options.details, meta: options.meta });
|
|
174
|
+
this.name = 'IngotNameHeldError';
|
|
175
|
+
this.holder = options.holder;
|
|
176
|
+
Object.setPrototypeOf(this, IngotNameHeldError.prototype);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
143
180
|
export class TusUploadError extends SparkVaultMobileError {
|
|
144
181
|
readonly cause: Error | null;
|
|
145
182
|
readonly filename: string | null;
|
|
@@ -259,8 +296,7 @@ export function isNetworkError(err: unknown): boolean {
|
|
|
259
296
|
* per-upload token minted with the ingot (expired, consumed, or bound to a
|
|
260
297
|
* different session) — not the vault session. Re-unsealing the vault cannot
|
|
261
298
|
* fix it; the caller creates a new upload session (`POST /ingots`) instead.
|
|
262
|
-
* Use `
|
|
263
|
-
* rejections apart.
|
|
299
|
+
* Use `vatRefusalReason` to tell the API's VAT refusals apart.
|
|
264
300
|
*/
|
|
265
301
|
export function isAuthError(err: unknown): boolean {
|
|
266
302
|
return (
|
|
@@ -271,31 +307,51 @@ export function isAuthError(err: unknown): boolean {
|
|
|
271
307
|
}
|
|
272
308
|
|
|
273
309
|
/**
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
310
|
+
* The reason the API refused the vault access token, or null when the error
|
|
311
|
+
* is not a VAT refusal:
|
|
312
|
+
* - `VAT_INVALID` (401): no, a malformed, unknown or swept token, or one a
|
|
313
|
+
* since-revoked DVAK minted — unseal again. Never the login.
|
|
314
|
+
* - `VAT_EXPIRED` (403): the token outlived its TTL — unseal again and
|
|
315
|
+
* retry the same request as-is.
|
|
316
|
+
* - `VAT_READ_ONLY` (403): a View Only token on a write — no unseal fixes it.
|
|
317
|
+
* A 403 with no reason is a token for another vault or account; a 401 with no
|
|
318
|
+
* reason is the login itself.
|
|
319
|
+
*/
|
|
320
|
+
export function vatRefusalReason(err: unknown): string | null {
|
|
321
|
+
if (!(err instanceof SparkVaultMobileError) || err instanceof TusUploadError) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
return readVatRefusalReason(err);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* True when the API refused the vault access token because it has expired
|
|
329
|
+
* (403 `VAT_EXPIRED`). Re-unsealing the vault mints a fresh VAT and the same
|
|
330
|
+
* request can be retried as-is.
|
|
278
331
|
*/
|
|
279
332
|
export function isVatExpiredError(err: unknown): err is SparkVaultMobileError {
|
|
280
|
-
return (
|
|
281
|
-
err instanceof SparkVaultMobileError &&
|
|
282
|
-
(err.statusCode === 401 || err.statusCode === 403) &&
|
|
283
|
-
err.details?.reason === 'VAT_EXPIRED'
|
|
284
|
-
);
|
|
333
|
+
return vatRefusalReason(err) === VAT_REFUSAL_REASONS.EXPIRED;
|
|
285
334
|
}
|
|
286
335
|
|
|
287
336
|
/**
|
|
288
|
-
* True when the vault access token is valid but read-only
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
337
|
+
* True when the vault access token is valid but read-only (403
|
|
338
|
+
* `VAT_READ_ONLY`): the session was unsealed with a read-only access level, so
|
|
339
|
+
* a write cannot succeed by retrying or by re-unsealing the same way — the
|
|
340
|
+
* caller needs write access to the vault.
|
|
292
341
|
*/
|
|
293
342
|
export function isReadOnlyVaultError(err: unknown): err is SparkVaultMobileError {
|
|
294
|
-
return (
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
343
|
+
return vatRefusalReason(err) === VAT_REFUSAL_REASONS.READ_ONLY;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* True for the API's 404 not-found answer: the resource it names does not
|
|
348
|
+
* exist (or is not this caller's to see). The only answer that says a vault,
|
|
349
|
+
* an ingot or a key's vault is missing; a rate limit, an outage or a refused
|
|
350
|
+
* session says nothing about it. Never true for a `TusUploadError`: Forge's
|
|
351
|
+
* 404 is an upload session that is gone, not a missing resource.
|
|
352
|
+
*/
|
|
353
|
+
export function isNotFoundError(err: unknown): err is SparkVaultMobileError {
|
|
354
|
+
return err instanceof SparkVaultMobileError && !(err instanceof TusUploadError) && err.statusCode === 404;
|
|
299
355
|
}
|
|
300
356
|
|
|
301
357
|
/** True for a 429 rate-limit response — retry after a cooldown, never immediately. */
|
|
@@ -311,18 +367,22 @@ export function isIngotNotReadyError(err: unknown): err is IngotNotReadyError {
|
|
|
311
367
|
return err instanceof IngotNotReadyError;
|
|
312
368
|
}
|
|
313
369
|
|
|
370
|
+
/** Type guard for a create refused over the same-name ingot holding its name. */
|
|
371
|
+
export function isIngotNameHeldError(err: unknown): err is IngotNameHeldError {
|
|
372
|
+
return err instanceof IngotNameHeldError;
|
|
373
|
+
}
|
|
374
|
+
|
|
314
375
|
/**
|
|
315
|
-
* True when retrying a failed download can succeed.
|
|
316
|
-
*
|
|
317
|
-
*
|
|
376
|
+
* True when retrying a failed download can succeed. A validation failure always
|
|
377
|
+
* fails the same way again, and an HTTP refusal is retried only when the
|
|
378
|
+
* platform's retry rule (`isTransientHttpStatus`, mirrored from
|
|
379
|
+
* `@sparkvault/app-sdk/transient`) calls it transient; every other failure
|
|
380
|
+
* (connectivity, an answer that could not be read) is worth another attempt.
|
|
318
381
|
*/
|
|
319
382
|
export function isRetryableDownloadError(err: unknown): boolean {
|
|
320
383
|
if (err instanceof SparkVaultValidationError) return false;
|
|
321
384
|
const status = err instanceof SparkVaultMobileError ? err.statusCode : undefined;
|
|
322
|
-
|
|
323
|
-
return false;
|
|
324
|
-
}
|
|
325
|
-
return true;
|
|
385
|
+
return typeof status !== 'number' || status < 400 || isTransientHttpStatus(status);
|
|
326
386
|
}
|
|
327
387
|
|
|
328
388
|
/**
|
package/src/folders.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
UpdateFolderRequest,
|
|
8
8
|
} from './types.js';
|
|
9
9
|
import { validateFolderId, validateIngotId, validateVaultId } from './validation.js';
|
|
10
|
+
import { apiPath } from './request-path.js';
|
|
10
11
|
|
|
11
12
|
export class MobileFoldersClient {
|
|
12
13
|
private readonly http: MobileHttpClient;
|
|
@@ -17,14 +18,14 @@ export class MobileFoldersClient {
|
|
|
17
18
|
|
|
18
19
|
async list(vaultId: string, vat: string): Promise<ListFoldersResponse> {
|
|
19
20
|
validateVaultId(vaultId);
|
|
20
|
-
const response = await this.http.get<ListFoldersResponse>(`/vaults/${vaultId}/folders`, { vat });
|
|
21
|
+
const response = await this.http.get<ListFoldersResponse>(apiPath`/vaults/${vaultId}/folders`, { vat });
|
|
21
22
|
return response.data;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
async get(vaultId: string, folderId: string, vat: string): Promise<Folder> {
|
|
25
26
|
validateVaultId(vaultId);
|
|
26
27
|
validateFolderId(folderId);
|
|
27
|
-
const response = await this.http.get<Folder>(`/vaults/${vaultId}/folders/${folderId}`, { vat });
|
|
28
|
+
const response = await this.http.get<Folder>(apiPath`/vaults/${vaultId}/folders/${folderId}`, { vat });
|
|
28
29
|
return response.data;
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -32,7 +33,7 @@ export class MobileFoldersClient {
|
|
|
32
33
|
validateVaultId(vaultId);
|
|
33
34
|
validateFolderId(folderId);
|
|
34
35
|
const response = await this.http.get<Folder & { breadcrumb?: FolderBreadcrumbItem[] }>(
|
|
35
|
-
`/vaults/${vaultId}/folders/${folderId}`,
|
|
36
|
+
apiPath`/vaults/${vaultId}/folders/${folderId}`,
|
|
36
37
|
{ vat }
|
|
37
38
|
);
|
|
38
39
|
return response.data.breadcrumb ?? [];
|
|
@@ -40,14 +41,14 @@ export class MobileFoldersClient {
|
|
|
40
41
|
|
|
41
42
|
async create(vaultId: string, vat: string, data: CreateFolderRequest): Promise<Folder> {
|
|
42
43
|
validateVaultId(vaultId);
|
|
43
|
-
const response = await this.http.post<Folder>(`/vaults/${vaultId}/folders`, data, { vat });
|
|
44
|
+
const response = await this.http.post<Folder>(apiPath`/vaults/${vaultId}/folders`, data, { vat });
|
|
44
45
|
return response.data;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
async update(vaultId: string, folderId: string, vat: string, data: UpdateFolderRequest): Promise<Folder> {
|
|
48
49
|
validateVaultId(vaultId);
|
|
49
50
|
validateFolderId(folderId);
|
|
50
|
-
const response = await this.http.put<Folder>(`/vaults/${vaultId}/folders/${folderId}`, data, { vat });
|
|
51
|
+
const response = await this.http.put<Folder>(apiPath`/vaults/${vaultId}/folders/${folderId}`, data, { vat });
|
|
51
52
|
return response.data;
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -62,7 +63,7 @@ export class MobileFoldersClient {
|
|
|
62
63
|
async delete(vaultId: string, folderId: string, vat: string): Promise<void> {
|
|
63
64
|
validateVaultId(vaultId);
|
|
64
65
|
validateFolderId(folderId);
|
|
65
|
-
await this.http.delete(`/vaults/${vaultId}/folders/${folderId}`, { vat });
|
|
66
|
+
await this.http.delete(apiPath`/vaults/${vaultId}/folders/${folderId}`, { vat });
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
async moveIngot(vaultId: string, ingotId: string, vat: string, folderId: string | null): Promise<void> {
|
|
@@ -71,6 +72,6 @@ export class MobileFoldersClient {
|
|
|
71
72
|
if (folderId !== null) {
|
|
72
73
|
validateFolderId(folderId);
|
|
73
74
|
}
|
|
74
|
-
await this.http.patch(`/vaults/${vaultId}/ingots/${ingotId}`, { folder_id: folderId }, { vat });
|
|
75
|
+
await this.http.patch(apiPath`/vaults/${vaultId}/ingots/${ingotId}`, { folder_id: folderId }, { vat });
|
|
75
76
|
}
|
|
76
77
|
}
|