@atlasauth/embed 0.1.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/dist/appearance.d.ts +293 -0
- package/dist/appearance.js +206 -0
- package/dist/appearance.js.map +1 -0
- package/dist/client.d.ts +12 -0
- package/dist/client.js +49 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +27 -0
- package/dist/config.js +61 -0
- package/dist/config.js.map +1 -0
- package/dist/element.d.ts +23 -0
- package/dist/element.js +51 -0
- package/dist/element.js.map +1 -0
- package/dist/embed.js +361 -0
- package/dist/icons.d.ts +17 -0
- package/dist/icons.js +53 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/mount.d.ts +7 -0
- package/dist/mount.js +33 -0
- package/dist/mount.js.map +1 -0
- package/dist/redirect.d.ts +17 -0
- package/dist/redirect.js +51 -0
- package/dist/redirect.js.map +1 -0
- package/dist/standalone.d.ts +25 -0
- package/dist/standalone.js +34 -0
- package/dist/standalone.js.map +1 -0
- package/dist/styles.d.ts +17 -0
- package/dist/styles.js +389 -0
- package/dist/styles.js.map +1 -0
- package/dist/widget/captcha.d.ts +21 -0
- package/dist/widget/captcha.js +197 -0
- package/dist/widget/captcha.js.map +1 -0
- package/dist/widget/constants.d.ts +16 -0
- package/dist/widget/constants.js +14 -0
- package/dist/widget/constants.js.map +1 -0
- package/dist/widget/dom.d.ts +18 -0
- package/dist/widget/dom.js +71 -0
- package/dist/widget/dom.js.map +1 -0
- package/dist/widget/flow.d.ts +41 -0
- package/dist/widget/flow.js +424 -0
- package/dist/widget/flow.js.map +1 -0
- package/dist/widget/forms.d.ts +35 -0
- package/dist/widget/forms.js +499 -0
- package/dist/widget/forms.js.map +1 -0
- package/dist/widget/passkey.d.ts +18 -0
- package/dist/widget/passkey.js +88 -0
- package/dist/widget/passkey.js.map +1 -0
- package/dist/widget/render.d.ts +21 -0
- package/dist/widget/render.js +210 -0
- package/dist/widget/render.js.map +1 -0
- package/dist/widget/social.d.ts +44 -0
- package/dist/widget/social.js +331 -0
- package/dist/widget/social.js.map +1 -0
- package/dist/widget.d.ts +124 -0
- package/dist/widget.js +182 -0
- package/dist/widget.js.map +1 -0
- package/package.json +27 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The widget's theming model, mirroring the React SDK's appearance system
|
|
3
|
+
* (packages/react/src/appearance.ts) but for a framework-agnostic custom
|
|
4
|
+
* element: design tokens become CSS custom properties on the widget root, and
|
|
5
|
+
* per-element class overrides are APPENDED to ours so a tenant's `class` makes
|
|
6
|
+
* a rounder button rather than an unstyled one.
|
|
7
|
+
*
|
|
8
|
+
* Two sources feed it:
|
|
9
|
+
* - the tenant's PUBLIC appearance, fetched from `GET /v1/appearance` — the
|
|
10
|
+
* same resolved/sanitised branding the hosted page uses, minus anything
|
|
11
|
+
* that is not safe to hand a third-party origin (see the endpoint).
|
|
12
|
+
* - an inline `appearance` option the embedding page passes, which wins.
|
|
13
|
+
*/
|
|
14
|
+
/** Design tokens a tenant (or the inline override) can set. */
|
|
15
|
+
export interface DesignTokens {
|
|
16
|
+
colorPrimary?: string;
|
|
17
|
+
colorText?: string;
|
|
18
|
+
colorBackground?: string;
|
|
19
|
+
colorAccent?: string;
|
|
20
|
+
colorCard?: string;
|
|
21
|
+
colorBorder?: string;
|
|
22
|
+
borderRadius?: string;
|
|
23
|
+
fontFamily?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Every element a page may target with an appended class. */
|
|
26
|
+
export type ElementKey = 'root' | 'card' | 'title' | 'subtitle' | 'field' | 'label' | 'input' | 'error' | 'buttonPrimary' | 'socialButton' | 'divider' | 'footer';
|
|
27
|
+
/**
|
|
28
|
+
* How the social sign-in buttons are drawn — the knobs a page opens up so a
|
|
29
|
+
* tenant can make them their own without writing CSS.
|
|
30
|
+
*
|
|
31
|
+
* variant — `block` is the full "Continue with Google" bar (default); `compact`
|
|
32
|
+
* is the brand mark + short name ("Google"); `icon` is the mark alone (great
|
|
33
|
+
* for a dense grid or carousel of many providers).
|
|
34
|
+
* size — button height/scale: `sm` | `md` (default) | `lg`.
|
|
35
|
+
* layout — `list` stacks them (default); `grid` wraps them into rows;
|
|
36
|
+
* `carousel` is a single swipeable, scroll-snapped row.
|
|
37
|
+
* labels — per-provider text override, e.g. `{ github: 'Sign in with GitHub' }`.
|
|
38
|
+
* Ignored by the `icon` variant, where the name becomes the tooltip / a11y label.
|
|
39
|
+
* search — a filter box above the buttons so a user can type to narrow a long
|
|
40
|
+
* provider list. `auto` (default) shows it only once there are more than
|
|
41
|
+
* {@link SEARCH_AUTO_THRESHOLD} social providers — so a handful stays clean but
|
|
42
|
+
* a wall of 20 becomes searchable; `always` forces it; `never` hides it.
|
|
43
|
+
*/
|
|
44
|
+
export type SocialButtonVariant = 'block' | 'compact' | 'icon';
|
|
45
|
+
export type SocialButtonSize = 'sm' | 'md' | 'lg';
|
|
46
|
+
export type SocialButtonLayout = 'list' | 'grid' | 'carousel';
|
|
47
|
+
export type SocialButtonSearch = 'auto' | 'always' | 'never';
|
|
48
|
+
/**
|
|
49
|
+
* How a verification code (email code, 2FA) is entered. `boxes` (default) is the
|
|
50
|
+
* segmented one-box-per-digit input that auto-advances and accepts a paste;
|
|
51
|
+
* `single` is one plain text field. Purely presentational — both submit the same
|
|
52
|
+
* `code`.
|
|
53
|
+
*/
|
|
54
|
+
export type CodeInputStyle = 'boxes' | 'single';
|
|
55
|
+
/** Resolve the code-input style: inline override wins, then server, then `boxes`. */
|
|
56
|
+
export declare function resolveCodeInput(server: CodeInputStyle | undefined, inline: CodeInputStyle | undefined): CodeInputStyle;
|
|
57
|
+
/**
|
|
58
|
+
* At more than this many social providers, `search: 'auto'` reveals the filter
|
|
59
|
+
* box. Chosen so the common handful (Google/GitHub/…) stays button-only while a
|
|
60
|
+
* genuinely long list gets a way to fit.
|
|
61
|
+
*/
|
|
62
|
+
export declare const SEARCH_AUTO_THRESHOLD = 8;
|
|
63
|
+
export interface SocialButtonsConfig {
|
|
64
|
+
variant?: SocialButtonVariant;
|
|
65
|
+
size?: SocialButtonSize;
|
|
66
|
+
layout?: SocialButtonLayout;
|
|
67
|
+
labels?: Record<string, string>;
|
|
68
|
+
search?: SocialButtonSearch;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The interaction / micro-polish layer — how controls *feel*, independent of the
|
|
72
|
+
* colour tokens. Every knob has a flattering default so the widget looks polished
|
|
73
|
+
* out of the box, and every one is a closed allow-list so a bad stored value can
|
|
74
|
+
* only fall back, never render a broken attribute.
|
|
75
|
+
*
|
|
76
|
+
* buttonStyle — the primary CTA's fill: `solid` (default), `outline`, `soft`
|
|
77
|
+
* (tinted primary at low alpha), or `ghost` (text-only until hover). Social
|
|
78
|
+
* buttons keep their neutral provider styling regardless — recognisability.
|
|
79
|
+
* buttonEffect — press/hover feedback on every button: `scale` (subtle press-in,
|
|
80
|
+
* default), `lift` (raises on hover), `glow` (primary halo on hover), `ripple`
|
|
81
|
+
* (a material tap ripple), or `none`.
|
|
82
|
+
* shadow — elevation of the card (and solid buttons): `none | sm | md | lg`.
|
|
83
|
+
* density — spacing scale: `comfortable` (default) or `compact`.
|
|
84
|
+
* inputStyle — field treatment: `outline` (default), `filled`, or `underline`.
|
|
85
|
+
* animations — master motion switch (default on). `prefers-reduced-motion` is
|
|
86
|
+
* always honoured on top of this, never overridden by it.
|
|
87
|
+
* motionSpeed — transition tempo: `slow | normal | fast`.
|
|
88
|
+
*/
|
|
89
|
+
export type ButtonStyle = 'solid' | 'outline' | 'soft' | 'ghost';
|
|
90
|
+
export type ButtonEffect = 'none' | 'scale' | 'lift' | 'glow' | 'ripple';
|
|
91
|
+
export type ShadowDepth = 'none' | 'sm' | 'md' | 'lg';
|
|
92
|
+
export type Density = 'comfortable' | 'compact';
|
|
93
|
+
export type InputStyle = 'outline' | 'filled' | 'underline';
|
|
94
|
+
export type MotionSpeed = 'slow' | 'normal' | 'fast';
|
|
95
|
+
/**
|
|
96
|
+
* How the widget shows it is working — while it fetches the tenant's appearance,
|
|
97
|
+
* exchanges an OAuth/hosted ticket on redirect-back, or a form is submitting.
|
|
98
|
+
* spinner — a centred brand spinner (default).
|
|
99
|
+
* skeleton — shimmer placeholders shaped like the form (title + fields + button).
|
|
100
|
+
* dots — three pulsing dots.
|
|
101
|
+
* bar — an indeterminate progress bar across the top of the card.
|
|
102
|
+
*/
|
|
103
|
+
export type LoadingStyle = 'spinner' | 'skeleton' | 'dots' | 'bar';
|
|
104
|
+
export interface InteractionConfig {
|
|
105
|
+
buttonStyle?: ButtonStyle;
|
|
106
|
+
buttonEffect?: ButtonEffect;
|
|
107
|
+
shadow?: ShadowDepth;
|
|
108
|
+
density?: Density;
|
|
109
|
+
inputStyle?: InputStyle;
|
|
110
|
+
animations?: boolean;
|
|
111
|
+
motionSpeed?: MotionSpeed;
|
|
112
|
+
loading?: LoadingStyle;
|
|
113
|
+
}
|
|
114
|
+
export interface ResolvedInteraction {
|
|
115
|
+
buttonStyle: ButtonStyle;
|
|
116
|
+
buttonEffect: ButtonEffect;
|
|
117
|
+
shadow: ShadowDepth;
|
|
118
|
+
density: Density;
|
|
119
|
+
inputStyle: InputStyle;
|
|
120
|
+
animations: boolean;
|
|
121
|
+
motionSpeed: MotionSpeed;
|
|
122
|
+
loading: LoadingStyle;
|
|
123
|
+
}
|
|
124
|
+
/** Transition duration (ms) per tempo — read by the widget into a CSS var. */
|
|
125
|
+
export declare const MOTION_MS: Record<MotionSpeed, number>;
|
|
126
|
+
/**
|
|
127
|
+
* Resolve the interaction config: inline override wins over the tenant's
|
|
128
|
+
* server-side default, each validated against its allow-list, else a flattering
|
|
129
|
+
* default. `buttonEffect: 'scale'` and `animations: true` are the defaults so the
|
|
130
|
+
* widget feels alive out of the box; a tenant can dial any of it back.
|
|
131
|
+
*/
|
|
132
|
+
export declare function resolveInteraction(server: InteractionConfig | undefined, inline: InteractionConfig | undefined): ResolvedInteraction;
|
|
133
|
+
export interface ResolvedSocialButtons {
|
|
134
|
+
variant: SocialButtonVariant;
|
|
135
|
+
size: SocialButtonSize;
|
|
136
|
+
layout: SocialButtonLayout;
|
|
137
|
+
labels: Record<string, string>;
|
|
138
|
+
search: SocialButtonSearch;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Resolve the social-button config from the tenant's server-side default merged
|
|
142
|
+
* with the inline override (which wins), falling back to safe defaults. Every
|
|
143
|
+
* value is validated against its allow-list, so a bad stored value can never
|
|
144
|
+
* produce a broken `data-variant` — it just falls back.
|
|
145
|
+
*/
|
|
146
|
+
export declare function resolveSocialButtons(server: SocialButtonsConfig | undefined, inline: SocialButtonsConfig | undefined): ResolvedSocialButtons;
|
|
147
|
+
/** The inline override an embedding page may pass as `appearance`. */
|
|
148
|
+
export interface WidgetAppearance {
|
|
149
|
+
variables?: DesignTokens;
|
|
150
|
+
elements?: Partial<Record<ElementKey, string>>;
|
|
151
|
+
/** How the social buttons look — variant, size, layout, per-provider labels. */
|
|
152
|
+
socialButtons?: SocialButtonsConfig;
|
|
153
|
+
/** How a verification code is entered — segmented `boxes` (default) or `single`. */
|
|
154
|
+
codeInput?: CodeInputStyle;
|
|
155
|
+
/** How controls feel — button style/effect, shadow, density, input style, motion. */
|
|
156
|
+
interaction?: InteractionConfig;
|
|
157
|
+
}
|
|
158
|
+
/** First-party strategies the instance has switched on. */
|
|
159
|
+
export interface PublicStrategies {
|
|
160
|
+
password: boolean;
|
|
161
|
+
emailCode: boolean;
|
|
162
|
+
emailLink: boolean;
|
|
163
|
+
passkey: boolean;
|
|
164
|
+
phoneCode: boolean;
|
|
165
|
+
siwe: boolean;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* The tenant's public appearance, exactly the shape `GET /v1/appearance`
|
|
169
|
+
* returns under `appearance`. Every value is already resolved and sanitised
|
|
170
|
+
* server-side; NONE of it is a secret.
|
|
171
|
+
*/
|
|
172
|
+
export interface PublicAppearance {
|
|
173
|
+
applicationName: string;
|
|
174
|
+
logoUrl: string | null;
|
|
175
|
+
colorPrimary: string;
|
|
176
|
+
colorBackground: string;
|
|
177
|
+
colorText: string;
|
|
178
|
+
colorAccent: string;
|
|
179
|
+
colorCard: string;
|
|
180
|
+
colorBorder: string;
|
|
181
|
+
borderRadius: string;
|
|
182
|
+
theme: 'light' | 'dark' | 'auto';
|
|
183
|
+
fontStack: string;
|
|
184
|
+
layout: 'centered' | 'split' | 'wall';
|
|
185
|
+
headline: string | null;
|
|
186
|
+
subheadline: string | null;
|
|
187
|
+
footer: string | null;
|
|
188
|
+
providerOrder: string[];
|
|
189
|
+
providerHidden: string[];
|
|
190
|
+
/** Tenant's server-side default for the social buttons; the inline override wins. */
|
|
191
|
+
socialButtons?: SocialButtonsConfig;
|
|
192
|
+
/** Tenant's server-side default for the code-input style; the inline override wins. */
|
|
193
|
+
codeInput?: CodeInputStyle;
|
|
194
|
+
/** Tenant's server-side interaction/polish defaults; the inline override wins. */
|
|
195
|
+
interaction?: InteractionConfig;
|
|
196
|
+
/** §5.1 legal consent on sign-up — Terms/Privacy links + whether it is required. */
|
|
197
|
+
legal?: {
|
|
198
|
+
termsUrl: string | null;
|
|
199
|
+
privacyUrl: string | null;
|
|
200
|
+
required: boolean;
|
|
201
|
+
};
|
|
202
|
+
/** §5 extra fields the create-account form collects (name, company, custom…). */
|
|
203
|
+
signUpFields?: PublicSignUpField[];
|
|
204
|
+
}
|
|
205
|
+
/** One configurable sign-up field, exactly as `/v1/appearance` resolves it. */
|
|
206
|
+
export interface PublicSignUpField {
|
|
207
|
+
key: string;
|
|
208
|
+
label: string;
|
|
209
|
+
type: 'text' | 'email' | 'tel' | 'url' | 'number' | 'date' | 'textarea' | 'checkbox' | 'select' | 'radio' | 'multiselect';
|
|
210
|
+
required: boolean;
|
|
211
|
+
placeholder: string | null;
|
|
212
|
+
options: string[];
|
|
213
|
+
builtin: boolean;
|
|
214
|
+
}
|
|
215
|
+
/** The full `GET /v1/appearance` envelope. */
|
|
216
|
+
export interface AppearanceResponse {
|
|
217
|
+
object: 'appearance';
|
|
218
|
+
appearance: PublicAppearance;
|
|
219
|
+
/** ENABLED social provider ids (keys only), e.g. `['google','github']`. */
|
|
220
|
+
providers: string[];
|
|
221
|
+
/**
|
|
222
|
+
* Enabled providers that support native / One-Tap sign-in, WITH their public
|
|
223
|
+
* client_id — a subset of `providers`. The widget uses this to render the
|
|
224
|
+
* official Google Identity Services button instead of the redirect flow. A
|
|
225
|
+
* client_id is public; no secret is ever here.
|
|
226
|
+
*/
|
|
227
|
+
native_providers?: {
|
|
228
|
+
provider: string;
|
|
229
|
+
client_id: string;
|
|
230
|
+
}[];
|
|
231
|
+
/**
|
|
232
|
+
* §6.5 per-provider sign-in / sign-up scope. A provider absent from this map
|
|
233
|
+
* is unscoped (shows on both screens). The widget uses it with its `mode` to
|
|
234
|
+
* render a provider only on the screen it belongs on — the server enforces
|
|
235
|
+
* the same at the resolver, so the button and the outcome always agree.
|
|
236
|
+
*/
|
|
237
|
+
provider_scopes?: Record<string, {
|
|
238
|
+
sign_in: boolean;
|
|
239
|
+
sign_up: boolean;
|
|
240
|
+
}>;
|
|
241
|
+
strategies: PublicStrategies;
|
|
242
|
+
/**
|
|
243
|
+
* "Sign in with Atlas" — the preset OIDC connection's state. When enabled, the
|
|
244
|
+
* widget renders a "Sign in with Atlas" button that starts the SSO flow for
|
|
245
|
+
* `connection_id`. Off (no button) when absent or `{ enabled: false }`.
|
|
246
|
+
*/
|
|
247
|
+
sign_in_with_atlas?: {
|
|
248
|
+
enabled: boolean;
|
|
249
|
+
connection_id?: string;
|
|
250
|
+
};
|
|
251
|
+
/** §7.5 whether the widget shows a "Remember me" checkbox at sign-in. */
|
|
252
|
+
rememberMe?: boolean;
|
|
253
|
+
/** §5.3 whether the widget shows a "Remember this device" checkbox at 2FA. */
|
|
254
|
+
rememberDevice?: boolean;
|
|
255
|
+
/**
|
|
256
|
+
* §5 per-flow captcha the widget renders. Public fields only (provider, site
|
|
257
|
+
* key, render mode) — the secret stays server-side. A disabled flow reports
|
|
258
|
+
* `{ enabled: false }`, so the widget draws nothing there.
|
|
259
|
+
*/
|
|
260
|
+
captchaFlows?: Partial<Record<CaptchaFlowKey, FlowCaptcha>>;
|
|
261
|
+
}
|
|
262
|
+
export type CaptchaFlowKey = 'signUp' | 'signIn' | 'guest' | 'passwordReset';
|
|
263
|
+
export type CaptchaWidgetMode = 'invisible' | 'managed' | 'visible';
|
|
264
|
+
export interface FlowCaptcha {
|
|
265
|
+
enabled: boolean;
|
|
266
|
+
provider?: string;
|
|
267
|
+
siteKey?: string | null;
|
|
268
|
+
widget?: CaptchaWidgetMode;
|
|
269
|
+
}
|
|
270
|
+
/** The unstyled defaults, so the widget looks right before any fetch resolves. */
|
|
271
|
+
export declare const DEFAULT_TOKENS: Required<DesignTokens>;
|
|
272
|
+
/** Tokens implied by a fetched public appearance. */
|
|
273
|
+
export declare function tokensFromAppearance(appearance: PublicAppearance | null): Required<DesignTokens>;
|
|
274
|
+
/**
|
|
275
|
+
* Resolve final tokens: defaults, then the tenant's appearance, then the inline
|
|
276
|
+
* override. Later wins and `undefined` never overwrites — a page setting one
|
|
277
|
+
* variable keeps the other seven from the tenant.
|
|
278
|
+
*/
|
|
279
|
+
export declare function resolveTokens(appearance: PublicAppearance | null, override?: WidgetAppearance): Required<DesignTokens>;
|
|
280
|
+
/** Tokens as `--atlas-embed-*` custom properties for the root element. */
|
|
281
|
+
export declare function cssVariables(tokens: Required<DesignTokens>): Record<string, string>;
|
|
282
|
+
/** Our base class plus the page's override, appended so theirs wins on ties. */
|
|
283
|
+
export declare function classFor(element: ElementKey, base: string, override?: WidgetAppearance): string;
|
|
284
|
+
/**
|
|
285
|
+
* The provider ids to render, in order, with hidden ones removed.
|
|
286
|
+
*
|
|
287
|
+
* The server already returns only ENABLED providers; `providerOrder` is the
|
|
288
|
+
* tenant's display preference and `providerHidden` a suppression list, both
|
|
289
|
+
* cosmetic. Ordered providers come first (in the tenant's order), then any
|
|
290
|
+
* remaining enabled providers in their original order.
|
|
291
|
+
*/
|
|
292
|
+
export declare function orderedProviders(enabled: readonly string[], appearance: PublicAppearance | null): string[];
|
|
293
|
+
export declare function providerLabel(id: string): string;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The widget's theming model, mirroring the React SDK's appearance system
|
|
4
|
+
* (packages/react/src/appearance.ts) but for a framework-agnostic custom
|
|
5
|
+
* element: design tokens become CSS custom properties on the widget root, and
|
|
6
|
+
* per-element class overrides are APPENDED to ours so a tenant's `class` makes
|
|
7
|
+
* a rounder button rather than an unstyled one.
|
|
8
|
+
*
|
|
9
|
+
* Two sources feed it:
|
|
10
|
+
* - the tenant's PUBLIC appearance, fetched from `GET /v1/appearance` — the
|
|
11
|
+
* same resolved/sanitised branding the hosted page uses, minus anything
|
|
12
|
+
* that is not safe to hand a third-party origin (see the endpoint).
|
|
13
|
+
* - an inline `appearance` option the embedding page passes, which wins.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.DEFAULT_TOKENS = exports.MOTION_MS = exports.SEARCH_AUTO_THRESHOLD = void 0;
|
|
17
|
+
exports.resolveCodeInput = resolveCodeInput;
|
|
18
|
+
exports.resolveInteraction = resolveInteraction;
|
|
19
|
+
exports.resolveSocialButtons = resolveSocialButtons;
|
|
20
|
+
exports.tokensFromAppearance = tokensFromAppearance;
|
|
21
|
+
exports.resolveTokens = resolveTokens;
|
|
22
|
+
exports.cssVariables = cssVariables;
|
|
23
|
+
exports.classFor = classFor;
|
|
24
|
+
exports.orderedProviders = orderedProviders;
|
|
25
|
+
exports.providerLabel = providerLabel;
|
|
26
|
+
const CODE_INPUT_STYLES = ['boxes', 'single'];
|
|
27
|
+
/** Resolve the code-input style: inline override wins, then server, then `boxes`. */
|
|
28
|
+
function resolveCodeInput(server, inline) {
|
|
29
|
+
for (const v of [inline, server])
|
|
30
|
+
if (v && CODE_INPUT_STYLES.includes(v))
|
|
31
|
+
return v;
|
|
32
|
+
return 'boxes';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* At more than this many social providers, `search: 'auto'` reveals the filter
|
|
36
|
+
* box. Chosen so the common handful (Google/GitHub/…) stays button-only while a
|
|
37
|
+
* genuinely long list gets a way to fit.
|
|
38
|
+
*/
|
|
39
|
+
exports.SEARCH_AUTO_THRESHOLD = 8;
|
|
40
|
+
const BUTTON_STYLES = ['solid', 'outline', 'soft', 'ghost'];
|
|
41
|
+
const BUTTON_EFFECTS = ['none', 'scale', 'lift', 'glow', 'ripple'];
|
|
42
|
+
const SHADOWS = ['none', 'sm', 'md', 'lg'];
|
|
43
|
+
const DENSITIES = ['comfortable', 'compact'];
|
|
44
|
+
const INPUT_STYLES = ['outline', 'filled', 'underline'];
|
|
45
|
+
const MOTION_SPEEDS = ['slow', 'normal', 'fast'];
|
|
46
|
+
const LOADING_STYLES = ['spinner', 'skeleton', 'dots', 'bar'];
|
|
47
|
+
/** Transition duration (ms) per tempo — read by the widget into a CSS var. */
|
|
48
|
+
exports.MOTION_MS = { slow: 280, normal: 160, fast: 80 };
|
|
49
|
+
/**
|
|
50
|
+
* Resolve the interaction config: inline override wins over the tenant's
|
|
51
|
+
* server-side default, each validated against its allow-list, else a flattering
|
|
52
|
+
* default. `buttonEffect: 'scale'` and `animations: true` are the defaults so the
|
|
53
|
+
* widget feels alive out of the box; a tenant can dial any of it back.
|
|
54
|
+
*/
|
|
55
|
+
function resolveInteraction(server, inline) {
|
|
56
|
+
const pick = (allowed, fallback, ...vals) => {
|
|
57
|
+
for (const v of vals)
|
|
58
|
+
if (v !== undefined && allowed.includes(v))
|
|
59
|
+
return v;
|
|
60
|
+
return fallback;
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
buttonStyle: pick(BUTTON_STYLES, 'solid', inline?.buttonStyle, server?.buttonStyle),
|
|
64
|
+
buttonEffect: pick(BUTTON_EFFECTS, 'scale', inline?.buttonEffect, server?.buttonEffect),
|
|
65
|
+
shadow: pick(SHADOWS, 'md', inline?.shadow, server?.shadow),
|
|
66
|
+
density: pick(DENSITIES, 'comfortable', inline?.density, server?.density),
|
|
67
|
+
inputStyle: pick(INPUT_STYLES, 'outline', inline?.inputStyle, server?.inputStyle),
|
|
68
|
+
animations: typeof inline?.animations === 'boolean'
|
|
69
|
+
? inline.animations
|
|
70
|
+
: typeof server?.animations === 'boolean'
|
|
71
|
+
? server.animations
|
|
72
|
+
: true,
|
|
73
|
+
motionSpeed: pick(MOTION_SPEEDS, 'normal', inline?.motionSpeed, server?.motionSpeed),
|
|
74
|
+
loading: pick(LOADING_STYLES, 'spinner', inline?.loading, server?.loading),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const VARIANTS = ['block', 'compact', 'icon'];
|
|
78
|
+
const SIZES = ['sm', 'md', 'lg'];
|
|
79
|
+
const LAYOUTS = ['list', 'grid', 'carousel'];
|
|
80
|
+
const SEARCH_MODES = ['auto', 'always', 'never'];
|
|
81
|
+
/**
|
|
82
|
+
* Resolve the social-button config from the tenant's server-side default merged
|
|
83
|
+
* with the inline override (which wins), falling back to safe defaults. Every
|
|
84
|
+
* value is validated against its allow-list, so a bad stored value can never
|
|
85
|
+
* produce a broken `data-variant` — it just falls back.
|
|
86
|
+
*/
|
|
87
|
+
function resolveSocialButtons(server, inline) {
|
|
88
|
+
const pick = (allowed, ...vals) => {
|
|
89
|
+
for (const v of vals)
|
|
90
|
+
if (v !== undefined && allowed.includes(v))
|
|
91
|
+
return v;
|
|
92
|
+
return allowed[0];
|
|
93
|
+
};
|
|
94
|
+
return {
|
|
95
|
+
variant: pick(VARIANTS, inline?.variant, server?.variant),
|
|
96
|
+
size: pick(SIZES, inline?.size ?? 'md', server?.size, 'md'),
|
|
97
|
+
layout: pick(LAYOUTS, inline?.layout, server?.layout),
|
|
98
|
+
labels: { ...(server?.labels ?? {}), ...(inline?.labels ?? {}) },
|
|
99
|
+
search: pick(SEARCH_MODES, inline?.search, server?.search),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/** The unstyled defaults, so the widget looks right before any fetch resolves. */
|
|
103
|
+
exports.DEFAULT_TOKENS = {
|
|
104
|
+
colorPrimary: '#5b5bd6',
|
|
105
|
+
colorText: '#1c1c1f',
|
|
106
|
+
colorBackground: '#ffffff',
|
|
107
|
+
colorAccent: '#5b5bd6',
|
|
108
|
+
colorCard: '#ffffff',
|
|
109
|
+
colorBorder: '#e4e4e9',
|
|
110
|
+
borderRadius: '10px',
|
|
111
|
+
fontFamily: 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
|
|
112
|
+
};
|
|
113
|
+
/** Tokens implied by a fetched public appearance. */
|
|
114
|
+
function tokensFromAppearance(appearance) {
|
|
115
|
+
if (!appearance)
|
|
116
|
+
return exports.DEFAULT_TOKENS;
|
|
117
|
+
return {
|
|
118
|
+
colorPrimary: appearance.colorPrimary || exports.DEFAULT_TOKENS.colorPrimary,
|
|
119
|
+
colorText: appearance.colorText || exports.DEFAULT_TOKENS.colorText,
|
|
120
|
+
colorBackground: appearance.colorBackground || exports.DEFAULT_TOKENS.colorBackground,
|
|
121
|
+
colorAccent: appearance.colorAccent || appearance.colorPrimary || exports.DEFAULT_TOKENS.colorAccent,
|
|
122
|
+
colorCard: appearance.colorCard || appearance.colorBackground || exports.DEFAULT_TOKENS.colorCard,
|
|
123
|
+
colorBorder: appearance.colorBorder || exports.DEFAULT_TOKENS.colorBorder,
|
|
124
|
+
borderRadius: appearance.borderRadius || exports.DEFAULT_TOKENS.borderRadius,
|
|
125
|
+
fontFamily: appearance.fontStack || exports.DEFAULT_TOKENS.fontFamily,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Resolve final tokens: defaults, then the tenant's appearance, then the inline
|
|
130
|
+
* override. Later wins and `undefined` never overwrites — a page setting one
|
|
131
|
+
* variable keeps the other seven from the tenant.
|
|
132
|
+
*/
|
|
133
|
+
function resolveTokens(appearance, override) {
|
|
134
|
+
const resolved = { ...tokensFromAppearance(appearance) };
|
|
135
|
+
const overrides = override?.variables ?? {};
|
|
136
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
137
|
+
if (value !== undefined)
|
|
138
|
+
resolved[key] = value;
|
|
139
|
+
}
|
|
140
|
+
return resolved;
|
|
141
|
+
}
|
|
142
|
+
/** Tokens as `--atlas-embed-*` custom properties for the root element. */
|
|
143
|
+
function cssVariables(tokens) {
|
|
144
|
+
const vars = {};
|
|
145
|
+
for (const [key, value] of Object.entries(tokens)) {
|
|
146
|
+
vars[`--atlas-embed-${key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}`] = value;
|
|
147
|
+
}
|
|
148
|
+
return vars;
|
|
149
|
+
}
|
|
150
|
+
/** Our base class plus the page's override, appended so theirs wins on ties. */
|
|
151
|
+
function classFor(element, base, override) {
|
|
152
|
+
const extra = override?.elements?.[element];
|
|
153
|
+
return extra ? `${base} ${extra}` : base;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* The provider ids to render, in order, with hidden ones removed.
|
|
157
|
+
*
|
|
158
|
+
* The server already returns only ENABLED providers; `providerOrder` is the
|
|
159
|
+
* tenant's display preference and `providerHidden` a suppression list, both
|
|
160
|
+
* cosmetic. Ordered providers come first (in the tenant's order), then any
|
|
161
|
+
* remaining enabled providers in their original order.
|
|
162
|
+
*/
|
|
163
|
+
function orderedProviders(enabled, appearance) {
|
|
164
|
+
const order = appearance?.providerOrder ?? [];
|
|
165
|
+
const hidden = new Set(appearance?.providerHidden ?? []);
|
|
166
|
+
const enabledSet = new Set(enabled.filter((id) => !hidden.has(id)));
|
|
167
|
+
const out = [];
|
|
168
|
+
for (const id of order) {
|
|
169
|
+
if (enabledSet.has(id) && !out.includes(id))
|
|
170
|
+
out.push(id);
|
|
171
|
+
}
|
|
172
|
+
for (const id of enabled) {
|
|
173
|
+
if (enabledSet.has(id) && !out.includes(id))
|
|
174
|
+
out.push(id);
|
|
175
|
+
}
|
|
176
|
+
return out;
|
|
177
|
+
}
|
|
178
|
+
/** A human label for a provider id, for the "Continue with …" buttons. */
|
|
179
|
+
const PROVIDER_LABELS = {
|
|
180
|
+
google: 'Google',
|
|
181
|
+
github: 'GitHub',
|
|
182
|
+
gitlab: 'GitLab',
|
|
183
|
+
facebook: 'Facebook',
|
|
184
|
+
apple: 'Apple',
|
|
185
|
+
microsoft: 'Microsoft',
|
|
186
|
+
discord: 'Discord',
|
|
187
|
+
linkedin: 'LinkedIn',
|
|
188
|
+
twitter: 'X',
|
|
189
|
+
twitter_v1: 'X',
|
|
190
|
+
x: 'X',
|
|
191
|
+
slack: 'Slack',
|
|
192
|
+
spotify: 'Spotify',
|
|
193
|
+
twitch: 'Twitch',
|
|
194
|
+
notion: 'Notion',
|
|
195
|
+
linear: 'Linear',
|
|
196
|
+
steam: 'Steam',
|
|
197
|
+
};
|
|
198
|
+
function providerLabel(id) {
|
|
199
|
+
if (PROVIDER_LABELS[id])
|
|
200
|
+
return PROVIDER_LABELS[id];
|
|
201
|
+
return id
|
|
202
|
+
.split(/[_-]/)
|
|
203
|
+
.map((part) => (part ? part[0].toUpperCase() + part.slice(1) : part))
|
|
204
|
+
.join(' ');
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=appearance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appearance.js","sourceRoot":"","sources":["../src/appearance.ts"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FapiClient } from '@atlasauth/js';
|
|
2
|
+
import type { AppearanceResponse } from './appearance';
|
|
3
|
+
export interface WidgetClientOptions {
|
|
4
|
+
publishableKey: string;
|
|
5
|
+
/** The instance's FAPI origin, e.g. `https://accounts.acme.com`. */
|
|
6
|
+
frontendApi: string;
|
|
7
|
+
fetchImpl?: typeof fetch;
|
|
8
|
+
}
|
|
9
|
+
/** A FAPI client wired for the widget: shared @atlasauth/js driver, pk in the query. */
|
|
10
|
+
export declare function createClient(options: WidgetClientOptions): FapiClient;
|
|
11
|
+
/** Fetch the tenant's public appearance + enabled providers/strategies. */
|
|
12
|
+
export declare function fetchAppearance(client: FapiClient): Promise<AppearanceResponse | null>;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createClient = createClient;
|
|
4
|
+
exports.fetchAppearance = fetchAppearance;
|
|
5
|
+
const js_1 = require("@atlasauth/js");
|
|
6
|
+
/**
|
|
7
|
+
* The widget talks to FAPI cross-origin, so the browser sends a CORS preflight
|
|
8
|
+
* before every non-simple request. A preflight `OPTIONS` carries NO custom
|
|
9
|
+
* headers — the `x-publishable-key` the SDK normally sends is stripped — so the
|
|
10
|
+
* API could not tell WHICH instance the request targets and could not decide
|
|
11
|
+
* whether the page's Origin is allow-listed for it.
|
|
12
|
+
*
|
|
13
|
+
* The fix is to also carry the publishable key in the query string. The
|
|
14
|
+
* preflight is sent to the same URL (query included), so the CORS gate resolves
|
|
15
|
+
* the exact instance and checks THAT instance's `allowedOrigins` — never a
|
|
16
|
+
* cross-tenant guess. The header is still sent on the real request; this only
|
|
17
|
+
* adds the query param the preflight needs.
|
|
18
|
+
*/
|
|
19
|
+
function widgetFetch(publishableKey, base) {
|
|
20
|
+
return ((input, init) => {
|
|
21
|
+
const href = typeof input === 'string' ? input : input.toString();
|
|
22
|
+
let url;
|
|
23
|
+
try {
|
|
24
|
+
url = new URL(href);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return base(input, init);
|
|
28
|
+
}
|
|
29
|
+
if (!url.searchParams.has('publishable_key')) {
|
|
30
|
+
url.searchParams.set('publishable_key', publishableKey);
|
|
31
|
+
}
|
|
32
|
+
return base(url.toString(), init);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/** A FAPI client wired for the widget: shared @atlasauth/js driver, pk in the query. */
|
|
36
|
+
function createClient(options) {
|
|
37
|
+
const base = options.fetchImpl ?? fetch;
|
|
38
|
+
return new js_1.FapiClient({
|
|
39
|
+
publishableKey: options.publishableKey,
|
|
40
|
+
baseUrl: options.frontendApi.replace(/\/$/, ''),
|
|
41
|
+
fetchImpl: widgetFetch(options.publishableKey, base),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/** Fetch the tenant's public appearance + enabled providers/strategies. */
|
|
45
|
+
async function fetchAppearance(client) {
|
|
46
|
+
const response = await client.get('/v1/appearance');
|
|
47
|
+
return response.ok ? response.data : null;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";;AAwCA,oCAOC;AAGD,0CAGC;AArDD,sCAA2C;AAG3C;;;;;;;;;;;;GAYG;AACH,SAAS,WAAW,CAAC,cAAsB,EAAE,IAAkB;IAC7D,OAAO,CAAC,CAAC,KAAwB,EAAE,IAAkB,EAAE,EAAE;QACvD,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QAClE,IAAI,GAAQ,CAAC;QACb,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,CAAC;YAC7C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC,CAAiB,CAAC;AACrB,CAAC;AASD,wFAAwF;AACxF,SAAgB,YAAY,CAAC,OAA4B;IACvD,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IACxC,OAAO,IAAI,eAAU,CAAC;QACpB,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QAC/C,SAAS,EAAE,WAAW,CAAC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC;KACrD,CAAC,CAAC;AACL,CAAC;AAED,2EAA2E;AACpE,KAAK,UAAU,eAAe,CAAC,MAAkB;IACtD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAAqB,gBAAgB,CAAC,CAAC;IACxE,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5C,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { WidgetOptions } from './widget';
|
|
2
|
+
/**
|
|
3
|
+
* Resolving widget options from the DOM.
|
|
4
|
+
*
|
|
5
|
+
* Configuration can come from three places, most specific first:
|
|
6
|
+
* 1. the element's own attributes (`data-atlas-key`, `publishable-key`, …),
|
|
7
|
+
* 2. the imperative `mount*()` options,
|
|
8
|
+
* 3. the `<script>` tag that loaded the widget (`data-atlas-key`, …), which
|
|
9
|
+
* supplies defaults every element on the page inherits.
|
|
10
|
+
*/
|
|
11
|
+
export interface ScriptDefaults {
|
|
12
|
+
publishableKey?: string;
|
|
13
|
+
frontendApi?: string;
|
|
14
|
+
redirect?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function setScriptDefaults(defaults: ScriptDefaults): void;
|
|
17
|
+
export declare function getScriptDefaults(): ScriptDefaults;
|
|
18
|
+
/** Read the `data-atlas-*` attributes off the loading script tag. */
|
|
19
|
+
export declare function readScriptTag(script: HTMLScriptElement | null | undefined): ScriptDefaults;
|
|
20
|
+
/**
|
|
21
|
+
* Build widget options for an element in a given mode, falling back through the
|
|
22
|
+
* element's attributes to the script-tag defaults. Returns null when the two
|
|
23
|
+
* required values — publishable key and FAPI origin — cannot be resolved.
|
|
24
|
+
*/
|
|
25
|
+
export declare function optionsFromElement(el: Element, mode: 'sign-in' | 'sign-up'): WidgetOptions | null;
|
|
26
|
+
/** Merge imperative options over the script-tag defaults. */
|
|
27
|
+
export declare function optionsFromApi(mode: 'sign-in' | 'sign-up', overrides?: Partial<WidgetOptions>): WidgetOptions | null;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setScriptDefaults = setScriptDefaults;
|
|
4
|
+
exports.getScriptDefaults = getScriptDefaults;
|
|
5
|
+
exports.readScriptTag = readScriptTag;
|
|
6
|
+
exports.optionsFromElement = optionsFromElement;
|
|
7
|
+
exports.optionsFromApi = optionsFromApi;
|
|
8
|
+
let scriptDefaults = {};
|
|
9
|
+
function setScriptDefaults(defaults) {
|
|
10
|
+
scriptDefaults = defaults;
|
|
11
|
+
}
|
|
12
|
+
function getScriptDefaults() {
|
|
13
|
+
return scriptDefaults;
|
|
14
|
+
}
|
|
15
|
+
/** Read the `data-atlas-*` attributes off the loading script tag. */
|
|
16
|
+
function readScriptTag(script) {
|
|
17
|
+
if (!script)
|
|
18
|
+
return {};
|
|
19
|
+
const d = script.dataset;
|
|
20
|
+
const out = {};
|
|
21
|
+
if (d.atlasKey)
|
|
22
|
+
out.publishableKey = d.atlasKey;
|
|
23
|
+
if (d.atlasFapi)
|
|
24
|
+
out.frontendApi = d.atlasFapi;
|
|
25
|
+
if (d.atlasRedirect)
|
|
26
|
+
out.redirect = d.atlasRedirect;
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Build widget options for an element in a given mode, falling back through the
|
|
31
|
+
* element's attributes to the script-tag defaults. Returns null when the two
|
|
32
|
+
* required values — publishable key and FAPI origin — cannot be resolved.
|
|
33
|
+
*/
|
|
34
|
+
function optionsFromElement(el, mode) {
|
|
35
|
+
const dataset = el.dataset;
|
|
36
|
+
const attr = (name) => el.getAttribute(name) ?? undefined;
|
|
37
|
+
const publishableKey = dataset.atlasKey ?? attr('publishable-key') ?? scriptDefaults.publishableKey;
|
|
38
|
+
const frontendApi = dataset.atlasFapi ?? attr('frontend-api') ?? scriptDefaults.frontendApi;
|
|
39
|
+
const redirect = dataset.atlasRedirect ?? attr('redirect') ?? scriptDefaults.redirect;
|
|
40
|
+
if (!publishableKey || !frontendApi)
|
|
41
|
+
return null;
|
|
42
|
+
const options = { publishableKey, frontendApi, mode };
|
|
43
|
+
if (redirect)
|
|
44
|
+
options.redirect = redirect;
|
|
45
|
+
return options;
|
|
46
|
+
}
|
|
47
|
+
/** Merge imperative options over the script-tag defaults. */
|
|
48
|
+
function optionsFromApi(mode, overrides = {}) {
|
|
49
|
+
const publishableKey = overrides.publishableKey ?? scriptDefaults.publishableKey;
|
|
50
|
+
const frontendApi = overrides.frontendApi ?? scriptDefaults.frontendApi;
|
|
51
|
+
if (!publishableKey || !frontendApi)
|
|
52
|
+
return null;
|
|
53
|
+
return {
|
|
54
|
+
...overrides,
|
|
55
|
+
publishableKey,
|
|
56
|
+
frontendApi,
|
|
57
|
+
redirect: overrides.redirect ?? scriptDefaults.redirect,
|
|
58
|
+
mode,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=config.js.map
|