@atlasauth/svelte 0.8.0 → 0.10.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/accessors.d.ts +66 -1
  3. package/dist/accessors.js +110 -3
  4. package/dist/appearance.d.ts +1 -1
  5. package/dist/components/AttemptFlow.svelte +188 -4
  6. package/dist/components/AttemptFlow.svelte.d.ts +28 -1
  7. package/dist/components/ImpersonationBanner.svelte +38 -0
  8. package/dist/components/ImpersonationBanner.svelte.d.ts +19 -0
  9. package/dist/components/OrganizationAuditLog.svelte +45 -0
  10. package/dist/components/OrganizationAuditLog.svelte.d.ts +31 -0
  11. package/dist/components/OrganizationProfile.svelte +308 -12
  12. package/dist/components/OrganizationProfile.svelte.d.ts +2 -0
  13. package/dist/components/PasskeySignUpStep.svelte +23 -0
  14. package/dist/components/PasskeySignUpStep.svelte.d.ts +24 -0
  15. package/dist/components/PolicyAcceptStep.svelte +52 -0
  16. package/dist/components/PolicyAcceptStep.svelte.d.ts +30 -0
  17. package/dist/components/PolicyClickwrap.svelte +23 -0
  18. package/dist/components/PolicyClickwrap.svelte.d.ts +19 -0
  19. package/dist/components/PolicyConsent.svelte +23 -0
  20. package/dist/components/PolicyConsent.svelte.d.ts +24 -0
  21. package/dist/components/PolicyGate.svelte +83 -0
  22. package/dist/components/PolicyGate.svelte.d.ts +24 -0
  23. package/dist/components/RecoveryCodes.svelte +55 -0
  24. package/dist/components/RecoveryCodes.svelte.d.ts +25 -0
  25. package/dist/components/SignIn.svelte +21 -1
  26. package/dist/components/SignUp.svelte +22 -3
  27. package/dist/components/SignUpForm.svelte +84 -19
  28. package/dist/components/SignUpForm.svelte.d.ts +6 -1
  29. package/dist/context.d.ts +9 -1
  30. package/dist/context.js +18 -1
  31. package/dist/i18n.js +58 -1
  32. package/dist/index.d.ts +8 -1
  33. package/dist/index.js +8 -1
  34. package/dist/policies.d.ts +6 -0
  35. package/dist/policies.js +17 -0
  36. package/dist/types.d.ts +6 -0
  37. package/dist/version.d.ts +1 -1
  38. package/dist/version.js +1 -1
  39. package/package.json +4 -4
@@ -0,0 +1,19 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import { type EndImpersonationResult } from '@atlasauth/js';
3
+ declare const __propDef: {
4
+ props: Record<string, never>;
5
+ events: {
6
+ ended: CustomEvent<EndImpersonationResult>;
7
+ } & {
8
+ [evt: string]: CustomEvent<any>;
9
+ };
10
+ slots: {};
11
+ exports?: {} | undefined;
12
+ bindings?: string | undefined;
13
+ };
14
+ export type ImpersonationBannerProps = typeof __propDef.props;
15
+ export type ImpersonationBannerEvents = typeof __propDef.events;
16
+ export type ImpersonationBannerSlots = typeof __propDef.slots;
17
+ export default class ImpersonationBanner extends SvelteComponent<ImpersonationBannerProps, ImpersonationBannerEvents, ImpersonationBannerSlots> {
18
+ }
19
+ export {};
@@ -0,0 +1,45 @@
1
+ <script>import { describeAuditAction } from "@atlasauth/js";
2
+ import { useAtlas } from "../context";
3
+ import { getOrganization, getOrganizationAuditLog } from "../accessors";
4
+ import { classFor } from "../appearance";
5
+ export let pageSize = void 0;
6
+ export let action = void 0;
7
+ export let labels = {};
8
+ export let formatDate = (ms) => new Date(ms).toLocaleString();
9
+ const atlas = useAtlas();
10
+ const org = getOrganization();
11
+ const log = getOrganizationAuditLog({ pageSize, action });
12
+ const cls = (key, base) => classFor(key, atlas.appearance, base);
13
+ </script>
14
+
15
+ {#if $org.organization && !$log.forbidden}
16
+ <div class={cls('card', 'atlas-card')} data-atlas="organization-audit-log">
17
+ <h2 class={cls('headerTitle', 'atlas-title')}>{labels.title ?? 'Audit log'}</h2>
18
+ {#if $log.entries.length === 0 && !$log.loading}
19
+ <p class={cls('headerSubtitle', 'atlas-subtitle')}>{labels.empty ?? 'No activity recorded yet.'}</p>
20
+ {:else}
21
+ <ul class={cls('list', 'atlas-audit-list')}>
22
+ {#each $log.entries as entry (entry.id)}
23
+ <li class={cls('listItem', 'atlas-audit-item')} data-action={entry.action}>
24
+ <strong>{describeAuditAction(entry.action)}</strong>
25
+ <span class="atlas-audit-meta">
26
+ · {formatDate(entry.created_at)}{entry.actor.id
27
+ ? ` · ${labels.actor ?? 'by'} ${entry.actor.type} ${entry.actor.id}`
28
+ : ''}
29
+ </span>
30
+ </li>
31
+ {/each}
32
+ </ul>
33
+ {/if}
34
+ {#if $log.hasMore}
35
+ <button
36
+ type="button"
37
+ class={cls('formButtonSecondary', 'atlas-button-secondary')}
38
+ disabled={$log.loading}
39
+ on:click={() => void log.loadMore()}
40
+ >
41
+ {labels.loadMore ?? 'Load more'}
42
+ </button>
43
+ {/if}
44
+ </div>
45
+ {/if}
@@ -0,0 +1,31 @@
1
+ import { SvelteComponent } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ /**
5
+ * `<OrganizationAuditLog/>` — the active organization's audit trail for its
6
+ * admins, next to `<OrganizationProfile/>`. Renders nothing for a member who
7
+ * may not read it (the server is the authority; the component just follows
8
+ * it). Labels are props so an app can localise them.
9
+ */ pageSize?: number | undefined;
10
+ action?: string | undefined;
11
+ labels?: {
12
+ title?: string;
13
+ empty?: string;
14
+ loadMore?: string;
15
+ actor?: string;
16
+ };
17
+ formatDate?: (ms: number) => string;
18
+ };
19
+ events: {
20
+ [evt: string]: CustomEvent<any>;
21
+ };
22
+ slots: {};
23
+ exports?: {} | undefined;
24
+ bindings?: string | undefined;
25
+ };
26
+ export type OrganizationAuditLogProps = typeof __propDef.props;
27
+ export type OrganizationAuditLogEvents = typeof __propDef.events;
28
+ export type OrganizationAuditLogSlots = typeof __propDef.slots;
29
+ export default class OrganizationAuditLog extends SvelteComponent<OrganizationAuditLogProps, OrganizationAuditLogEvents, OrganizationAuditLogSlots> {
30
+ }
31
+ export {};
@@ -1,22 +1,318 @@
1
- <script>import { useAtlas } from "../context";
2
- import { getOrganization } from "../accessors";
1
+ <script>import { createEventDispatcher, onDestroy } from "svelte";
2
+ import {
3
+ OrganizationProfileController,
4
+ localizedErrorMessage,
5
+ reauthenticate
6
+ } from "@atlasauth/js";
7
+ import { useAtlas } from "../context";
8
+ import { getAuth, getOrganization } from "../accessors";
3
9
  import { classFor } from "../appearance";
4
10
  import { translate } from "../i18n";
5
- import Protect from "./Protect.svelte";
11
+ import { FapiClient } from "../fapi";
12
+ import { ATLAS_SVELTE_SDK } from "../version";
6
13
  const atlas = useAtlas();
7
14
  const org = getOrganization();
8
- const t = (key) => translate(atlas.catalog, key);
9
- $: cardClass = classFor("card", atlas.appearance, "atlas-card");
15
+ const auth = getAuth();
16
+ const dispatch = createEventDispatcher();
17
+ const t = (key, vars) => translate(atlas.catalog, key, vars);
18
+ const cls = (key, base) => classFor(key, atlas.appearance, base);
19
+ const errorText = (err) => localizedErrorMessage(err, atlas.errorCatalog ?? atlas.catalog, atlas.catalog);
20
+ const client = new FapiClient({
21
+ publishableKey: atlas.publishableKey,
22
+ baseUrl: atlas.frontendApi,
23
+ locale: atlas.locale,
24
+ sdk: ATLAS_SVELTE_SDK
25
+ });
26
+ let controller = null;
27
+ let state = null;
28
+ let unsubscribe = null;
29
+ let currentId = null;
30
+ let tab = "members";
31
+ let editingRole = null;
32
+ let roleKey = "";
33
+ let roleName = "";
34
+ let rolePerms = [];
35
+ let email = "";
36
+ let inviteRole = "org:member";
37
+ let confirm = null;
38
+ let needsPassword = false;
39
+ let password = "";
40
+ $: canManage = $auth.has({ permission: "org:sys_memberships:manage" });
41
+ $: isAdmin = $org.membership?.role === "org:admin";
42
+ $: roles = state && state.roles.length > 0 ? state.roles : [{ key: "org:member", name: "org:member" }];
43
+ $: errorMessage = state?.error && !state.error.needsReverification ? errorText(state.error) : null;
44
+ $: tabs = ["members", ...canManage ? ["invitations", "roles"] : [], "settings"];
45
+ $: customRoles = state ? state.roles.filter((r) => Boolean(r.organization_id)) : [];
46
+ $: {
47
+ const id = $org.organization?.id ?? null;
48
+ if (id !== currentId) {
49
+ currentId = id;
50
+ unsubscribe?.();
51
+ controller = null;
52
+ state = null;
53
+ if (id) {
54
+ controller = new OrganizationProfileController(client, id, {
55
+ canManage,
56
+ onChange: async () => {
57
+ await atlas.reload();
58
+ }
59
+ });
60
+ unsubscribe = controller.subscribe((next) => state = next);
61
+ state = controller.getState();
62
+ void controller.load();
63
+ }
64
+ }
65
+ }
66
+ onDestroy(() => unsubscribe?.());
67
+ const memberName = (m) => [m.user?.first_name, m.user?.last_name].filter(Boolean).join(" ") || t("organization.unnamedMember");
68
+ async function sendInvite() {
69
+ const address = email.trim();
70
+ if (controller && address && await controller.invite(address, inviteRole)) email = "";
71
+ }
72
+ async function leave() {
73
+ if (controller && await controller.leave()) dispatch("exit", "left");
74
+ confirm = null;
75
+ }
76
+ async function remove() {
77
+ if (!controller) return;
78
+ if (needsPassword) {
79
+ try {
80
+ await reauthenticate(
81
+ { strategy: "password", password },
82
+ { api: atlas.frontendApi, publishableKey: atlas.publishableKey, locale: atlas.locale }
83
+ );
84
+ } catch {
85
+ return;
86
+ }
87
+ password = "";
88
+ needsPassword = false;
89
+ }
90
+ if (await controller.deleteOrganization()) {
91
+ dispatch("exit", "deleted");
92
+ confirm = null;
93
+ return;
94
+ }
95
+ const err = controller.getState().error;
96
+ if (err?.needsReverification) needsPassword = true;
97
+ }
98
+ function resetRoleForm() {
99
+ editingRole = null;
100
+ roleKey = "";
101
+ roleName = "";
102
+ rolePerms = [];
103
+ }
104
+ function editRole(role) {
105
+ editingRole = role.id ?? null;
106
+ roleKey = role.key;
107
+ roleName = role.name;
108
+ rolePerms = [...role.permissions ?? []];
109
+ }
110
+ function togglePermission(key, on) {
111
+ rolePerms = on ? [...rolePerms, key] : rolePerms.filter((k) => k !== key);
112
+ }
113
+ async function saveRole() {
114
+ if (!controller) return;
115
+ const out = editingRole ? await controller.updateRole(editingRole, { name: roleName.trim(), permissions: rolePerms }) : await controller.createRole({ key: roleKey.trim(), name: roleName.trim(), permissions: rolePerms });
116
+ if (out) resetRoleForm();
117
+ }
118
+ function cancel() {
119
+ confirm = null;
120
+ needsPassword = false;
121
+ password = "";
122
+ }
10
123
  </script>
11
124
 
12
- {#if $org.organization}
13
- <div class={cardClass}>
125
+ {#if $org.organization && controller && state}
126
+ <div class={cls('card', 'atlas-card')} data-atlas="organization-profile">
14
127
  <h1>{$org.organization.name}</h1>
15
128
  <p>{t('organization.roleLabel')}: {$org.membership?.role}</p>
16
- <!-- Management controls are gated on the ACTIVE org role, matching the
17
- server's §9.2 rule rather than duplicating a looser one. -->
18
- <Protect permission="org:sys_memberships:manage">
19
- <button>{t('organization.invite')}</button>
20
- </Protect>
129
+
130
+ <div class={cls('menu', 'atlas-menu')} role="tablist">
131
+ {#each tabs as key}
132
+ <button
133
+ type="button"
134
+ role="tab"
135
+ aria-selected={tab === key}
136
+ class={cls('menuItem', 'atlas-menu-item')}
137
+ data-atlas={`organization-tab-${key}`}
138
+ on:click={() => (tab = key)}
139
+ >
140
+ {t(`organization.${key}`)}
141
+ </button>
142
+ {/each}
143
+ </div>
144
+
145
+ {#if errorMessage}
146
+ <p class={cls('formFieldError', 'atlas-error')} role="alert">{errorMessage}</p>
147
+ {/if}
148
+
149
+ {#if tab === 'members'}
150
+ <ul class={'atlas-list'} data-atlas="organization-members">
151
+ {#each state.members as m (m.id)}
152
+ <li data-atlas="organization-member">
153
+ <span>{memberName(m)}{m.user?.id === $auth.userId ? ` ${t('organization.you')}` : ''}</span>
154
+ {#if canManage && m.user && m.user.id !== $auth.userId}
155
+ <select
156
+ aria-label={t('organization.roleLabel')}
157
+ class={cls('formFieldInput', 'atlas-input')}
158
+ value={m.role}
159
+ disabled={state.busy === `role:${m.user.id}`}
160
+ on:change={(e) => m.user && controller?.changeRole(m.user.id, e.currentTarget.value)}
161
+ >
162
+ {#each roles as r}<option value={r.key}>{r.name}</option>{/each}
163
+ </select>
164
+ <button
165
+ type="button"
166
+ class={cls('formButtonSecondary', 'atlas-button-secondary')}
167
+ disabled={state.busy === `remove:${m.user.id}`}
168
+ on:click={() => m.user && controller?.removeMember(m.user.id)}
169
+ >
170
+ {t('organization.remove')}
171
+ </button>
172
+ {:else}
173
+ <span> · {m.role}</span>
174
+ {/if}
175
+ </li>
176
+ {/each}
177
+ </ul>
178
+ {/if}
179
+
180
+ {#if tab === 'invitations' && canManage}
181
+ <div data-atlas="organization-invitations">
182
+ <form on:submit|preventDefault={sendInvite}>
183
+ <label class={cls('formField', 'atlas-field')}>
184
+ <span class={cls('formFieldLabel', 'atlas-field-label')}>{t('organization.emailLabel')}</span>
185
+ <input type="email" class={cls('formFieldInput', 'atlas-input')} bind:value={email} />
186
+ </label>
187
+ <label class={cls('formField', 'atlas-field')}>
188
+ <span class={cls('formFieldLabel', 'atlas-field-label')}>{t('organization.roleLabel')}</span>
189
+ <select class={cls('formFieldInput', 'atlas-input')} bind:value={inviteRole}>
190
+ {#each roles as r}<option value={r.key}>{r.name}</option>{/each}
191
+ </select>
192
+ </label>
193
+ <button type="submit" class={cls('formButtonPrimary', 'atlas-button')} disabled={state.busy === 'invite' || !email.trim()}>
194
+ {t('organization.sendInvite')}
195
+ </button>
196
+ </form>
197
+ {#if state.invitations.length === 0}
198
+ <p class={'atlas-text'}>{t('organization.noInvitations')}</p>
199
+ {:else}
200
+ <ul class={'atlas-list'}>
201
+ {#each state.invitations as inv (inv.id)}
202
+ <li data-atlas="organization-invitation">
203
+ <span>{inv.email_address} · {inv.role ?? ''}</span>
204
+ <button type="button" class={cls('formButtonSecondary', 'atlas-button-secondary')} on:click={() => controller?.resendInvitation(inv.id)}>
205
+ {t('organization.resend')}
206
+ </button>
207
+ <button type="button" class={cls('formButtonSecondary', 'atlas-button-secondary')} on:click={() => controller?.revokeInvitation(inv.id)}>
208
+ {t('organization.revoke')}
209
+ </button>
210
+ </li>
211
+ {/each}
212
+ </ul>
213
+ {/if}
214
+ </div>
215
+ {/if}
216
+
217
+ {#if tab === 'roles' && canManage}
218
+ <div data-atlas="organization-roles">
219
+ {#if customRoles.length === 0}
220
+ <p class={'atlas-text'}>{t('organization.noCustomRoles')}</p>
221
+ {:else}
222
+ <ul class={'atlas-list'}>
223
+ {#each customRoles as role (role.id ?? role.key)}
224
+ <li data-atlas="organization-role">
225
+ <span>{role.name} · {role.key}</span>
226
+ <button type="button" class={cls('formButtonSecondary', 'atlas-button-secondary')} on:click={() => editRole(role)}>
227
+ {t('organization.editRole')}
228
+ </button>
229
+ <button
230
+ type="button"
231
+ class={cls('formButtonSecondary', 'atlas-button-secondary')}
232
+ disabled={!role.id || state.busy === `role:delete:${role.id}`}
233
+ on:click={() => role.id && controller?.deleteRole(role.id)}
234
+ >
235
+ {t('organization.deleteRole')}
236
+ </button>
237
+ </li>
238
+ {/each}
239
+ </ul>
240
+ {/if}
241
+ <form data-atlas="organization-role-form" on:submit|preventDefault={saveRole}>
242
+ {#if !editingRole}
243
+ <label class={cls('formField', 'atlas-field')}>
244
+ <span class={cls('formFieldLabel', 'atlas-field-label')}>{t('organization.roleKey')}</span>
245
+ <input name="role_key" class={cls('formFieldInput', 'atlas-input')} bind:value={roleKey} />
246
+ </label>
247
+ {/if}
248
+ <label class={cls('formField', 'atlas-field')}>
249
+ <span class={cls('formFieldLabel', 'atlas-field-label')}>{t('organization.roleName')}</span>
250
+ <input name="role_name" class={cls('formFieldInput', 'atlas-input')} bind:value={roleName} />
251
+ </label>
252
+ <fieldset class={cls('formField', 'atlas-field')}>
253
+ <legend class={cls('formFieldLabel', 'atlas-field-label')}>{t('organization.rolePermissions')}</legend>
254
+ {#each state.permissions as p (p.key)}
255
+ <label data-atlas="organization-role-permission">
256
+ <input
257
+ type="checkbox"
258
+ value={p.key}
259
+ checked={rolePerms.includes(p.key)}
260
+ on:change={(e) => togglePermission(p.key, e.currentTarget.checked)}
261
+ />
262
+ {p.name}
263
+ </label>
264
+ {/each}
265
+ </fieldset>
266
+ <button
267
+ type="submit"
268
+ class={cls('formButtonPrimary', 'atlas-button')}
269
+ disabled={!roleName.trim() || (!editingRole && !roleKey.trim()) || Boolean(state.busy?.startsWith('role:'))}
270
+ >
271
+ {editingRole ? t('organization.saveRole') : t('organization.createRole')}
272
+ </button>
273
+ {#if editingRole}
274
+ <button type="button" class={cls('formButtonSecondary', 'atlas-button-secondary')} on:click={resetRoleForm}>
275
+ {t('organization.cancel')}
276
+ </button>
277
+ {/if}
278
+ </form>
279
+ </div>
280
+ {/if}
281
+
282
+ {#if tab === 'settings'}
283
+ <div data-atlas="organization-settings">
284
+ {#if confirm}
285
+ <p class={'atlas-text'}>
286
+ {t(confirm === 'leave' ? 'organization.leaveConfirm' : 'organization.deleteConfirm', { name: $org.organization.name })}
287
+ </p>
288
+ {#if needsPassword}
289
+ <label class={cls('formField', 'atlas-field')}>
290
+ <span class={cls('formFieldLabel', 'atlas-field-label')}>{t('organization.passwordPrompt')}</span>
291
+ <input type="password" autocomplete="current-password" class={cls('formFieldInput', 'atlas-input')} bind:value={password} />
292
+ </label>
293
+ {/if}
294
+ <button
295
+ type="button"
296
+ class={cls('formButtonPrimary', 'atlas-button')}
297
+ disabled={state.busy === 'leave' || state.busy === 'delete'}
298
+ on:click={() => (confirm === 'leave' ? leave() : remove())}
299
+ >
300
+ {t('organization.confirm')}
301
+ </button>
302
+ <button type="button" class={cls('formButtonSecondary', 'atlas-button-secondary')} on:click={cancel}>
303
+ {t('organization.cancel')}
304
+ </button>
305
+ {:else}
306
+ <button type="button" class={cls('formButtonSecondary', 'atlas-button-secondary')} on:click={() => (confirm = 'leave')}>
307
+ {t('organization.leave')}
308
+ </button>
309
+ {#if isAdmin}
310
+ <button type="button" class={cls('formButtonSecondary', 'atlas-button-secondary')} on:click={() => (confirm = 'delete')}>
311
+ {t('organization.delete')}
312
+ </button>
313
+ {/if}
314
+ {/if}
315
+ </div>
316
+ {/if}
21
317
  </div>
22
318
  {/if}
@@ -2,6 +2,8 @@ import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: Record<string, never>;
4
4
  events: {
5
+ exit: CustomEvent<"left" | "deleted">;
6
+ } & {
5
7
  [evt: string]: CustomEvent<any>;
6
8
  };
7
9
  slots: {};
@@ -0,0 +1,23 @@
1
+ <script>import { localizedErrorMessage } from "@atlasauth/js";
2
+ import { useAtlas } from "../context";
3
+ import { classFor } from "../appearance";
4
+ import { translate } from "../i18n";
5
+ export let busy = false;
6
+ export let errors = [];
7
+ export let onCreate;
8
+ const atlas = useAtlas();
9
+ const cls = (key, base) => classFor(key, atlas.appearance, base);
10
+ const t = (key) => translate(atlas.catalog, key);
11
+ const errorText = (error) => localizedErrorMessage(error, atlas.errorCatalog ?? atlas.catalog, atlas.catalog);
12
+ </script>
13
+
14
+ <div class={cls('card', 'atlas-card')} data-atlas-passkey-sign-up="">
15
+ <h1 class={cls('headerTitle', 'atlas-title')}>{t('signUp.title')}</h1>
16
+ {#each errors as error (error.code)}
17
+ <p class={cls('formFieldError', 'atlas-error')} role="alert">{errorText(error)}</p>
18
+ {/each}
19
+ <p class="atlas-text">{t('signUp.passkeyPrompt')}</p>
20
+ <button class={cls('formButtonPrimary', 'atlas-button')} type="button" disabled={busy} on:click={() => onCreate()}>
21
+ {t('signUp.passkeyCreate')}
22
+ </button>
23
+ </div>
@@ -0,0 +1,24 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import { type FieldError } from '@atlasauth/js';
3
+ declare const __propDef: {
4
+ props: {
5
+ /**
6
+ * The passkey-only sign-up's last step: create the passkey. The server
7
+ * creates the account only when this registration verifies.
8
+ */ busy?: boolean;
9
+ errors?: FieldError[];
10
+ onCreate: () => void;
11
+ };
12
+ events: {
13
+ [evt: string]: CustomEvent<any>;
14
+ };
15
+ slots: {};
16
+ exports?: {} | undefined;
17
+ bindings?: string | undefined;
18
+ };
19
+ export type PasskeySignUpStepProps = typeof __propDef.props;
20
+ export type PasskeySignUpStepEvents = typeof __propDef.events;
21
+ export type PasskeySignUpStepSlots = typeof __propDef.slots;
22
+ export default class PasskeySignUpStep extends SvelteComponent<PasskeySignUpStepProps, PasskeySignUpStepEvents, PasskeySignUpStepSlots> {
23
+ }
24
+ export {};
@@ -0,0 +1,52 @@
1
+ <script>import { onMount } from "svelte";
2
+ import {
3
+ FapiClient,
4
+ controllerForPending,
5
+ injectPolicyStyles,
6
+ mountPolicyAcceptStep
7
+ } from "@atlasauth/js";
8
+ import { useAtlas } from "../context";
9
+ import { usePolicyUi } from "../policies";
10
+ import { ATLAS_SVELTE_SDK } from "../version";
11
+ export let attempt = null;
12
+ export let busy = false;
13
+ export let onSubmit;
14
+ export let onDecline = void 0;
15
+ const atlas = useAtlas();
16
+ const ui = usePolicyUi();
17
+ let controller = null;
18
+ let step = null;
19
+ function host(node, c) {
20
+ injectPolicyStyles(node.ownerDocument);
21
+ step = mountPolicyAcceptStep(node, c, {
22
+ ...ui,
23
+ onSubmit: (acceptances) => onSubmit({ policy_acceptances: JSON.stringify(acceptances) }),
24
+ onDecline: (policy) => onDecline?.(policy)
25
+ });
26
+ step.setBusy(busy);
27
+ return {
28
+ destroy() {
29
+ step?.destroy();
30
+ step = null;
31
+ }
32
+ };
33
+ }
34
+ $: step?.setBusy(busy);
35
+ onMount(() => {
36
+ const client = new FapiClient({
37
+ publishableKey: atlas.publishableKey,
38
+ baseUrl: atlas.frontendApi,
39
+ locale: atlas.locale,
40
+ sdk: ATLAS_SVELTE_SDK
41
+ });
42
+ void controllerForPending(client, attempt, { locale: atlas.locale ?? void 0 }).then((c) => {
43
+ controller = c;
44
+ });
45
+ });
46
+ </script>
47
+
48
+ {#if controller}
49
+ <div use:host={controller} />
50
+ {:else}
51
+ <p class="atlas-text">{ui.t('policy.loading')}</p>
52
+ {/if}
@@ -0,0 +1,30 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import { type Policy } from '@atlasauth/js';
3
+ declare const __propDef: {
4
+ props: {
5
+ /**
6
+ * The sign-in step for a `needs_policy_acceptance` attempt: scroll-mode
7
+ * policies in their unskippable dialog, the rest as checkboxes, then "Accept
8
+ * and continue", which calls `onSubmit` with the `policy_acceptances` JSON for
9
+ * the flow. Declining a policy calls `onDecline` (cancel the attempt).
10
+ */ attempt?: {
11
+ id: string | null;
12
+ pending_policies?: unknown;
13
+ } | null;
14
+ busy?: boolean;
15
+ onSubmit: (values: Record<string, string>) => void;
16
+ onDecline?: ((policy: Policy) => void) | undefined;
17
+ };
18
+ events: {
19
+ [evt: string]: CustomEvent<any>;
20
+ };
21
+ slots: {};
22
+ exports?: {} | undefined;
23
+ bindings?: string | undefined;
24
+ };
25
+ export type PolicyAcceptStepProps = typeof __propDef.props;
26
+ export type PolicyAcceptStepEvents = typeof __propDef.events;
27
+ export type PolicyAcceptStepSlots = typeof __propDef.slots;
28
+ export default class PolicyAcceptStep extends SvelteComponent<PolicyAcceptStepProps, PolicyAcceptStepEvents, PolicyAcceptStepSlots> {
29
+ }
30
+ export {};
@@ -0,0 +1,23 @@
1
+ <script>import { injectPolicyStyles, mountPolicyClickwrap } from "@atlasauth/js";
2
+ import { usePolicyUi } from "../policies";
3
+ export let controller;
4
+ const ui = usePolicyUi();
5
+ function host(node, c) {
6
+ injectPolicyStyles(node.ownerDocument);
7
+ let current = c;
8
+ let mounted = mountPolicyClickwrap(node, current, ui);
9
+ return {
10
+ update(next) {
11
+ if (next === current) return;
12
+ mounted.destroy();
13
+ current = next;
14
+ mounted = mountPolicyClickwrap(node, current, ui);
15
+ },
16
+ destroy() {
17
+ mounted.destroy();
18
+ }
19
+ };
20
+ }
21
+ </script>
22
+
23
+ <p use:host={controller} />
@@ -0,0 +1,19 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import { type PolicyAcceptanceController } from '@atlasauth/js';
3
+ declare const __propDef: {
4
+ props: {
5
+ /** The clickwrap sentence ("By continuing you agree to …") for `link`-mode policies, under the submit button. */ controller: PolicyAcceptanceController;
6
+ };
7
+ events: {
8
+ [evt: string]: CustomEvent<any>;
9
+ };
10
+ slots: {};
11
+ exports?: {} | undefined;
12
+ bindings?: string | undefined;
13
+ };
14
+ export type PolicyClickwrapProps = typeof __propDef.props;
15
+ export type PolicyClickwrapEvents = typeof __propDef.events;
16
+ export type PolicyClickwrapSlots = typeof __propDef.slots;
17
+ export default class PolicyClickwrap extends SvelteComponent<PolicyClickwrapProps, PolicyClickwrapEvents, PolicyClickwrapSlots> {
18
+ }
19
+ export {};
@@ -0,0 +1,23 @@
1
+ <script>import { injectPolicyStyles, mountPolicyConsent } from "@atlasauth/js";
2
+ import { usePolicyUi } from "../policies";
3
+ export let controller;
4
+ const ui = usePolicyUi();
5
+ function host(node, c) {
6
+ injectPolicyStyles(node.ownerDocument);
7
+ let current = c;
8
+ let mounted = mountPolicyConsent(node, current, ui);
9
+ return {
10
+ update(next) {
11
+ if (next === current) return;
12
+ mounted.destroy();
13
+ current = next;
14
+ mounted = mountPolicyConsent(node, current, ui);
15
+ },
16
+ destroy() {
17
+ mounted.destroy();
18
+ }
19
+ };
20
+ }
21
+ </script>
22
+
23
+ <div use:host={controller} />
@@ -0,0 +1,24 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import { type PolicyAcceptanceController } from '@atlasauth/js';
3
+ declare const __propDef: {
4
+ props: {
5
+ /**
6
+ * The checkbox-mode policies (terms, privacy, …): one combined checkbox or one
7
+ * each, the names opening the document in an in-page viewer. The state lives in
8
+ * the `controller` (`policies` of `signUp()`, or a `PolicyAcceptanceController`
9
+ * of your own). The DOM is drawn by `@atlasauth/js`, shared with every SDK.
10
+ */ controller: PolicyAcceptanceController;
11
+ };
12
+ events: {
13
+ [evt: string]: CustomEvent<any>;
14
+ };
15
+ slots: {};
16
+ exports?: {} | undefined;
17
+ bindings?: string | undefined;
18
+ };
19
+ export type PolicyConsentProps = typeof __propDef.props;
20
+ export type PolicyConsentEvents = typeof __propDef.events;
21
+ export type PolicyConsentSlots = typeof __propDef.slots;
22
+ export default class PolicyConsent extends SvelteComponent<PolicyConsentProps, PolicyConsentEvents, PolicyConsentSlots> {
23
+ }
24
+ export {};