@sparkvault/sdk-mobile 7.1.11 → 7.1.13
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 +2 -1
- package/dist/account.d.ts +0 -5
- package/dist/account.js +0 -9
- package/dist/account.js.map +1 -1
- package/dist/account.test-d.d.ts +26 -0
- package/dist/account.test-d.js +17 -0
- package/dist/account.test-d.js.map +1 -0
- package/dist/errors.d.ts +0 -3
- package/dist/errors.js +0 -7
- package/dist/errors.js.map +1 -1
- package/dist/http.d.ts +7 -13
- package/dist/http.js +7 -41
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/ingots.d.ts +17 -8
- package/dist/ingots.js +48 -26
- package/dist/ingots.js.map +1 -1
- package/dist/ingots.test-d.d.ts +11 -6
- package/dist/ingots.test-d.js +7 -4
- package/dist/ingots.test-d.js.map +1 -1
- package/dist/products/identity.d.ts +8 -5
- package/dist/products/identity.js +12 -29
- package/dist/products/identity.js.map +1 -1
- package/dist/sparks.d.ts +1 -2
- package/dist/sparks.js +0 -1
- package/dist/sparks.js.map +1 -1
- package/dist/tus.d.ts +0 -1
- package/dist/tus.js +1 -5
- package/dist/tus.js.map +1 -1
- package/dist/types.d.ts +27 -52
- package/dist/vaults.d.ts +1 -4
- package/dist/vaults.js +3 -3
- package/dist/vaults.js.map +1 -1
- package/dist/vaults.test-d.d.ts +7 -4
- package/dist/vaults.test-d.js +4 -2
- package/dist/vaults.test-d.js.map +1 -1
- package/package.json +1 -1
- package/src/account.test-d.ts +39 -0
- package/src/account.ts +0 -14
- package/src/errors.ts +0 -8
- package/src/http.ts +8 -50
- package/src/index.ts +0 -1
- package/src/ingots.test-d.ts +28 -5
- package/src/ingots.ts +56 -42
- package/src/products/identity.ts +16 -46
- package/src/sparks.ts +1 -3
- package/src/tus.ts +1 -6
- package/src/types.ts +27 -56
- package/src/vaults.test-d.ts +6 -3
- package/src/vaults.ts +4 -8
package/dist/types.d.ts
CHANGED
|
@@ -42,14 +42,8 @@ export interface ApiErrorBody {
|
|
|
42
42
|
details?: Record<string, unknown>;
|
|
43
43
|
};
|
|
44
44
|
message?: string;
|
|
45
|
-
data?: unknown;
|
|
46
45
|
meta?: ApiMeta;
|
|
47
46
|
}
|
|
48
|
-
export interface RateLimitInfo {
|
|
49
|
-
limit: number;
|
|
50
|
-
remaining: number;
|
|
51
|
-
reset: number;
|
|
52
|
-
}
|
|
53
47
|
/** Account-scoped role. Owners and admins manage the organization and team. */
|
|
54
48
|
export type OrgRole = 'owner' | 'admin' | 'user';
|
|
55
49
|
export interface SparkVaultUser {
|
|
@@ -248,7 +242,6 @@ export interface IdentityBranding {
|
|
|
248
242
|
companyName: string | null;
|
|
249
243
|
logoLight: string | null;
|
|
250
244
|
logoDark: string | null;
|
|
251
|
-
backdrop_blur?: boolean;
|
|
252
245
|
themeMode?: 'light' | 'dark';
|
|
253
246
|
}
|
|
254
247
|
export interface IdentityConfig {
|
|
@@ -337,44 +330,32 @@ export interface VerifyOtpRequest {
|
|
|
337
330
|
pin: string;
|
|
338
331
|
recipient: string;
|
|
339
332
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
organization_name?: string;
|
|
343
|
-
}
|
|
344
|
-
export interface RequestPinResponse {
|
|
333
|
+
/** A code sent: the kindling its verify names, and when the code expires (Unix seconds). */
|
|
334
|
+
export interface SendOtpResponse {
|
|
345
335
|
kindling: string;
|
|
346
336
|
expires_at: number;
|
|
347
|
-
retry_after?: number;
|
|
348
|
-
is_signup: boolean;
|
|
349
337
|
}
|
|
350
338
|
export interface PinVerifyLoginResponse {
|
|
351
339
|
access_token: string;
|
|
352
340
|
refresh_token: string;
|
|
353
|
-
token_type: 'Bearer';
|
|
354
|
-
expires_in: number;
|
|
355
341
|
user: SparkVaultUser;
|
|
356
342
|
}
|
|
343
|
+
/**
|
|
344
|
+
* A verified identity with no SparkVault user yet: `signup_token` (an
|
|
345
|
+
* identity-only access token) authorizes `account.completeSignup`.
|
|
346
|
+
*/
|
|
357
347
|
export interface PinVerifySignupPendingResponse {
|
|
358
348
|
verified: true;
|
|
359
349
|
needs_signup_info: true;
|
|
360
350
|
signup_token: string;
|
|
361
|
-
expires_at: number;
|
|
362
351
|
}
|
|
363
352
|
/**
|
|
364
|
-
*
|
|
365
|
-
* the
|
|
366
|
-
* `
|
|
367
|
-
*
|
|
353
|
+
* The Core exchange of a verified Identity token. A second factor is collected
|
|
354
|
+
* at the Identity Product before its token exists (`products.identity.verifyOtp`
|
|
355
|
+
* throws for it, and `submitSecondFactor` finishes it), so the exchange is a
|
|
356
|
+
* login or a pending signup, never a second-factor challenge.
|
|
368
357
|
*/
|
|
369
|
-
export
|
|
370
|
-
verified: false;
|
|
371
|
-
second_factor_required: true;
|
|
372
|
-
/** Opaque pending-login handle to submit the second factor against. */
|
|
373
|
-
ticket: string;
|
|
374
|
-
/** Methods the user may use, e.g. ['authenticator','recovery_code']. */
|
|
375
|
-
methods: string[];
|
|
376
|
-
}
|
|
377
|
-
export type PinVerifyResponse = PinVerifyLoginResponse | PinVerifySignupPendingResponse | PinVerifySecondFactorPendingResponse;
|
|
358
|
+
export type PinVerifyResponse = PinVerifyLoginResponse | PinVerifySignupPendingResponse;
|
|
378
359
|
export interface SubmitSecondFactorRequest {
|
|
379
360
|
/** Pending-login ticket from a second_factor_required response. */
|
|
380
361
|
ticket: string;
|
|
@@ -388,8 +369,6 @@ export interface CompleteSignupRequest {
|
|
|
388
369
|
export interface CompleteSignupResponse {
|
|
389
370
|
access_token: string;
|
|
390
371
|
refresh_token: string;
|
|
391
|
-
token_type?: 'Bearer';
|
|
392
|
-
expires_in?: number;
|
|
393
372
|
user: SparkVaultUser;
|
|
394
373
|
account: SparkVaultAccount;
|
|
395
374
|
}
|
|
@@ -398,10 +377,14 @@ export interface PasskeyAuthOptions {
|
|
|
398
377
|
challenge: string;
|
|
399
378
|
timeout: number;
|
|
400
379
|
rpId: string;
|
|
380
|
+
/**
|
|
381
|
+
* The person's credentials when the ceremony is scoped to an identifier:
|
|
382
|
+
* ids only, since a transport list would hand an unauthenticated probe a
|
|
383
|
+
* device fingerprint.
|
|
384
|
+
*/
|
|
401
385
|
allowCredentials?: Array<{
|
|
402
386
|
id: string;
|
|
403
387
|
type: 'public-key';
|
|
404
|
-
transports?: string[];
|
|
405
388
|
}>;
|
|
406
389
|
userVerification: 'required' | 'preferred' | 'discouraged';
|
|
407
390
|
}
|
|
@@ -462,14 +445,14 @@ export interface PasskeyCredential {
|
|
|
462
445
|
}
|
|
463
446
|
export interface PasskeyStatus {
|
|
464
447
|
has_passkey: boolean;
|
|
465
|
-
registered_at?: number;
|
|
466
|
-
last_used_at?: number;
|
|
467
448
|
}
|
|
449
|
+
/**
|
|
450
|
+
* POST /auth/identity/verify: a session for the verified identity's member, or,
|
|
451
|
+
* with `user` and `account` null, an identity-only token for a signup.
|
|
452
|
+
*/
|
|
468
453
|
export interface IdentitySessionResponse {
|
|
469
454
|
access_token: string;
|
|
470
455
|
refresh_token: string;
|
|
471
|
-
token_type?: 'Bearer';
|
|
472
|
-
expires_in?: number;
|
|
473
456
|
user: SparkVaultUser | null;
|
|
474
457
|
account: SparkVaultAccount | null;
|
|
475
458
|
}
|
|
@@ -485,17 +468,15 @@ export interface Vault {
|
|
|
485
468
|
last_unsealed_at?: number;
|
|
486
469
|
unseal_count?: number;
|
|
487
470
|
}
|
|
471
|
+
/** A vault is created with its name alone; a description is set with `update`. */
|
|
488
472
|
export interface CreateVaultRequest {
|
|
489
473
|
name: string;
|
|
490
|
-
description?: string;
|
|
491
474
|
}
|
|
492
475
|
export interface CreateVaultResponse {
|
|
493
476
|
vault_id: string;
|
|
494
477
|
name: string;
|
|
495
|
-
description?: string;
|
|
496
478
|
vmk: string;
|
|
497
479
|
vmk_warning: string;
|
|
498
|
-
status: 'sealed';
|
|
499
480
|
created_at: number;
|
|
500
481
|
}
|
|
501
482
|
export interface UpdateVaultRequest {
|
|
@@ -616,7 +597,6 @@ export interface Ingot {
|
|
|
616
597
|
name: string;
|
|
617
598
|
content_type: string;
|
|
618
599
|
size_bytes: number;
|
|
619
|
-
storage_location: 'dynamodb' | 's3';
|
|
620
600
|
/** `uploading` until Forge activates the upload, then `active`. */
|
|
621
601
|
status?: 'uploading' | 'active';
|
|
622
602
|
folder_id?: string | null;
|
|
@@ -628,10 +608,7 @@ export interface Ingot {
|
|
|
628
608
|
*/
|
|
629
609
|
managed_by?: string | null;
|
|
630
610
|
created_at: number;
|
|
631
|
-
accessed_at?: number;
|
|
632
|
-
last_accessed_at?: number;
|
|
633
611
|
access_count?: number;
|
|
634
|
-
expires_at?: number;
|
|
635
612
|
/**
|
|
636
613
|
* Client-computed, vault-keyed HMAC of the plaintext (43-char base64url),
|
|
637
614
|
* or null when the uploader did not supply one. Used for upload dedup and
|
|
@@ -649,9 +626,14 @@ export interface HashCheckMatch {
|
|
|
649
626
|
export interface HashCheckResponse {
|
|
650
627
|
found: HashCheckMatch[];
|
|
651
628
|
}
|
|
629
|
+
/**
|
|
630
|
+
* One page of GET /vaults/:id/ingots or /ingots/search. `count` is this
|
|
631
|
+
* page's length: the server reports no total.
|
|
632
|
+
*/
|
|
652
633
|
export interface ListIngotsResponse {
|
|
653
634
|
ingots: Ingot[];
|
|
654
|
-
|
|
635
|
+
count: number;
|
|
636
|
+
has_more: boolean;
|
|
655
637
|
next_cursor?: string;
|
|
656
638
|
}
|
|
657
639
|
/**
|
|
@@ -680,7 +662,6 @@ export interface UploadResult {
|
|
|
680
662
|
*/
|
|
681
663
|
name: string;
|
|
682
664
|
size_bytes: number;
|
|
683
|
-
storage_location: 's3';
|
|
684
665
|
/**
|
|
685
666
|
* Whether this upload minted the ingot (true) or went into an existing row
|
|
686
667
|
* (false): the one holding the name, as the create's name-conflict policy
|
|
@@ -756,8 +737,6 @@ export interface IngotAccessLog {
|
|
|
756
737
|
}
|
|
757
738
|
export interface IngotAccessStats {
|
|
758
739
|
total_downloads: number;
|
|
759
|
-
unique_visitors?: number;
|
|
760
|
-
last_accessed_at?: number;
|
|
761
740
|
}
|
|
762
741
|
/** A folder as every folder route answers it (Core's FolderRepository.toPublicFormat). */
|
|
763
742
|
export interface Folder {
|
|
@@ -794,13 +773,10 @@ export interface TokenStorageAdapter {
|
|
|
794
773
|
clearTokens(): Promise<void>;
|
|
795
774
|
getUser?<T = SparkVaultUser>(): Promise<T | null>;
|
|
796
775
|
setUser?<T = SparkVaultUser>(user: T): Promise<void>;
|
|
797
|
-
getAccount?<T = SparkVaultAccount>(): Promise<T | null>;
|
|
798
776
|
setAccount?<T = SparkVaultAccount>(account: T): Promise<void>;
|
|
799
|
-
clearAll?(): Promise<void>;
|
|
800
777
|
}
|
|
801
778
|
export type FetchLike = (url: string, options?: RequestInit & {
|
|
802
779
|
timeoutMs?: number;
|
|
803
|
-
responseType?: XMLHttpRequestResponseType;
|
|
804
780
|
}) => Promise<Response>;
|
|
805
781
|
export interface SparkVaultLogger {
|
|
806
782
|
debug(message: string, context?: Record<string, unknown>): void;
|
|
@@ -998,7 +974,6 @@ export interface MobileFileDownloader {
|
|
|
998
974
|
}
|
|
999
975
|
export interface DebugLogger {
|
|
1000
976
|
log(message: string): void;
|
|
1001
|
-
warn(message: string): void;
|
|
1002
977
|
error(message: string): void;
|
|
1003
978
|
success(message: string): void;
|
|
1004
979
|
}
|
package/dist/vaults.d.ts
CHANGED
|
@@ -5,7 +5,6 @@ export declare class MobileVaultsClient {
|
|
|
5
5
|
constructor(http: MobileHttpClient);
|
|
6
6
|
create(request: CreateVaultRequest): Promise<CreateVaultResponse>;
|
|
7
7
|
list(options?: {
|
|
8
|
-
status?: 'sealed' | 'unsealed';
|
|
9
8
|
limit?: number;
|
|
10
9
|
cursor?: string;
|
|
11
10
|
}): Promise<ListVaultsResponse>;
|
|
@@ -15,9 +14,7 @@ export declare class MobileVaultsClient {
|
|
|
15
14
|
* filtered down to nothing still carries a cursor. A failed page fails the
|
|
16
15
|
* listing: a vault picker must never show a silently partial account.
|
|
17
16
|
*/
|
|
18
|
-
listAll(
|
|
19
|
-
status?: 'sealed' | 'unsealed';
|
|
20
|
-
}): Promise<Vault[]>;
|
|
17
|
+
listAll(): Promise<Vault[]>;
|
|
21
18
|
get(vaultId: string): Promise<Vault>;
|
|
22
19
|
update(vaultId: string, request: UpdateVaultRequest): Promise<UpdateVaultResponse>;
|
|
23
20
|
/**
|
package/dist/vaults.js
CHANGED
|
@@ -13,7 +13,7 @@ export class MobileVaultsClient {
|
|
|
13
13
|
}
|
|
14
14
|
async list(options) {
|
|
15
15
|
const response = await this.http.get(apiPath `/vaults`, {
|
|
16
|
-
query: {
|
|
16
|
+
query: { limit: options?.limit || undefined, cursor: options?.cursor },
|
|
17
17
|
});
|
|
18
18
|
return response.data;
|
|
19
19
|
}
|
|
@@ -23,11 +23,11 @@ export class MobileVaultsClient {
|
|
|
23
23
|
* filtered down to nothing still carries a cursor. A failed page fails the
|
|
24
24
|
* listing: a vault picker must never show a silently partial account.
|
|
25
25
|
*/
|
|
26
|
-
async listAll(
|
|
26
|
+
async listAll() {
|
|
27
27
|
const vaults = [];
|
|
28
28
|
let cursor;
|
|
29
29
|
do {
|
|
30
|
-
const page = await this.list({
|
|
30
|
+
const page = await this.list({ limit: VAULT_LIST_PAGE_LIMIT, cursor });
|
|
31
31
|
vaults.push(...page.vaults);
|
|
32
32
|
cursor = page.has_more ? page.next_cursor : undefined;
|
|
33
33
|
} while (cursor);
|
package/dist/vaults.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vaults.js","sourceRoot":"","sources":["../src/vaults.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C,uEAAuE;AACvE,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAElC,MAAM,OAAO,kBAAkB;IAG7B,YAAY,IAAsB;QAChC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAA2B;QACtC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAsB,OAAO,CAAA,SAAS,EAAE,OAAO,CAAC,CAAC;QACtF,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"vaults.js","sourceRoot":"","sources":["../src/vaults.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C,uEAAuE;AACvE,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAElC,MAAM,OAAO,kBAAkB;IAG7B,YAAY,IAAsB;QAChC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAA2B;QACtC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAsB,OAAO,CAAA,SAAS,EAAE,OAAO,CAAC,CAAC;QACtF,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAA6C;QACtD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAqB,OAAO,CAAA,SAAS,EAAE;YACzE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE;SACvE,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,MAAM,GAAY,EAAE,CAAC;QAC3B,IAAI,MAA0B,CAAC;QAC/B,GAAG,CAAC;YACF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,MAAM,EAAE,CAAC,CAAC;YACvE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5B,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;QACxD,CAAC,QAAQ,MAAM,EAAE;QACjB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,OAAe;QACvB,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAQ,OAAO,CAAA,WAAW,OAAO,EAAE,CAAC,CAAC;QACzE,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAe,EAAE,OAA2B;QACvD,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAsB,OAAO,CAAA,WAAW,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;QAChG,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,MAAM,CAAC,OAAe,EAAE,IAAY;QACxC,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAA,WAAW,OAAO,EAAE,EAAE;YAClD,OAAO,EAAE,EAAE,oBAAoB,EAAE,gBAAgB,CAAC,IAAI,CAAC,EAAE;SAC1D,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAe,EAAE,OAA2B;QACvD,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAsB,OAAO,CAAA,WAAW,OAAO,SAAS,EAAE,OAAO,CAAC,CAAC;QACxG,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,IAAI,CAAC,OAAe;QACxB,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAA,WAAW,OAAO,OAAO,CAAC,CAAC;IACzD,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,UAAU,CAAC,OAAe,EAAE,GAAW;QAC3C,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAA,WAAW,OAAO,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,OAAe;QACpC,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAqB,OAAO,CAAA,WAAW,OAAO,UAAU,CAAC,CAAC;QAC9F,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,aAAa,CACjB,OAAe,EACf,GAAW,EACX,OAA6B;QAE7B,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAA6B,OAAO,CAAA,WAAW,OAAO,iBAAiB,EAAE;YAC5G,GAAG;YACH,iBAAiB,EAAE,OAAO,EAAE,iBAAiB;YAC7C,sBAAsB,EAAE,OAAO,EAAE,sBAAsB;SACxD,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,OAAe;QAClC,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAA8B,OAAO,CAAA,WAAW,OAAO,kBAAkB,CAAC,CAAC;QAChH,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,mBAAmB,CACvB,OAAe,EACf,MAA2B,EAC3B,GAAY;QAEZ,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAA6B,OAAO,CAAA,WAAW,OAAO,UAAU,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACvH,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,OAAe;QACzC,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAqB,OAAO,CAAA,WAAW,OAAO,uBAAuB,CAAC,CAAC;QAC3G,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,wBAAwB,CAC5B,OAAe,EACf,gBAAwB;QAExB,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAmC,OAAO,CAAA,WAAW,OAAO,uBAAuB,EAAE;YACvH,4BAA4B,EAAE,gBAAgB;SAC/C,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,OAAe;QACnC,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAe,OAAO,CAAA,WAAW,OAAO,SAAS,CAAC,CAAC;QACvF,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,OAAe,EAAE,GAAW;QACnD,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAsB,OAAO,CAAA,WAAW,OAAO,uBAAuB,EAAE;YAC3G,GAAG;SACJ,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,OAAe;QACvC,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAuB,OAAO,CAAA,WAAW,OAAO,wBAAwB,CAAC,CAAC;QAC/G,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,OAAe,EAAE,GAAW;QACnD,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAsB,OAAO,CAAA,WAAW,OAAO,uBAAuB,EAAE;YAC3G,GAAG;SACJ,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,OAAe;QACvC,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAuB,OAAO,CAAA,WAAW,OAAO,wBAAwB,CAAC,CAAC;QAC/G,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,kBAAkB,CACtB,OAAe,EACf,MAGC;QAED,eAAe,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAqB,OAAO,CAAA,WAAW,OAAO,SAAS,EAAE,MAAM,CAAC,CAAC;QACrG,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;CACF"}
|
package/dist/vaults.test-d.d.ts
CHANGED
|
@@ -7,16 +7,19 @@
|
|
|
7
7
|
* `*.test.ts`, which tsconfig excludes.
|
|
8
8
|
*
|
|
9
9
|
* Mirrored server sources: apps/api/src/folders/list.js (listFolders answers
|
|
10
|
-
* `{ folders }`, the whole list and nothing else)
|
|
11
|
-
* (one page: `{ vaults, count, has_more, next_cursor? }`)
|
|
10
|
+
* `{ folders }`, the whole list and nothing else), apps/api/src/vaults/list.js
|
|
11
|
+
* (one page: `{ vaults, count, has_more, next_cursor? }`) and
|
|
12
|
+
* apps/api/src/vaults/create.js (createVault answers `{ vault_id, name, vmk,
|
|
13
|
+
* vmk_warning, created_at }`).
|
|
12
14
|
*/
|
|
13
|
-
import type { ListFoldersResponse, ListVaultsResponse } from './types.js';
|
|
15
|
+
import type { CreateVaultResponse, ListFoldersResponse, ListVaultsResponse } from './types.js';
|
|
14
16
|
/** Fails to compile unless `T` resolves to exactly `true`. */
|
|
15
17
|
type Assert<T extends true> = T;
|
|
16
18
|
/** Invariant type equality: the SAME type, not merely mutually assignable. */
|
|
17
19
|
type Equals<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;
|
|
18
20
|
export type VaultsWireContract = [
|
|
19
21
|
Assert<Equals<keyof ListFoldersResponse, 'folders'>>,
|
|
20
|
-
Assert<Equals<keyof ListVaultsResponse, 'vaults' | 'count' | 'has_more' | 'next_cursor'
|
|
22
|
+
Assert<Equals<keyof ListVaultsResponse, 'vaults' | 'count' | 'has_more' | 'next_cursor'>>,
|
|
23
|
+
Assert<Equals<keyof CreateVaultResponse, 'vault_id' | 'name' | 'vmk' | 'vmk_warning' | 'created_at'>>
|
|
21
24
|
];
|
|
22
25
|
export {};
|
package/dist/vaults.test-d.js
CHANGED
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* `*.test.ts`, which tsconfig excludes.
|
|
8
8
|
*
|
|
9
9
|
* Mirrored server sources: apps/api/src/folders/list.js (listFolders answers
|
|
10
|
-
* `{ folders }`, the whole list and nothing else)
|
|
11
|
-
* (one page: `{ vaults, count, has_more, next_cursor? }`)
|
|
10
|
+
* `{ folders }`, the whole list and nothing else), apps/api/src/vaults/list.js
|
|
11
|
+
* (one page: `{ vaults, count, has_more, next_cursor? }`) and
|
|
12
|
+
* apps/api/src/vaults/create.js (createVault answers `{ vault_id, name, vmk,
|
|
13
|
+
* vmk_warning, created_at }`).
|
|
12
14
|
*/
|
|
13
15
|
export {};
|
|
14
16
|
//# sourceMappingURL=vaults.test-d.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vaults.test-d.js","sourceRoot":"","sources":["../src/vaults.test-d.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"vaults.test-d.js","sourceRoot":"","sources":["../src/vaults.test-d.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core session wire-contract assertions — COMPILE-TIME ONLY.
|
|
3
|
+
*
|
|
4
|
+
* These types have no runtime, so a unit test can never catch them drifting from
|
|
5
|
+
* the server: the transpiler strips them and every assertion passes. `tsc
|
|
6
|
+
* --noEmit` (`pnpm typecheck`) is the gate instead. It is deliberately NOT a
|
|
7
|
+
* `*.test.ts`, which tsconfig excludes.
|
|
8
|
+
*
|
|
9
|
+
* Mirrored server sources: apps/api/src/auth/auth-with-identity.js
|
|
10
|
+
* (handleIdentityVerify answers `{ access_token, refresh_token, user, account }`,
|
|
11
|
+
* with `user` and `account` null for an identity that has no member yet) and
|
|
12
|
+
* apps/api/src/auth/complete-signup.js (the same four fields). Neither names a
|
|
13
|
+
* token type or lifetime: a field the server never sends reads as undefined
|
|
14
|
+
* wherever an app trusts it.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
CompleteSignupResponse,
|
|
19
|
+
IdentitySessionResponse,
|
|
20
|
+
PinVerifyLoginResponse,
|
|
21
|
+
PinVerifyResponse,
|
|
22
|
+
PinVerifySignupPendingResponse,
|
|
23
|
+
} from './types.js';
|
|
24
|
+
|
|
25
|
+
/** Fails to compile unless `T` resolves to exactly `true`. */
|
|
26
|
+
type Assert<T extends true> = T;
|
|
27
|
+
|
|
28
|
+
/** Invariant type equality: the SAME type, not merely mutually assignable. */
|
|
29
|
+
type Equals<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2
|
|
30
|
+
? true
|
|
31
|
+
: false;
|
|
32
|
+
|
|
33
|
+
export type AccountWireContract = [
|
|
34
|
+
Assert<Equals<keyof IdentitySessionResponse, 'access_token' | 'refresh_token' | 'user' | 'account'>>,
|
|
35
|
+
Assert<Equals<keyof CompleteSignupResponse, 'access_token' | 'refresh_token' | 'user' | 'account'>>,
|
|
36
|
+
// The exchange is a login or a pending signup: a second factor is collected
|
|
37
|
+
// at the Identity Product before its token exists.
|
|
38
|
+
Assert<Equals<PinVerifyResponse, PinVerifyLoginResponse | PinVerifySignupPendingResponse>>,
|
|
39
|
+
];
|
package/src/account.ts
CHANGED
|
@@ -68,8 +68,6 @@ export class MobileAccountClient {
|
|
|
68
68
|
return {
|
|
69
69
|
access_token: session.access_token,
|
|
70
70
|
refresh_token: session.refresh_token,
|
|
71
|
-
token_type: session.token_type ?? 'Bearer',
|
|
72
|
-
expires_in: session.expires_in ?? 3600,
|
|
73
71
|
user: session.user,
|
|
74
72
|
};
|
|
75
73
|
}
|
|
@@ -78,7 +76,6 @@ export class MobileAccountClient {
|
|
|
78
76
|
verified: true,
|
|
79
77
|
needs_signup_info: true,
|
|
80
78
|
signup_token: session.access_token,
|
|
81
|
-
expires_at: Math.floor(Date.now() / 1000) + (session.expires_in ?? 3600),
|
|
82
79
|
};
|
|
83
80
|
}
|
|
84
81
|
|
|
@@ -99,17 +96,6 @@ export class MobileAccountClient {
|
|
|
99
96
|
return response.data;
|
|
100
97
|
}
|
|
101
98
|
|
|
102
|
-
async signup(params: {
|
|
103
|
-
signup_token: string;
|
|
104
|
-
organization_name: string;
|
|
105
|
-
full_name?: string;
|
|
106
|
-
}): Promise<CompleteSignupResponse> {
|
|
107
|
-
return this.completeSignup(params.signup_token, {
|
|
108
|
-
organization_name: params.organization_name,
|
|
109
|
-
full_name: params.full_name,
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
99
|
async getProfile(): Promise<SparkVaultUser> {
|
|
114
100
|
const response = await this.http.get<SparkVaultUser>(apiPath`/profile`);
|
|
115
101
|
if (this.config.tokenStorage.setUser) {
|
package/src/errors.ts
CHANGED
|
@@ -51,14 +51,6 @@ export class SparkVaultAuthenticationError extends SparkVaultMobileError {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
export class SparkVaultAuthorizationError extends SparkVaultMobileError {
|
|
55
|
-
constructor(message = 'Forbidden', details?: Record<string, unknown>) {
|
|
56
|
-
super(message, { code: 'authorization_error', statusCode: 403, details });
|
|
57
|
-
this.name = 'SparkVaultAuthorizationError';
|
|
58
|
-
Object.setPrototypeOf(this, SparkVaultAuthorizationError.prototype);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
54
|
/**
|
|
63
55
|
* Thrown on a 402 PLAN_REQUIRED gate: the account has no active subscription
|
|
64
56
|
* and attempted a billable action. Callers route the user into the in-app
|
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
|
|
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 =
|
|
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
package/src/ingots.test-d.ts
CHANGED
|
@@ -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
|
|
10
|
-
* the one shape GET and PUT /vaults/:id/ingots/:ingot_id/sharing answer)
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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
|
];
|