@sparkvault/sdk-mobile 6.0.2 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/account.d.ts +4 -3
- package/dist/account.js +16 -11
- package/dist/account.js.map +1 -1
- package/dist/app-sdk/email-address.d.ts +37 -0
- package/dist/app-sdk/email-address.js +91 -0
- package/dist/app-sdk/email-address.js.map +1 -0
- package/dist/app-sdk/encoding.d.ts +64 -0
- package/dist/app-sdk/encoding.js +245 -0
- package/dist/app-sdk/encoding.js.map +1 -0
- package/dist/app-sdk/entropy-contract.d.ts +33 -0
- package/dist/app-sdk/entropy-contract.js +51 -0
- package/dist/app-sdk/entropy-contract.js.map +1 -0
- package/dist/app-sdk/filename.d.ts +93 -0
- package/dist/app-sdk/filename.js +161 -0
- package/dist/app-sdk/filename.js.map +1 -0
- package/dist/app-sdk/forge-url.d.ts +107 -0
- package/dist/app-sdk/forge-url.js +222 -0
- package/dist/app-sdk/forge-url.js.map +1 -0
- package/dist/app-sdk/ids.d.ts +48 -0
- package/dist/app-sdk/ids.js +65 -0
- package/dist/app-sdk/ids.js.map +1 -0
- package/dist/app-sdk/ingot-access-log.d.ts +29 -0
- package/dist/app-sdk/ingot-access-log.js +49 -0
- package/dist/app-sdk/ingot-access-log.js.map +1 -0
- package/dist/app-sdk/phone.d.ts +37 -0
- package/dist/app-sdk/phone.js +134 -0
- package/dist/app-sdk/phone.js.map +1 -0
- package/dist/app-sdk/platform-domains.d.ts +41 -0
- package/dist/app-sdk/platform-domains.js +82 -0
- package/dist/app-sdk/platform-domains.js.map +1 -0
- package/dist/app-sdk/request-path.d.ts +49 -0
- package/dist/app-sdk/request-path.js +81 -0
- package/dist/app-sdk/request-path.js.map +1 -0
- package/dist/app-sdk/transient.d.ts +23 -0
- package/dist/app-sdk/transient.js +29 -0
- package/dist/app-sdk/transient.js.map +1 -0
- package/dist/app-sdk/vat-refusal.d.ts +66 -0
- package/dist/app-sdk/vat-refusal.js +69 -0
- package/dist/app-sdk/vat-refusal.js.map +1 -0
- package/dist/billing.d.ts +9 -17
- package/dist/billing.js +14 -17
- package/dist/billing.js.map +1 -1
- package/dist/config.d.ts +7 -1
- package/dist/config.js +10 -17
- package/dist/config.js.map +1 -1
- package/dist/encoding.d.ts +30 -4
- package/dist/encoding.js +89 -83
- package/dist/encoding.js.map +1 -1
- package/dist/entropy.d.ts +25 -4
- package/dist/entropy.js +62 -15
- package/dist/entropy.js.map +1 -1
- package/dist/entropy.test-d.d.ts +40 -0
- package/dist/entropy.test-d.js +12 -0
- package/dist/entropy.test-d.js.map +1 -0
- package/dist/errors.d.ts +67 -17
- package/dist/errors.js +70 -27
- package/dist/errors.js.map +1 -1
- package/dist/folders.js +8 -7
- package/dist/folders.js.map +1 -1
- package/dist/http.d.ts +57 -15
- package/dist/http.js +125 -138
- package/dist/http.js.map +1 -1
- package/dist/identity-dialog.js +4 -30
- package/dist/identity-dialog.js.map +1 -1
- package/dist/identity-input.d.ts +39 -0
- package/dist/identity-input.js +59 -0
- package/dist/identity-input.js.map +1 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/ingots.d.ts +51 -18
- package/dist/ingots.js +132 -122
- package/dist/ingots.js.map +1 -1
- package/dist/mutex.d.ts +1 -2
- package/dist/mutex.js +0 -6
- package/dist/mutex.js.map +1 -1
- package/dist/notify.d.ts +7 -2
- package/dist/notify.js +12 -15
- package/dist/notify.js.map +1 -1
- package/dist/notify.test-d.d.ts +3 -2
- package/dist/notify.test-d.js +1 -1
- package/dist/products/identity.d.ts +5 -0
- package/dist/products/identity.js +40 -35
- package/dist/products/identity.js.map +1 -1
- package/dist/request-path.d.ts +32 -0
- package/dist/request-path.js +35 -0
- package/dist/request-path.js.map +1 -0
- package/dist/sparks.js +16 -15
- package/dist/sparks.js.map +1 -1
- package/dist/tus.d.ts +19 -2
- package/dist/tus.js +86 -35
- package/dist/tus.js.map +1 -1
- package/dist/types.d.ts +167 -15
- package/dist/users.d.ts +12 -5
- package/dist/users.js +27 -19
- package/dist/users.js.map +1 -1
- package/dist/validation.d.ts +20 -5
- package/dist/validation.js +29 -27
- package/dist/validation.js.map +1 -1
- package/dist/vaults.d.ts +34 -83
- package/dist/vaults.js +64 -44
- package/dist/vaults.js.map +1 -1
- package/dist/vaults.test-d.d.ts +22 -0
- package/dist/vaults.test-d.js +14 -0
- package/dist/vaults.test-d.js.map +1 -0
- package/package.json +4 -2
- package/src/account.ts +16 -11
- package/src/app-sdk/email-address.js +94 -0
- package/src/app-sdk/encoding.js +250 -0
- package/src/app-sdk/entropy-contract.js +56 -0
- package/src/app-sdk/filename.js +174 -0
- package/src/app-sdk/forge-url.js +224 -0
- package/src/app-sdk/ids.js +71 -0
- package/src/app-sdk/ingot-access-log.js +51 -0
- package/src/app-sdk/phone.js +139 -0
- package/src/app-sdk/platform-domains.js +99 -0
- package/src/app-sdk/request-path.js +81 -0
- package/src/app-sdk/transient.js +30 -0
- package/src/app-sdk/vat-refusal.js +74 -0
- package/src/billing.ts +16 -25
- package/src/config.ts +16 -23
- package/src/encoding.ts +91 -103
- package/src/entropy.test-d.ts +47 -0
- package/src/entropy.ts +86 -20
- package/src/errors.ts +93 -33
- package/src/folders.ts +8 -7
- package/src/http.ts +145 -150
- package/src/identity-dialog.tsx +4 -37
- package/src/identity-input.ts +70 -0
- package/src/index.ts +11 -3
- package/src/ingots.ts +173 -159
- package/src/mutex.ts +1 -8
- package/src/notify.test-d.ts +3 -1
- package/src/notify.ts +19 -17
- package/src/products/identity.ts +45 -39
- package/src/request-path.ts +45 -0
- package/src/sparks.ts +16 -13
- package/src/tus.ts +90 -37
- package/src/types.ts +165 -15
- package/src/users.ts +27 -17
- package/src/validation.ts +30 -24
- package/src/vaults.test-d.ts +29 -0
- package/src/vaults.ts +87 -153
- package/dist/platform-domains.generated.d.ts +0 -2
- package/dist/platform-domains.generated.js +0 -16
- package/dist/platform-domains.generated.js.map +0 -1
- package/src/platform-domains.generated.ts +0 -17
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether sending the SAME request again can succeed, read from its HTTP
|
|
3
|
+
* status: the one retry rule every client and every provider adapter applies.
|
|
4
|
+
*
|
|
5
|
+
* A retry rule restated beside every client drifts, and the copy that drifted
|
|
6
|
+
* decides whether a deliverable message is retried or abandoned, or whether a
|
|
7
|
+
* download is tried again. Each caller keeps its provider's own extras on top of
|
|
8
|
+
* this rule (a success body, a dead-token reason, a vendor error name).
|
|
9
|
+
*
|
|
10
|
+
* Browser-safe and import-free: sdk-js bundles it and sdk-mobile mirrors it.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Is this HTTP status one a retry of the same request can turn into a success?
|
|
14
|
+
*
|
|
15
|
+
* RFC 9110: 408 means the server never received the whole request, 429 that it
|
|
16
|
+
* throttled it, and a 5xx that it failed; every other status is the server's
|
|
17
|
+
* verdict on this exact request. A caller that got no response at all (a network
|
|
18
|
+
* error, a timeout) has no status to pass and decides that case itself.
|
|
19
|
+
*
|
|
20
|
+
* @param {number} status
|
|
21
|
+
* @returns {boolean}
|
|
22
|
+
*/
|
|
23
|
+
export function isTransientHttpStatus(status: number): boolean;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// GENERATED FILE — do not edit by hand.
|
|
2
|
+
// Source of truth: packages/app-sdk/src/transient.js
|
|
3
|
+
// Regenerate with: pnpm --filter @sparkvault/sdk-mobile build
|
|
4
|
+
/**
|
|
5
|
+
* Whether sending the SAME request again can succeed, read from its HTTP
|
|
6
|
+
* status: the one retry rule every client and every provider adapter applies.
|
|
7
|
+
*
|
|
8
|
+
* A retry rule restated beside every client drifts, and the copy that drifted
|
|
9
|
+
* decides whether a deliverable message is retried or abandoned, or whether a
|
|
10
|
+
* download is tried again. Each caller keeps its provider's own extras on top of
|
|
11
|
+
* this rule (a success body, a dead-token reason, a vendor error name).
|
|
12
|
+
*
|
|
13
|
+
* Browser-safe and import-free: sdk-js bundles it and sdk-mobile mirrors it.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Is this HTTP status one a retry of the same request can turn into a success?
|
|
17
|
+
*
|
|
18
|
+
* RFC 9110: 408 means the server never received the whole request, 429 that it
|
|
19
|
+
* throttled it, and a 5xx that it failed; every other status is the server's
|
|
20
|
+
* verdict on this exact request. A caller that got no response at all (a network
|
|
21
|
+
* error, a timeout) has no status to pass and decides that case itself.
|
|
22
|
+
*
|
|
23
|
+
* @param {number} status
|
|
24
|
+
* @returns {boolean}
|
|
25
|
+
*/
|
|
26
|
+
export function isTransientHttpStatus(status) {
|
|
27
|
+
return status === 408 || status === 429 || status >= 500;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=transient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transient.js","sourceRoot":"","sources":["../../src/app-sdk/transient.js"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,qDAAqD;AACrD,8DAA8D;AAE9D;;;;;;;;;;GAUG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAM;IAC1C,OAAO,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The VAT refusal a Core error answer names, or null when it is not one.
|
|
3
|
+
*
|
|
4
|
+
* @param {{ statusCode?: number, details?: { reason?: string } }} error - An
|
|
5
|
+
* error as parsed from a Core response (status plus the body's `details`)
|
|
6
|
+
* @returns {string|null} One of VAT_REFUSAL_REASONS, or null
|
|
7
|
+
*/
|
|
8
|
+
export function vatRefusalReason(error: {
|
|
9
|
+
statusCode?: number;
|
|
10
|
+
details?: {
|
|
11
|
+
reason?: string;
|
|
12
|
+
};
|
|
13
|
+
}): string | null;
|
|
14
|
+
/**
|
|
15
|
+
* Whether a refusal says the token's vault session is over: an invalid or an
|
|
16
|
+
* expired token, which only a new unseal replaces, so a client forgets it. A
|
|
17
|
+
* read-only refusal leaves the session standing (the token still reads), and
|
|
18
|
+
* so does a refusal that names no VAT reason.
|
|
19
|
+
*
|
|
20
|
+
* @param {string|null} reason - What vatRefusalReason returned
|
|
21
|
+
* @returns {boolean}
|
|
22
|
+
*/
|
|
23
|
+
export function endsVatSession(reason: string | null): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* True when Core refused the request's login, not a Vault Access Token it
|
|
26
|
+
* carried: a 401 bearing Core's session code (AUTHENTICATION_ERROR, the
|
|
27
|
+
* mapped error's `upstreamCode`) that names no VAT refusal. Its token was
|
|
28
|
+
* refused, or its account is unavailable (details.reason ACCOUNT_UNAVAILABLE,
|
|
29
|
+
* ./refusal-reasons.js). A 401 for a credential in the request body (an
|
|
30
|
+
* unseal key) carries Core's credential code (UNAUTHORIZED) and is not one.
|
|
31
|
+
*
|
|
32
|
+
* @param {{ statusCode?: number, upstreamCode?: string|null, details?: { reason?: string } }} error -
|
|
33
|
+
* An error as parsed from a Core response (parseApiError)
|
|
34
|
+
* @returns {boolean}
|
|
35
|
+
*/
|
|
36
|
+
export function isRefusedLogin(error: {
|
|
37
|
+
statusCode?: number;
|
|
38
|
+
upstreamCode?: string | null;
|
|
39
|
+
details?: {
|
|
40
|
+
reason?: string;
|
|
41
|
+
};
|
|
42
|
+
}): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Why Core refused a Vault Access Token (VAT): the `details.reason` every VAT
|
|
45
|
+
* refusal carries, and the one reading of it clients share.
|
|
46
|
+
*
|
|
47
|
+
* 401 VAT_INVALID no token, or a malformed, unknown or swept one, or one a
|
|
48
|
+
* since-revoked DVAK minted — unseal again
|
|
49
|
+
* 403 VAT_EXPIRED the token outlived its TTL — unseal again, retry as-is
|
|
50
|
+
* 403 VAT_READ_ONLY a View Only token on a write — no unseal fixes it
|
|
51
|
+
*
|
|
52
|
+
* A 403 with no reason is a token for another vault or account, which no
|
|
53
|
+
* unseal of this vault fixes; a 401 with no reason is the login itself, never
|
|
54
|
+
* the VAT. Browser-safe: no imports.
|
|
55
|
+
*/
|
|
56
|
+
export const VAT_REFUSAL_REASONS: Readonly<{
|
|
57
|
+
INVALID: "VAT_INVALID";
|
|
58
|
+
EXPIRED: "VAT_EXPIRED";
|
|
59
|
+
READ_ONLY: "VAT_READ_ONLY";
|
|
60
|
+
}>;
|
|
61
|
+
/** The status each refusal is answered with. */
|
|
62
|
+
export const VAT_REFUSAL_STATUS: Readonly<{
|
|
63
|
+
VAT_INVALID: 401;
|
|
64
|
+
VAT_EXPIRED: 403;
|
|
65
|
+
VAT_READ_ONLY: 403;
|
|
66
|
+
}>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// GENERATED FILE — do not edit by hand.
|
|
2
|
+
// Source of truth: packages/app-sdk/src/vat-refusal.js
|
|
3
|
+
// Regenerate with: pnpm --filter @sparkvault/sdk-mobile build
|
|
4
|
+
/**
|
|
5
|
+
* Why Core refused a Vault Access Token (VAT): the `details.reason` every VAT
|
|
6
|
+
* refusal carries, and the one reading of it clients share.
|
|
7
|
+
*
|
|
8
|
+
* 401 VAT_INVALID no token, or a malformed, unknown or swept one, or one a
|
|
9
|
+
* since-revoked DVAK minted — unseal again
|
|
10
|
+
* 403 VAT_EXPIRED the token outlived its TTL — unseal again, retry as-is
|
|
11
|
+
* 403 VAT_READ_ONLY a View Only token on a write — no unseal fixes it
|
|
12
|
+
*
|
|
13
|
+
* A 403 with no reason is a token for another vault or account, which no
|
|
14
|
+
* unseal of this vault fixes; a 401 with no reason is the login itself, never
|
|
15
|
+
* the VAT. Browser-safe: no imports.
|
|
16
|
+
*/
|
|
17
|
+
export const VAT_REFUSAL_REASONS = Object.freeze({
|
|
18
|
+
INVALID: 'VAT_INVALID',
|
|
19
|
+
EXPIRED: 'VAT_EXPIRED',
|
|
20
|
+
READ_ONLY: 'VAT_READ_ONLY'
|
|
21
|
+
});
|
|
22
|
+
/** The status each refusal is answered with. */
|
|
23
|
+
export const VAT_REFUSAL_STATUS = Object.freeze({
|
|
24
|
+
[VAT_REFUSAL_REASONS.INVALID]: 401,
|
|
25
|
+
[VAT_REFUSAL_REASONS.EXPIRED]: 403,
|
|
26
|
+
[VAT_REFUSAL_REASONS.READ_ONLY]: 403
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* The VAT refusal a Core error answer names, or null when it is not one.
|
|
30
|
+
*
|
|
31
|
+
* @param {{ statusCode?: number, details?: { reason?: string } }} error - An
|
|
32
|
+
* error as parsed from a Core response (status plus the body's `details`)
|
|
33
|
+
* @returns {string|null} One of VAT_REFUSAL_REASONS, or null
|
|
34
|
+
*/
|
|
35
|
+
export function vatRefusalReason(error) {
|
|
36
|
+
const reason = error?.details?.reason;
|
|
37
|
+
return typeof reason === 'string' && Object.hasOwn(VAT_REFUSAL_STATUS, reason) &&
|
|
38
|
+
VAT_REFUSAL_STATUS[reason] === error?.statusCode
|
|
39
|
+
? reason
|
|
40
|
+
: null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Whether a refusal says the token's vault session is over: an invalid or an
|
|
44
|
+
* expired token, which only a new unseal replaces, so a client forgets it. A
|
|
45
|
+
* read-only refusal leaves the session standing (the token still reads), and
|
|
46
|
+
* so does a refusal that names no VAT reason.
|
|
47
|
+
*
|
|
48
|
+
* @param {string|null} reason - What vatRefusalReason returned
|
|
49
|
+
* @returns {boolean}
|
|
50
|
+
*/
|
|
51
|
+
export function endsVatSession(reason) {
|
|
52
|
+
return reason === VAT_REFUSAL_REASONS.INVALID || reason === VAT_REFUSAL_REASONS.EXPIRED;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* True when Core refused the request's login, not a Vault Access Token it
|
|
56
|
+
* carried: a 401 bearing Core's session code (AUTHENTICATION_ERROR, the
|
|
57
|
+
* mapped error's `upstreamCode`) that names no VAT refusal. Its token was
|
|
58
|
+
* refused, or its account is unavailable (details.reason ACCOUNT_UNAVAILABLE,
|
|
59
|
+
* ./refusal-reasons.js). A 401 for a credential in the request body (an
|
|
60
|
+
* unseal key) carries Core's credential code (UNAUTHORIZED) and is not one.
|
|
61
|
+
*
|
|
62
|
+
* @param {{ statusCode?: number, upstreamCode?: string|null, details?: { reason?: string } }} error -
|
|
63
|
+
* An error as parsed from a Core response (parseApiError)
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
export function isRefusedLogin(error) {
|
|
67
|
+
return error?.statusCode === 401 && error?.upstreamCode === 'AUTHENTICATION_ERROR' && vatRefusalReason(error) === null;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=vat-refusal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vat-refusal.js","sourceRoot":"","sources":["../../src/app-sdk/vat-refusal.js"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,uDAAuD;AACvD,8DAA8D;AAE9D;;;;;;;;;;;;GAYG;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,CAAC;IAC/C,OAAO,EAAE,aAAa;IACtB,OAAO,EAAE,aAAa;IACtB,SAAS,EAAE,eAAe;CAC3B,CAAC,CAAC;AAEH,gDAAgD;AAChD,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,CAAC;IAC9C,CAAC,mBAAmB,CAAC,OAAO,CAAC,EAAE,GAAG;IAClC,CAAC,mBAAmB,CAAC,OAAO,CAAC,EAAE,GAAG;IAClC,CAAC,mBAAmB,CAAC,SAAS,CAAC,EAAE,GAAG;CACrC,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAK;IACpC,MAAM,MAAM,GAAG,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC;IACtC,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAAC;QAC5E,kBAAkB,CAAC,MAAM,CAAC,KAAK,KAAK,EAAE,UAAU;QAChD,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,MAAM;IACnC,OAAO,MAAM,KAAK,mBAAmB,CAAC,OAAO,IAAI,MAAM,KAAK,mBAAmB,CAAC,OAAO,CAAC;AAC1F,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,KAAK;IAClC,OAAO,KAAK,EAAE,UAAU,KAAK,GAAG,IAAI,KAAK,EAAE,YAAY,KAAK,sBAAsB,IAAI,gBAAgB,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;AACzH,CAAC"}
|
package/dist/billing.d.ts
CHANGED
|
@@ -1,27 +1,19 @@
|
|
|
1
1
|
import type { MobileHttpClient } from './http.js';
|
|
2
|
-
import type { IapPlatform, SparkVaultSubscription } from './types.js';
|
|
3
|
-
/** Short-lived Stripe Customer Portal URL for managing the subscription on web. */
|
|
4
|
-
export interface PortalSession {
|
|
5
|
-
portal_url: string;
|
|
6
|
-
}
|
|
2
|
+
import type { IapPlatform, IapValidation, SparkVaultSubscription } from './types.js';
|
|
7
3
|
export declare class MobileBillingClient {
|
|
8
4
|
private readonly http;
|
|
9
5
|
constructor(http: MobileHttpClient);
|
|
10
6
|
/**
|
|
11
|
-
* Current subscription:
|
|
12
|
-
* pool usage, and Identity attempts. Drives the subscription /
|
|
7
|
+
* Current subscription: billing lifecycle, licensed vs. used seats, capacity
|
|
8
|
+
* blocks, live pool usage, and Identity attempts. Drives the subscription /
|
|
9
|
+
* usage view.
|
|
13
10
|
*/
|
|
14
11
|
getSubscription(): Promise<SparkVaultSubscription>;
|
|
15
12
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
|
|
20
|
-
createPortalSession(): Promise<PortalSession>;
|
|
21
|
-
/**
|
|
22
|
-
* Validate a native in-app purchase receipt server-side and apply the
|
|
23
|
-
* resulting entitlements. Returns the refreshed subscription so the client
|
|
24
|
-
* can update its plan / usage view immediately after purchase.
|
|
13
|
+
* Validate a native in-app purchase server-side and apply the entitlements
|
|
14
|
+
* it buys. `receipt` is the App Store signed transaction (JWS) or the Play
|
|
15
|
+
* purchase token. Answers whether Core provisioned the purchase; read the
|
|
16
|
+
* subscription again with getSubscription() to show the change.
|
|
25
17
|
*/
|
|
26
|
-
iapValidate(platform: IapPlatform, productId: string, receipt: string): Promise<
|
|
18
|
+
iapValidate(platform: IapPlatform, productId: string, receipt: string): Promise<IapValidation>;
|
|
27
19
|
}
|
package/dist/billing.js
CHANGED
|
@@ -1,34 +1,31 @@
|
|
|
1
|
+
import { apiPath } from './request-path.js';
|
|
1
2
|
export class MobileBillingClient {
|
|
2
3
|
constructor(http) {
|
|
3
4
|
this.http = http;
|
|
4
5
|
}
|
|
5
6
|
/**
|
|
6
|
-
* Current subscription:
|
|
7
|
-
* pool usage, and Identity attempts. Drives the subscription /
|
|
7
|
+
* Current subscription: billing lifecycle, licensed vs. used seats, capacity
|
|
8
|
+
* blocks, live pool usage, and Identity attempts. Drives the subscription /
|
|
9
|
+
* usage view.
|
|
8
10
|
*/
|
|
9
11
|
async getSubscription() {
|
|
10
|
-
const response = await this.http.get(
|
|
12
|
+
const response = await this.http.get(apiPath `/billing/subscription`);
|
|
11
13
|
return response.data;
|
|
12
14
|
}
|
|
13
15
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
|
|
18
|
-
async createPortalSession() {
|
|
19
|
-
const response = await this.http.post('/billing/portal');
|
|
20
|
-
return response.data;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Validate a native in-app purchase receipt server-side and apply the
|
|
24
|
-
* resulting entitlements. Returns the refreshed subscription so the client
|
|
25
|
-
* can update its plan / usage view immediately after purchase.
|
|
16
|
+
* Validate a native in-app purchase server-side and apply the entitlements
|
|
17
|
+
* it buys. `receipt` is the App Store signed transaction (JWS) or the Play
|
|
18
|
+
* purchase token. Answers whether Core provisioned the purchase; read the
|
|
19
|
+
* subscription again with getSubscription() to show the change.
|
|
26
20
|
*/
|
|
27
21
|
async iapValidate(platform, productId, receipt) {
|
|
28
|
-
|
|
22
|
+
// Core reads an App Store purchase's signed transaction as `receipt` and a
|
|
23
|
+
// Play purchase's token as `purchase_token`.
|
|
24
|
+
const credential = platform === 'android' ? { purchase_token: receipt } : { receipt };
|
|
25
|
+
const response = await this.http.post(apiPath `/billing/iap/validate`, {
|
|
29
26
|
platform,
|
|
30
27
|
product_id: productId,
|
|
31
|
-
|
|
28
|
+
...credential,
|
|
32
29
|
});
|
|
33
30
|
return response.data;
|
|
34
31
|
}
|
package/dist/billing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"billing.js","sourceRoot":"","sources":["../src/billing.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"billing.js","sourceRoot":"","sources":["../src/billing.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C,MAAM,OAAO,mBAAmB;IAG9B,YAAY,IAAsB;QAChC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,eAAe;QACnB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAyB,OAAO,CAAA,uBAAuB,CAAC,CAAC;QAC7F,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,WAAW,CACf,QAAqB,EACrB,SAAiB,EACjB,OAAe;QAEf,2EAA2E;QAC3E,6CAA6C;QAC7C,MAAM,UAAU,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;QACtF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAgB,OAAO,CAAA,uBAAuB,EAAE;YACnF,QAAQ;YACR,UAAU,EAAE,SAAS;YACrB,GAAG,UAAU;SACd,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;CACF"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FetchLike, MobileBackgroundTransfer, MobileBackgroundUploader, MobileFileDownloader, MobileFileReader, SparkVaultLogger, TokenStorageAdapter } from './types.js';
|
|
2
|
+
import { type ApiPath } from './request-path.js';
|
|
2
3
|
export interface SparkVaultMobileConfig {
|
|
3
4
|
tokenStorage: TokenStorageAdapter;
|
|
4
5
|
apiBaseUrl?: string;
|
|
@@ -31,6 +32,11 @@ export interface SparkVaultMobileConfig {
|
|
|
31
32
|
*/
|
|
32
33
|
backgroundUploader?: MobileBackgroundUploader;
|
|
33
34
|
logger?: Partial<SparkVaultLogger>;
|
|
35
|
+
/**
|
|
36
|
+
* Host allowlist for the backend-issued Forge transfer URLs the SDK sends an
|
|
37
|
+
* ingot's ISTK to (downloads and uploads alike). Defaults to the SparkVault
|
|
38
|
+
* platform's own hosts; override only when pointing at another deployment.
|
|
39
|
+
*/
|
|
34
40
|
allowedDownloadHostPatterns?: RegExp[];
|
|
35
41
|
}
|
|
36
42
|
export interface ResolvedMobileConfig {
|
|
@@ -53,4 +59,4 @@ export interface ResolvedMobileConfig {
|
|
|
53
59
|
allowedDownloadHostPatterns: RegExp[];
|
|
54
60
|
}
|
|
55
61
|
export declare function resolveMobileConfig(config: SparkVaultMobileConfig): ResolvedMobileConfig;
|
|
56
|
-
export declare function identityUrl(config: ResolvedMobileConfig, path:
|
|
62
|
+
export declare function identityUrl(config: ResolvedMobileConfig, path: ApiPath): string;
|
package/dist/config.js
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
import { SparkVaultValidationError } from './errors.js';
|
|
2
|
-
import {
|
|
2
|
+
import { isResourceIdOf } from './app-sdk/ids.js';
|
|
3
|
+
import { SPARKVAULT_IDENTITY_HOST, SPARKVAULT_OWNED_HOST_PATTERNS } from './app-sdk/platform-domains.js';
|
|
4
|
+
import { apiPath } from './request-path.js';
|
|
3
5
|
const DEFAULT_API_BASE_URL = 'https://api.sparkvault.com/v1';
|
|
4
|
-
const DEFAULT_IDENTITY_BASE_URL =
|
|
5
|
-
function escapeRegExp(value) {
|
|
6
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
7
|
-
}
|
|
8
|
-
// Hosts that are allowed to serve backend-issued ingot download URLs, derived
|
|
9
|
-
// from the canonical platform domain registry baked in at build time.
|
|
10
|
-
const DEFAULT_ALLOWED_DOWNLOAD_HOST_PATTERNS = [
|
|
11
|
-
new RegExp(`(^|\\.)${escapeRegExp(SPARKVAULT_ROOT_DOMAIN)}$`, 'i'),
|
|
12
|
-
new RegExp(`(^|\\.)(${SPARKVAULT_PLATFORM_SV_HOSTS.map(escapeRegExp).join('|')})$`, 'i'),
|
|
13
|
-
/\.amazonaws\.com$/i,
|
|
14
|
-
/\.cloudfront\.net$/i,
|
|
15
|
-
];
|
|
6
|
+
const DEFAULT_IDENTITY_BASE_URL = `https://${SPARKVAULT_IDENTITY_HOST}`;
|
|
16
7
|
const noopLogger = {
|
|
17
8
|
debug: () => undefined,
|
|
18
9
|
info: () => undefined,
|
|
@@ -60,8 +51,10 @@ export function resolveMobileConfig(config) {
|
|
|
60
51
|
received: identityAccountId,
|
|
61
52
|
});
|
|
62
53
|
}
|
|
63
|
-
|
|
64
|
-
|
|
54
|
+
// The account id names Identity request paths: the platform's one id
|
|
55
|
+
// grammar keeps it a single path segment.
|
|
56
|
+
if (!isResourceIdOf('account', identityAccountId)) {
|
|
57
|
+
throw new SparkVaultValidationError('identityAccountId must be a SparkVault account id (acc_...)', {
|
|
65
58
|
field: 'identityAccountId',
|
|
66
59
|
received: identityAccountId,
|
|
67
60
|
});
|
|
@@ -83,10 +76,10 @@ export function resolveMobileConfig(config) {
|
|
|
83
76
|
backgroundTransfer: config.backgroundTransfer,
|
|
84
77
|
backgroundUploader: config.backgroundUploader,
|
|
85
78
|
logger: { ...noopLogger, ...config.logger },
|
|
86
|
-
allowedDownloadHostPatterns: config.allowedDownloadHostPatterns ??
|
|
79
|
+
allowedDownloadHostPatterns: config.allowedDownloadHostPatterns ?? [...SPARKVAULT_OWNED_HOST_PATTERNS],
|
|
87
80
|
};
|
|
88
81
|
}
|
|
89
82
|
export function identityUrl(config, path) {
|
|
90
|
-
return `${config.identityBaseUrl}
|
|
83
|
+
return `${config.identityBaseUrl}${apiPath `/${config.identityAccountId}`}${path}`;
|
|
91
84
|
}
|
|
92
85
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/encoding.d.ts
CHANGED
|
@@ -1,7 +1,33 @@
|
|
|
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 { bytesToBase64, bytesToBase64url } from './app-sdk/encoding.js';
|
|
13
|
+
export { bytesToBase64, bytesToBase64url };
|
|
14
|
+
/**
|
|
15
|
+
* Decode standard base64.
|
|
16
|
+
* @throws SparkVaultValidationError when the text is not base64
|
|
17
|
+
*/
|
|
18
|
+
export declare function base64ToBytes(value: string): Uint8Array;
|
|
19
|
+
/**
|
|
20
|
+
* Decode base64url.
|
|
21
|
+
* @throws SparkVaultValidationError when the text is not base64url
|
|
22
|
+
*/
|
|
23
|
+
export declare function base64UrlToBytes(value: string): Uint8Array;
|
|
1
24
|
export declare function utf8ToBytes(value: string): Uint8Array;
|
|
2
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Decode UTF-8 by the WHATWG decoder's rules in fatal mode: a byte that cannot
|
|
27
|
+
* start a sequence, a sequence cut short, an overlong form, a surrogate or a
|
|
28
|
+
* code point past U+10FFFF refuses the whole input.
|
|
29
|
+
* @throws SparkVaultValidationError when the bytes are not UTF-8
|
|
30
|
+
*/
|
|
3
31
|
export declare function bytesToUtf8(bytes: Uint8Array): string;
|
|
4
|
-
export declare function
|
|
32
|
+
export declare function base64EncodeUtf8(value: string): string;
|
|
5
33
|
export declare function base64UrlToUtf8(value: string): string;
|
|
6
|
-
export declare function bytesToBase64(bytes: Uint8Array): string;
|
|
7
|
-
export declare function base64ToBytes(value: string): Uint8Array;
|
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
|