@sparkvault/sdk-mobile 6.0.2 → 7.0.1
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/transfer-retry.d.ts +298 -0
- package/dist/app-sdk/transfer-retry.js +535 -0
- package/dist/app-sdk/transfer-retry.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/upload-publication.d.ts +81 -0
- package/dist/app-sdk/upload-publication.js +93 -0
- package/dist/app-sdk/upload-publication.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 +33 -1
- package/dist/config.js +23 -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 +105 -21
- package/dist/errors.js +74 -28
- 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 +8 -7
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/ingots.d.ts +77 -32
- package/dist/ingots.js +152 -136
- 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 +198 -16
- package/dist/tus.js +816 -327
- package/dist/tus.js.map +1 -1
- package/dist/types.d.ts +183 -23
- 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/transfer-retry.js +563 -0
- package/src/app-sdk/transient.js +30 -0
- package/src/app-sdk/upload-publication.js +102 -0
- package/src/app-sdk/vat-refusal.js +74 -0
- package/src/billing.ts +16 -25
- package/src/config.ts +56 -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 +135 -38
- 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 +13 -3
- package/src/ingots.ts +225 -180
- 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 +1054 -408
- package/src/types.ts +181 -23
- 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/dist/encoding.js
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Base64 and UTF-8 for the mobile SDK.
|
|
3
|
+
*
|
|
4
|
+
* The base64 codec is the platform's one copy, mirrored from
|
|
5
|
+
* `@sparkvault/app-sdk/encoding` into `./app-sdk/encoding.js`; the decoders here
|
|
6
|
+
* raise this SDK's validation error where that codec refuses a text. UTF-8 is
|
|
7
|
+
* converted here rather than with TextEncoder/TextDecoder, which the JavaScript
|
|
8
|
+
* engines of the React Native versions this SDK supports need not provide.
|
|
9
|
+
* Decoding UTF-8 is fatal: bytes that are not UTF-8 are refused, never read as
|
|
10
|
+
* invented characters.
|
|
11
|
+
*/
|
|
12
|
+
import { base64ToBytes as decodeBase64, base64urlToBytes as decodeBase64url, bytesToBase64, bytesToBase64url, } from './app-sdk/encoding.js';
|
|
13
|
+
import { SparkVaultValidationError } from './errors.js';
|
|
14
|
+
export { bytesToBase64, bytesToBase64url };
|
|
15
|
+
/**
|
|
16
|
+
* Decode standard base64.
|
|
17
|
+
* @throws SparkVaultValidationError when the text is not base64
|
|
18
|
+
*/
|
|
19
|
+
export function base64ToBytes(value) {
|
|
20
|
+
const bytes = decodeBase64(value);
|
|
21
|
+
if (bytes === null) {
|
|
22
|
+
throw new SparkVaultValidationError('Invalid base64 input');
|
|
23
|
+
}
|
|
24
|
+
return bytes;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Decode base64url.
|
|
28
|
+
* @throws SparkVaultValidationError when the text is not base64url
|
|
29
|
+
*/
|
|
30
|
+
export function base64UrlToBytes(value) {
|
|
31
|
+
const bytes = decodeBase64url(value);
|
|
32
|
+
if (bytes === null) {
|
|
33
|
+
throw new SparkVaultValidationError('Invalid base64url input');
|
|
34
|
+
}
|
|
35
|
+
return bytes;
|
|
36
|
+
}
|
|
3
37
|
export function utf8ToBytes(value) {
|
|
4
38
|
const bytes = [];
|
|
5
39
|
for (let i = 0; i < value.length; i++) {
|
|
@@ -26,98 +60,70 @@ export function utf8ToBytes(value) {
|
|
|
26
60
|
}
|
|
27
61
|
return new Uint8Array(bytes);
|
|
28
62
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Decode UTF-8 by the WHATWG decoder's rules in fatal mode: a byte that cannot
|
|
65
|
+
* start a sequence, a sequence cut short, an overlong form, a surrogate or a
|
|
66
|
+
* code point past U+10FFFF refuses the whole input.
|
|
67
|
+
* @throws SparkVaultValidationError when the bytes are not UTF-8
|
|
68
|
+
*/
|
|
32
69
|
export function bytesToUtf8(bytes) {
|
|
33
70
|
let output = '';
|
|
34
|
-
let
|
|
35
|
-
while (
|
|
36
|
-
const
|
|
37
|
-
if (
|
|
38
|
-
output += String.fromCharCode(
|
|
71
|
+
let index = 0;
|
|
72
|
+
while (index < bytes.length) {
|
|
73
|
+
const lead = bytes[index++];
|
|
74
|
+
if (lead < 0x80) {
|
|
75
|
+
output += String.fromCharCode(lead);
|
|
39
76
|
continue;
|
|
40
77
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
78
|
+
// The first continuation byte's range is what excludes overlong forms,
|
|
79
|
+
// surrogates and code points past U+10FFFF.
|
|
80
|
+
let continuations;
|
|
81
|
+
let codePoint;
|
|
82
|
+
let lower = 0x80;
|
|
83
|
+
let upper = 0xbf;
|
|
84
|
+
if (lead >= 0xc2 && lead <= 0xdf) {
|
|
85
|
+
continuations = 1;
|
|
86
|
+
codePoint = lead & 0x1f;
|
|
45
87
|
}
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
88
|
+
else if (lead >= 0xe0 && lead <= 0xef) {
|
|
89
|
+
continuations = 2;
|
|
90
|
+
codePoint = lead & 0x0f;
|
|
91
|
+
if (lead === 0xe0)
|
|
92
|
+
lower = 0xa0;
|
|
93
|
+
if (lead === 0xed)
|
|
94
|
+
upper = 0x9f;
|
|
53
95
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
(
|
|
60
|
-
|
|
61
|
-
|
|
96
|
+
else if (lead >= 0xf0 && lead <= 0xf4) {
|
|
97
|
+
continuations = 3;
|
|
98
|
+
codePoint = lead & 0x07;
|
|
99
|
+
if (lead === 0xf0)
|
|
100
|
+
lower = 0x90;
|
|
101
|
+
if (lead === 0xf4)
|
|
102
|
+
upper = 0x8f;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
throw new SparkVaultValidationError('Invalid UTF-8 input');
|
|
106
|
+
}
|
|
107
|
+
for (let seen = 0; seen < continuations; seen++) {
|
|
108
|
+
if (index >= bytes.length) {
|
|
109
|
+
throw new SparkVaultValidationError('Invalid UTF-8 input');
|
|
110
|
+
}
|
|
111
|
+
const next = bytes[index++];
|
|
112
|
+
if (next < lower || next > upper) {
|
|
113
|
+
throw new SparkVaultValidationError('Invalid UTF-8 input');
|
|
114
|
+
}
|
|
115
|
+
lower = 0x80;
|
|
116
|
+
upper = 0xbf;
|
|
117
|
+
codePoint = (codePoint << 6) | (next & 0x3f);
|
|
118
|
+
}
|
|
119
|
+
output += String.fromCodePoint(codePoint);
|
|
62
120
|
}
|
|
63
121
|
return output;
|
|
64
122
|
}
|
|
65
|
-
export function
|
|
66
|
-
return
|
|
123
|
+
export function base64EncodeUtf8(value) {
|
|
124
|
+
return bytesToBase64(utf8ToBytes(value));
|
|
67
125
|
}
|
|
68
126
|
export function base64UrlToUtf8(value) {
|
|
69
127
|
return bytesToUtf8(base64UrlToBytes(value));
|
|
70
128
|
}
|
|
71
|
-
export function bytesToBase64(bytes) {
|
|
72
|
-
let output = '';
|
|
73
|
-
for (let i = 0; i < bytes.length; i += 3) {
|
|
74
|
-
const byte1 = bytes[i];
|
|
75
|
-
const byte2 = bytes[i + 1] ?? 0;
|
|
76
|
-
const byte3 = bytes[i + 2] ?? 0;
|
|
77
|
-
const triplet = (byte1 << 16) | (byte2 << 8) | byte3;
|
|
78
|
-
output += BASE64_ALPHABET[(triplet >> 18) & 0x3f];
|
|
79
|
-
output += BASE64_ALPHABET[(triplet >> 12) & 0x3f];
|
|
80
|
-
output += i + 1 < bytes.length ? BASE64_ALPHABET[(triplet >> 6) & 0x3f] : '=';
|
|
81
|
-
output += i + 2 < bytes.length ? BASE64_ALPHABET[triplet & 0x3f] : '=';
|
|
82
|
-
}
|
|
83
|
-
return output;
|
|
84
|
-
}
|
|
85
|
-
export function base64ToBytes(value) {
|
|
86
|
-
const sanitized = value.replace(/\s/g, '').replace(/-/g, '+').replace(/_/g, '/');
|
|
87
|
-
if (sanitized.length % 4 === 1) {
|
|
88
|
-
throw new Error('Invalid base64 input');
|
|
89
|
-
}
|
|
90
|
-
const padded = sanitized.padEnd(Math.ceil(sanitized.length / 4) * 4, '=');
|
|
91
|
-
// Preallocate the exact output size: 3 bytes per quartet, minus one byte per
|
|
92
|
-
// '=' (a '=' in quartet positions 1-2 throws below, so counting every '=' is
|
|
93
|
-
// safe). Multi-megabyte upload chunks decode through here, so per-byte array
|
|
94
|
-
// growth is not acceptable.
|
|
95
|
-
let paddingCount = 0;
|
|
96
|
-
for (let i = 0; i < padded.length; i++) {
|
|
97
|
-
if (padded[i] === '=')
|
|
98
|
-
paddingCount++;
|
|
99
|
-
}
|
|
100
|
-
const bytes = new Uint8Array((padded.length / 4) * 3 - paddingCount);
|
|
101
|
-
let outIndex = 0;
|
|
102
|
-
for (let i = 0; i < padded.length; i += 4) {
|
|
103
|
-
const c1 = padded[i];
|
|
104
|
-
const c2 = padded[i + 1];
|
|
105
|
-
const c3 = padded[i + 2];
|
|
106
|
-
const c4 = padded[i + 3];
|
|
107
|
-
const v1 = BASE64_LOOKUP.get(c1);
|
|
108
|
-
const v2 = BASE64_LOOKUP.get(c2);
|
|
109
|
-
const v3 = c3 === '=' ? 0 : BASE64_LOOKUP.get(c3);
|
|
110
|
-
const v4 = c4 === '=' ? 0 : BASE64_LOOKUP.get(c4);
|
|
111
|
-
if (v1 === undefined || v2 === undefined || v3 === undefined || v4 === undefined) {
|
|
112
|
-
throw new Error('Invalid base64 input');
|
|
113
|
-
}
|
|
114
|
-
const triplet = (v1 << 18) | (v2 << 12) | (v3 << 6) | v4;
|
|
115
|
-
bytes[outIndex++] = (triplet >> 16) & 0xff;
|
|
116
|
-
if (c3 !== '=')
|
|
117
|
-
bytes[outIndex++] = (triplet >> 8) & 0xff;
|
|
118
|
-
if (c4 !== '=')
|
|
119
|
-
bytes[outIndex++] = triplet & 0xff;
|
|
120
|
-
}
|
|
121
|
-
return bytes;
|
|
122
|
-
}
|
|
123
129
|
//# sourceMappingURL=encoding.js.map
|
package/dist/encoding.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encoding.js","sourceRoot":"","sources":["../src/encoding.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"encoding.js","sourceRoot":"","sources":["../src/encoding.ts"],"names":[],"mappings":"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"}
|
package/dist/entropy.d.ts
CHANGED
|
@@ -1,18 +1,39 @@
|
|
|
1
1
|
import type { MobileHttpClient } from './http.js';
|
|
2
|
-
|
|
2
|
+
/** An output format the entropy endpoint serves (the platform's entropy contract). */
|
|
3
|
+
export type EntropyFormat = 'hex' | 'base64' | 'base64url' | 'alphanumeric' | 'alphanumeric-mixed' | 'password' | 'numeric' | 'uuid' | 'bytes';
|
|
3
4
|
export interface GenerateEntropyOptions {
|
|
5
|
+
/** Number of random bytes: 1–1024, or 1–512 for alphanumeric, alphanumeric-mixed and password. */
|
|
4
6
|
bytes: number;
|
|
7
|
+
/** Output format. Defaults to base64. */
|
|
5
8
|
format?: EntropyFormat;
|
|
6
9
|
}
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
/** The endpoint's answer: `value` is a number array for the `bytes` format and a string for every other. */
|
|
11
|
+
export interface EntropyResponse<Value extends string | number[] = string | number[]> {
|
|
12
|
+
value: Value;
|
|
9
13
|
num_bytes: number;
|
|
10
14
|
format?: EntropyFormat;
|
|
15
|
+
reference_id?: string;
|
|
11
16
|
}
|
|
12
17
|
export declare class MobileEntropyClient {
|
|
13
18
|
private readonly http;
|
|
14
19
|
constructor(http: MobileHttpClient);
|
|
20
|
+
generate(options: GenerateEntropyOptions & {
|
|
21
|
+
format: 'bytes';
|
|
22
|
+
}): Promise<EntropyResponse<number[]>>;
|
|
23
|
+
generate(options: GenerateEntropyOptions & {
|
|
24
|
+
format?: Exclude<EntropyFormat, 'bytes'>;
|
|
25
|
+
}): Promise<EntropyResponse<string>>;
|
|
15
26
|
generate(options: GenerateEntropyOptions): Promise<EntropyResponse>;
|
|
16
27
|
generateBytes(bytes: number): Promise<Uint8Array>;
|
|
17
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Random bytes for the signed-in client's own working values (a key's id, a
|
|
30
|
+
* secret's server share), from POST /v1/entropy/first-party: answered to an
|
|
31
|
+
* interactive SparkVault session only, never plan- or bandwidth-gated and
|
|
32
|
+
* never recorded as the account's usage. Values an integration asks for come
|
|
33
|
+
* from `generate` / `generateBytes`.
|
|
34
|
+
*
|
|
35
|
+
* @param bytes - 1 to the entropy contract's ceiling (MAX_DRAW_BYTES)
|
|
36
|
+
* @returns exactly `bytes` bytes
|
|
37
|
+
*/
|
|
38
|
+
drawFirstParty(bytes: number): Promise<Uint8Array>;
|
|
18
39
|
}
|
package/dist/entropy.js
CHANGED
|
@@ -1,35 +1,82 @@
|
|
|
1
|
-
import { base64ToBytes } from './encoding.js';
|
|
1
|
+
import { base64ToBytes } from './app-sdk/encoding.js';
|
|
2
|
+
import { ENTROPY_FORMATS, MAX_DRAW_BYTES, isEntropyFormat, maxBytesFor } from './app-sdk/entropy-contract.js';
|
|
2
3
|
import { SparkVaultMobileError, SparkVaultValidationError } from './errors.js';
|
|
4
|
+
import { apiPath } from './request-path.js';
|
|
3
5
|
export class MobileEntropyClient {
|
|
4
6
|
constructor(http) {
|
|
5
7
|
this.http = http;
|
|
6
8
|
}
|
|
7
9
|
async generate(options) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
const format = options.format ?? 'base64';
|
|
11
|
+
validateEntropyRequest(options.bytes, format);
|
|
12
|
+
const response = await this.http.post(apiPath `/entropy/generate`, {
|
|
10
13
|
num_bytes: options.bytes,
|
|
11
|
-
format
|
|
14
|
+
format,
|
|
12
15
|
});
|
|
13
16
|
return response.data;
|
|
14
17
|
}
|
|
15
18
|
async generateBytes(bytes) {
|
|
16
19
|
const response = await this.generate({ bytes, format: 'base64' });
|
|
17
|
-
|
|
18
|
-
throw new SparkVaultMobileError('Entropy endpoint returned no value');
|
|
19
|
-
}
|
|
20
|
-
const entropy = base64ToBytes(response.value);
|
|
21
|
-
if (entropy.length !== bytes) {
|
|
22
|
-
throw new SparkVaultMobileError(`Entropy length mismatch: expected ${bytes}, got ${entropy.length}`);
|
|
23
|
-
}
|
|
24
|
-
return entropy;
|
|
20
|
+
return drawnBytes(response.value, bytes);
|
|
25
21
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Random bytes for the signed-in client's own working values (a key's id, a
|
|
24
|
+
* secret's server share), from POST /v1/entropy/first-party: answered to an
|
|
25
|
+
* interactive SparkVault session only, never plan- or bandwidth-gated and
|
|
26
|
+
* never recorded as the account's usage. Values an integration asks for come
|
|
27
|
+
* from `generate` / `generateBytes`.
|
|
28
|
+
*
|
|
29
|
+
* @param bytes - 1 to the entropy contract's ceiling (MAX_DRAW_BYTES)
|
|
30
|
+
* @returns exactly `bytes` bytes
|
|
31
|
+
*/
|
|
32
|
+
async drawFirstParty(bytes) {
|
|
33
|
+
if (!Number.isInteger(bytes) || bytes < 1 || bytes > MAX_DRAW_BYTES) {
|
|
34
|
+
throw new SparkVaultValidationError(`Entropy byte count must be an integer from 1 to ${MAX_DRAW_BYTES}`, {
|
|
29
35
|
field: 'bytes',
|
|
30
36
|
received: bytes,
|
|
31
37
|
});
|
|
32
38
|
}
|
|
39
|
+
const response = await this.http.post(apiPath `/entropy/first-party`, {
|
|
40
|
+
num_bytes: bytes,
|
|
41
|
+
});
|
|
42
|
+
return drawnBytes(response.data.value, bytes);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The bytes a draw answered as base64, refused unless they are exactly the
|
|
47
|
+
* bytes asked for, so a malformed answer fails the operation that asked for it.
|
|
48
|
+
*/
|
|
49
|
+
function drawnBytes(value, bytes) {
|
|
50
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
51
|
+
throw new SparkVaultMobileError('Entropy endpoint returned no value');
|
|
52
|
+
}
|
|
53
|
+
const entropy = base64ToBytes(value);
|
|
54
|
+
if (entropy === null) {
|
|
55
|
+
throw new SparkVaultMobileError('Entropy endpoint returned a value that is not base64');
|
|
56
|
+
}
|
|
57
|
+
if (entropy.length !== bytes) {
|
|
58
|
+
throw new SparkVaultMobileError(`Entropy length mismatch: expected ${bytes}, got ${entropy.length}`);
|
|
59
|
+
}
|
|
60
|
+
return entropy;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Refuse a request the endpoint would refuse, before it is sent: the format must
|
|
64
|
+
* be one the endpoint serves, and the byte count an integer from 1 to that
|
|
65
|
+
* format's ceiling.
|
|
66
|
+
*/
|
|
67
|
+
function validateEntropyRequest(bytes, format) {
|
|
68
|
+
if (!isEntropyFormat(format)) {
|
|
69
|
+
throw new SparkVaultValidationError(`Entropy format must be one of: ${ENTROPY_FORMATS.join(', ')}`, {
|
|
70
|
+
field: 'format',
|
|
71
|
+
received: format,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const max = maxBytesFor(format);
|
|
75
|
+
if (!Number.isInteger(bytes) || bytes < 1 || bytes > max) {
|
|
76
|
+
throw new SparkVaultValidationError(`Entropy byte count must be an integer from 1 to ${max} for the ${format} format`, {
|
|
77
|
+
field: 'bytes',
|
|
78
|
+
received: bytes,
|
|
79
|
+
});
|
|
33
80
|
}
|
|
34
81
|
}
|
|
35
82
|
//# sourceMappingURL=entropy.js.map
|
package/dist/entropy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entropy.js","sourceRoot":"","sources":["../src/entropy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"entropy.js","sourceRoot":"","sources":["../src/entropy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entropy wire-contract assertions — COMPILE-TIME ONLY (see notify.test-d.ts for
|
|
3
|
+
* why these live in `src` under a `.test-d.ts` name).
|
|
4
|
+
*
|
|
5
|
+
* The endpoint answers a `bytes` request with a number array and every other
|
|
6
|
+
* format with a string, so `generate` is typed per format, and a format known
|
|
7
|
+
* only as an EntropyFormat resolves to either shape. The format list
|
|
8
|
+
* itself is pinned to the platform contract at runtime by
|
|
9
|
+
* tests/app-sdk-mirrors.test.ts.
|
|
10
|
+
*/
|
|
11
|
+
import type { EntropyFormat, MobileEntropyClient } from './entropy.js';
|
|
12
|
+
/** Fails to compile unless `T` resolves to exactly `true`. */
|
|
13
|
+
type Assert<T extends true> = T;
|
|
14
|
+
/** Invariant type equality: `A` and `B` must be the same type, not merely mutually assignable. */
|
|
15
|
+
type Equals<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;
|
|
16
|
+
/** What `generate` resolves to for a call with `Options`: its first overload that accepts them. */
|
|
17
|
+
type Answer<Options> = MobileEntropyClient['generate'] extends {
|
|
18
|
+
(options: infer First): infer FirstResult;
|
|
19
|
+
(options: infer Second): infer SecondResult;
|
|
20
|
+
(options: infer Third): infer ThirdResult;
|
|
21
|
+
} ? Options extends First ? Awaited<FirstResult> : Options extends Second ? Awaited<SecondResult> : Options extends Third ? Awaited<ThirdResult> : never : never;
|
|
22
|
+
export type EntropyWireContract = [
|
|
23
|
+
Assert<Equals<EntropyFormat, 'hex' | 'base64' | 'base64url' | 'alphanumeric' | 'alphanumeric-mixed' | 'password' | 'numeric' | 'uuid' | 'bytes'>>,
|
|
24
|
+
Assert<Equals<Answer<{
|
|
25
|
+
bytes: number;
|
|
26
|
+
format: 'bytes';
|
|
27
|
+
}>['value'], number[]>>,
|
|
28
|
+
Assert<Equals<Answer<{
|
|
29
|
+
bytes: number;
|
|
30
|
+
format: 'password';
|
|
31
|
+
}>['value'], string>>,
|
|
32
|
+
Assert<Equals<Answer<{
|
|
33
|
+
bytes: number;
|
|
34
|
+
}>['value'], string>>,
|
|
35
|
+
Assert<Equals<Answer<{
|
|
36
|
+
bytes: number;
|
|
37
|
+
format: EntropyFormat;
|
|
38
|
+
}>['value'], string | number[]>>
|
|
39
|
+
];
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entropy wire-contract assertions — COMPILE-TIME ONLY (see notify.test-d.ts for
|
|
3
|
+
* why these live in `src` under a `.test-d.ts` name).
|
|
4
|
+
*
|
|
5
|
+
* The endpoint answers a `bytes` request with a number array and every other
|
|
6
|
+
* format with a string, so `generate` is typed per format, and a format known
|
|
7
|
+
* only as an EntropyFormat resolves to either shape. The format list
|
|
8
|
+
* itself is pinned to the platform contract at runtime by
|
|
9
|
+
* tests/app-sdk-mirrors.test.ts.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=entropy.test-d.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entropy.test-d.js","sourceRoot":"","sources":["../src/entropy.test-d.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -50,10 +50,10 @@ export declare class SparkVaultTimeoutError extends SparkVaultMobileError {
|
|
|
50
50
|
constructor(message?: string);
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
* Thrown when a download is requested against an ingot
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
53
|
+
* Thrown when a download is requested against an ingot whose upload Forge has
|
|
54
|
+
* not activated yet (status `uploading`). Transient by nature, but retrying
|
|
55
|
+
* immediately will not help — callers should back off until the ingot becomes
|
|
56
|
+
* active.
|
|
57
57
|
*/
|
|
58
58
|
export declare class IngotNotReadyError extends SparkVaultMobileError {
|
|
59
59
|
/** Server-reported ingot status at the time of the request, when known. */
|
|
@@ -64,25 +64,87 @@ export declare class IngotNotReadyError extends SparkVaultMobileError {
|
|
|
64
64
|
meta?: ApiMeta;
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
|
+
/** The ingot a create was refused over, as the server read it. */
|
|
68
|
+
export interface IngotNameHolder {
|
|
69
|
+
ingotId: string;
|
|
70
|
+
/**
|
|
71
|
+
* Lifecycle status of the holder: `active` when its upload finished,
|
|
72
|
+
* `uploading` when it has not (only a `refuse` create is refused over an
|
|
73
|
+
* unfinished holder; `complete_pending` completes one).
|
|
74
|
+
*/
|
|
75
|
+
status: string;
|
|
76
|
+
sizeBytes: number;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Thrown when a create is refused because an ingot already holds the name in
|
|
80
|
+
* the target folder (409 `details.reason === 'NAME_HELD'`): with
|
|
81
|
+
* `onNameConflict: 'complete_pending'` a published holder, with `'refuse'` any
|
|
82
|
+
* holder. The server decided before writing anything, so the holder is
|
|
83
|
+
* untouched; `holder` is the row it read, which lets the caller act on it
|
|
84
|
+
* (send its bytes into its own row by id, adopt it, or try another name)
|
|
85
|
+
* without a second read.
|
|
86
|
+
*/
|
|
87
|
+
export declare class IngotNameHeldError extends SparkVaultMobileError {
|
|
88
|
+
readonly holder: IngotNameHolder;
|
|
89
|
+
constructor(message: string, options: {
|
|
90
|
+
holder: IngotNameHolder;
|
|
91
|
+
details?: Record<string, unknown>;
|
|
92
|
+
meta?: ApiMeta;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
67
95
|
export declare class TusUploadError extends SparkVaultMobileError {
|
|
68
96
|
readonly cause: Error | null;
|
|
69
97
|
readonly filename: string | null;
|
|
70
98
|
readonly fileSize: number | null;
|
|
71
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Where the upload stopped: `finalize` is after the transfer, when every
|
|
101
|
+
* byte was on Forge but the ingot was not confirmed published.
|
|
102
|
+
*/
|
|
103
|
+
readonly phase: 'create' | 'upload' | 'finalize' | 'network' | 'timeout' | 'cancelled' | 'stalled' | 'unknown';
|
|
72
104
|
/**
|
|
73
105
|
* True when Forge no longer has the session (it answered a status
|
|
74
|
-
* `isSessionGone` recognises
|
|
75
|
-
* cannot carry this file. The persisted session is worthless, so
|
|
76
|
-
* caller starts a fresh upload instead of retrying it.
|
|
106
|
+
* `isSessionGone` recognises, or the expiry it named has passed), or the
|
|
107
|
+
* session cannot carry this file. The persisted session is worthless, so
|
|
108
|
+
* the caller starts a fresh upload instead of retrying it.
|
|
77
109
|
*/
|
|
78
110
|
readonly sessionLost: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* The `Retry-After` of the answer that ended the upload, in ms: the
|
|
113
|
+
* earliest a next attempt can expect another answer. Null when that answer
|
|
114
|
+
* named none, or no answer arrived.
|
|
115
|
+
*/
|
|
116
|
+
readonly retryAfterMs: number | null;
|
|
117
|
+
/**
|
|
118
|
+
* True when every byte is on Forge and the session is intact but the ingot
|
|
119
|
+
* was not confirmed published: the vault did not read it `active` within
|
|
120
|
+
* the activation poll budget, or Forge was still saving the file at the
|
|
121
|
+
* upload's idle limit (code `upload_received_publishing`; it finishes
|
|
122
|
+
* without this device). The caller retries later by resuming the session
|
|
123
|
+
* it persisted, which asks Forge for the receipt again and sends no bytes.
|
|
124
|
+
*/
|
|
125
|
+
readonly retryLater: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* The code Core ended this upload with, as Forge named it. With code
|
|
128
|
+
* `upload_refused` a final refusal (`UPLOAD_POOL_FULL`: the account's
|
|
129
|
+
* storage pool cannot take it; `UPLOAD_SUPERSEDED`: a later upload replaced
|
|
130
|
+
* it), whose message is Core's own sentence and which no new upload of the
|
|
131
|
+
* same file remedies; with `sessionLost` any other code (`NOT_FOUND`: the
|
|
132
|
+
* file the upload was for is gone; `UPLOAD_PROTOCOL_RETIRED`; a 412's),
|
|
133
|
+
* which a fresh upload starts over from. Null when the answer named none.
|
|
134
|
+
*/
|
|
135
|
+
readonly coreCode: string | null;
|
|
79
136
|
constructor(message: string, options?: {
|
|
80
137
|
cause?: Error | null;
|
|
81
138
|
httpStatus?: number | null;
|
|
139
|
+
/** The error's code when it names an outcome of its own; otherwise the HTTP status, or `tus_upload_error`. */
|
|
140
|
+
code?: string;
|
|
82
141
|
filename?: string | null;
|
|
83
142
|
fileSize?: number | null;
|
|
84
143
|
phase?: TusUploadError['phase'];
|
|
85
144
|
sessionLost?: boolean;
|
|
145
|
+
retryAfterMs?: number | null;
|
|
146
|
+
retryLater?: boolean;
|
|
147
|
+
coreCode?: string | null;
|
|
86
148
|
});
|
|
87
149
|
/**
|
|
88
150
|
* Wrap an error the SDK did not produce (a `fileReader` read, an
|
|
@@ -136,32 +198,54 @@ export declare function isNetworkError(err: unknown): boolean;
|
|
|
136
198
|
* per-upload token minted with the ingot (expired, consumed, or bound to a
|
|
137
199
|
* different session) — not the vault session. Re-unsealing the vault cannot
|
|
138
200
|
* fix it; the caller creates a new upload session (`POST /ingots`) instead.
|
|
139
|
-
* Use `
|
|
140
|
-
* rejections apart.
|
|
201
|
+
* Use `vatRefusalReason` to tell the API's VAT refusals apart.
|
|
141
202
|
*/
|
|
142
203
|
export declare function isAuthError(err: unknown): boolean;
|
|
143
204
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
205
|
+
* The reason the API refused the vault access token, or null when the error
|
|
206
|
+
* is not a VAT refusal:
|
|
207
|
+
* - `VAT_INVALID` (401): no, a malformed, unknown or swept token, or one a
|
|
208
|
+
* since-revoked DVAK minted — unseal again. Never the login.
|
|
209
|
+
* - `VAT_EXPIRED` (403): the token outlived its TTL — unseal again and
|
|
210
|
+
* retry the same request as-is.
|
|
211
|
+
* - `VAT_READ_ONLY` (403): a View Only token on a write — no unseal fixes it.
|
|
212
|
+
* A 403 with no reason is a token for another vault or account; a 401 with no
|
|
213
|
+
* reason is the login itself.
|
|
214
|
+
*/
|
|
215
|
+
export declare function vatRefusalReason(err: unknown): string | null;
|
|
216
|
+
/**
|
|
217
|
+
* True when the API refused the vault access token because it has expired
|
|
218
|
+
* (403 `VAT_EXPIRED`). Re-unsealing the vault mints a fresh VAT and the same
|
|
219
|
+
* request can be retried as-is.
|
|
148
220
|
*/
|
|
149
221
|
export declare function isVatExpiredError(err: unknown): err is SparkVaultMobileError;
|
|
150
222
|
/**
|
|
151
|
-
* True when the vault access token is valid but read-only
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
223
|
+
* True when the vault access token is valid but read-only (403
|
|
224
|
+
* `VAT_READ_ONLY`): the session was unsealed with a read-only access level, so
|
|
225
|
+
* a write cannot succeed by retrying or by re-unsealing the same way — the
|
|
226
|
+
* caller needs write access to the vault.
|
|
155
227
|
*/
|
|
156
228
|
export declare function isReadOnlyVaultError(err: unknown): err is SparkVaultMobileError;
|
|
229
|
+
/**
|
|
230
|
+
* True for the API's 404 not-found answer: the resource it names does not
|
|
231
|
+
* exist (or is not this caller's to see). The only answer that says a vault,
|
|
232
|
+
* an ingot or a key's vault is missing; a rate limit, an outage or a refused
|
|
233
|
+
* session says nothing about it. Never true for a `TusUploadError`: Forge's
|
|
234
|
+
* 404 is an upload session that is gone, not a missing resource.
|
|
235
|
+
*/
|
|
236
|
+
export declare function isNotFoundError(err: unknown): err is SparkVaultMobileError;
|
|
157
237
|
/** True for a 429 rate-limit response — retry after a cooldown, never immediately. */
|
|
158
238
|
export declare function isRateLimitError(err: unknown): boolean;
|
|
159
239
|
/** Type guard for the still-processing-ingot download failure. */
|
|
160
240
|
export declare function isIngotNotReadyError(err: unknown): err is IngotNotReadyError;
|
|
241
|
+
/** Type guard for a create refused over the same-name ingot holding its name. */
|
|
242
|
+
export declare function isIngotNameHeldError(err: unknown): err is IngotNameHeldError;
|
|
161
243
|
/**
|
|
162
|
-
* True when retrying a failed download can succeed.
|
|
163
|
-
*
|
|
164
|
-
*
|
|
244
|
+
* True when retrying a failed download can succeed. A validation failure always
|
|
245
|
+
* fails the same way again, and an HTTP refusal is retried only when the
|
|
246
|
+
* platform's retry rule (`isTransientHttpStatus`, mirrored from
|
|
247
|
+
* `@sparkvault/app-sdk/transient`) calls it transient; every other failure
|
|
248
|
+
* (connectivity, an answer that could not be read) is worth another attempt.
|
|
165
249
|
*/
|
|
166
250
|
export declare function isRetryableDownloadError(err: unknown): boolean;
|
|
167
251
|
/**
|