@sparkvault/sdk-mobile 1.0.2 → 1.0.4
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 +1 -1
- package/dist/auth.d.ts +29 -1
- package/dist/auth.js +56 -0
- package/dist/auth.js.map +1 -1
- package/dist/billing.d.ts +21 -15
- package/dist/billing.js +28 -2
- package/dist/billing.js.map +1 -1
- package/dist/errors.d.ts +35 -1
- package/dist/errors.js +61 -0
- package/dist/errors.js.map +1 -1
- package/dist/http.d.ts +7 -0
- package/dist/http.js +25 -17
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/tus.js +34 -1
- package/dist/tus.js.map +1 -1
- package/dist/types.d.ts +85 -2
- package/package.json +1 -1
- package/src/auth.ts +81 -0
- package/src/billing.ts +38 -16
- package/src/errors.ts +83 -1
- package/src/http.ts +31 -23
- package/src/index.ts +4 -1
- package/src/tus.ts +40 -2
- package/src/types.ts +93 -2
package/src/index.ts
CHANGED
|
@@ -65,7 +65,7 @@ export {
|
|
|
65
65
|
MobileBillingClient,
|
|
66
66
|
} from './billing.js';
|
|
67
67
|
export type {
|
|
68
|
-
|
|
68
|
+
PortalSession,
|
|
69
69
|
} from './billing.js';
|
|
70
70
|
export {
|
|
71
71
|
MobileTusUploader,
|
|
@@ -85,6 +85,9 @@ export {
|
|
|
85
85
|
utf8ToBytes,
|
|
86
86
|
} from './encoding.js';
|
|
87
87
|
export {
|
|
88
|
+
gateErrorFromBody,
|
|
89
|
+
PlanRequiredError,
|
|
90
|
+
QuotaExceededError,
|
|
88
91
|
SparkVaultAuthenticationError,
|
|
89
92
|
SparkVaultAuthorizationError,
|
|
90
93
|
SparkVaultMobileError,
|
package/src/tus.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { ResolvedMobileConfig } from './config.js';
|
|
2
2
|
import { base64EncodeUtf8, base64ToBytes } from './encoding.js';
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
gateErrorFromBody,
|
|
5
|
+
SparkVaultMobileError,
|
|
6
|
+
SparkVaultValidationError,
|
|
7
|
+
TusUploadError,
|
|
8
|
+
} from './errors.js';
|
|
9
|
+
import type { ApiErrorBody, DebugLogger, MobileFileReader, UploadProgressCallback } from './types.js';
|
|
5
10
|
|
|
6
11
|
export interface ParsedForgeUrl {
|
|
7
12
|
baseUrl: string;
|
|
@@ -47,6 +52,21 @@ export function parseForgeUrl(forgeUrl: string): ParsedForgeUrl {
|
|
|
47
52
|
};
|
|
48
53
|
}
|
|
49
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Parse a Forge error response body (JSON text) into the shared ApiErrorBody
|
|
57
|
+
* shape so gate mapping can read its `error.code` / `error.details.resource`.
|
|
58
|
+
* Returns an empty body on non-JSON text, which simply matches no gate.
|
|
59
|
+
*/
|
|
60
|
+
function parseErrorBody(text: string): ApiErrorBody {
|
|
61
|
+
if (!text) return {};
|
|
62
|
+
try {
|
|
63
|
+
const parsed: unknown = JSON.parse(text);
|
|
64
|
+
return parsed && typeof parsed === 'object' ? (parsed as ApiErrorBody) : {};
|
|
65
|
+
} catch {
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
50
70
|
function getErrorName(err: unknown): string {
|
|
51
71
|
return err instanceof Error ? err.name : 'UnknownError';
|
|
52
72
|
}
|
|
@@ -119,6 +139,15 @@ async function createTusUpload(
|
|
|
119
139
|
|
|
120
140
|
if (!response.ok) {
|
|
121
141
|
const errorText = await response.text().catch(() => '');
|
|
142
|
+
|
|
143
|
+
// A 402 here is a billable gate hit mid-create (e.g. storage/bandwidth pool
|
|
144
|
+
// exhausted). Route it through the same typed-gate mapping as the JSON HTTP
|
|
145
|
+
// path so callers handle subscribe / add-on UX off one set of error types.
|
|
146
|
+
const gateError = gateErrorFromBody(response.status, parseErrorBody(errorText));
|
|
147
|
+
if (gateError) {
|
|
148
|
+
throw gateError;
|
|
149
|
+
}
|
|
150
|
+
|
|
122
151
|
throw new TusUploadError(`TUS create failed: ${response.status} - ${errorText}`, {
|
|
123
152
|
httpStatus: response.status,
|
|
124
153
|
filename,
|
|
@@ -191,6 +220,11 @@ function uploadChunk(
|
|
|
191
220
|
const newOffset = offsetHeader ? parseInt(offsetHeader, 10) : offset + chunkSize;
|
|
192
221
|
finish(() => resolve(newOffset));
|
|
193
222
|
} else {
|
|
223
|
+
const gateError = gateErrorFromBody(xhr.status, parseErrorBody(xhr.responseText));
|
|
224
|
+
if (gateError) {
|
|
225
|
+
finish(() => reject(gateError));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
194
228
|
finish(() => reject(new TusUploadError(`Chunk upload failed: ${xhr.status} - ${xhr.responseText.substring(0, 200)}`, {
|
|
195
229
|
httpStatus: xhr.status,
|
|
196
230
|
phase: 'upload',
|
|
@@ -287,6 +321,10 @@ export class MobileTusUploader {
|
|
|
287
321
|
}
|
|
288
322
|
|
|
289
323
|
if (err instanceof TusUploadError) throw err;
|
|
324
|
+
// Typed gate errors (PlanRequiredError / QuotaExceededError) must reach
|
|
325
|
+
// the caller intact so the subscribe / add-on UX fires; never bury them
|
|
326
|
+
// in a generic TusUploadError.
|
|
327
|
+
if (err instanceof SparkVaultMobileError) throw err;
|
|
290
328
|
throw TusUploadError.fromError(error, {
|
|
291
329
|
filename: options.filename,
|
|
292
330
|
fileSize: options.fileSize,
|
package/src/types.ts
CHANGED
|
@@ -1,9 +1,31 @@
|
|
|
1
|
+
/** Utilization state shared by storage, bandwidth, and Identity pools. */
|
|
2
|
+
export type PoolState = 'ok' | 'notice' | 'warning' | 'exhausted';
|
|
3
|
+
|
|
4
|
+
/** Live usage for a single capacity pool. `limit_gb` is null on unlimited plans. */
|
|
5
|
+
export interface PoolStatus {
|
|
6
|
+
used_gb: number;
|
|
7
|
+
limit_gb: number | null;
|
|
8
|
+
pct: number;
|
|
9
|
+
state: PoolState;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Bandwidth pool additionally reports when the metered cycle resets. */
|
|
13
|
+
export interface BandwidthPoolStatus extends PoolStatus {
|
|
14
|
+
cycle_resets_at: number | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Storage + bandwidth pool snapshot; rides responses on `meta.pools`. */
|
|
18
|
+
export interface SubscriptionPools {
|
|
19
|
+
storage: PoolStatus;
|
|
20
|
+
bandwidth: BandwidthPoolStatus;
|
|
21
|
+
}
|
|
22
|
+
|
|
1
23
|
export interface ApiMeta {
|
|
2
24
|
request_id: string;
|
|
3
25
|
response_ms: number;
|
|
4
26
|
timestamp: number;
|
|
5
27
|
api_version: string;
|
|
6
|
-
|
|
28
|
+
pools?: SubscriptionPools;
|
|
7
29
|
quota?: {
|
|
8
30
|
limit: number;
|
|
9
31
|
used: number;
|
|
@@ -48,12 +70,49 @@ export interface SparkVaultAccount {
|
|
|
48
70
|
organization_name: string;
|
|
49
71
|
email: string;
|
|
50
72
|
status: 'active' | 'suspended';
|
|
51
|
-
balance: string;
|
|
52
73
|
logo_url?: string;
|
|
53
74
|
logo_dark_url?: string;
|
|
54
75
|
created_at: number;
|
|
55
76
|
}
|
|
56
77
|
|
|
78
|
+
/** Identity attempts usage against the purchased tier cap. */
|
|
79
|
+
export interface SubscriptionIdentityStatus {
|
|
80
|
+
attempts_cap: number | null;
|
|
81
|
+
attempts_used: number;
|
|
82
|
+
soft_cap: number | null;
|
|
83
|
+
state: PoolState;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* GET /v1/billing/subscription — the account's current plan, licensed vs. used
|
|
88
|
+
* seats, capacity blocks, live pool usage, and Identity attempts. Drives the
|
|
89
|
+
* subscription / usage view. `renewal_amount_usd` is a formatted dollars string;
|
|
90
|
+
* `renewal_date` is an epoch (seconds) or null when there is no active plan.
|
|
91
|
+
*/
|
|
92
|
+
export interface SparkVaultSubscription {
|
|
93
|
+
plan: 'subscription' | 'internal' | 'unsubscribed';
|
|
94
|
+
unlimited: boolean;
|
|
95
|
+
subscription_status: string | null;
|
|
96
|
+
past_due: boolean;
|
|
97
|
+
renewal_date: number | null;
|
|
98
|
+
renewal_amount_usd: string;
|
|
99
|
+
seats: {
|
|
100
|
+
full: number;
|
|
101
|
+
viewer: number;
|
|
102
|
+
licensed: number;
|
|
103
|
+
used: number;
|
|
104
|
+
};
|
|
105
|
+
blocks: {
|
|
106
|
+
storage: number;
|
|
107
|
+
bandwidth: number;
|
|
108
|
+
};
|
|
109
|
+
pools: SubscriptionPools;
|
|
110
|
+
identity: SubscriptionIdentityStatus;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Native in-app purchase store channel for receipt validation. */
|
|
114
|
+
export type IapPlatform = 'ios' | 'android';
|
|
115
|
+
|
|
57
116
|
export type IdentityType = 'email' | 'phone';
|
|
58
117
|
export type VerifiedIdentityType = IdentityType | 'social';
|
|
59
118
|
|
|
@@ -118,6 +177,17 @@ export interface IdentityTokenClaims {
|
|
|
118
177
|
method: string;
|
|
119
178
|
}
|
|
120
179
|
|
|
180
|
+
/**
|
|
181
|
+
* A server-decided post-login nudge. Computed once per login (default mode);
|
|
182
|
+
* clients act on the steps they support, in order.
|
|
183
|
+
*/
|
|
184
|
+
export type Recommendation =
|
|
185
|
+
| { type: 'passkey' }
|
|
186
|
+
| { type: 'backup_identifier'; missingType: 'email' | 'phone' };
|
|
187
|
+
|
|
188
|
+
/** Canonical dismiss key for a recommendation (suppresses it server-side 30d). */
|
|
189
|
+
export type RecommendationKey = 'passkey' | 'backup_email' | 'backup_phone';
|
|
190
|
+
|
|
121
191
|
export interface IdentityVerifyResult {
|
|
122
192
|
token: string;
|
|
123
193
|
identity: string;
|
|
@@ -126,6 +196,27 @@ export interface IdentityVerifyResult {
|
|
|
126
196
|
redirect?: string;
|
|
127
197
|
jwksUri: string;
|
|
128
198
|
jwks?: IdentityJwks;
|
|
199
|
+
/** Ordered post-login nudges (default-mode logins only). */
|
|
200
|
+
recommendations?: Recommendation[];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Response to POST /backup-verification/send. */
|
|
204
|
+
export interface BackupSendResponse {
|
|
205
|
+
/** Opaque pending-attach session id (present when a code was sent). */
|
|
206
|
+
session_id?: string;
|
|
207
|
+
/** Delivery channel actually used. */
|
|
208
|
+
method?: 'email' | 'sms' | 'voice';
|
|
209
|
+
/** Code expiry (Unix seconds). */
|
|
210
|
+
expires_at?: number;
|
|
211
|
+
/** True when the identifier is already on the identity — no code was sent. */
|
|
212
|
+
already_verified?: boolean;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Response to POST /backup-verification/verify. */
|
|
216
|
+
export interface BackupVerifyResponse {
|
|
217
|
+
verified: boolean;
|
|
218
|
+
type: 'email' | 'phone';
|
|
219
|
+
value: string;
|
|
129
220
|
}
|
|
130
221
|
|
|
131
222
|
export interface SendTotpRequest {
|