@atlasauth/js 0.18.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.
- package/CHANGELOG.md +8 -0
- package/dist/attempt.d.ts +34 -1
- package/dist/attempt.js +2 -0
- package/dist/attempt.js.map +1 -1
- package/dist/captcha-controller.d.ts +3 -0
- package/dist/captcha-controller.js +10 -1
- package/dist/captcha-controller.js.map +1 -1
- package/dist/fapi.d.ts +10 -0
- package/dist/fapi.js +27 -1
- package/dist/fapi.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/passkey-sign-up.d.ts +3 -0
- package/dist/passkey-sign-up.js +1 -0
- package/dist/passkey-sign-up.js.map +1 -1
- package/dist/policies.d.ts +156 -0
- package/dist/policies.js +103 -0
- package/dist/policies.js.map +1 -0
- package/dist/policy-acceptance.d.ts +259 -0
- package/dist/policy-acceptance.js +698 -0
- package/dist/policy-acceptance.js.map +1 -0
- package/dist/policy-dom.d.ts +106 -0
- package/dist/policy-dom.js +438 -0
- package/dist/policy-dom.js.map +1 -0
- package/dist/redirect.d.ts +5 -1
- package/dist/redirect.js +4 -1
- package/dist/redirect.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/policies.js
ADDED
|
@@ -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
|
+
}
|