@atlasauth/js 0.17.0 → 0.19.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.
@@ -1 +1 @@
1
- {"version":3,"file":"passkey-sign-up.js","sourceRoot":"","sources":["../src/passkey-sign-up.ts"],"names":[],"mappings":";;AAmCA,gDAgBC;AAGD,0CAOC;AAQD,sDAiDC;AAtHD;;;;;;;;;;;;;;;;GAgBG;AACH,iCAAsE;AAEtE,uCAA2F;AAe3F,uFAAuF;AAChF,KAAK,UAAU,kBAAkB,CAAC,MAAkB,EAAE,KAAyB;IACpF,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAA0D,6BAA6B,EAAE;QACzH,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3C,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrE,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI;QAAE,OAAO,EAAE,GAAG,kBAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IACvF,OAAO;QACL,GAAG,kBAAW;QACd,OAAO,EAAE,QAAQ,CAAC,IAAI;QACtB,0EAA0E;QAC1E,UAAU,EAAE,QAAQ,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI;KAC9C,CAAC;AACJ,CAAC;AAED,iFAAiF;AACjF,SAAgB,eAAe,CAAC,OAAuC;IACrE,OAAO,CACL,CAAC,CAAC,OAAO;QACT,OAAO,CAAC,MAAM,KAAK,iBAAiB;QACpC,OAAO,CAAC,MAAM,KAAK,oBAAoB;QACvC,CAAC,OAAO,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAC5D,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,qBAAqB,CACzC,MAAkB,EAClB,KAAgB,EAChB,OAAyE,EAAE;IAE3E,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QAC7B,OAAO,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,uCAAuC,EAAE,CAAC,EAAE,CAAC;IAC1H,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,CAA0B,uBAAuB,EAAE,gBAAgB,EAAE;QAClG,WAAW,EAAE,KAAK,CAAC,UAAU;KAC9B,CAAC,CAAC;IACH,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI;QAAE,OAAO,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;IAErF,IAAI,IAAI,CAAC;IACT,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,IAAA,uBAAa,EAAC,KAAK,CAAC,IAAI,EAAE;YACrC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,0FAA0F;YAC1F,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACtC,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACpF,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,GAAG,KAAK;YACR,IAAI,EAAE,KAAK;YACX,MAAM,EAAE;gBACN;oBACE,IAAI,EAAE,mBAAmB;oBACzB,gFAAgF;oBAChF,OAAO,EAAE,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,8BAA8B;iBAClG;aACF;SACF,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAoC,uBAAuB,EAAE,iBAAiB,EAAE;QAC9G,WAAW,EAAE,KAAK,CAAC,UAAU;QAC7B,GAAG,IAAI;KACR,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;QAAE,OAAO,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IACxF,OAAO;QACL,GAAG,KAAK;QACR,OAAO,EAAE,MAAM,CAAC,IAAI;QACpB,MAAM,EAAE,EAAE;QACV,IAAI,EAAE,KAAK;QACX,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI;KACnC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"passkey-sign-up.js","sourceRoot":"","sources":["../src/passkey-sign-up.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Policy acceptance (Terms of Service, privacy policy, any policy the
3
+ * application defines) — the Frontend API helpers every framework SDK and
4
+ * custom UI share.
5
+ *
6
+ * - `getPolicies()` — the current version of every policy, ready to render
7
+ * (`content_html` is already sanitised; `content_sha256` is its hash).
8
+ * - `buildPolicyAcceptances()` — turn what the user saw into the
9
+ * `policy_acceptances` a sign-up, a held sign-in or an in-app acceptance sends.
10
+ * - `startSignUp({ policyAcceptances })` — accept at sign-up.
11
+ * - `acceptAttemptPolicies()` — continue a sign-in (or social sign-up) the
12
+ * server held at `needs_policy_acceptance`; the completion carries the
13
+ * ticket exactly like any other step.
14
+ * - `getMyPolicies()` / `acceptPolicies()` — the signed-in user's standing,
15
+ * and accepting from inside the app (e.g. a banner during a grace window).
16
+ */
17
+ import type { FieldError, PendingPolicy } from './attempt';
18
+ import type { FapiClient, FlowState } from './fapi';
19
+ export type PolicyDisplayMode = 'checkbox' | 'scroll' | 'link';
20
+ /** A policy's current version as `GET /v1/client/policies` returns it. */
21
+ export interface PublicPolicy {
22
+ object: 'policy';
23
+ key: string;
24
+ title: string;
25
+ display: PolicyDisplayMode;
26
+ required: boolean;
27
+ applies_to: 'sign_up' | 'sign_in_and_sign_up';
28
+ position: number;
29
+ version_id: string;
30
+ version: string;
31
+ content_markdown: string | null;
32
+ /** Sanitised HTML: render this (it is what `content_sha256` covers). */
33
+ content_html: string;
34
+ content_sha256: string;
35
+ content_url: string | null;
36
+ summary_of_changes: string | null;
37
+ locale: string | null;
38
+ published_at: number | null;
39
+ effective_at: number | null;
40
+ }
41
+ export interface PublicPolicies {
42
+ object: 'list';
43
+ data: PublicPolicy[];
44
+ /** The legacy single legal checkbox (send `consent: true` on sign-up), when configured. */
45
+ legacy_consent: {
46
+ required: boolean;
47
+ terms_url: string | null;
48
+ privacy_url: string | null;
49
+ } | null;
50
+ }
51
+ /** One entry of `policy_acceptances`. */
52
+ export interface PolicyAcceptance {
53
+ policy: string;
54
+ version_id: string;
55
+ content_sha256: string;
56
+ display?: PolicyDisplayMode;
57
+ /** Scroll mode: the user reached the end (client-attested evidence). */
58
+ scrolled_to_end?: boolean;
59
+ /** How long the document was open, in ms (client-attested evidence). */
60
+ view_ms?: number;
61
+ locale?: string;
62
+ }
63
+ export interface PolicyStanding {
64
+ object: 'policy_standing';
65
+ policy: string;
66
+ policy_id: string;
67
+ title: string;
68
+ display: PolicyDisplayMode;
69
+ required: boolean;
70
+ applies_to: 'sign_up' | 'sign_in_and_sign_up';
71
+ reaccept: 'none' | 'next_sign_in' | 'grace';
72
+ current_version_id: string | null;
73
+ current_version: string | null;
74
+ content_sha256: string | null;
75
+ summary_of_changes: string | null;
76
+ accepted_version_id: string | null;
77
+ accepted_version: string | null;
78
+ accepted_at: number | null;
79
+ needs_reaccept: boolean;
80
+ /** The next sign-in will stop for this policy. */
81
+ blocking: boolean;
82
+ /** A grace window is running until then (show a banner; sign-in still works). */
83
+ grace_ends_at: number | null;
84
+ }
85
+ export interface MyPolicies {
86
+ object: 'list';
87
+ data: PolicyStanding[];
88
+ /** A required policy's current version is not accepted (banner time). */
89
+ needs_acceptance: boolean;
90
+ /** The next sign-in will stop for at least one policy. */
91
+ blocking: boolean;
92
+ }
93
+ export type PolicyResult<T> = {
94
+ ok: true;
95
+ data: T;
96
+ } | {
97
+ ok: false;
98
+ errors: FieldError[];
99
+ };
100
+ /** `GET /v1/client/policies` — the current version of every policy. */
101
+ export declare function getPolicies(client: FapiClient, opts?: {
102
+ locale?: string;
103
+ }): Promise<PolicyResult<PublicPolicies>>;
104
+ /** What the user was shown, per policy: enough to build an acceptance. */
105
+ export interface ShownPolicy {
106
+ /** The policy key. */
107
+ policy?: string;
108
+ key?: string;
109
+ version_id: string;
110
+ content_sha256: string;
111
+ display?: PolicyDisplayMode | string;
112
+ }
113
+ /**
114
+ * Build `policy_acceptances` from the policies the user was shown and agreed
115
+ * to (public policies or an attempt's `pending_policies`). `evidence` carries
116
+ * the per-policy reading signals the UI measured.
117
+ */
118
+ export declare function buildPolicyAcceptances(shown: ReadonlyArray<ShownPolicy | PublicPolicy | PendingPolicy>, evidence?: Record<string, {
119
+ scrolledToEnd?: boolean;
120
+ viewMs?: number;
121
+ }>, opts?: {
122
+ locale?: string;
123
+ }): PolicyAcceptance[];
124
+ /**
125
+ * The `accept_policies` request body: the acceptances plus the proof of the
126
+ * held attempt — the one-time `policy_acceptance_token` (the flow's
127
+ * `policyToken`, else the token on the held attempt view), else the attempt's
128
+ * `poll_secret` (a magic-link or QR tab). The attempt id alone is never enough.
129
+ */
130
+ export declare function acceptPoliciesBody(proof: {
131
+ policyToken?: string | null;
132
+ attempt?: {
133
+ policy_acceptance_token?: unknown;
134
+ } | null;
135
+ pollSecret?: string | null;
136
+ }, acceptances: PolicyAcceptance[]): {
137
+ policy_acceptances: PolicyAcceptance[];
138
+ policy_acceptance_token?: string;
139
+ poll_secret?: string;
140
+ };
141
+ /**
142
+ * `POST /v1/client/sign_ins/:id/accept_policies` — continue an attempt held at
143
+ * `needs_policy_acceptance`. Uses the flow's `policyToken` (or its poll secret
144
+ * for magic-link / QR tabs). The result is the next flow state: usually
145
+ * `complete` with a `ticket` to exchange, or still `needs_policy_acceptance`
146
+ * when a newer version was published meanwhile.
147
+ */
148
+ export declare function acceptAttemptPolicies(client: FapiClient, state: FlowState, acceptances: PolicyAcceptance[]): Promise<FlowState>;
149
+ /** `GET /v1/client/me/policies` — the signed-in user's standing on every policy. */
150
+ export declare function getMyPolicies(client: FapiClient): Promise<PolicyResult<MyPolicies>>;
151
+ /** `POST /v1/client/me/policies/accept` — accept from inside the app. */
152
+ export declare function acceptPolicies(client: FapiClient, acceptances: PolicyAcceptance[]): Promise<PolicyResult<{
153
+ object: 'list';
154
+ data: unknown[];
155
+ standings: PolicyStanding[];
156
+ }>>;
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ /**
3
+ * Policy acceptance (Terms of Service, privacy policy, any policy the
4
+ * application defines) — the Frontend API helpers every framework SDK and
5
+ * custom UI share.
6
+ *
7
+ * - `getPolicies()` — the current version of every policy, ready to render
8
+ * (`content_html` is already sanitised; `content_sha256` is its hash).
9
+ * - `buildPolicyAcceptances()` — turn what the user saw into the
10
+ * `policy_acceptances` a sign-up, a held sign-in or an in-app acceptance sends.
11
+ * - `startSignUp({ policyAcceptances })` — accept at sign-up.
12
+ * - `acceptAttemptPolicies()` — continue a sign-in (or social sign-up) the
13
+ * server held at `needs_policy_acceptance`; the completion carries the
14
+ * ticket exactly like any other step.
15
+ * - `getMyPolicies()` / `acceptPolicies()` — the signed-in user's standing,
16
+ * and accepting from inside the app (e.g. a banner during a grace window).
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.getPolicies = getPolicies;
20
+ exports.buildPolicyAcceptances = buildPolicyAcceptances;
21
+ exports.acceptPoliciesBody = acceptPoliciesBody;
22
+ exports.acceptAttemptPolicies = acceptAttemptPolicies;
23
+ exports.getMyPolicies = getMyPolicies;
24
+ exports.acceptPolicies = acceptPolicies;
25
+ /** `GET /v1/client/policies` — the current version of every policy. */
26
+ async function getPolicies(client, opts = {}) {
27
+ const q = opts.locale ? `?locale=${encodeURIComponent(opts.locale)}` : '';
28
+ const res = await client.request(`/v1/client/policies${q}`, { method: 'GET' });
29
+ return res.ok && res.data ? { ok: true, data: res.data } : { ok: false, errors: res.errors };
30
+ }
31
+ /**
32
+ * Build `policy_acceptances` from the policies the user was shown and agreed
33
+ * to (public policies or an attempt's `pending_policies`). `evidence` carries
34
+ * the per-policy reading signals the UI measured.
35
+ */
36
+ function buildPolicyAcceptances(shown, evidence = {}, opts = {}) {
37
+ return shown.map((p) => {
38
+ const key = ('key' in p && typeof p.key === 'string' ? p.key : undefined) ?? p.policy ?? '';
39
+ const e = evidence[key] ?? {};
40
+ const display = (p.display === 'checkbox' || p.display === 'scroll' || p.display === 'link' ? p.display : undefined);
41
+ return {
42
+ policy: key,
43
+ version_id: p.version_id,
44
+ content_sha256: p.content_sha256,
45
+ ...(display ? { display } : {}),
46
+ ...(e.scrolledToEnd !== undefined ? { scrolled_to_end: e.scrolledToEnd } : {}),
47
+ ...(e.viewMs !== undefined ? { view_ms: Math.max(0, Math.round(e.viewMs)) } : {}),
48
+ ...(opts.locale ? { locale: opts.locale } : {}),
49
+ };
50
+ });
51
+ }
52
+ /**
53
+ * The `accept_policies` request body: the acceptances plus the proof of the
54
+ * held attempt — the one-time `policy_acceptance_token` (the flow's
55
+ * `policyToken`, else the token on the held attempt view), else the attempt's
56
+ * `poll_secret` (a magic-link or QR tab). The attempt id alone is never enough.
57
+ */
58
+ function acceptPoliciesBody(proof, acceptances) {
59
+ const viewToken = typeof proof.attempt?.policy_acceptance_token === 'string' ? proof.attempt.policy_acceptance_token : null;
60
+ const token = proof.policyToken || viewToken;
61
+ return {
62
+ policy_acceptances: acceptances,
63
+ ...(token ? { policy_acceptance_token: token } : proof.pollSecret ? { poll_secret: proof.pollSecret } : {}),
64
+ };
65
+ }
66
+ /**
67
+ * `POST /v1/client/sign_ins/:id/accept_policies` — continue an attempt held at
68
+ * `needs_policy_acceptance`. Uses the flow's `policyToken` (or its poll secret
69
+ * for magic-link / QR tabs). The result is the next flow state: usually
70
+ * `complete` with a `ticket` to exchange, or still `needs_policy_acceptance`
71
+ * when a newer version was published meanwhile.
72
+ */
73
+ async function acceptAttemptPolicies(client, state, acceptances) {
74
+ const id = state.attempt?.id;
75
+ if (!id)
76
+ return { ...state, errors: [{ code: 'VALIDATION_FAILED', message: 'There is no sign-in attempt to continue.' }] };
77
+ const res = await client.post(`/v1/client/sign_ins/${encodeURIComponent(id)}/accept_policies`, acceptPoliciesBody({ policyToken: state.policyToken, attempt: state.attempt, pollSecret: state.pollSecret }, acceptances));
78
+ if (!res.ok || !res.data)
79
+ return { ...state, busy: false, errors: res.errors };
80
+ const view = res.data;
81
+ return {
82
+ attempt: view,
83
+ errors: [],
84
+ busy: false,
85
+ pollSecret: state.pollSecret,
86
+ ticket: view.ticket ?? null,
87
+ prepared: null,
88
+ policyToken: view.policy_acceptance_token ?? null,
89
+ };
90
+ }
91
+ /** `GET /v1/client/me/policies` — the signed-in user's standing on every policy. */
92
+ async function getMyPolicies(client) {
93
+ const res = await client.request('/v1/client/me/policies', { method: 'GET' });
94
+ return res.ok && res.data ? { ok: true, data: res.data } : { ok: false, errors: res.errors };
95
+ }
96
+ /** `POST /v1/client/me/policies/accept` — accept from inside the app. */
97
+ async function acceptPolicies(client, acceptances) {
98
+ const res = await client.post('/v1/client/me/policies/accept', {
99
+ policy_acceptances: acceptances,
100
+ });
101
+ return res.ok && res.data ? { ok: true, data: res.data } : { ok: false, errors: res.errors };
102
+ }
103
+ //# sourceMappingURL=policies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policies.js","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Policy acceptance (terms of service, privacy, any policy) at sign-up and
3
+ * sign-in: the framework-agnostic half every prebuilt UI shares.
4
+ *
5
+ * What lives here:
6
+ * - the wire types and tolerant normalisers for `GET /v1/client/policies`,
7
+ * `GET /v1/client/me/policies` and a sign-in attempt's `pending_policies`;
8
+ * - {@link PolicyAcceptanceController}, a small observable state machine: the
9
+ * policies to show, one checked/read flag per policy, the document the user
10
+ * has open (and for how long), and the `policy_acceptances` payload to send;
11
+ * - the pure helpers a UI needs: scroll-to-end detection, a focus trap, a safe
12
+ * markdown renderer for policies delivered as markdown, and the 422
13
+ * `POLICY_ACCEPTANCE_REQUIRED` reader;
14
+ * - the FAPI calls: load policies, accept them in a sign-in attempt, accept
15
+ * them from inside the app.
16
+ *
17
+ * Display modes (a policy's `display`):
18
+ * - `checkbox`: a checkbox, combined into one sentence when several policies
19
+ * use it (or one each); the policy name opens the document in an in-page
20
+ * viewer, not a new tab;
21
+ * - `scroll`: an unskippable modal, accepted only after the end of the text is
22
+ * seen (or the text fits); declining cancels;
23
+ * - `link`: a clickwrap sentence under the submit button; submitting accepts.
24
+ *
25
+ * Nothing here is a security boundary: `scrolled_to_end` and `view_ms` are
26
+ * client-attested evidence the server records alongside the acceptance.
27
+ */
28
+ import type { FieldError } from './attempt';
29
+ import type { FapiClient } from './fapi';
30
+ import { acceptAttemptPolicies, type PolicyAcceptance, type PolicyDisplayMode } from './policies';
31
+ /** A policy's display mode (same as {@link PolicyDisplayMode}). */
32
+ export type PolicyDisplay = PolicyDisplayMode;
33
+ export type PolicyAppliesTo = 'sign_up' | 'sign_in_and_sign_up';
34
+ /** One policy's current version as the Frontend API serves it, camelCased. */
35
+ export interface Policy {
36
+ key: string;
37
+ title: string;
38
+ display: PolicyDisplay;
39
+ required: boolean;
40
+ appliesTo: PolicyAppliesTo;
41
+ versionId: string;
42
+ version: string;
43
+ /** Sanitised HTML — what the user sees and what {@link Policy.contentSha256} covers. */
44
+ contentHtml: string | null;
45
+ contentMarkdown: string | null;
46
+ contentUrl: string | null;
47
+ contentSha256: string;
48
+ effectiveAt: string | null;
49
+ summaryOfChanges: string | null;
50
+ /** On a sign-in's pending policy: whether the user accepted an earlier version (so "what changed" applies). Null when unknown. */
51
+ reaccept: boolean | null;
52
+ }
53
+ /** One entry of the `policy_acceptances` request body, as {@link PolicyAcceptanceController.build} makes it (always with `display`). */
54
+ export type PolicyAcceptancePayload = PolicyAcceptance & {
55
+ display: PolicyDisplay;
56
+ };
57
+ /** A signed-in user's standing on one policy (`GET /v1/client/me/policies`). */
58
+ export interface MyPolicyStatus {
59
+ key: string;
60
+ title: string;
61
+ acceptedVersion: string | null;
62
+ currentVersion: string | null;
63
+ needsReaccept: boolean;
64
+ graceEndsAt: string | null;
65
+ }
66
+ /** Read one policy from the wire (snake_case, flat or with a nested `version`/`current_version` object). */
67
+ export declare function normalizePolicy(raw: unknown): Policy | null;
68
+ /** Read a list of policies from any of the shapes the API uses (`[..]`, `{policies}`, `{data}`). */
69
+ export declare function normalizePolicies(body: unknown): Policy[];
70
+ /** The policies a sign-in attempt is waiting on (`pending_policies`). */
71
+ export declare function pendingPoliciesOf(attempt: {
72
+ pending_policies?: unknown;
73
+ } | null | undefined): Policy[];
74
+ /** Read `GET /v1/client/me/policies`. */
75
+ export declare function normalizeMyPolicies(body: unknown): MyPolicyStatus[];
76
+ /** Whether an error list contains the server's "accept the policies" refusal. */
77
+ export declare function isPolicyAcceptanceRequired(errors: readonly FieldError[] | null | undefined): boolean;
78
+ /** `GET /v1/client/policies`, read into {@link Policy} objects (empty on failure). */
79
+ export declare function loadPolicies(client: FapiClient, opts?: {
80
+ locale?: string;
81
+ signIn?: boolean;
82
+ }): Promise<Policy[]>;
83
+ /**
84
+ * The policies a sign-up must show: every one with a published version. Required
85
+ * ones block the submit; optional ones may be left unticked.
86
+ */
87
+ export declare function getSignUpPolicies(client: FapiClient, opts?: {
88
+ locale?: string;
89
+ }): Promise<Policy[]>;
90
+ /** `GET /v1/client/me/policies`, read into {@link MyPolicyStatus} entries (empty on failure). */
91
+ export declare function loadMyPolicies(client: FapiClient): Promise<MyPolicyStatus[]>;
92
+ /** `POST /v1/client/me/policies/accept`: accept from inside the app (a banner). */
93
+ export declare function acceptMyPolicies(client: FapiClient, acceptances: PolicyAcceptance[]): Promise<{
94
+ ok: boolean;
95
+ errors: FieldError[];
96
+ }>;
97
+ /**
98
+ * `POST /v1/client/sign_ins/:id/accept_policies`: accept the `pending_policies`
99
+ * of an attempt in `needs_policy_acceptance` (the same call as
100
+ * {@link acceptAttemptPolicies}). Returns the continued flow (a session ticket,
101
+ * or still held when a newer version was published); on a refusal the attempt is kept.
102
+ */
103
+ export declare const acceptSignInPolicies: typeof acceptAttemptPolicies;
104
+ /** Parse the JSON the headless `advance` step carries in `values.policy_acceptances`. */
105
+ export declare function parsePolicyAcceptances(value: string | undefined): PolicyAcceptance[];
106
+ export interface PolicyControllerOptions {
107
+ /** One checkbox for every checkbox-mode policy (default) or one each. */
108
+ combined?: boolean;
109
+ /** Clock, injectable for tests. */
110
+ now?: () => number;
111
+ }
112
+ export interface PolicySnapshot {
113
+ policies: Policy[];
114
+ combined: boolean;
115
+ /** Key of the document open in the in-page viewer, or null. */
116
+ viewing: string | null;
117
+ /** Whether the checkbox-mode policies are all ticked (combined box) . */
118
+ combinedChecked: boolean;
119
+ }
120
+ /**
121
+ * The state machine behind every policy UI. Subscribe for changes; read
122
+ * {@link satisfied} to enable the submit and {@link build} for the request body.
123
+ * State is kept per `versionId`, so a refreshed list (after a 422) keeps the
124
+ * ticks the user already made on versions that did not change.
125
+ */
126
+ export declare class PolicyAcceptanceController {
127
+ private list;
128
+ private entries;
129
+ private open;
130
+ private listeners;
131
+ private readonly now;
132
+ readonly combined: boolean;
133
+ constructor(policies?: Policy[], options?: PolicyControllerOptions);
134
+ subscribe(fn: () => void): () => void;
135
+ private emit;
136
+ private entry;
137
+ get policies(): Policy[];
138
+ get viewing(): Policy | null;
139
+ snapshot(): PolicySnapshot;
140
+ /** Replace the policies (e.g. after a refresh); ticks on unchanged versions survive. */
141
+ setPolicies(policies: Policy[]): void;
142
+ checkboxPolicies(): Policy[];
143
+ scrollPolicies(): Policy[];
144
+ linkPolicies(): Policy[];
145
+ isChecked(key: string): boolean;
146
+ setChecked(key: string, checked: boolean): void;
147
+ /** Tick or untick every checkbox-mode policy at once (the combined box). */
148
+ setAllChecked(checked: boolean): void;
149
+ /** The user accepted a scroll-mode policy in its modal. */
150
+ acceptScroll(key: string): void;
151
+ /** The first scroll-mode policy still to be read and accepted, or null. */
152
+ pendingScroll(): Policy | null;
153
+ /** Open a document in the in-page viewer and start its reading clock. */
154
+ openDocument(key: string): void;
155
+ closeDocument(): void;
156
+ /** Start the reading clock for a policy shown some other way (a modal). */
157
+ startClock(key: string): void;
158
+ stopClock(key: string): void;
159
+ /** The scroll-to-end flag was reached while reading `key`. */
160
+ markScrolledToEnd(key: string): void;
161
+ hasScrolledToEnd(key: string): boolean;
162
+ viewMs(key: string): number;
163
+ /** The required policies still unaccepted. Scroll-mode ones are accepted in their modal, not by a box. */
164
+ missing(): Policy[];
165
+ /** Whether the submit may go: every required policy is accepted. Scroll-mode ones count as pending until read. */
166
+ satisfied(): boolean;
167
+ /** Whether the submit button may be enabled: scroll-mode policies are read on submit, so they do not block it. */
168
+ canSubmit(): boolean;
169
+ /** The `policy_acceptances` body: every accepted policy (and every clickwrap one). */
170
+ build(): PolicyAcceptancePayload[];
171
+ }
172
+ /** Build a controller already holding `policies` (a sign-in's `pending_policies`, a banner's, …). */
173
+ export declare function createPolicyController(policies?: Policy[], options?: PolicyControllerOptions): PolicyAcceptanceController;
174
+ /** Whether a scroll container has reached its end (also true when the content fits). */
175
+ export declare function isScrolledToEnd(el: {
176
+ scrollTop: number;
177
+ scrollHeight: number;
178
+ clientHeight: number;
179
+ }, slack?: number): boolean;
180
+ /**
181
+ * Call `onEnd` once the end of a scroll container has been seen: an
182
+ * IntersectionObserver on an end sentinel (scrolling, the keyboard, a screen
183
+ * reader all count), with a scroll listener fallback; and straight away when the
184
+ * content fits. Returns a function that stops watching.
185
+ */
186
+ export declare function observeScrollEnd(scroller: HTMLElement, sentinel: HTMLElement | null, onEnd: () => void): () => void;
187
+ /**
188
+ * Keep Tab inside `container` (a modal) and, when `escape` is `false`, swallow
189
+ * Escape so the dialog can only be left by its buttons. Returns a release
190
+ * function that also restores focus to whatever had it.
191
+ */
192
+ export declare function trapFocus(container: HTMLElement, options?: {
193
+ initialFocus?: HTMLElement | null;
194
+ escape?: boolean;
195
+ }): () => void;
196
+ /**
197
+ * A small, safe markdown renderer (headings, paragraphs, lists, bold, italics,
198
+ * code, http(s) links). Every character of input is HTML-escaped first, so it
199
+ * can never inject markup. It is the fallback for a policy delivered as
200
+ * markdown only; the server normally sends sanitised HTML.
201
+ */
202
+ export declare function markdownToSafeHtml(markdown: string): string;
203
+ /** The HTML to draw for a policy: the server's sanitised HTML, else its markdown rendered safely. */
204
+ export declare function policyHtml(policy: Pick<Policy, 'contentHtml' | 'contentMarkdown'>): string;
205
+ /** A readable "effective 24 Oct 2026" style date, or '' when unparsable. */
206
+ export declare function formatPolicyDate(iso: string | null | undefined): string;
207
+ /** The English strings every policy UI draws (`policy.*` catalog keys). */
208
+ export declare const POLICY_STRINGS: Readonly<Record<string, string>>;
209
+ /** A `t` function over a catalog lookup that falls back to the English policy strings. */
210
+ export declare function policyTranslator(lookup: (key: string, vars?: Record<string, string>) => string): (key: string, vars?: Record<string, string>) => string;
211
+ /** Join titles as "A, B and C". */
212
+ export declare function joinTitles(titles: string[], and: string): string;
213
+ /** Split a `{name}` template into the text before and after the placeholder (for sentences that embed links). */
214
+ export declare function splitTemplate(template: string, name: string): [string, string];
215
+ /**
216
+ * After the server answered 422 `POLICY_ACCEPTANCE_REQUIRED` (a policy was
217
+ * published or changed while the form was open): reload the policies into the
218
+ * controller so the form re-shows what is needed now. Returns whether the error
219
+ * was that refusal.
220
+ */
221
+ export declare function refreshPoliciesOnRequired(client: FapiClient, controller: PolicyAcceptanceController, errors: readonly FieldError[] | null | undefined, opts?: {
222
+ locale?: string;
223
+ }): Promise<boolean>;
224
+ /**
225
+ * For a sign-in attempt in `needs_policy_acceptance`: a controller holding its
226
+ * `pending_policies` — with the content fetched from `GET /v1/client/policies`
227
+ * when the attempt only listed them.
228
+ */
229
+ export declare function controllerForPending(client: FapiClient, attempt: {
230
+ pending_policies?: unknown;
231
+ } | null | undefined, opts?: PolicyControllerOptions & {
232
+ locale?: string;
233
+ }): Promise<PolicyAcceptanceController>;
234
+ /**
235
+ * In-app re-acceptance for a signed-in user, framework-free: loads
236
+ * `GET /v1/client/me/policies`, builds a controller for the policies that need
237
+ * re-accepting ({@link begin}) and posts the acceptance ({@link accept}). The
238
+ * framework SDKs' `<PolicyGate>` wraps it.
239
+ */
240
+ export declare class PolicyGateController {
241
+ private readonly client;
242
+ private readonly locale?;
243
+ /** The policies that need re-acceptance (empty when none, or before {@link load}). */
244
+ pending: MyPolicyStatus[];
245
+ loaded: boolean;
246
+ private listeners;
247
+ constructor(client: FapiClient, locale?: string | undefined);
248
+ subscribe(fn: () => void): () => void;
249
+ private emit;
250
+ load(): Promise<void>;
251
+ /** The soonest end of grace among the pending policies, or null. */
252
+ get graceEndsAt(): string | null;
253
+ /** A controller over the pending policies' current versions, all required. */
254
+ begin(): Promise<PolicyAcceptanceController>;
255
+ accept(controller: PolicyAcceptanceController): Promise<{
256
+ ok: boolean;
257
+ errors: FieldError[];
258
+ }>;
259
+ }