@bravely-studios/account-web 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +10 -0
- package/README.md +262 -0
- package/dist/ActivationStateMachine.d.ts +50 -0
- package/dist/ActivationStateMachine.d.ts.map +1 -0
- package/dist/ActivationStateMachine.js +141 -0
- package/dist/ActivationStateMachine.js.map +1 -0
- package/dist/BravelyAccountManager.d.ts +156 -0
- package/dist/BravelyAccountManager.d.ts.map +1 -0
- package/dist/BravelyAccountManager.js +621 -0
- package/dist/BravelyAccountManager.js.map +1 -0
- package/dist/EntitlementCache.d.ts +50 -0
- package/dist/EntitlementCache.d.ts.map +1 -0
- package/dist/EntitlementCache.js +116 -0
- package/dist/EntitlementCache.js.map +1 -0
- package/dist/components/ActivationLadder.d.ts +78 -0
- package/dist/components/ActivationLadder.d.ts.map +1 -0
- package/dist/components/ActivationLadder.js +145 -0
- package/dist/components/ActivationLadder.js.map +1 -0
- package/dist/components/CrossAppCard.d.ts +48 -0
- package/dist/components/CrossAppCard.d.ts.map +1 -0
- package/dist/components/CrossAppCard.js +45 -0
- package/dist/components/CrossAppCard.js.map +1 -0
- package/dist/deprecation.d.ts +19 -0
- package/dist/deprecation.d.ts.map +1 -0
- package/dist/deprecation.js +83 -0
- package/dist/deprecation.js.map +1 -0
- package/dist/displayName.d.ts +15 -0
- package/dist/displayName.d.ts.map +1 -0
- package/dist/displayName.js +41 -0
- package/dist/displayName.js.map +1 -0
- package/dist/dpop.d.ts +30 -0
- package/dist/dpop.d.ts.map +1 -0
- package/dist/dpop.js +87 -0
- package/dist/dpop.js.map +1 -0
- package/dist/hooks/useActivationLaneFromUrl.d.ts +54 -0
- package/dist/hooks/useActivationLaneFromUrl.d.ts.map +1 -0
- package/dist/hooks/useActivationLaneFromUrl.js +105 -0
- package/dist/hooks/useActivationLaneFromUrl.js.map +1 -0
- package/dist/hooks/useFreshLaunchRestoration.d.ts +62 -0
- package/dist/hooks/useFreshLaunchRestoration.d.ts.map +1 -0
- package/dist/hooks/useFreshLaunchRestoration.js +135 -0
- package/dist/hooks/useFreshLaunchRestoration.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/oauth.d.ts +50 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +107 -0
- package/dist/oauth.js.map +1 -0
- package/dist/storage.d.ts +48 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +153 -0
- package/dist/storage.js.map +1 -0
- package/dist/types.d.ts +172 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"displayName.d.ts","sourceRoot":"","sources":["../src/displayName.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAUjD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAInD"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Slug → user-facing display name lookup.
|
|
2
|
+
//
|
|
3
|
+
// Source of truth: the H1 of each `<app>-free-vs-pro.md` in
|
|
4
|
+
// `00 - AI Instructions/`. The display name is what the user sees in the
|
|
5
|
+
// activation ladder copy (`Activating PrintScreen.ly Pro…`), in the cross-app
|
|
6
|
+
// card (`You own 3 Bravely Pro apps on this account.`), and any other
|
|
7
|
+
// product-facing string the facade renders.
|
|
8
|
+
//
|
|
9
|
+
// Lookup is exhaustive over the `AppSlug` union — adding a new utility means
|
|
10
|
+
// adding a row here. Unknown slugs fall through to a Title-cased fallback so
|
|
11
|
+
// host pages never crash; observability surface logs the lookup miss.
|
|
12
|
+
/**
|
|
13
|
+
* Canonical slug → display name map. Sourced from the H1 of each
|
|
14
|
+
* `<app>-free-vs-pro.md`. Branding owners: keep this in lockstep with the
|
|
15
|
+
* marketing pages on `bravely.dev/<slug>` and the Settings strings inside
|
|
16
|
+
* each utility.
|
|
17
|
+
*/
|
|
18
|
+
export const DISPLAY_NAMES = {
|
|
19
|
+
diskaroo: "Diskaroo",
|
|
20
|
+
markdly: "Markd.ly",
|
|
21
|
+
printscreenly: "PrintScreen.ly",
|
|
22
|
+
prodjectly: "Prodject.ly",
|
|
23
|
+
saycopypaste: "SayCopyPaste",
|
|
24
|
+
scry: "Scry",
|
|
25
|
+
stickily: "Sticki.ly",
|
|
26
|
+
terminaltags: "Terminal Tags",
|
|
27
|
+
todoingly: "Todoing.ly",
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Returns the display name for a slug. Unknown slugs fall back to a
|
|
31
|
+
* naive Title-case rendering so host pages never crash on an unrecognized
|
|
32
|
+
* slug (e.g. a new utility added before this lib version ships).
|
|
33
|
+
*/
|
|
34
|
+
export function displayNameFor(slug) {
|
|
35
|
+
if (slug in DISPLAY_NAMES)
|
|
36
|
+
return DISPLAY_NAMES[slug];
|
|
37
|
+
if (slug.length === 0)
|
|
38
|
+
return slug;
|
|
39
|
+
return slug.charAt(0).toUpperCase() + slug.slice(1);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=displayName.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"displayName.js","sourceRoot":"","sources":["../src/displayName.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,EAAE;AACF,4DAA4D;AAC5D,yEAAyE;AACzE,8EAA8E;AAC9E,sEAAsE;AACtE,4CAA4C;AAC5C,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,sEAAsE;AAItE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAA4B;IACpD,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,UAAU;IACnB,aAAa,EAAE,gBAAgB;IAC/B,UAAU,EAAE,aAAa;IACzB,YAAY,EAAE,cAAc;IAC5B,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,WAAW;IACrB,YAAY,EAAE,eAAe;IAC7B,SAAS,EAAE,YAAY;CACxB,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,IAAI,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC,IAAe,CAAC,CAAC;IACjE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC"}
|
package/dist/dpop.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface DPoPKeypair {
|
|
2
|
+
publicJwk: JsonWebKey;
|
|
3
|
+
/** RFC 7638 thumbprint of the public JWK. */
|
|
4
|
+
jktThumbprint: string;
|
|
5
|
+
/** Private key handle. Not serialisable. */
|
|
6
|
+
privateKey: CryptoKey;
|
|
7
|
+
}
|
|
8
|
+
/** Generate a fresh ES256 keypair with `extractable: false` on the private key. */
|
|
9
|
+
export declare function generateKeypair(): Promise<DPoPKeypair>;
|
|
10
|
+
/**
|
|
11
|
+
* RFC 7638 JWK thumbprint. EC keys hash the canonical-JSON of
|
|
12
|
+
* `{ crv, kty, x, y }` (lexicographically sorted, no whitespace).
|
|
13
|
+
*/
|
|
14
|
+
export declare function computeJwkThumbprint(jwk: JsonWebKey): Promise<string>;
|
|
15
|
+
/** SHA-256(access_token), base64url-encoded — for the `ath` claim. */
|
|
16
|
+
export declare function accessTokenHash(accessToken: string): Promise<string>;
|
|
17
|
+
/**
|
|
18
|
+
* Build a DPoP proof JWT for the given HTTP method + URL. If an access token
|
|
19
|
+
* is supplied, includes the `ath` claim (access-token hash) per §4.2.
|
|
20
|
+
*
|
|
21
|
+
* Gate 1: function returns a real JWT; the server simply does not require
|
|
22
|
+
* the `DPoP` header. Host pages may still attach the header to seed traffic.
|
|
23
|
+
*/
|
|
24
|
+
export declare function generateProof(args: {
|
|
25
|
+
keypair: DPoPKeypair;
|
|
26
|
+
htm: string;
|
|
27
|
+
htu: string;
|
|
28
|
+
accessToken?: string;
|
|
29
|
+
}): Promise<string>;
|
|
30
|
+
//# sourceMappingURL=dpop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dpop.d.ts","sourceRoot":"","sources":["../src/dpop.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,UAAU,CAAC;IACtB,6CAA6C;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,UAAU,EAAE,SAAS,CAAC;CACvB;AAED,mFAAmF;AACnF,wBAAsB,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC,CAU5D;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAM3E;AAED,sEAAsE;AACtE,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI1E;AAQD;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,OAAO,EAAE,WAAW,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBlB"}
|
package/dist/dpop.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// RFC 9449 DPoP proof helpers.
|
|
2
|
+
//
|
|
3
|
+
// **Gate status:** lib SHIPS this code at Gate 1 but the server runs in
|
|
4
|
+
// `off` mode and ignores DPoP. The function call surface is finalised here
|
|
5
|
+
// so the Gate 2 transition is a one-line server flip; client code does not
|
|
6
|
+
// rewrite.
|
|
7
|
+
//
|
|
8
|
+
// Key lifecycle:
|
|
9
|
+
// - ES256 (P-256 ECDSA) keypair, `extractable: false`. Private key is a
|
|
10
|
+
// WebCrypto handle stored in IndexedDB (handle reference; bytes never
|
|
11
|
+
// leave the subtle worker).
|
|
12
|
+
// - Public key JWK + RFC 7638 thumbprint = `jkt` claim baked into BAS rows
|
|
13
|
+
// for binding.
|
|
14
|
+
//
|
|
15
|
+
// Proof JWT (per §4.2):
|
|
16
|
+
// header: { alg: "ES256", typ: "dpop+jwt", jwk: <public JWK> }
|
|
17
|
+
// payload: { jti: <random>, htm: <method>, htu: <url>, iat: <epoch> [, ath: <hash(at)>] }
|
|
18
|
+
// signature: ES256 over `${b64(header)}.${b64(payload)}`
|
|
19
|
+
import { base64urlEncode } from "./oauth.js";
|
|
20
|
+
const ALG = { name: "ECDSA", namedCurve: "P-256" };
|
|
21
|
+
const SIGN_ALG = { name: "ECDSA", hash: "SHA-256" };
|
|
22
|
+
/** Generate a fresh ES256 keypair with `extractable: false` on the private key. */
|
|
23
|
+
export async function generateKeypair() {
|
|
24
|
+
const kp = await crypto.subtle.generateKey(ALG, false, ["sign", "verify"]);
|
|
25
|
+
const publicJwk = await crypto.subtle.exportKey("jwk", kp.publicKey);
|
|
26
|
+
// Make sure only the public-portion fields are kept (sanity strip; private
|
|
27
|
+
// can't actually be exported since extractable=false).
|
|
28
|
+
delete publicJwk.d;
|
|
29
|
+
delete publicJwk.key_ops;
|
|
30
|
+
delete publicJwk.ext;
|
|
31
|
+
const jktThumbprint = await computeJwkThumbprint(publicJwk);
|
|
32
|
+
return { publicJwk, jktThumbprint, privateKey: kp.privateKey };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* RFC 7638 JWK thumbprint. EC keys hash the canonical-JSON of
|
|
36
|
+
* `{ crv, kty, x, y }` (lexicographically sorted, no whitespace).
|
|
37
|
+
*/
|
|
38
|
+
export async function computeJwkThumbprint(jwk) {
|
|
39
|
+
if (jwk.kty !== "EC")
|
|
40
|
+
throw new Error(`Unsupported jwk.kty: ${jwk.kty}`);
|
|
41
|
+
const canonical = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y };
|
|
42
|
+
const bytes = new TextEncoder().encode(JSON.stringify(canonical));
|
|
43
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
44
|
+
return base64urlEncode(new Uint8Array(digest));
|
|
45
|
+
}
|
|
46
|
+
/** SHA-256(access_token), base64url-encoded — for the `ath` claim. */
|
|
47
|
+
export async function accessTokenHash(accessToken) {
|
|
48
|
+
const bytes = new TextEncoder().encode(accessToken);
|
|
49
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
50
|
+
return base64urlEncode(new Uint8Array(digest));
|
|
51
|
+
}
|
|
52
|
+
function randomJti() {
|
|
53
|
+
const bytes = new Uint8Array(16);
|
|
54
|
+
crypto.getRandomValues(bytes);
|
|
55
|
+
return base64urlEncode(bytes);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Build a DPoP proof JWT for the given HTTP method + URL. If an access token
|
|
59
|
+
* is supplied, includes the `ath` claim (access-token hash) per §4.2.
|
|
60
|
+
*
|
|
61
|
+
* Gate 1: function returns a real JWT; the server simply does not require
|
|
62
|
+
* the `DPoP` header. Host pages may still attach the header to seed traffic.
|
|
63
|
+
*/
|
|
64
|
+
export async function generateProof(args) {
|
|
65
|
+
const header = {
|
|
66
|
+
alg: "ES256",
|
|
67
|
+
typ: "dpop+jwt",
|
|
68
|
+
jwk: args.keypair.publicJwk,
|
|
69
|
+
};
|
|
70
|
+
const payload = {
|
|
71
|
+
jti: randomJti(),
|
|
72
|
+
htm: args.htm.toUpperCase(),
|
|
73
|
+
htu: args.htu,
|
|
74
|
+
iat: Math.floor(Date.now() / 1000),
|
|
75
|
+
};
|
|
76
|
+
if (args.accessToken)
|
|
77
|
+
payload.ath = await accessTokenHash(args.accessToken);
|
|
78
|
+
const encH = base64urlEncode(new TextEncoder().encode(JSON.stringify(header)));
|
|
79
|
+
const encP = base64urlEncode(new TextEncoder().encode(JSON.stringify(payload)));
|
|
80
|
+
const signingInput = `${encH}.${encP}`;
|
|
81
|
+
const sig = await crypto.subtle.sign(SIGN_ALG, args.keypair.privateKey, new TextEncoder().encode(signingInput));
|
|
82
|
+
// WebCrypto returns raw r||s for ECDSA; that's already the JOSE-mandated
|
|
83
|
+
// shape (no DER-unwrap needed).
|
|
84
|
+
const encS = base64urlEncode(new Uint8Array(sig));
|
|
85
|
+
return `${signingInput}.${encS}`;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=dpop.js.map
|
package/dist/dpop.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dpop.js","sourceRoot":"","sources":["../src/dpop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { BravelyAccountManager } from "../BravelyAccountManager.js";
|
|
2
|
+
export type ActivationLaneSource = "upgraded" | "checkout" | "subscription" | null;
|
|
3
|
+
export interface ActivationLaneResult {
|
|
4
|
+
/** True iff any recognized post-checkout return param is present. */
|
|
5
|
+
inActivationLane: boolean;
|
|
6
|
+
/** Which param was detected (or null). */
|
|
7
|
+
source: ActivationLaneSource;
|
|
8
|
+
/** The raw query param value (e.g. `"true"`, `"complete"`, `"success"`). */
|
|
9
|
+
rawValue: string | null;
|
|
10
|
+
/**
|
|
11
|
+
* Removes the recognized post-checkout query param via
|
|
12
|
+
* `history.replaceState`. Idempotent. Host pages should call this once
|
|
13
|
+
* they have observed the activation lane is engaged.
|
|
14
|
+
*/
|
|
15
|
+
clearUrlParam: () => void;
|
|
16
|
+
}
|
|
17
|
+
interface DetectedLane {
|
|
18
|
+
source: Exclude<ActivationLaneSource, null>;
|
|
19
|
+
paramName: string;
|
|
20
|
+
rawValue: string;
|
|
21
|
+
}
|
|
22
|
+
/** Pure detector — used by the hook and unit-testable on its own. */
|
|
23
|
+
export declare function detectActivationLane(search: string | URLSearchParams): DetectedLane | null;
|
|
24
|
+
export interface UseActivationLaneFromUrlOptions {
|
|
25
|
+
/**
|
|
26
|
+
* If supplied, the hook will call `manager.notifyCheckoutCompleted()`
|
|
27
|
+
* once when the lane is engaged.
|
|
28
|
+
*/
|
|
29
|
+
manager?: BravelyAccountManager | null;
|
|
30
|
+
/**
|
|
31
|
+
* If true (default false) AND a manager is supplied AND it exposes
|
|
32
|
+
* `pollForActivation()`, the hook will start the polling loop in the
|
|
33
|
+
* background once the lane is engaged. Failures are swallowed; the
|
|
34
|
+
* caller is responsible for surfacing them via the manager's state.
|
|
35
|
+
*/
|
|
36
|
+
autoStartPolling?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Override the URL source. Defaults to `window.location.search`. Useful
|
|
39
|
+
* for SSR / testing.
|
|
40
|
+
*/
|
|
41
|
+
urlSearch?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Override the URL clearer. Defaults to `window.history.replaceState`.
|
|
44
|
+
*/
|
|
45
|
+
onClearParam?: (paramName: string) => void;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* React hook surfacing the post-checkout activation lane state. Stable
|
|
49
|
+
* across re-renders. Calls `manager.notifyCheckoutCompleted()` exactly
|
|
50
|
+
* once per detected lane.
|
|
51
|
+
*/
|
|
52
|
+
export declare function useActivationLaneFromUrl(opts?: UseActivationLaneFromUrlOptions): ActivationLaneResult;
|
|
53
|
+
export {};
|
|
54
|
+
//# sourceMappingURL=useActivationLaneFromUrl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useActivationLaneFromUrl.d.ts","sourceRoot":"","sources":["../../src/hooks/useActivationLaneFromUrl.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAEzE,MAAM,MAAM,oBAAoB,GAC5B,UAAU,GACV,UAAU,GACV,cAAc,GACd,IAAI,CAAC;AAET,MAAM,WAAW,oBAAoB;IACnC,qEAAqE;IACrE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,0CAA0C;IAC1C,MAAM,EAAE,oBAAoB,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;;;OAIG;IACH,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,UAAU,YAAY;IACpB,MAAM,EAAE,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AASD,qEAAqE;AACrE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,GAAG,YAAY,GAAG,IAAI,CAc1F;AAED,MAAM,WAAW,+BAA+B;IAC9C;;;OAGG;IACH,OAAO,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACvC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,GAAE,+BAAoC,GACzC,oBAAoB,CAuEtB"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// `useActivationLaneFromUrl()` — detect post-checkout return URL params.
|
|
2
|
+
//
|
|
3
|
+
// Three patterns observed across the 4 web variants per the rollout plan:
|
|
4
|
+
// - `?upgraded=true` (prodjectly)
|
|
5
|
+
// - `?checkout=complete` (scry-web)
|
|
6
|
+
// - `?subscription=success` (todoingly-web)
|
|
7
|
+
//
|
|
8
|
+
// The hook reports whether any of those signals is present, which one fired,
|
|
9
|
+
// and exposes a `clearUrlParam()` callback that removes the param via
|
|
10
|
+
// `window.history.replaceState` so a refresh doesn't re-trigger the
|
|
11
|
+
// activation lane. As a side effect, when `inActivationLane === true`, it
|
|
12
|
+
// also fires `manager.notifyCheckoutCompleted()` once, then optionally
|
|
13
|
+
// starts the `pollForActivation()` runner if `autoStartPolling` is set.
|
|
14
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
15
|
+
/** Maps each recognized param to its `source` label. */
|
|
16
|
+
const PATTERNS = [
|
|
17
|
+
{ param: "upgraded", source: "upgraded" },
|
|
18
|
+
{ param: "checkout", source: "checkout" },
|
|
19
|
+
{ param: "subscription", source: "subscription" },
|
|
20
|
+
];
|
|
21
|
+
/** Pure detector — used by the hook and unit-testable on its own. */
|
|
22
|
+
export function detectActivationLane(search) {
|
|
23
|
+
const params = typeof search === "string"
|
|
24
|
+
? new URLSearchParams(search.startsWith("?") ? search.slice(1) : search)
|
|
25
|
+
: search;
|
|
26
|
+
for (const { param, source } of PATTERNS) {
|
|
27
|
+
const value = params.get(param);
|
|
28
|
+
if (value === null)
|
|
29
|
+
continue;
|
|
30
|
+
// Treat any truthy / completion-style value as engaged. The three
|
|
31
|
+
// variants use `true`, `complete`, `success` respectively. Empty string
|
|
32
|
+
// is treated as "engaged" since some checkout providers send `?foo=`.
|
|
33
|
+
return { source, paramName: param, rawValue: value };
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* React hook surfacing the post-checkout activation lane state. Stable
|
|
39
|
+
* across re-renders. Calls `manager.notifyCheckoutCompleted()` exactly
|
|
40
|
+
* once per detected lane.
|
|
41
|
+
*/
|
|
42
|
+
export function useActivationLaneFromUrl(opts = {}) {
|
|
43
|
+
const { manager, autoStartPolling, urlSearch, onClearParam } = opts;
|
|
44
|
+
const detected = useMemo(() => {
|
|
45
|
+
const source = urlSearch !== undefined
|
|
46
|
+
? urlSearch
|
|
47
|
+
: typeof window !== "undefined"
|
|
48
|
+
? window.location.search
|
|
49
|
+
: "";
|
|
50
|
+
return detectActivationLane(source);
|
|
51
|
+
}, [urlSearch]);
|
|
52
|
+
// We hold a separate piece of state so `clearUrlParam()` calls can reset
|
|
53
|
+
// it (the hook should report `inActivationLane === false` after the
|
|
54
|
+
// caller has cleared the param). But re-deriving from `urlSearch` makes
|
|
55
|
+
// the hook deterministic.
|
|
56
|
+
const [lane, setLane] = useState(detected);
|
|
57
|
+
// Track whether we've already fired the notify side-effect for this lane.
|
|
58
|
+
const notifiedRef = useRef(null);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
setLane(detected);
|
|
61
|
+
}, [detected]);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (!lane)
|
|
64
|
+
return;
|
|
65
|
+
if (notifiedRef.current === lane)
|
|
66
|
+
return;
|
|
67
|
+
notifiedRef.current = lane;
|
|
68
|
+
if (manager) {
|
|
69
|
+
try {
|
|
70
|
+
manager.notifyCheckoutCompleted();
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Manager state machine may reject the event from the current
|
|
74
|
+
// state; that's a no-op by design.
|
|
75
|
+
}
|
|
76
|
+
if (autoStartPolling) {
|
|
77
|
+
// Fire-and-forget; manager surfaces results via state change.
|
|
78
|
+
const m = manager;
|
|
79
|
+
if (typeof m.pollForActivation === "function") {
|
|
80
|
+
void m.pollForActivation().catch(() => undefined);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}, [lane, manager, autoStartPolling]);
|
|
85
|
+
const clearUrlParam = () => {
|
|
86
|
+
if (!lane)
|
|
87
|
+
return;
|
|
88
|
+
if (onClearParam) {
|
|
89
|
+
onClearParam(lane.paramName);
|
|
90
|
+
}
|
|
91
|
+
else if (typeof window !== "undefined" && window.history?.replaceState) {
|
|
92
|
+
const url = new URL(window.location.href);
|
|
93
|
+
url.searchParams.delete(lane.paramName);
|
|
94
|
+
window.history.replaceState(window.history.state, "", url.pathname + (url.search ? url.search : "") + url.hash);
|
|
95
|
+
}
|
|
96
|
+
setLane(null);
|
|
97
|
+
};
|
|
98
|
+
return {
|
|
99
|
+
inActivationLane: lane !== null,
|
|
100
|
+
source: lane?.source ?? null,
|
|
101
|
+
rawValue: lane?.rawValue ?? null,
|
|
102
|
+
clearUrlParam,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=useActivationLaneFromUrl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useActivationLaneFromUrl.js","sourceRoot":"","sources":["../../src/hooks/useActivationLaneFromUrl.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { BravelyAccountManager } from "../BravelyAccountManager.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returned shape from `useFreshLaunchRestoration()`. `shouldShowToast`
|
|
4
|
+
* flips true after the 3 s timer when this is a first launch with a
|
|
5
|
+
* successfully-hydrated entitlement; flips back to false when the host
|
|
6
|
+
* page calls `dismissToast()`.
|
|
7
|
+
*/
|
|
8
|
+
export interface FreshLaunchRestorationResult {
|
|
9
|
+
/** Count of items synced (host page provides via `setSyncedCount`). */
|
|
10
|
+
syncedCount: number | null;
|
|
11
|
+
/** Resolved name of the other device (Gate 2). Always null in Gate 1. */
|
|
12
|
+
otherDeviceName: string | null;
|
|
13
|
+
/** True iff the toast should currently render. */
|
|
14
|
+
shouldShowToast: boolean;
|
|
15
|
+
/** Plural-aware item label. Defaults to `items`. */
|
|
16
|
+
itemsLabel: string;
|
|
17
|
+
/**
|
|
18
|
+
* Fully-rendered toast copy ready to mount. Drops the `[other-device]`
|
|
19
|
+
* anchor when null (Gate 1 fallback per plan doc decision #2). Empty
|
|
20
|
+
* string when nothing should render.
|
|
21
|
+
*/
|
|
22
|
+
toastText: string;
|
|
23
|
+
/** Host page reports how many items were synced (tasks, notes, etc.). */
|
|
24
|
+
setSyncedCount: (n: number) => void;
|
|
25
|
+
/** Host page closes the toast (UI dismiss). */
|
|
26
|
+
dismissToast: () => void;
|
|
27
|
+
}
|
|
28
|
+
export interface UseFreshLaunchRestorationOptions {
|
|
29
|
+
/** Manager singleton. Required for the entitlement-cache check. */
|
|
30
|
+
manager: BravelyAccountManager;
|
|
31
|
+
/**
|
|
32
|
+
* Storage key holding the boolean first-launch marker. Defaults to
|
|
33
|
+
* `bravely:fresh_launch_marker_v1`. Bump the suffix if the meaning of
|
|
34
|
+
* "first launch" changes.
|
|
35
|
+
*/
|
|
36
|
+
markerKey?: string;
|
|
37
|
+
/** Item-label shown in the toast (e.g. "tasks", "stickies"). */
|
|
38
|
+
itemsLabel?: string;
|
|
39
|
+
/**
|
|
40
|
+
* ms after detection until the toast fires. Defaults to 3000 per the
|
|
41
|
+
* state machine `fresh_launch_restoration` 0-3s window.
|
|
42
|
+
*/
|
|
43
|
+
toastDelayMs?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Resolver for the "other device" string (Gate 2). Pass a function that
|
|
46
|
+
* returns the most-recent OTHER device fingerprint -> human name (e.g.
|
|
47
|
+
* "iPad" / "Mac mini"). When null, copy drops the anchor.
|
|
48
|
+
*/
|
|
49
|
+
resolveOtherDeviceName?: () => string | null;
|
|
50
|
+
/** Override the localStorage / sessionStorage accessor for tests. */
|
|
51
|
+
storage?: {
|
|
52
|
+
getItem(k: string): string | null;
|
|
53
|
+
setItem(k: string, v: string): void;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Pure-function copy renderer for the M2 toast. Drops the `[other-device]`
|
|
58
|
+
* anchor when null. Banned: the entire "Welcome back" phrase family.
|
|
59
|
+
*/
|
|
60
|
+
export declare function renderFreshLaunchToastCopy(syncedCount: number | null, otherDeviceName: string | null, itemsLabel?: string): string;
|
|
61
|
+
export declare function useFreshLaunchRestoration(opts: UseFreshLaunchRestorationOptions): FreshLaunchRestorationResult;
|
|
62
|
+
//# sourceMappingURL=useFreshLaunchRestoration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFreshLaunchRestoration.d.ts","sourceRoot":"","sources":["../../src/hooks/useFreshLaunchRestoration.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAGzE;;;;;GAKG;AACH,MAAM,WAAW,4BAA4B;IAC3C,uEAAuE;IACvE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,yEAAyE;IACzE,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kDAAkD;IAClD,eAAe,EAAE,OAAO,CAAC;IACzB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,cAAc,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,+CAA+C;IAC/C,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,gCAAgC;IAC/C,mEAAmE;IACnE,OAAO,EAAE,qBAAqB,CAAC;IAC/B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAC7C,qEAAqE;IACrE,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;QAClC,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACrC,CAAC;CACH;AAMD;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,EAC9B,UAAU,GAAE,MAA4B,GACvC,MAAM,CAOR;AA2BD,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,gCAAgC,GACrC,4BAA4B,CAuF9B"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// `useFreshLaunchRestoration()` — M2 silent rehydrate + brief toast.
|
|
2
|
+
//
|
|
3
|
+
// Detects a "fresh launch on this device" — i.e. the user signed in on a
|
|
4
|
+
// new device (or cleared the cache) and we just hydrated an entitlement
|
|
5
|
+
// from the server. UI is silent for the first 3 s while data loads; after
|
|
6
|
+
// 3 s a bottom-edge toast surfaces with `Synced [N] [items] from your
|
|
7
|
+
// [other-device].` (or the device-anchor-less variant when the resolver
|
|
8
|
+
// is not available yet — Gate 1 fallback per plan doc decision #2).
|
|
9
|
+
//
|
|
10
|
+
// Banned: NO "Welcome back. Restoring your Pro features." Anywhere. Anti-
|
|
11
|
+
// pattern #3 from the journey doc.
|
|
12
|
+
//
|
|
13
|
+
// First-launch detection uses a single boolean key in storage (set on first
|
|
14
|
+
// hydrate). Subsequent launches see `first-launch-marker !== null` and the
|
|
15
|
+
// hook returns `shouldShowToast = false`.
|
|
16
|
+
import { useEffect, useRef, useState } from "react";
|
|
17
|
+
const DEFAULT_MARKER_KEY = "bravely:fresh_launch_marker_v1";
|
|
18
|
+
const DEFAULT_TOAST_DELAY_MS = 3000;
|
|
19
|
+
const DEFAULT_ITEMS_LABEL = "items";
|
|
20
|
+
/**
|
|
21
|
+
* Pure-function copy renderer for the M2 toast. Drops the `[other-device]`
|
|
22
|
+
* anchor when null. Banned: the entire "Welcome back" phrase family.
|
|
23
|
+
*/
|
|
24
|
+
export function renderFreshLaunchToastCopy(syncedCount, otherDeviceName, itemsLabel = DEFAULT_ITEMS_LABEL) {
|
|
25
|
+
if (syncedCount === null || syncedCount <= 0)
|
|
26
|
+
return "";
|
|
27
|
+
const label = itemsLabel || DEFAULT_ITEMS_LABEL;
|
|
28
|
+
if (otherDeviceName && otherDeviceName.length > 0) {
|
|
29
|
+
return `Synced ${syncedCount} ${label} from your ${otherDeviceName}.`;
|
|
30
|
+
}
|
|
31
|
+
return `Synced ${syncedCount} ${label}.`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Choose which storage to use. localStorage on browser; in-memory shim on
|
|
35
|
+
* SSR/test. The host page's preferences in `manager.storage` are ignored
|
|
36
|
+
* here because the marker is a UI-only signal (resetting it on sign-out
|
|
37
|
+
* is undesirable — the user is still on a "fresh device" mathematically).
|
|
38
|
+
*/
|
|
39
|
+
function pickStorage(override) {
|
|
40
|
+
if (override)
|
|
41
|
+
return override;
|
|
42
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
43
|
+
return {
|
|
44
|
+
getItem: (k) => window.localStorage.getItem(k),
|
|
45
|
+
setItem: (k, v) => window.localStorage.setItem(k, v),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const m = new Map();
|
|
49
|
+
return {
|
|
50
|
+
getItem: (k) => m.get(k) ?? null,
|
|
51
|
+
setItem: (k, v) => {
|
|
52
|
+
m.set(k, v);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export function useFreshLaunchRestoration(opts) {
|
|
57
|
+
const { manager, markerKey = DEFAULT_MARKER_KEY, itemsLabel = DEFAULT_ITEMS_LABEL, toastDelayMs = DEFAULT_TOAST_DELAY_MS, resolveOtherDeviceName, storage, } = opts;
|
|
58
|
+
const store = pickStorage(storage);
|
|
59
|
+
const [syncedCount, setSyncedCountState] = useState(null);
|
|
60
|
+
const [shouldShowToast, setShouldShowToast] = useState(false);
|
|
61
|
+
const [otherDeviceName, setOtherDeviceName] = useState(() => {
|
|
62
|
+
try {
|
|
63
|
+
return resolveOtherDeviceName ? resolveOtherDeviceName() ?? null : null;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
// Track whether we've armed the toast for this hook lifetime so we don't
|
|
70
|
+
// double-fire on re-render.
|
|
71
|
+
const armedRef = useRef(false);
|
|
72
|
+
const timerRef = useRef(null);
|
|
73
|
+
// Track whether we've already marked first-launch so subsequent calls
|
|
74
|
+
// (e.g. React strict mode double-mount in dev) don't re-arm.
|
|
75
|
+
const markedRef = useRef(false);
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
// Listen for the manager state to flip to `signed_in`. The first time
|
|
78
|
+
// that happens on a "fresh" device (no marker in storage), we arm the
|
|
79
|
+
// toast for `toastDelayMs` ms.
|
|
80
|
+
const handleState = (state) => {
|
|
81
|
+
if (state.kind !== "signed_in")
|
|
82
|
+
return;
|
|
83
|
+
if (markedRef.current)
|
|
84
|
+
return;
|
|
85
|
+
const marker = store.getItem(markerKey);
|
|
86
|
+
if (marker !== null) {
|
|
87
|
+
// Not a fresh launch; do nothing.
|
|
88
|
+
markedRef.current = true;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// Fresh launch. Mark it now so a subsequent sign-out + sign-in in
|
|
92
|
+
// the same tab doesn't re-trigger.
|
|
93
|
+
store.setItem(markerKey, new Date().toISOString());
|
|
94
|
+
markedRef.current = true;
|
|
95
|
+
if (armedRef.current)
|
|
96
|
+
return;
|
|
97
|
+
armedRef.current = true;
|
|
98
|
+
timerRef.current = setTimeout(() => {
|
|
99
|
+
setShouldShowToast(true);
|
|
100
|
+
}, toastDelayMs);
|
|
101
|
+
};
|
|
102
|
+
// Fire once for the current state in case we mounted post-restore.
|
|
103
|
+
handleState(manager.getState());
|
|
104
|
+
const off = manager.onStateChange(handleState);
|
|
105
|
+
return () => {
|
|
106
|
+
off();
|
|
107
|
+
if (timerRef.current !== null)
|
|
108
|
+
clearTimeout(timerRef.current);
|
|
109
|
+
};
|
|
110
|
+
}, [manager, markerKey, store, toastDelayMs]);
|
|
111
|
+
// Re-resolve the device name if the resolver itself changes (e.g. Gate
|
|
112
|
+
// 2 plug-in mounts after first paint).
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
try {
|
|
115
|
+
setOtherDeviceName(resolveOtherDeviceName ? resolveOtherDeviceName() ?? null : null);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
setOtherDeviceName(null);
|
|
119
|
+
}
|
|
120
|
+
}, [resolveOtherDeviceName]);
|
|
121
|
+
const toastText = renderFreshLaunchToastCopy(syncedCount, otherDeviceName, itemsLabel);
|
|
122
|
+
// Suppress the toast surface when the copy is empty (no items, or
|
|
123
|
+
// negative count) — there is nothing to say.
|
|
124
|
+
const effectiveShow = shouldShowToast && toastText.length > 0;
|
|
125
|
+
return {
|
|
126
|
+
syncedCount,
|
|
127
|
+
otherDeviceName,
|
|
128
|
+
shouldShowToast: effectiveShow,
|
|
129
|
+
itemsLabel,
|
|
130
|
+
toastText,
|
|
131
|
+
setSyncedCount: setSyncedCountState,
|
|
132
|
+
dismissToast: () => setShouldShowToast(false),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=useFreshLaunchRestoration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFreshLaunchRestoration.js","sourceRoot":"","sources":["../../src/hooks/useFreshLaunchRestoration.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { BravelyAccountManager, BravelyClientKilledError, OAuthError } from "./BravelyAccountManager.js";
|
|
2
|
+
export type { ActivationResult } from "./BravelyAccountManager.js";
|
|
3
|
+
export { EntitlementCache, ENTITLEMENT_CACHE_TTL_SECONDS } from "./EntitlementCache.js";
|
|
4
|
+
export { ActivationStateMachine, AUTO_ADVANCE_MS, AUTO_ADVANCE_TO, POST_CHECKOUT_RETRY, type ActivationEvent, } from "./ActivationStateMachine.js";
|
|
5
|
+
export { parseDeprecation } from "./deprecation.js";
|
|
6
|
+
export { generateCodeVerifier, generateCodeChallenge, generateState, buildAuthorizeUrl, parseCallback, preparePkce, base64urlEncode, base64urlDecode, } from "./oauth.js";
|
|
7
|
+
export { defaultStorage, memoryStorage, sessionStorageBacked, indexedDbBacked, CompositeStorage } from "./storage.js";
|
|
8
|
+
export type { Storage } from "./storage.js";
|
|
9
|
+
export { generateKeypair, computeJwkThumbprint, generateProof, accessTokenHash, type DPoPKeypair, } from "./dpop.js";
|
|
10
|
+
export type { AppSlug, Entitlement, EntitlementSnapshot, OAuthTokenResponse, BasResponse, BravelyAccountState, ActivationState, ActivationStateName, ManagerConfig, LibVersionPolicy, DeprecationDirective, DeprecationEndpoint, DeprecationClient, CheckoutPlan, PaddlePortalSession, CachedEntitlements, DPoPProof, } from "./types.js";
|
|
11
|
+
export { ActivationLadder, ACTIVATION_LADDER_PHASE_TEMPLATES, ladderPhaseForElapsed, renderLadderCopy, buildResolvedLadder, type ActivationLadderProps, type ActivationLadderPhase, } from "./components/ActivationLadder.js";
|
|
12
|
+
export { CrossAppCard, filterCrossAppEntitlements, countCrossAppEntitlements, renderCrossAppCopy, type CrossAppCardProps, } from "./components/CrossAppCard.js";
|
|
13
|
+
export { useActivationLaneFromUrl, detectActivationLane, type ActivationLaneResult, type ActivationLaneSource, type UseActivationLaneFromUrlOptions, } from "./hooks/useActivationLaneFromUrl.js";
|
|
14
|
+
export { useFreshLaunchRestoration, renderFreshLaunchToastCopy, type FreshLaunchRestorationResult, type UseFreshLaunchRestorationOptions, } from "./hooks/useFreshLaunchRestoration.js";
|
|
15
|
+
export { displayNameFor, DISPLAY_NAMES } from "./displayName.js";
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACzG,YAAY,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAC;AACxF,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,KAAK,eAAe,GACrB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACtH,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,KAAK,WAAW,GACjB,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,OAAO,EACP,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,mBAAmB,EACnB,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,SAAS,GACV,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,gBAAgB,EAChB,iCAAiC,EACjC,qBAAqB,EACrB,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,GAC3B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,YAAY,EACZ,0BAA0B,EAC1B,yBAAyB,EACzB,kBAAkB,EAClB,KAAK,iBAAiB,GACvB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,+BAA+B,GACrC,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,yBAAyB,EACzB,0BAA0B,EAC1B,KAAK,4BAA4B,EACjC,KAAK,gCAAgC,GACtC,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Barrel re-exports for `@bravely-studios/account-web`.
|
|
2
|
+
export { BravelyAccountManager, BravelyClientKilledError, OAuthError } from "./BravelyAccountManager.js";
|
|
3
|
+
export { EntitlementCache, ENTITLEMENT_CACHE_TTL_SECONDS } from "./EntitlementCache.js";
|
|
4
|
+
export { ActivationStateMachine, AUTO_ADVANCE_MS, AUTO_ADVANCE_TO, POST_CHECKOUT_RETRY, } from "./ActivationStateMachine.js";
|
|
5
|
+
export { parseDeprecation } from "./deprecation.js";
|
|
6
|
+
export { generateCodeVerifier, generateCodeChallenge, generateState, buildAuthorizeUrl, parseCallback, preparePkce, base64urlEncode, base64urlDecode, } from "./oauth.js";
|
|
7
|
+
export { defaultStorage, memoryStorage, sessionStorageBacked, indexedDbBacked, CompositeStorage } from "./storage.js";
|
|
8
|
+
export { generateKeypair, computeJwkThumbprint, generateProof, accessTokenHash, } from "./dpop.js";
|
|
9
|
+
// ---- C-ux M2/M3/M4 surfaces (Wave A) ---------------------------------------
|
|
10
|
+
export { ActivationLadder, ACTIVATION_LADDER_PHASE_TEMPLATES, ladderPhaseForElapsed, renderLadderCopy, buildResolvedLadder, } from "./components/ActivationLadder.js";
|
|
11
|
+
export { CrossAppCard, filterCrossAppEntitlements, countCrossAppEntitlements, renderCrossAppCopy, } from "./components/CrossAppCard.js";
|
|
12
|
+
export { useActivationLaneFromUrl, detectActivationLane, } from "./hooks/useActivationLaneFromUrl.js";
|
|
13
|
+
export { useFreshLaunchRestoration, renderFreshLaunchToastCopy, } from "./hooks/useFreshLaunchRestoration.js";
|
|
14
|
+
export { displayNameFor, DISPLAY_NAMES } from "./displayName.js";
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAExD,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAEzG,OAAO,EAAE,gBAAgB,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAC;AACxF,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,eAAe,EACf,mBAAmB,GAEpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEtH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,eAAe,GAEhB,MAAM,WAAW,CAAC;AAqBnB,+EAA+E;AAC/E,OAAO,EACL,gBAAgB,EAChB,iCAAiC,EACjC,qBAAqB,EACrB,gBAAgB,EAChB,mBAAmB,GAGpB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,YAAY,EACZ,0BAA0B,EAC1B,yBAAyB,EACzB,kBAAkB,GAEnB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,wBAAwB,EACxB,oBAAoB,GAIrB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,yBAAyB,EACzB,0BAA0B,GAG3B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
|