@sparkvault/sdk-mobile 7.1.10 → 7.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +2 -1
  2. package/dist/account.d.ts +0 -5
  3. package/dist/account.js +0 -9
  4. package/dist/account.js.map +1 -1
  5. package/dist/account.test-d.d.ts +26 -0
  6. package/dist/account.test-d.js +17 -0
  7. package/dist/account.test-d.js.map +1 -0
  8. package/dist/errors.d.ts +0 -3
  9. package/dist/errors.js +0 -7
  10. package/dist/errors.js.map +1 -1
  11. package/dist/http.d.ts +7 -13
  12. package/dist/http.js +7 -41
  13. package/dist/http.js.map +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/ingots.d.ts +6 -8
  18. package/dist/ingots.js +3 -15
  19. package/dist/ingots.js.map +1 -1
  20. package/dist/ingots.test-d.d.ts +11 -6
  21. package/dist/ingots.test-d.js +7 -4
  22. package/dist/ingots.test-d.js.map +1 -1
  23. package/dist/products/identity.d.ts +8 -5
  24. package/dist/products/identity.js +12 -29
  25. package/dist/products/identity.js.map +1 -1
  26. package/dist/sparks.d.ts +5 -2
  27. package/dist/sparks.js +0 -1
  28. package/dist/sparks.js.map +1 -1
  29. package/dist/tus.d.ts +0 -1
  30. package/dist/tus.js +1 -5
  31. package/dist/tus.js.map +1 -1
  32. package/dist/types.d.ts +27 -52
  33. package/dist/vaults.d.ts +1 -4
  34. package/dist/vaults.js +3 -3
  35. package/dist/vaults.js.map +1 -1
  36. package/dist/vaults.test-d.d.ts +7 -4
  37. package/dist/vaults.test-d.js +4 -2
  38. package/dist/vaults.test-d.js.map +1 -1
  39. package/package.json +1 -1
  40. package/src/account.test-d.ts +39 -0
  41. package/src/account.ts +0 -14
  42. package/src/errors.ts +0 -8
  43. package/src/http.ts +8 -50
  44. package/src/index.ts +0 -1
  45. package/src/ingots.test-d.ts +28 -5
  46. package/src/ingots.ts +9 -31
  47. package/src/products/identity.ts +16 -46
  48. package/src/sparks.ts +5 -3
  49. package/src/tus.ts +1 -6
  50. package/src/types.ts +27 -56
  51. package/src/vaults.test-d.ts +6 -3
  52. package/src/vaults.ts +4 -8
package/src/http.ts CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  import { isTransientHttpStatus } from './app-sdk/transient.js';
11
11
  import { createDedupMutex } from './mutex.js';
12
12
  import { queryString, type ApiPath, type QueryParams } from './request-path.js';
13
- import type { ApiErrorBody, ApiMeta, ApiResponse, RateLimitInfo } from './types.js';
13
+ import type { ApiErrorBody, ApiMeta, ApiResponse } from './types.js';
14
14
 
15
15
  export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
16
16
 
@@ -21,7 +21,6 @@ export interface RequestOptions {
21
21
  query?: QueryParams;
22
22
  skipAuth?: boolean;
23
23
  vat?: string;
24
- timeoutMs?: number;
25
24
  }
26
25
 
27
26
  export type AuthEvent = 'logout' | 'token_refreshed';
@@ -72,24 +71,11 @@ export class MobileHttpClient {
72
71
  private readonly authListeners = new Set<AuthEventListener>();
73
72
  private readonly refreshMutex = createDedupMutex<RefreshOutcome>();
74
73
  private lastMeta: ApiMeta | null = null;
75
- private rateLimitInfo: RateLimitInfo | null = null;
76
74
 
77
75
  constructor(config: ResolvedMobileConfig) {
78
76
  this.config = config;
79
77
  }
80
78
 
81
- get baseUrl(): string {
82
- return this.config.apiBaseUrl;
83
- }
84
-
85
- get meta(): ApiMeta | null {
86
- return this.lastMeta;
87
- }
88
-
89
- get rateLimit(): RateLimitInfo | null {
90
- return this.rateLimitInfo;
91
- }
92
-
93
79
  onAuthEvent(listener: AuthEventListener): () => void {
94
80
  this.authListeners.add(listener);
95
81
  return () => this.authListeners.delete(listener);
@@ -133,32 +119,6 @@ export class MobileHttpClient {
133
119
  return headers;
134
120
  }
135
121
 
136
- private parseRateLimitHeaders(headers: Headers): void {
137
- const limit = headers.get('X-RateLimit-Limit');
138
- const remaining = headers.get('X-RateLimit-Remaining');
139
- const reset = headers.get('X-RateLimit-Reset');
140
-
141
- if (!limit || !remaining || !reset) return;
142
-
143
- const parsedLimit = parseInt(limit, 10);
144
- const parsedRemaining = parseInt(remaining, 10);
145
- const parsedReset = parseInt(reset, 10);
146
-
147
- if (
148
- Number.isFinite(parsedLimit) &&
149
- Number.isFinite(parsedRemaining) &&
150
- Number.isFinite(parsedReset)
151
- ) {
152
- this.rateLimitInfo = {
153
- limit: parsedLimit,
154
- remaining: parsedRemaining,
155
- reset: parsedReset,
156
- };
157
- } else {
158
- this.config.logger.warn('Malformed rate limit headers', { limit, remaining, reset });
159
- }
160
- }
161
-
162
122
  /** The SDK error for a request the fetch adapter rejected (no HTTP response). */
163
123
  private transportError(err: unknown): SparkVaultMobileError {
164
124
  if (err instanceof SparkVaultMobileError) {
@@ -181,12 +141,6 @@ export class MobileHttpClient {
181
141
  return new SparkVaultMobileError(err instanceof Error ? err.message : 'Unknown request error');
182
142
  }
183
143
 
184
- /**
185
- * Single mapping for non-OK responses: 402s become typed gate errors
186
- * (PlanRequiredError / QuotaExceededError) so the subscribe / add-on UX keys
187
- * off one set of types; everything else becomes a SparkVaultMobileError
188
- * carrying the server code, status, details, and meta.
189
- */
190
144
  /** An error response's JSON body, or an empty one when it has none. */
191
145
  private async readErrorBody(response: Response): Promise<ApiErrorBody> {
192
146
  try {
@@ -209,6 +163,12 @@ export class MobileHttpClient {
209
163
  }
210
164
  }
211
165
 
166
+ /**
167
+ * Single mapping for non-OK responses: 402s become typed gate errors
168
+ * (PlanRequiredError / QuotaExceededError) so the subscribe / add-on UX keys
169
+ * off one set of types; everything else becomes a SparkVaultMobileError
170
+ * carrying the server code, status, details, and meta.
171
+ */
212
172
  private httpError(status: number, errorBody: ApiErrorBody): SparkVaultMobileError {
213
173
  if (errorBody.meta) {
214
174
  this.lastMeta = errorBody.meta;
@@ -358,7 +318,7 @@ export class MobileHttpClient {
358
318
 
359
319
  async request<T>(method: HttpMethod, path: ApiPath, options: RequestOptions = {}): Promise<ApiResponse<T>> {
360
320
  const url = this.makeUrl(path, options.query);
361
- const timeoutMs = options.timeoutMs ?? this.config.timeoutMs;
321
+ const timeoutMs = this.config.timeoutMs;
362
322
  const body = method === 'GET' ? undefined : this.serializeBody(options.body);
363
323
  let headers = await this.getHeaders({ ...options, body: options.body });
364
324
 
@@ -368,7 +328,6 @@ export class MobileHttpClient {
368
328
  } catch (err) {
369
329
  throw this.transportError(err);
370
330
  }
371
- this.parseRateLimitHeaders(response.headers);
372
331
 
373
332
  if (response.status === 401 && !options.skipAuth) {
374
333
  await this.throwIfVatRefused(response);
@@ -379,7 +338,6 @@ export class MobileHttpClient {
379
338
  } catch (err) {
380
339
  throw this.transportError(err);
381
340
  }
382
- this.parseRateLimitHeaders(response.headers);
383
341
  }
384
342
 
385
343
  if (response.status === 204) {
package/src/index.ts CHANGED
@@ -133,7 +133,6 @@ export {
133
133
  vatRefusalReason,
134
134
  QuotaExceededError,
135
135
  SparkVaultAuthenticationError,
136
- SparkVaultAuthorizationError,
137
136
  SparkVaultMobileError,
138
137
  SparkVaultNetworkError,
139
138
  SparkVaultTimeoutError,
@@ -6,13 +6,16 @@
6
6
  * and every assertion would pass. `tsc --noEmit` (`pnpm typecheck`) is the gate
7
7
  * instead. It is deliberately NOT a `*.test.ts`, which tsconfig excludes.
8
8
  *
9
- * Mirrored server source: apps/api/src/ingots/sharing.js (`formatSharingConfig`,
10
- * the one shape GET and PUT /vaults/:id/ingots/:ingot_id/sharing answer). A
11
- * field the server never sends reads as undefined wherever an app trusts it:
12
- * a link section keyed on it never renders.
9
+ * Mirrored server sources: apps/api/src/ingots/sharing.js (`formatSharingConfig`,
10
+ * the one shape GET and PUT /vaults/:id/ingots/:ingot_id/sharing answer),
11
+ * apps/api/src/repositories/IngotRepository.js (`toPublicFormat`, the one
12
+ * projection every ingot answer uses) and apps/api/src/lib/pagination.js
13
+ * (`buildPaginatedResponse`: one page, `{ ingots, count, has_more,
14
+ * next_cursor? }`). A field the server never sends reads as undefined wherever
15
+ * an app trusts it: a link section keyed on it never renders.
13
16
  */
14
17
 
15
- import type { IngotSharingConfig } from './types.js';
18
+ import type { Ingot, IngotSharingConfig, ListIngotsResponse } from './types.js';
16
19
 
17
20
  /** Fails to compile unless `T` resolves to exactly `true`. */
18
21
  type Assert<T extends true> = T;
@@ -43,4 +46,24 @@ export type IngotsWireContract = [
43
46
  Assert<Equals<IngotSharingConfig['public_url'], string | null>>,
44
47
  Assert<Equals<IngotSharingConfig['link_code'], string | null>>,
45
48
  Assert<Equals<IngotSharingConfig['visibility'], 'invite_only' | 'authenticated' | 'public' | null>>,
49
+ // A page reports its own length and whether more follow; it carries no total.
50
+ Assert<Equals<keyof ListIngotsResponse, 'ingots' | 'count' | 'has_more' | 'next_cursor'>>,
51
+ // Fields toPublicFormat projects, and none it keeps to the row (storage
52
+ // location, last access, the abandoned-upload expiry).
53
+ Assert<
54
+ Equals<
55
+ keyof Ingot,
56
+ | 'ingot_id'
57
+ | 'name'
58
+ | 'content_type'
59
+ | 'size_bytes'
60
+ | 'status'
61
+ | 'folder_id'
62
+ | 'managed_by'
63
+ | 'created_at'
64
+ | 'access_count'
65
+ | 'content_hmac'
66
+ | 'content_version'
67
+ >
68
+ >,
46
69
  ];
package/src/ingots.ts CHANGED
@@ -121,8 +121,12 @@ export interface UploadFromUriOptions extends UploadOptions {
121
121
  debug?: DebugLogger;
122
122
  }
123
123
 
124
- /** A replace addresses its ingot by id: no name is placed, so the create-only hooks do not apply. */
125
- export interface ReplaceFromUriOptions extends Omit<UploadFromUriOptions, 'name' | 'onNameConflict' | 'onIngotCreated'> {
124
+ /**
125
+ * A replace addresses its ingot by id: no name is placed and the ingot stays
126
+ * in its folder, so the create-only hooks and the folder target do not apply.
127
+ */
128
+ export interface ReplaceFromUriOptions
129
+ extends Omit<UploadFromUriOptions, 'name' | 'folder' | 'folderId' | 'onNameConflict' | 'onIngotCreated'> {
126
130
  ingotId: string;
127
131
  /**
128
132
  * A new name for the ingot, stored in the same request as the content.
@@ -195,11 +199,6 @@ export interface DownloadToFileResult {
195
199
  sizeBytes: number;
196
200
  }
197
201
 
198
- export interface AddInviteOptions {
199
- /** Invite lifetime in seconds. Omit to inherit the link's existing expiry. */
200
- expiresInSeconds?: number;
201
- }
202
-
203
202
  export interface CreateUploadOptions {
204
203
  vaultId: string;
205
204
  vat: string;
@@ -411,7 +410,6 @@ export class MobileIngotsClient {
411
410
  ingot_id: init.ingotId,
412
411
  name,
413
412
  size_bytes: init.sizeBytes ?? options.fileSize,
414
- storage_location: 's3',
415
413
  // A server that does not report it minted the row (the upsert is the
416
414
  // only other outcome, and it always says so).
417
415
  created,
@@ -464,7 +462,6 @@ export class MobileIngotsClient {
464
462
  ingot_id: options.ingotId,
465
463
  name: options.name,
466
464
  size_bytes: options.fileSize,
467
- storage_location: 's3',
468
465
  created: false,
469
466
  status: ingot.status,
470
467
  ...publicationOf(ingot),
@@ -600,7 +597,6 @@ export class MobileIngotsClient {
600
597
  ingot_id: response.data.ingot_id,
601
598
  name,
602
599
  size_bytes: fileSize,
603
- storage_location: 's3',
604
600
  // A replace never mints an ingot: the row is the caller's existing one.
605
601
  created: false,
606
602
  status: ingot.status,
@@ -868,18 +864,12 @@ export class MobileIngotsClient {
868
864
  return response.data;
869
865
  }
870
866
 
871
- async addInvite(
872
- vaultId: string,
873
- ingotId: string,
874
- vat: string,
875
- identity: string,
876
- options: AddInviteOptions = {}
877
- ): Promise<IngotInvite> {
867
+ async addInvite(vaultId: string, ingotId: string, vat: string, identity: string): Promise<IngotInvite> {
878
868
  validateVaultId(vaultId);
879
869
  validateIngotId(ingotId);
880
870
  const response = await this.http.post<IngotInvite>(
881
871
  apiPath`/vaults/${vaultId}/ingots/${ingotId}/sharing/invite`,
882
- { identity, expires_in_seconds: options.expiresInSeconds },
872
+ { identity },
883
873
  { vat }
884
874
  );
885
875
  return response.data;
@@ -925,19 +915,7 @@ export class MobileIngotsClient {
925
915
  validateVaultId(vaultId);
926
916
  validateIngotId(ingotId);
927
917
  const ingot = await this.get(vaultId, ingotId, vat);
928
- return {
929
- total_downloads: ingot.access_count ?? 0,
930
- last_accessed_at: ingot.last_accessed_at ?? ingot.accessed_at,
931
- };
932
- }
933
-
934
- async purgeAccessLogs(vaultId: string, ingotId: string, vat: string): Promise<void> {
935
- validateVaultId(vaultId);
936
- validateIngotId(ingotId);
937
- void vat;
938
- throw new SparkVaultValidationError(
939
- 'Ingot audit logs are read-only. Configure vault access-log retention to expire old entries.'
940
- );
918
+ return { total_downloads: ingot.access_count ?? 0 };
941
919
  }
942
920
 
943
921
  /**
@@ -20,9 +20,8 @@ import type {
20
20
  PasskeyRegisterChallenge,
21
21
  PasskeySession,
22
22
  PasskeyStatus,
23
- RequestPinRequest,
24
- RequestPinResponse,
25
23
  SendOtpRequest,
24
+ SendOtpResponse,
26
25
  SparkVaultUser,
27
26
  SubmitSecondFactorRequest,
28
27
  VerifyOtpRequest,
@@ -127,10 +126,6 @@ export class MobileIdentityClient {
127
126
  }
128
127
  }
129
128
 
130
- isConfigPreloaded(): boolean {
131
- return this.configCache !== null;
132
- }
133
-
134
129
  async getJwks(options: { forceRefresh?: boolean } = {}): Promise<IdentityJwks> {
135
130
  const now = Date.now();
136
131
  const expired = now - this.jwksCachedAt > JWKS_CACHE_TTL_MS;
@@ -247,31 +242,14 @@ export class MobileIdentityClient {
247
242
  return key;
248
243
  }
249
244
 
250
- async sendOtp(request: SendOtpRequest): Promise<RequestPinResponse> {
245
+ async sendOtp(request: SendOtpRequest): Promise<SendOtpResponse> {
251
246
  const identity = this.normalizeIdentity(request.identity, request.identityType);
252
- const result = await this.postIdentity<{
253
- kindling: string;
254
- expires_at?: number;
255
- retry_after?: number;
256
- }>(apiPath`/otp/send`, {
247
+ const result = await this.postIdentity<SendOtpResponse>(apiPath`/otp/send`, {
257
248
  recipient: identity,
258
249
  method: request.method,
259
250
  });
260
251
 
261
- return {
262
- kindling: result.kindling,
263
- expires_at: result.expires_at ?? Math.floor(Date.now() / 1000) + 900,
264
- retry_after: result.retry_after,
265
- is_signup: false,
266
- };
267
- }
268
-
269
- async requestPin(request: RequestPinRequest): Promise<RequestPinResponse> {
270
- return this.sendOtp({
271
- identity: request.email,
272
- identityType: 'email',
273
- method: 'email',
274
- });
252
+ return { kindling: result.kindling, expires_at: result.expires_at };
275
253
  }
276
254
 
277
255
  async verifyOtp(request: VerifyOtpRequest): Promise<IdentityVerifyResult> {
@@ -371,7 +349,12 @@ export class MobileIdentityClient {
371
349
  }
372
350
  }
373
351
 
374
- async getPasskeyAuthOptions(_identity?: string, _identityType?: IdentityType): Promise<PasskeyAuthChallenge> {
352
+ /**
353
+ * Start a discoverable passkey ceremony: the request names no identity, so
354
+ * the authenticator offers every passkey it holds for the Identity relying
355
+ * party, and the one chosen names its person (its user handle is the SVID).
356
+ */
357
+ async getPasskeyAuthOptions(): Promise<PasskeyAuthChallenge> {
375
358
  return this.postIdentity<PasskeyAuthChallenge>(apiPath`/passkey/verify`, {});
376
359
  }
377
360
 
@@ -507,9 +490,7 @@ export class MobileIdentityClient {
507
490
  recipient: this.normalizeIdentity(request.recipient, inferIdentityType(request.recipient)),
508
491
  });
509
492
  } catch (err) {
510
- const apiError = err as SparkVaultMobileError & {
511
- data?: { kindling?: string; expires_at?: number };
512
- };
493
+ const apiError = err as SparkVaultMobileError;
513
494
  const message = apiError.message || 'Verification failed';
514
495
  const code = apiError.statusCode ?? apiError.code;
515
496
 
@@ -599,15 +580,10 @@ export class MobileIdentityClient {
599
580
  return (data.data ?? data) as T;
600
581
  }
601
582
 
602
- private async postIdentity<T>(
603
- path: ApiPath,
604
- body: Record<string, unknown>,
605
- options: { bearerToken?: string } = {}
606
- ): Promise<T> {
607
- const headers = this.identityHeaders(options.bearerToken);
583
+ private async postIdentity<T>(path: ApiPath, body: Record<string, unknown>): Promise<T> {
608
584
  const response = await this.config.fetch(identityUrl(this.config, path), {
609
585
  method: 'POST',
610
- headers,
586
+ headers: this.identityHeaders(),
611
587
  body: JSON.stringify(body),
612
588
  timeoutMs: this.config.timeoutMs,
613
589
  });
@@ -615,30 +591,24 @@ export class MobileIdentityClient {
615
591
  if (!response.ok) {
616
592
  const errorBody = await response.json().catch(() => ({} as ApiErrorBody));
617
593
  const message = errorBody.error?.message ?? errorBody.message ?? 'Identity verification failed';
618
- const error = new SparkVaultMobileError(message, {
594
+ throw new SparkVaultMobileError(message, {
619
595
  code: errorBody.error?.code ?? response.status,
620
596
  statusCode: response.status,
621
597
  details: errorBody.error?.details,
622
598
  meta: errorBody.meta,
623
599
  });
624
- (error as SparkVaultMobileError & { data?: unknown }).data = errorBody.data ?? errorBody;
625
- throw error;
626
600
  }
627
601
 
628
602
  const data = await response.json();
629
603
  return (data.data ?? data) as T;
630
604
  }
631
605
 
632
- private identityHeaders(bearerToken?: string): Record<string, string> {
633
- const headers: Record<string, string> = {
606
+ private identityHeaders(): Record<string, string> {
607
+ return {
634
608
  'Content-Type': 'application/json',
635
609
  Accept: 'application/json',
636
610
  'User-Agent': this.config.userAgent,
637
611
  };
638
- if (bearerToken) {
639
- headers.Authorization = `Bearer ${bearerToken}`;
640
- }
641
- return headers;
642
612
  }
643
613
 
644
614
  private identityLookupBody(identity: string, identityType?: IdentityType): Record<string, string> {
package/src/sparks.ts CHANGED
@@ -3,10 +3,10 @@ import type { ShareSparkResponse, SparkShareStatus, UnshareSparkResponse } from
3
3
  import { apiPath } from './request-path.js';
4
4
  import { validateResourceId } from './validation.js';
5
5
 
6
+ /** A Spark as GET /sparks lists it: never its payload, which only `get` reads. */
6
7
  export interface Spark {
7
8
  spark_id: string;
8
9
  status: string;
9
- payload?: string;
10
10
  content_type?: string;
11
11
  }
12
12
 
@@ -31,9 +31,12 @@ export interface SparkResponse {
31
31
  export interface CreateSparkOptions {
32
32
  payload: string;
33
33
  contentType?: string;
34
+ /**
35
+ * Time-to-live: a whole number of minutes from 1 to 1440 (24h). Defaults to
36
+ * 1440 server-side. A fraction is refused with a 400 `VALIDATION_ERROR`.
37
+ */
34
38
  ttlMinutes?: number;
35
39
  kindling?: string;
36
- name?: string;
37
40
  }
38
41
 
39
42
  export interface CreateSparkResponse {
@@ -75,7 +78,6 @@ export class MobileSparksClient {
75
78
  content_type: options.contentType,
76
79
  ttl_minutes: options.ttlMinutes,
77
80
  kindling: options.kindling,
78
- name: options.name,
79
81
  });
80
82
  return response.data;
81
83
  }
package/src/tus.ts CHANGED
@@ -37,7 +37,6 @@ import type {
37
37
  } from './types.js';
38
38
 
39
39
  export interface ParsedForgeUrl {
40
- baseUrl: string;
41
40
  tusEndpoint: string;
42
41
  istk: string;
43
42
  }
@@ -169,11 +168,7 @@ export function parseForgeUrl(
169
168
  reason: read.reason,
170
169
  });
171
170
  }
172
- return {
173
- baseUrl: read.origin,
174
- tusEndpoint: read.endpoint,
175
- istk: read.istk,
176
- };
171
+ return { tusEndpoint: read.endpoint, istk: read.istk };
177
172
  }
178
173
 
179
174
  /**
package/src/types.ts CHANGED
@@ -48,16 +48,9 @@ export interface ApiErrorBody {
48
48
  details?: Record<string, unknown>;
49
49
  };
50
50
  message?: string;
51
- data?: unknown;
52
51
  meta?: ApiMeta;
53
52
  }
54
53
 
55
- export interface RateLimitInfo {
56
- limit: number;
57
- remaining: number;
58
- reset: number;
59
- }
60
-
61
54
  /** Account-scoped role. Owners and admins manage the organization and team. */
62
55
  export type OrgRole = 'owner' | 'admin' | 'user';
63
56
 
@@ -282,7 +275,6 @@ export interface IdentityBranding {
282
275
  companyName: string | null;
283
276
  logoLight: string | null;
284
277
  logoDark: string | null;
285
- backdrop_blur?: boolean;
286
278
  themeMode?: 'light' | 'dark';
287
279
  }
288
280
 
@@ -380,52 +372,37 @@ export interface VerifyOtpRequest {
380
372
  recipient: string;
381
373
  }
382
374
 
383
- export interface RequestPinRequest {
384
- email: string;
385
- organization_name?: string;
386
- }
387
-
388
- export interface RequestPinResponse {
375
+ /** A code sent: the kindling its verify names, and when the code expires (Unix seconds). */
376
+ export interface SendOtpResponse {
389
377
  kindling: string;
390
378
  expires_at: number;
391
- retry_after?: number;
392
- is_signup: boolean;
393
379
  }
394
380
 
395
381
  export interface PinVerifyLoginResponse {
396
382
  access_token: string;
397
383
  refresh_token: string;
398
- token_type: 'Bearer';
399
- expires_in: number;
400
384
  user: SparkVaultUser;
401
385
  }
402
386
 
387
+ /**
388
+ * A verified identity with no SparkVault user yet: `signup_token` (an
389
+ * identity-only access token) authorizes `account.completeSignup`.
390
+ */
403
391
  export interface PinVerifySignupPendingResponse {
404
392
  verified: true;
405
393
  needs_signup_info: true;
406
394
  signup_token: string;
407
- expires_at: number;
408
395
  }
409
396
 
410
397
  /**
411
- * Variant of the Core exchange result indicating the account has 2FA enabled:
412
- * the caller must collect a second factor via
413
- * `products.identity.submitSecondFactor({ ticket, code })` and re-exchange the
414
- * resulting Identity token via `account.exchangeIdentityToken`.
398
+ * The Core exchange of a verified Identity token. A second factor is collected
399
+ * at the Identity Product before its token exists (`products.identity.verifyOtp`
400
+ * throws for it, and `submitSecondFactor` finishes it), so the exchange is a
401
+ * login or a pending signup, never a second-factor challenge.
415
402
  */
416
- export interface PinVerifySecondFactorPendingResponse {
417
- verified: false;
418
- second_factor_required: true;
419
- /** Opaque pending-login handle to submit the second factor against. */
420
- ticket: string;
421
- /** Methods the user may use, e.g. ['authenticator','recovery_code']. */
422
- methods: string[];
423
- }
424
-
425
403
  export type PinVerifyResponse =
426
404
  | PinVerifyLoginResponse
427
- | PinVerifySignupPendingResponse
428
- | PinVerifySecondFactorPendingResponse;
405
+ | PinVerifySignupPendingResponse;
429
406
 
430
407
  export interface SubmitSecondFactorRequest {
431
408
  /** Pending-login ticket from a second_factor_required response. */
@@ -442,8 +419,6 @@ export interface CompleteSignupRequest {
442
419
  export interface CompleteSignupResponse {
443
420
  access_token: string;
444
421
  refresh_token: string;
445
- token_type?: 'Bearer';
446
- expires_in?: number;
447
422
  user: SparkVaultUser;
448
423
  account: SparkVaultAccount;
449
424
  }
@@ -454,10 +429,14 @@ export interface PasskeyAuthOptions {
454
429
  challenge: string;
455
430
  timeout: number;
456
431
  rpId: string;
432
+ /**
433
+ * The person's credentials when the ceremony is scoped to an identifier:
434
+ * ids only, since a transport list would hand an unauthenticated probe a
435
+ * device fingerprint.
436
+ */
457
437
  allowCredentials?: Array<{
458
438
  id: string;
459
439
  type: 'public-key';
460
- transports?: string[];
461
440
  }>;
462
441
  userVerification: 'required' | 'preferred' | 'discouraged';
463
442
  }
@@ -523,15 +502,15 @@ export interface PasskeyCredential {
523
502
 
524
503
  export interface PasskeyStatus {
525
504
  has_passkey: boolean;
526
- registered_at?: number;
527
- last_used_at?: number;
528
505
  }
529
506
 
507
+ /**
508
+ * POST /auth/identity/verify: a session for the verified identity's member, or,
509
+ * with `user` and `account` null, an identity-only token for a signup.
510
+ */
530
511
  export interface IdentitySessionResponse {
531
512
  access_token: string;
532
513
  refresh_token: string;
533
- token_type?: 'Bearer';
534
- expires_in?: number;
535
514
  user: SparkVaultUser | null;
536
515
  account: SparkVaultAccount | null;
537
516
  }
@@ -549,18 +528,16 @@ export interface Vault {
549
528
  unseal_count?: number;
550
529
  }
551
530
 
531
+ /** A vault is created with its name alone; a description is set with `update`. */
552
532
  export interface CreateVaultRequest {
553
533
  name: string;
554
- description?: string;
555
534
  }
556
535
 
557
536
  export interface CreateVaultResponse {
558
537
  vault_id: string;
559
538
  name: string;
560
- description?: string;
561
539
  vmk: string;
562
540
  vmk_warning: string;
563
- status: 'sealed';
564
541
  created_at: number;
565
542
  }
566
543
 
@@ -700,7 +677,6 @@ export interface Ingot {
700
677
  name: string;
701
678
  content_type: string;
702
679
  size_bytes: number;
703
- storage_location: 'dynamodb' | 's3';
704
680
  /** `uploading` until Forge activates the upload, then `active`. */
705
681
  status?: 'uploading' | 'active';
706
682
  folder_id?: string | null;
@@ -712,10 +688,7 @@ export interface Ingot {
712
688
  */
713
689
  managed_by?: string | null;
714
690
  created_at: number;
715
- accessed_at?: number;
716
- last_accessed_at?: number;
717
691
  access_count?: number;
718
- expires_at?: number;
719
692
  /**
720
693
  * Client-computed, vault-keyed HMAC of the plaintext (43-char base64url),
721
694
  * or null when the uploader did not supply one. Used for upload dedup and
@@ -736,9 +709,14 @@ export interface HashCheckResponse {
736
709
  found: HashCheckMatch[];
737
710
  }
738
711
 
712
+ /**
713
+ * One page of GET /vaults/:id/ingots or /ingots/search. `count` is this
714
+ * page's length: the server reports no total.
715
+ */
739
716
  export interface ListIngotsResponse {
740
717
  ingots: Ingot[];
741
- total: number;
718
+ count: number;
719
+ has_more: boolean;
742
720
  next_cursor?: string;
743
721
  }
744
722
 
@@ -762,7 +740,6 @@ export interface UploadResult {
762
740
  */
763
741
  name: string;
764
742
  size_bytes: number;
765
- storage_location: 's3';
766
743
  /**
767
744
  * Whether this upload minted the ingot (true) or went into an existing row
768
745
  * (false): the one holding the name, as the create's name-conflict policy
@@ -842,8 +819,6 @@ export interface IngotAccessLog {
842
819
 
843
820
  export interface IngotAccessStats {
844
821
  total_downloads: number;
845
- unique_visitors?: number;
846
- last_accessed_at?: number;
847
822
  }
848
823
 
849
824
  /** A folder as every folder route answers it (Core's FolderRepository.toPublicFormat). */
@@ -886,16 +861,13 @@ export interface TokenStorageAdapter {
886
861
  clearTokens(): Promise<void>;
887
862
  getUser?<T = SparkVaultUser>(): Promise<T | null>;
888
863
  setUser?<T = SparkVaultUser>(user: T): Promise<void>;
889
- getAccount?<T = SparkVaultAccount>(): Promise<T | null>;
890
864
  setAccount?<T = SparkVaultAccount>(account: T): Promise<void>;
891
- clearAll?(): Promise<void>;
892
865
  }
893
866
 
894
867
  export type FetchLike = (
895
868
  url: string,
896
869
  options?: RequestInit & {
897
870
  timeoutMs?: number;
898
- responseType?: XMLHttpRequestResponseType;
899
871
  }
900
872
  ) => Promise<Response>;
901
873
 
@@ -1106,7 +1078,6 @@ export interface MobileFileDownloader {
1106
1078
 
1107
1079
  export interface DebugLogger {
1108
1080
  log(message: string): void;
1109
- warn(message: string): void;
1110
1081
  error(message: string): void;
1111
1082
  success(message: string): void;
1112
1083
  }