@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
package/dist/styles.js
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EMBED_CSS = exports.STYLE_MARKER = void 0;
|
|
4
|
+
exports.injectStyles = injectStyles;
|
|
5
|
+
/**
|
|
6
|
+
* Minimal, scoped stylesheet for the widget.
|
|
7
|
+
*
|
|
8
|
+
* Injected once into the document head (keyed by a data attribute so mounting
|
|
9
|
+
* two widgets does not duplicate it) rather than an inline `style` per element —
|
|
10
|
+
* a single sheet is what a tenant can override with their own CSS, which is the
|
|
11
|
+
* whole reason the widget renders in the host DOM instead of an iframe.
|
|
12
|
+
*
|
|
13
|
+
* Every class is namespaced `atlas-embed-*` so it cannot collide with the host
|
|
14
|
+
* page, and every colour/radius/font reads a `--atlas-embed-*` custom property
|
|
15
|
+
* with a hard-coded fallback, so the widget looks right the instant it mounts —
|
|
16
|
+
* before `GET /v1/appearance` resolves — and re-themes when it does.
|
|
17
|
+
*/
|
|
18
|
+
exports.STYLE_MARKER = 'atlas-embed-styles';
|
|
19
|
+
exports.EMBED_CSS = `
|
|
20
|
+
.atlas-embed-root {
|
|
21
|
+
--_bg: var(--atlas-embed-color-background, #ffffff);
|
|
22
|
+
--_card: var(--atlas-embed-color-card, #ffffff);
|
|
23
|
+
--_text: var(--atlas-embed-color-text, #1c1c1f);
|
|
24
|
+
--_primary: var(--atlas-embed-color-primary, #5b5bd6);
|
|
25
|
+
--_border: var(--atlas-embed-color-border, #e4e4e9);
|
|
26
|
+
--_radius: var(--atlas-embed-border-radius, 10px);
|
|
27
|
+
/* Transition tempo — the widget sets --atlas-embed-motion from motionSpeed and
|
|
28
|
+
to 0s when animations are off; reduced-motion forces 0s below regardless. */
|
|
29
|
+
--_motion: var(--atlas-embed-motion, 0.16s);
|
|
30
|
+
font-family: var(--atlas-embed-font-family, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
|
|
31
|
+
color: var(--_text);
|
|
32
|
+
box-sizing: border-box;
|
|
33
|
+
display: block;
|
|
34
|
+
}
|
|
35
|
+
.atlas-embed-root *, .atlas-embed-root *::before, .atlas-embed-root *::after { box-sizing: border-box; }
|
|
36
|
+
.atlas-embed-card {
|
|
37
|
+
max-width: 400px;
|
|
38
|
+
margin: 0 auto;
|
|
39
|
+
padding: 28px 26px;
|
|
40
|
+
background: var(--_card);
|
|
41
|
+
border: 1px solid var(--_border);
|
|
42
|
+
border-radius: calc(var(--_radius) + 4px);
|
|
43
|
+
box-shadow: 0 1px 2px rgba(0,0,0,0.04), 0 8px 28px rgba(0,0,0,0.06);
|
|
44
|
+
}
|
|
45
|
+
.atlas-embed-logo { display: block; max-height: 40px; margin: 0 auto 16px; }
|
|
46
|
+
.atlas-embed-title { font-size: 20px; font-weight: 650; text-align: center; margin: 0 0 4px; }
|
|
47
|
+
.atlas-embed-subtitle { font-size: 14px; color: #6b7280; text-align: center; margin: 0 0 20px; }
|
|
48
|
+
.atlas-embed-social { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
|
|
49
|
+
.atlas-embed-social-button {
|
|
50
|
+
display: flex; align-items: center; justify-content: center; gap: 8px;
|
|
51
|
+
width: 100%; padding: 10px 12px; font-size: 14px; font-weight: 550; cursor: pointer;
|
|
52
|
+
color: var(--_text); background: var(--_card);
|
|
53
|
+
border: 1px solid var(--_border); border-radius: var(--_radius);
|
|
54
|
+
}
|
|
55
|
+
.atlas-embed-social-button:hover { filter: brightness(0.97); }
|
|
56
|
+
.atlas-embed-social-icon { display: inline-flex; flex: none; line-height: 0; }
|
|
57
|
+
.atlas-embed-social-icon svg { width: 18px; height: 18px; display: block; }
|
|
58
|
+
.atlas-embed-social-label { display: inline-block; }
|
|
59
|
+
|
|
60
|
+
/* Size — button scale and icon size together. */
|
|
61
|
+
.atlas-embed-social[data-size="sm"] .atlas-embed-social-button { padding: 8px 10px; font-size: 13px; }
|
|
62
|
+
.atlas-embed-social[data-size="sm"] .atlas-embed-social-icon svg { width: 16px; height: 16px; }
|
|
63
|
+
.atlas-embed-social[data-size="lg"] .atlas-embed-social-button { padding: 13px 16px; font-size: 15px; }
|
|
64
|
+
.atlas-embed-social[data-size="lg"] .atlas-embed-social-icon svg { width: 22px; height: 22px; }
|
|
65
|
+
|
|
66
|
+
/* Icon-only variant: a square button, name in the tooltip. Always laid out as a
|
|
67
|
+
centred wrap (or a carousel), never a full-width bar. */
|
|
68
|
+
.atlas-embed-social-button[data-variant="icon"] { width: auto; padding: 10px; aspect-ratio: 1; }
|
|
69
|
+
.atlas-embed-social[data-size="sm"] .atlas-embed-social-button[data-variant="icon"] { padding: 8px; }
|
|
70
|
+
.atlas-embed-social[data-size="lg"] .atlas-embed-social-button[data-variant="icon"] { padding: 13px; }
|
|
71
|
+
.atlas-embed-social[data-variant="icon"]:not([data-layout="carousel"]) {
|
|
72
|
+
flex-direction: row; flex-wrap: wrap; justify-content: center;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* Grid — bars share a row and wrap. */
|
|
76
|
+
.atlas-embed-social[data-layout="grid"] { flex-direction: row; flex-wrap: wrap; }
|
|
77
|
+
.atlas-embed-social[data-layout="grid"] .atlas-embed-social-button { width: auto; flex: 1 1 auto; }
|
|
78
|
+
.atlas-embed-social[data-layout="grid"] .atlas-embed-social-button[data-variant="icon"] { flex: 0 0 auto; }
|
|
79
|
+
|
|
80
|
+
/* Carousel — one swipeable, scroll-snapped row. The raw scrollbar is hidden; the
|
|
81
|
+
arrow buttons (below) are the affordance instead. */
|
|
82
|
+
.atlas-embed-social[data-layout="carousel"] {
|
|
83
|
+
flex-direction: row; flex-wrap: nowrap; overflow-x: auto;
|
|
84
|
+
scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; scroll-behavior: smooth;
|
|
85
|
+
scrollbar-width: none;
|
|
86
|
+
}
|
|
87
|
+
.atlas-embed-social[data-layout="carousel"]::-webkit-scrollbar { display: none; }
|
|
88
|
+
.atlas-embed-social[data-layout="carousel"] .atlas-embed-social-button { width: auto; flex: 0 0 auto; scroll-snap-align: start; }
|
|
89
|
+
.atlas-embed-social[data-layout="carousel"] .atlas-embed-social-button[data-variant="block"] { min-width: 60%; }
|
|
90
|
+
|
|
91
|
+
/* Carousel shell — prev/next controls flanking the scroll row. */
|
|
92
|
+
.atlas-embed-carousel { position: relative; display: flex; align-items: center; gap: 4px; margin-bottom: 16px; }
|
|
93
|
+
.atlas-embed-carousel .atlas-embed-social { flex: 1 1 auto; min-width: 0; margin-bottom: 0; }
|
|
94
|
+
.atlas-embed-carousel-nav {
|
|
95
|
+
flex: none; display: inline-flex; align-items: center; justify-content: center;
|
|
96
|
+
width: 30px; height: 30px; padding: 0; cursor: pointer;
|
|
97
|
+
color: var(--_text); background: var(--_card);
|
|
98
|
+
border: 1px solid var(--_border); border-radius: 999px; transition: opacity 0.15s;
|
|
99
|
+
}
|
|
100
|
+
.atlas-embed-carousel-nav svg { width: 16px; height: 16px; display: block; }
|
|
101
|
+
.atlas-embed-carousel-nav:hover:not(:disabled) { filter: brightness(0.97); }
|
|
102
|
+
.atlas-embed-carousel-nav:disabled { opacity: 0.35; cursor: default; }
|
|
103
|
+
/* Nothing overflows → no controls at all. */
|
|
104
|
+
.atlas-embed-carousel[data-overflow="false"] .atlas-embed-carousel-nav { display: none; }
|
|
105
|
+
/* When the carousel sits inside a search field, the field owns the bottom margin. */
|
|
106
|
+
.atlas-embed-social-field .atlas-embed-carousel { margin-bottom: 0; }
|
|
107
|
+
|
|
108
|
+
/* Search — a filter box above a long provider list (socialButtons.search). The
|
|
109
|
+
field wraps the button container so its own margin replaces the button block's. */
|
|
110
|
+
.atlas-embed-social-field { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
|
|
111
|
+
.atlas-embed-social-field .atlas-embed-social { margin-bottom: 0; }
|
|
112
|
+
.atlas-embed-social-search {
|
|
113
|
+
width: 100%; padding: 9px 12px; font-size: 14px; color: var(--_text);
|
|
114
|
+
background: var(--_bg); border: 1px solid var(--_border); border-radius: var(--_radius);
|
|
115
|
+
}
|
|
116
|
+
.atlas-embed-social-search:focus { outline: 2px solid var(--_primary); outline-offset: -1px; }
|
|
117
|
+
.atlas-embed-social-button[hidden] { display: none; }
|
|
118
|
+
.atlas-embed-social-empty { font-size: 13px; color: #9ca3af; text-align: center; padding: 6px 0; }
|
|
119
|
+
.atlas-embed-social-empty[hidden] { display: none; }
|
|
120
|
+
.atlas-embed-divider {
|
|
121
|
+
display: flex; align-items: center; text-align: center; gap: 10px;
|
|
122
|
+
color: #9ca3af; font-size: 12px; margin: 14px 0;
|
|
123
|
+
}
|
|
124
|
+
.atlas-embed-divider::before, .atlas-embed-divider::after {
|
|
125
|
+
content: ""; flex: 1; height: 1px; background: var(--_border);
|
|
126
|
+
}
|
|
127
|
+
.atlas-embed-field { display: block; margin: 0 0 12px; }
|
|
128
|
+
.atlas-embed-label { display: block; font-size: 13px; margin: 0 0 5px; color: var(--_text); }
|
|
129
|
+
.atlas-embed-input {
|
|
130
|
+
width: 100%; padding: 10px 12px; font-size: 14px; color: var(--_text);
|
|
131
|
+
background: var(--_bg); border: 1px solid var(--_border); border-radius: var(--_radius);
|
|
132
|
+
}
|
|
133
|
+
.atlas-embed-input:focus { outline: 2px solid var(--_primary); outline-offset: -1px; }
|
|
134
|
+
/* Password field with a show/hide toggle. */
|
|
135
|
+
.atlas-embed-password { position: relative; display: flex; align-items: center; }
|
|
136
|
+
.atlas-embed-password .atlas-embed-input { width: 100%; padding-right: 54px; }
|
|
137
|
+
.atlas-embed-password-toggle {
|
|
138
|
+
position: absolute; right: 6px; padding: 4px 7px; font: inherit; font-size: 12px; font-weight: 600;
|
|
139
|
+
cursor: pointer; color: var(--_primary); background: none; border: 0; border-radius: 6px;
|
|
140
|
+
}
|
|
141
|
+
.atlas-embed-password-toggle:hover { text-decoration: underline; }
|
|
142
|
+
select.atlas-embed-input { appearance: auto; }
|
|
143
|
+
.atlas-embed-field-checkbox { display: flex; flex-direction: row-reverse; align-items: center; justify-content: flex-end; gap: 8px; }
|
|
144
|
+
.atlas-embed-field-checkbox .atlas-embed-label { margin: 0; }
|
|
145
|
+
.atlas-embed-checkbox { width: 16px; height: 16px; margin: 0; flex: none; }
|
|
146
|
+
textarea.atlas-embed-input { min-height: 72px; resize: vertical; font-family: inherit; }
|
|
147
|
+
fieldset.atlas-embed-field { border: 0; padding: 0; margin: 0 0 12px; }
|
|
148
|
+
.atlas-embed-choices { display: flex; flex-direction: column; gap: 6px; }
|
|
149
|
+
.atlas-embed-choice { display: flex; flex-direction: row; align-items: center; gap: 8px; font-size: 14px; cursor: pointer; }
|
|
150
|
+
.atlas-embed-choice input { width: 16px; height: 16px; margin: 0; flex: none; accent-color: var(--_primary); }
|
|
151
|
+
.atlas-embed-button {
|
|
152
|
+
width: 100%; margin-top: 6px; padding: 11px 12px; font-size: 14px; font-weight: 650;
|
|
153
|
+
cursor: pointer; color: #ffffff; background: var(--_primary);
|
|
154
|
+
border: 0; border-radius: var(--_radius);
|
|
155
|
+
}
|
|
156
|
+
.atlas-embed-button:disabled { opacity: 0.6; cursor: default; }
|
|
157
|
+
.atlas-embed-handle-back {
|
|
158
|
+
width: 100%; margin-top: 8px; padding: 6px; font-size: 13px; font-weight: 550;
|
|
159
|
+
cursor: pointer; color: #6b7280; background: none; border: 0;
|
|
160
|
+
}
|
|
161
|
+
.atlas-embed-handle-back:hover { color: #374151; }
|
|
162
|
+
.atlas-embed-error { display: block; font-size: 13px; color: #d64545; margin: 6px 0 0; }
|
|
163
|
+
.atlas-embed-form-error { font-size: 13px; color: #d64545; margin: 0 0 12px; min-height: 0; }
|
|
164
|
+
/* A success/confirmation banner (e.g. after a force-login password reset). */
|
|
165
|
+
.atlas-embed-flash {
|
|
166
|
+
font-size: 13px; color: #0f7a4d; background: rgba(16,163,101,0.08);
|
|
167
|
+
border: 1px solid rgba(16,163,101,0.25); border-radius: var(--_radius);
|
|
168
|
+
padding: 9px 11px; margin: 0 0 14px; text-align: center;
|
|
169
|
+
}
|
|
170
|
+
.atlas-embed-footer { font-size: 13px; color: #6b7280; text-align: center; margin: 16px 0 0; }
|
|
171
|
+
/* §5.1 legal consent row on sign-up. */
|
|
172
|
+
.atlas-embed-consent { display: flex; align-items: flex-start; gap: 8px; margin: 2px 0 14px; font-size: 13px; color: #6b7280; }
|
|
173
|
+
.atlas-embed-consent-check { margin: 2px 0 0; width: 15px; height: 15px; flex: none; accent-color: var(--_primary); cursor: pointer; }
|
|
174
|
+
.atlas-embed-consent-link { color: var(--_primary); text-decoration: underline; }
|
|
175
|
+
/* §7.5 Remember-me row. */
|
|
176
|
+
.atlas-embed-remember { display: flex; align-items: center; gap: 8px; margin: -2px 0 14px; font-size: 13px; color: var(--_text); cursor: pointer; }
|
|
177
|
+
.atlas-embed-remember-check { width: 15px; height: 15px; flex: none; accent-color: var(--_primary); cursor: pointer; }
|
|
178
|
+
.atlas-embed-note { font-size: 14px; color: #6b7280; text-align: center; margin: 8px 0; }
|
|
179
|
+
|
|
180
|
+
/* Verification step — a continuation of the email panel, not a new screen: the
|
|
181
|
+
context line ("Enter the code we sent to …") + a way back, then a segmented
|
|
182
|
+
one-box-per-digit code input that spawns in. */
|
|
183
|
+
.atlas-embed-step-context { text-align: center; margin: 0 0 20px; }
|
|
184
|
+
.atlas-embed-step-context .atlas-embed-subtitle { margin: 0 0 6px; }
|
|
185
|
+
.atlas-embed-step-back {
|
|
186
|
+
display: inline-block; padding: 2px 4px; font: inherit; font-size: 13px; font-weight: 550;
|
|
187
|
+
cursor: pointer; color: var(--_primary); background: none; border: 0;
|
|
188
|
+
}
|
|
189
|
+
.atlas-embed-step-back:hover { text-decoration: underline; }
|
|
190
|
+
/* A standalone action under the submit button (Forgot password?, Back to sign in,
|
|
191
|
+
the 2FA toggle) reads as a centred link, not a left-aligned fragment. */
|
|
192
|
+
form > .atlas-embed-step-back { display: block; width: 100%; text-align: center; margin-top: 10px; }
|
|
193
|
+
.atlas-embed-code { display: flex; gap: 8px; justify-content: space-between; }
|
|
194
|
+
.atlas-embed-code-box {
|
|
195
|
+
flex: 1 1 0; min-width: 0; aspect-ratio: 1 / 1.15; padding: 0; text-align: center;
|
|
196
|
+
font-size: 20px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--_text);
|
|
197
|
+
background: var(--_bg); border: 1px solid var(--_border); border-radius: var(--_radius);
|
|
198
|
+
}
|
|
199
|
+
.atlas-embed-code-box:focus { outline: 2px solid var(--_primary); outline-offset: -1px; }
|
|
200
|
+
/* A real step change eases its content in; error/busy re-renders (same step) do
|
|
201
|
+
not, and a reduced-motion viewer gets no animation at all. */
|
|
202
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
203
|
+
.atlas-embed-card[data-enter] > form,
|
|
204
|
+
.atlas-embed-card[data-enter] > .atlas-embed-step-context {
|
|
205
|
+
animation: atlas-embed-spawn 0.26s cubic-bezier(0.22, 0.61, 0.36, 1) both;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
@keyframes atlas-embed-spawn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
|
|
209
|
+
|
|
210
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
211
|
+
Interaction / polish layer — how controls FEEL. Driven by data-attributes on
|
|
212
|
+
the root (data-button-style / -button-effect / -shadow / -density /
|
|
213
|
+
-input-style) that the widget sets from the resolved interaction config, plus
|
|
214
|
+
the --_motion tempo var. Every rule is additive: with no attributes set the
|
|
215
|
+
widget looks exactly as it did before this layer existed.
|
|
216
|
+
───────────────────────────────────────────────────────────────────────── */
|
|
217
|
+
|
|
218
|
+
/* Smooth every interactive control at the configured tempo. */
|
|
219
|
+
.atlas-embed-button, .atlas-embed-social-button, .atlas-embed-carousel-nav,
|
|
220
|
+
.atlas-embed-handle-back, .atlas-embed-step-back, .atlas-embed-password-toggle,
|
|
221
|
+
.atlas-embed-input, .atlas-embed-code-box {
|
|
222
|
+
transition:
|
|
223
|
+
transform var(--_motion) ease, box-shadow var(--_motion) ease,
|
|
224
|
+
background-color var(--_motion) ease, border-color var(--_motion) ease,
|
|
225
|
+
color var(--_motion) ease, filter var(--_motion) ease, outline-color var(--_motion) ease;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* Primary CTA — a subtle hover darken by default (was static). */
|
|
229
|
+
.atlas-embed-button:hover:not(:disabled) { filter: brightness(1.06); }
|
|
230
|
+
|
|
231
|
+
/* Button fill style (primary CTA only — social buttons keep provider styling). */
|
|
232
|
+
.atlas-embed-root[data-button-style="outline"] .atlas-embed-button {
|
|
233
|
+
background: transparent; color: var(--_primary); border: 1px solid var(--_primary);
|
|
234
|
+
}
|
|
235
|
+
.atlas-embed-root[data-button-style="outline"] .atlas-embed-button:hover:not(:disabled) {
|
|
236
|
+
filter: none; background: color-mix(in srgb, var(--_primary) 10%, transparent);
|
|
237
|
+
}
|
|
238
|
+
.atlas-embed-root[data-button-style="soft"] .atlas-embed-button {
|
|
239
|
+
background: color-mix(in srgb, var(--_primary) 14%, transparent); color: var(--_primary);
|
|
240
|
+
}
|
|
241
|
+
.atlas-embed-root[data-button-style="soft"] .atlas-embed-button:hover:not(:disabled) {
|
|
242
|
+
filter: none; background: color-mix(in srgb, var(--_primary) 22%, transparent);
|
|
243
|
+
}
|
|
244
|
+
.atlas-embed-root[data-button-style="ghost"] .atlas-embed-button {
|
|
245
|
+
background: transparent; color: var(--_primary);
|
|
246
|
+
}
|
|
247
|
+
.atlas-embed-root[data-button-style="ghost"] .atlas-embed-button:hover:not(:disabled) {
|
|
248
|
+
filter: none; background: color-mix(in srgb, var(--_primary) 12%, transparent);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* Press/hover effect — applies to every button. */
|
|
252
|
+
.atlas-embed-root[data-button-effect="scale"] .atlas-embed-button:active:not(:disabled),
|
|
253
|
+
.atlas-embed-root[data-button-effect="scale"] .atlas-embed-social-button:active,
|
|
254
|
+
.atlas-embed-root[data-button-effect="scale"] .atlas-embed-carousel-nav:active:not(:disabled) {
|
|
255
|
+
transform: scale(0.97);
|
|
256
|
+
}
|
|
257
|
+
.atlas-embed-root[data-button-effect="lift"] .atlas-embed-button:hover:not(:disabled),
|
|
258
|
+
.atlas-embed-root[data-button-effect="lift"] .atlas-embed-social-button:hover {
|
|
259
|
+
transform: translateY(-1px); box-shadow: 0 4px 14px rgba(0,0,0,0.14); filter: none;
|
|
260
|
+
}
|
|
261
|
+
.atlas-embed-root[data-button-effect="lift"] .atlas-embed-button:active:not(:disabled),
|
|
262
|
+
.atlas-embed-root[data-button-effect="lift"] .atlas-embed-social-button:active { transform: translateY(0); }
|
|
263
|
+
.atlas-embed-root[data-button-effect="glow"] .atlas-embed-button:hover:not(:disabled),
|
|
264
|
+
.atlas-embed-root[data-button-effect="glow"] .atlas-embed-social-button:hover {
|
|
265
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--_primary) 35%, transparent); filter: none;
|
|
266
|
+
}
|
|
267
|
+
/* Ripple — the widget appends a span on pointer-down; buttons must clip it. */
|
|
268
|
+
.atlas-embed-root[data-button-effect="ripple"] .atlas-embed-button,
|
|
269
|
+
.atlas-embed-root[data-button-effect="ripple"] .atlas-embed-social-button {
|
|
270
|
+
position: relative; overflow: hidden;
|
|
271
|
+
}
|
|
272
|
+
.atlas-embed-ripple {
|
|
273
|
+
position: absolute; border-radius: 50%; transform: scale(0);
|
|
274
|
+
background: currentColor; opacity: 0.28; pointer-events: none;
|
|
275
|
+
animation: atlas-embed-ripple 0.55s ease-out;
|
|
276
|
+
}
|
|
277
|
+
@keyframes atlas-embed-ripple { to { transform: scale(2.6); opacity: 0; } }
|
|
278
|
+
|
|
279
|
+
/* Card + solid-button elevation. */
|
|
280
|
+
.atlas-embed-root[data-shadow="none"] .atlas-embed-card { box-shadow: none; }
|
|
281
|
+
.atlas-embed-root[data-shadow="sm"] .atlas-embed-card { box-shadow: 0 1px 2px rgba(0,0,0,0.05); }
|
|
282
|
+
.atlas-embed-root[data-shadow="lg"] .atlas-embed-card {
|
|
283
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.08), 0 16px 48px rgba(0,0,0,0.12);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* Density — compact tightens padding and gaps. */
|
|
287
|
+
.atlas-embed-root[data-density="compact"] .atlas-embed-card { padding: 20px 18px; }
|
|
288
|
+
.atlas-embed-root[data-density="compact"] .atlas-embed-social { gap: 6px; margin-bottom: 12px; }
|
|
289
|
+
.atlas-embed-root[data-density="compact"] .atlas-embed-field { margin-bottom: 9px; }
|
|
290
|
+
.atlas-embed-root[data-density="compact"] .atlas-embed-button { padding: 9px 12px; }
|
|
291
|
+
.atlas-embed-root[data-density="compact"] .atlas-embed-social-button { padding: 8px 10px; }
|
|
292
|
+
.atlas-embed-root[data-density="compact"] .atlas-embed-subtitle { margin-bottom: 14px; }
|
|
293
|
+
|
|
294
|
+
/* Input style — filled and underline treatments (default is the outlined field). */
|
|
295
|
+
.atlas-embed-root[data-input-style="filled"] .atlas-embed-input,
|
|
296
|
+
.atlas-embed-root[data-input-style="filled"] .atlas-embed-code-box,
|
|
297
|
+
.atlas-embed-root[data-input-style="filled"] .atlas-embed-social-search {
|
|
298
|
+
background: color-mix(in srgb, var(--_border) 24%, transparent); border-color: transparent;
|
|
299
|
+
}
|
|
300
|
+
.atlas-embed-root[data-input-style="filled"] .atlas-embed-input:focus,
|
|
301
|
+
.atlas-embed-root[data-input-style="filled"] .atlas-embed-code-box:focus,
|
|
302
|
+
.atlas-embed-root[data-input-style="filled"] .atlas-embed-social-search:focus {
|
|
303
|
+
background: var(--_bg); border-color: var(--_primary);
|
|
304
|
+
}
|
|
305
|
+
.atlas-embed-root[data-input-style="underline"] .atlas-embed-input,
|
|
306
|
+
.atlas-embed-root[data-input-style="underline"] .atlas-embed-code-box,
|
|
307
|
+
.atlas-embed-root[data-input-style="underline"] .atlas-embed-social-search {
|
|
308
|
+
background: transparent; border: 0; border-bottom: 1.5px solid var(--_border);
|
|
309
|
+
border-radius: 0; padding-left: 2px; padding-right: 2px;
|
|
310
|
+
}
|
|
311
|
+
.atlas-embed-root[data-input-style="underline"] .atlas-embed-input:focus,
|
|
312
|
+
.atlas-embed-root[data-input-style="underline"] .atlas-embed-code-box:focus,
|
|
313
|
+
.atlas-embed-root[data-input-style="underline"] .atlas-embed-social-search:focus {
|
|
314
|
+
outline: none; border-bottom-color: var(--_primary);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* ── Loading states — spinner / skeleton / dots / bar (interaction.loading).
|
|
318
|
+
Shown while appearance resolves, while a redirect ticket is exchanged, and the
|
|
319
|
+
button spinner while a form submits — so the widget never flashes blank. ── */
|
|
320
|
+
.atlas-embed-button { display: flex; align-items: center; justify-content: center; gap: 8px; }
|
|
321
|
+
.atlas-embed-loading { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; min-height: 168px; padding: 12px 0; }
|
|
322
|
+
.atlas-embed-sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
|
|
323
|
+
|
|
324
|
+
/* Spinner (default) + the in-button spinner. */
|
|
325
|
+
.atlas-embed-spinner, .atlas-embed-btn-spinner {
|
|
326
|
+
border-radius: 50%; border-style: solid; flex: none;
|
|
327
|
+
animation: atlas-embed-spin 0.7s linear infinite;
|
|
328
|
+
}
|
|
329
|
+
.atlas-embed-spinner { width: 30px; height: 30px; border-width: 3px;
|
|
330
|
+
border-color: color-mix(in srgb, var(--_primary) 22%, transparent); border-top-color: var(--_primary); }
|
|
331
|
+
.atlas-embed-btn-spinner { width: 17px; height: 17px; border-width: 2px;
|
|
332
|
+
border-color: color-mix(in srgb, currentColor 35%, transparent); border-top-color: currentColor; }
|
|
333
|
+
@keyframes atlas-embed-spin { to { transform: rotate(360deg); } }
|
|
334
|
+
|
|
335
|
+
/* Dots. */
|
|
336
|
+
.atlas-embed-loading[data-loading="dots"] { flex-direction: row; }
|
|
337
|
+
.atlas-embed-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--_primary);
|
|
338
|
+
animation: atlas-embed-bounce 1.2s ease-in-out infinite; }
|
|
339
|
+
.atlas-embed-dot:nth-child(2) { animation-delay: 0.15s; }
|
|
340
|
+
.atlas-embed-dot:nth-child(3) { animation-delay: 0.30s; }
|
|
341
|
+
@keyframes atlas-embed-bounce { 0%,80%,100% { transform: scale(0.5); opacity: 0.4; } 40% { transform: scale(1); opacity: 1; } }
|
|
342
|
+
|
|
343
|
+
/* Indeterminate bar. */
|
|
344
|
+
.atlas-embed-loading[data-loading="bar"] { min-height: 132px; }
|
|
345
|
+
.atlas-embed-loadbar { position: relative; width: 64%; height: 4px; border-radius: 999px; overflow: hidden;
|
|
346
|
+
background: color-mix(in srgb, var(--_border) 60%, transparent); }
|
|
347
|
+
.atlas-embed-loadbar::before { content: ""; position: absolute; top: 0; bottom: 0; width: 40%; border-radius: 999px;
|
|
348
|
+
background: var(--_primary); animation: atlas-embed-slide 1.1s ease-in-out infinite; }
|
|
349
|
+
@keyframes atlas-embed-slide { 0% { left: -45%; } 100% { left: 105%; } }
|
|
350
|
+
|
|
351
|
+
/* Skeleton — placeholders shaped like the form. */
|
|
352
|
+
.atlas-embed-loading[data-loading="skeleton"] { align-items: stretch; min-height: 0; gap: 12px; }
|
|
353
|
+
.atlas-embed-skel { border-radius: var(--_radius); position: relative; overflow: hidden;
|
|
354
|
+
background: color-mix(in srgb, var(--_border) 55%, transparent); }
|
|
355
|
+
.atlas-embed-skel::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%);
|
|
356
|
+
background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--_text) 8%, transparent), transparent);
|
|
357
|
+
animation: atlas-embed-shimmer 1.3s infinite; }
|
|
358
|
+
.atlas-embed-skel-title { height: 22px; width: 55%; margin: 2px auto 2px; }
|
|
359
|
+
.atlas-embed-skel-sub { height: 13px; width: 75%; margin: 0 auto 8px; }
|
|
360
|
+
.atlas-embed-skel-input { height: 42px; }
|
|
361
|
+
.atlas-embed-skel-button { height: 44px; margin-top: 6px; }
|
|
362
|
+
@keyframes atlas-embed-shimmer { to { transform: translateX(100%); } }
|
|
363
|
+
|
|
364
|
+
/* Reduced motion always wins — no transitions, presses, ripples, or spawn; and
|
|
365
|
+
loaders swap their spin/bounce/slide/shimmer for a minimal opacity pulse. */
|
|
366
|
+
@media (prefers-reduced-motion: reduce) {
|
|
367
|
+
.atlas-embed-root { --_motion: 0s; }
|
|
368
|
+
.atlas-embed-root [class^="atlas-embed-"] { transition: none !important; }
|
|
369
|
+
.atlas-embed-root .atlas-embed-button:active,
|
|
370
|
+
.atlas-embed-root .atlas-embed-social-button:active { transform: none !important; }
|
|
371
|
+
.atlas-embed-ripple { display: none; }
|
|
372
|
+
.atlas-embed-spinner, .atlas-embed-btn-spinner, .atlas-embed-dot,
|
|
373
|
+
.atlas-embed-loadbar::before, .atlas-embed-skel::after {
|
|
374
|
+
animation: atlas-embed-pulse 1.4s ease-in-out infinite !important;
|
|
375
|
+
}
|
|
376
|
+
.atlas-embed-loadbar::before { position: static; width: 100%; }
|
|
377
|
+
}
|
|
378
|
+
@keyframes atlas-embed-pulse { 0%,100% { opacity: 0.4; } 50% { opacity: 1; } }
|
|
379
|
+
`;
|
|
380
|
+
/** Inject the sheet once per document. Safe to call on every mount. */
|
|
381
|
+
function injectStyles(doc = document) {
|
|
382
|
+
if (doc.querySelector(`style[data-${exports.STYLE_MARKER}]`))
|
|
383
|
+
return;
|
|
384
|
+
const style = doc.createElement('style');
|
|
385
|
+
style.setAttribute(`data-${exports.STYLE_MARKER}`, '');
|
|
386
|
+
style.textContent = exports.EMBED_CSS;
|
|
387
|
+
(doc.head ?? doc.documentElement).appendChild(style);
|
|
388
|
+
}
|
|
389
|
+
//# sourceMappingURL=styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.js","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":";;;AA0XA,oCAMC;AAhYD;;;;;;;;;;;;GAYG;AACU,QAAA,YAAY,GAAG,oBAAoB,CAAC;AAEpC,QAAA,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwWxB,CAAC;AAEF,uEAAuE;AACvE,SAAgB,YAAY,CAAC,MAAgB,QAAQ;IACnD,IAAI,GAAG,CAAC,aAAa,CAAC,cAAc,oBAAY,GAAG,CAAC;QAAE,OAAO;IAC7D,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACzC,KAAK,CAAC,YAAY,CAAC,QAAQ,oBAAY,EAAE,EAAE,EAAE,CAAC,CAAC;IAC/C,KAAK,CAAC,WAAW,GAAG,iBAAS,CAAC;IAC9B,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { FlowCaptcha } from '../appearance';
|
|
2
|
+
export declare function isRenderable(provider?: string | null): boolean;
|
|
3
|
+
/** True when this flow needs a rendered token before its request can proceed. */
|
|
4
|
+
export declare function flowNeedsCaptcha(flow: FlowCaptcha | undefined): flow is FlowCaptcha;
|
|
5
|
+
type Win = Window & Record<string, unknown>;
|
|
6
|
+
/**
|
|
7
|
+
* Load a provider's script and resolve its global. Idempotent: a second call for
|
|
8
|
+
* the same provider reuses the in-flight or resolved load. When the global is
|
|
9
|
+
* already present (a prior load, or a test that preset it) it resolves at once.
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadProvider(provider: string, siteKey: string, win: Win): Promise<unknown>;
|
|
12
|
+
/**
|
|
13
|
+
* Obtain a captcha token for a flow. Loads the provider, renders/executes the
|
|
14
|
+
* right widget for the mode, and resolves the token. `container` is where a
|
|
15
|
+
* visible/managed widget draws; for score providers it is unused.
|
|
16
|
+
*
|
|
17
|
+
* Rejects on provider error/timeout — the caller decides whether to surface it
|
|
18
|
+
* or let the (fail-closed) server reject the tokenless request.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getCaptchaToken(flow: FlowCaptcha, container: HTMLElement): Promise<string>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isRenderable = isRenderable;
|
|
4
|
+
exports.flowNeedsCaptcha = flowNeedsCaptcha;
|
|
5
|
+
exports.loadProvider = loadProvider;
|
|
6
|
+
exports.getCaptchaToken = getCaptchaToken;
|
|
7
|
+
/**
|
|
8
|
+
* Client-side captcha rendering for the hosted widget.
|
|
9
|
+
*
|
|
10
|
+
* The server tells the widget, per flow, which provider to render and in what
|
|
11
|
+
* mode (see `/v1/appearance` → `captchaFlows`). This module loads that
|
|
12
|
+
* provider's script once and resolves a token the flow's API call then carries
|
|
13
|
+
* as `captcha_token`. Only the providers that expose a standard browser widget
|
|
14
|
+
* are renderable here; score/edge/fingerprint providers (SEON, PerimeterX,
|
|
15
|
+
* Akamai, Kasada, Incapsula, mCaptcha, AWS WAF, …) are verified server-side from
|
|
16
|
+
* a token a custom integration supplies, so the hosted widget does not draw
|
|
17
|
+
* them — `isRenderable` returns false and the caller proceeds without a token
|
|
18
|
+
* (the server still fails closed if it required one).
|
|
19
|
+
*/
|
|
20
|
+
/** The providers the hosted widget can natively render. */
|
|
21
|
+
const RENDERABLE = new Set([
|
|
22
|
+
'turnstile',
|
|
23
|
+
'hcaptcha',
|
|
24
|
+
'recaptcha',
|
|
25
|
+
'recaptcha_v3',
|
|
26
|
+
'recaptcha_enterprise',
|
|
27
|
+
]);
|
|
28
|
+
function isRenderable(provider) {
|
|
29
|
+
return !!provider && RENDERABLE.has(provider);
|
|
30
|
+
}
|
|
31
|
+
/** True when this flow needs a rendered token before its request can proceed. */
|
|
32
|
+
function flowNeedsCaptcha(flow) {
|
|
33
|
+
return !!flow && flow.enabled === true && isRenderable(flow.provider);
|
|
34
|
+
}
|
|
35
|
+
const SCRIPTS = {
|
|
36
|
+
turnstile: {
|
|
37
|
+
src: () => 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit',
|
|
38
|
+
global: 'turnstile',
|
|
39
|
+
},
|
|
40
|
+
hcaptcha: {
|
|
41
|
+
src: () => 'https://js.hcaptcha.com/1/api.js?render=explicit',
|
|
42
|
+
global: 'hcaptcha',
|
|
43
|
+
},
|
|
44
|
+
recaptcha: {
|
|
45
|
+
src: () => 'https://www.google.com/recaptcha/api.js?render=explicit',
|
|
46
|
+
global: 'grecaptcha',
|
|
47
|
+
},
|
|
48
|
+
recaptcha_v3: {
|
|
49
|
+
src: (siteKey) => `https://www.google.com/recaptcha/api.js?render=${encodeURIComponent(siteKey)}`,
|
|
50
|
+
global: 'grecaptcha',
|
|
51
|
+
},
|
|
52
|
+
recaptcha_enterprise: {
|
|
53
|
+
src: (siteKey) => `https://www.google.com/recaptcha/enterprise.js?render=${encodeURIComponent(siteKey)}`,
|
|
54
|
+
global: 'grecaptcha',
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Load a provider's script and resolve its global. Idempotent: a second call for
|
|
59
|
+
* the same provider reuses the in-flight or resolved load. When the global is
|
|
60
|
+
* already present (a prior load, or a test that preset it) it resolves at once.
|
|
61
|
+
*/
|
|
62
|
+
function loadProvider(provider, siteKey, win) {
|
|
63
|
+
const spec = SCRIPTS[provider];
|
|
64
|
+
if (!spec)
|
|
65
|
+
return Promise.reject(new Error(`captcha: ${provider} is not renderable`));
|
|
66
|
+
if (win[spec.global])
|
|
67
|
+
return Promise.resolve(win[spec.global]);
|
|
68
|
+
const doc = win.document;
|
|
69
|
+
const cacheKey = `__atlasCaptchaLoad_${provider}`;
|
|
70
|
+
const existing = win[cacheKey];
|
|
71
|
+
if (existing)
|
|
72
|
+
return existing;
|
|
73
|
+
const promise = new Promise((resolve, reject) => {
|
|
74
|
+
const done = () => {
|
|
75
|
+
if (win[spec.global])
|
|
76
|
+
resolve(win[spec.global]);
|
|
77
|
+
else
|
|
78
|
+
reject(new Error(`captcha: ${provider} script loaded but global missing`));
|
|
79
|
+
};
|
|
80
|
+
const src = spec.src(siteKey);
|
|
81
|
+
// Reuse a script tag another flow may have injected for the same src.
|
|
82
|
+
let el = doc.querySelector(`script[src="${src}"]`);
|
|
83
|
+
if (el) {
|
|
84
|
+
if (win[spec.global])
|
|
85
|
+
return resolve(win[spec.global]);
|
|
86
|
+
el.addEventListener('load', done, { once: true });
|
|
87
|
+
el.addEventListener('error', () => reject(new Error(`captcha: ${provider} script failed`)), {
|
|
88
|
+
once: true,
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
el = doc.createElement('script');
|
|
93
|
+
el.src = src;
|
|
94
|
+
el.async = true;
|
|
95
|
+
el.defer = true;
|
|
96
|
+
el.addEventListener('load', done, { once: true });
|
|
97
|
+
el.addEventListener('error', () => reject(new Error(`captcha: ${provider} script failed`)), {
|
|
98
|
+
once: true,
|
|
99
|
+
});
|
|
100
|
+
(doc.head ?? doc.body ?? doc.documentElement).appendChild(el);
|
|
101
|
+
});
|
|
102
|
+
win[cacheKey] = promise;
|
|
103
|
+
return promise;
|
|
104
|
+
}
|
|
105
|
+
/** Cloudflare Turnstile: render into the container, resolve on the callback. */
|
|
106
|
+
function turnstileToken(api, el, siteKey, mode) {
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
const id = api.render(el, {
|
|
109
|
+
sitekey: siteKey,
|
|
110
|
+
// Turnstile's own modes map cleanly onto ours.
|
|
111
|
+
appearance: mode === 'invisible' ? 'interaction-only' : 'always',
|
|
112
|
+
callback: (token) => resolve(token),
|
|
113
|
+
'error-callback': () => reject(new Error('captcha: turnstile error')),
|
|
114
|
+
'timeout-callback': () => reject(new Error('captcha: turnstile timeout')),
|
|
115
|
+
});
|
|
116
|
+
if (mode === 'invisible' && api.execute)
|
|
117
|
+
api.execute(id);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/** hCaptcha: render, then execute for invisible; resolve on the callback. */
|
|
121
|
+
function hcaptchaToken(api, el, siteKey, mode) {
|
|
122
|
+
return new Promise((resolve, reject) => {
|
|
123
|
+
const id = api.render(el, {
|
|
124
|
+
sitekey: siteKey,
|
|
125
|
+
size: mode === 'invisible' ? 'invisible' : 'normal',
|
|
126
|
+
callback: (token) => resolve(token),
|
|
127
|
+
'error-callback': () => reject(new Error('captcha: hcaptcha error')),
|
|
128
|
+
});
|
|
129
|
+
if (mode === 'invisible') {
|
|
130
|
+
const r = api.execute(id, { async: true });
|
|
131
|
+
if (r && typeof r.then === 'function') {
|
|
132
|
+
r.then((v) => resolve(v.response), reject);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/** reCAPTCHA v2 (checkbox/invisible): render, resolve on callback. */
|
|
138
|
+
function recaptchaV2Token(api, el, siteKey, mode) {
|
|
139
|
+
return new Promise((resolve, reject) => {
|
|
140
|
+
const id = api.render(el, {
|
|
141
|
+
sitekey: siteKey,
|
|
142
|
+
size: mode === 'invisible' ? 'invisible' : 'normal',
|
|
143
|
+
callback: (token) => resolve(token),
|
|
144
|
+
'error-callback': () => reject(new Error('captcha: recaptcha error')),
|
|
145
|
+
});
|
|
146
|
+
if (mode === 'invisible' && api.execute) {
|
|
147
|
+
const r = api.execute(undefined, { widgetId: id });
|
|
148
|
+
if (r && typeof r.then === 'function') {
|
|
149
|
+
r.then(resolve, reject);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/** reCAPTCHA v3 / Enterprise (score): execute programmatically, no visible widget. */
|
|
155
|
+
function recaptchaV3Token(api, siteKey) {
|
|
156
|
+
const run = () => {
|
|
157
|
+
const r = api.execute(siteKey, { action: 'submit' });
|
|
158
|
+
return r && typeof r.then === 'function'
|
|
159
|
+
? r
|
|
160
|
+
: Promise.reject(new Error('captcha: recaptcha v3 execute returned no promise'));
|
|
161
|
+
};
|
|
162
|
+
return new Promise((resolve, reject) => {
|
|
163
|
+
if (api.ready)
|
|
164
|
+
api.ready(() => run().then(resolve, reject));
|
|
165
|
+
else
|
|
166
|
+
run().then(resolve, reject);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Obtain a captcha token for a flow. Loads the provider, renders/executes the
|
|
171
|
+
* right widget for the mode, and resolves the token. `container` is where a
|
|
172
|
+
* visible/managed widget draws; for score providers it is unused.
|
|
173
|
+
*
|
|
174
|
+
* Rejects on provider error/timeout — the caller decides whether to surface it
|
|
175
|
+
* or let the (fail-closed) server reject the tokenless request.
|
|
176
|
+
*/
|
|
177
|
+
async function getCaptchaToken(flow, container) {
|
|
178
|
+
const provider = flow.provider ?? '';
|
|
179
|
+
const siteKey = flow.siteKey ?? '';
|
|
180
|
+
const mode = flow.widget ?? 'managed';
|
|
181
|
+
const win = (container.ownerDocument?.defaultView ?? globalThis);
|
|
182
|
+
const api = await loadProvider(provider, siteKey, win);
|
|
183
|
+
switch (provider) {
|
|
184
|
+
case 'turnstile':
|
|
185
|
+
return turnstileToken(api, container, siteKey, mode);
|
|
186
|
+
case 'hcaptcha':
|
|
187
|
+
return hcaptchaToken(api, container, siteKey, mode);
|
|
188
|
+
case 'recaptcha':
|
|
189
|
+
return recaptchaV2Token(api, container, siteKey, mode);
|
|
190
|
+
case 'recaptcha_v3':
|
|
191
|
+
case 'recaptcha_enterprise':
|
|
192
|
+
return recaptchaV3Token(api, siteKey);
|
|
193
|
+
default:
|
|
194
|
+
throw new Error(`captcha: ${provider} is not renderable`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=captcha.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"captcha.js","sourceRoot":"","sources":["../../src/widget/captcha.ts"],"names":[],"mappings":";;AAyBA,oCAEC;AAGD,4CAEC;AAoDD,oCAsCC;AA0FD,0CAqBC;AAvOD;;;;;;;;;;;;GAYG;AAEH,2DAA2D;AAC3D,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,WAAW;IACX,UAAU;IACV,WAAW;IACX,cAAc;IACd,sBAAsB;CACvB,CAAC,CAAC;AAEH,SAAgB,YAAY,CAAC,QAAwB;IACnD,OAAO,CAAC,CAAC,QAAQ,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAChD,CAAC;AAED,iFAAiF;AACjF,SAAgB,gBAAgB,CAAC,IAA6B;IAC5D,OAAO,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACxE,CAAC;AAuBD,MAAM,OAAO,GAAyE;IACpF,SAAS,EAAE;QACT,GAAG,EAAE,GAAG,EAAE,CAAC,uEAAuE;QAClF,MAAM,EAAE,WAAW;KACpB;IACD,QAAQ,EAAE;QACR,GAAG,EAAE,GAAG,EAAE,CAAC,kDAAkD;QAC7D,MAAM,EAAE,UAAU;KACnB;IACD,SAAS,EAAE;QACT,GAAG,EAAE,GAAG,EAAE,CAAC,yDAAyD;QACpE,MAAM,EAAE,YAAY;KACrB;IACD,YAAY,EAAE;QACZ,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,kDAAkD,kBAAkB,CAAC,OAAO,CAAC,EAAE;QACjG,MAAM,EAAE,YAAY;KACrB;IACD,oBAAoB,EAAE;QACpB,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,CACf,yDAAyD,kBAAkB,CAAC,OAAO,CAAC,EAAE;QACxF,MAAM,EAAE,YAAY;KACrB;CACF,CAAC;AAEF;;;;GAIG;AACH,SAAgB,YAAY,CAAC,QAAgB,EAAE,OAAe,EAAE,GAAQ;IACtE,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/B,IAAI,CAAC,IAAI;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,YAAY,QAAQ,oBAAoB,CAAC,CAAC,CAAC;IACtF,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAE/D,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC;IACzB,MAAM,QAAQ,GAAG,sBAAsB,QAAQ,EAAE,CAAC;IAClD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAiC,CAAC;IAC/D,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,OAAO,GAAG,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACvD,MAAM,IAAI,GAAG,GAAG,EAAE;YAChB,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;;gBAC3C,MAAM,CAAC,IAAI,KAAK,CAAC,YAAY,QAAQ,mCAAmC,CAAC,CAAC,CAAC;QAClF,CAAC,CAAC;QACF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC9B,sEAAsE;QACtE,IAAI,EAAE,GAAG,GAAG,CAAC,aAAa,CAAC,eAAe,GAAG,IAAI,CAA6B,CAAC;QAC/E,IAAI,EAAE,EAAE,CAAC;YACP,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;gBAAE,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YACvD,EAAE,CAAC,gBAAgB,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,YAAY,QAAQ,gBAAgB,CAAC,CAAC,EAAE;gBAC1F,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,EAAE,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QACjC,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC;QACb,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC;QAChB,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC;QAChB,EAAE,CAAC,gBAAgB,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,YAAY,QAAQ,gBAAgB,CAAC,CAAC,EAAE;YAC1F,IAAI,EAAE,IAAI;SACX,CAAC,CAAC;QACH,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC;IACxB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,gFAAgF;AAChF,SAAS,cAAc,CACrB,GAAiB,EACjB,EAAe,EACf,OAAe,EACf,IAAuB;IAEvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE;YACxB,OAAO,EAAE,OAAO;YAChB,+CAA+C;YAC/C,UAAU,EAAE,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,QAAQ;YAChE,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;YAC3C,gBAAgB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;YACrE,kBAAkB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;SAC1E,CAAC,CAAC;QACH,IAAI,IAAI,KAAK,WAAW,IAAI,GAAG,CAAC,OAAO;YAAE,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC;AAED,6EAA6E;AAC7E,SAAS,aAAa,CACpB,GAAgB,EAChB,EAAe,EACf,OAAe,EACf,IAAuB;IAEvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE;YACxB,OAAO,EAAE,OAAO;YAChB,IAAI,EAAE,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ;YACnD,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;YAC3C,gBAAgB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;SACrE,CAAC,CAAC;QACH,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;YACzB,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3C,IAAI,CAAC,IAAI,OAAQ,CAAmC,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBACxE,CAAmC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;YAChF,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,sEAAsE;AACtE,SAAS,gBAAgB,CACvB,GAAkB,EAClB,EAAe,EACf,OAAe,EACf,IAAuB;IAEvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE;YACxB,OAAO,EAAE,OAAO;YAChB,IAAI,EAAE,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ;YACnD,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;YAC3C,gBAAgB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;SACtE,CAAC,CAAC;QACH,IAAI,IAAI,KAAK,WAAW,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YACxC,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;YACnD,IAAI,CAAC,IAAI,OAAQ,CAAqB,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC1D,CAAqB,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,sFAAsF;AACtF,SAAS,gBAAgB,CAAC,GAAkB,EAAE,OAAe;IAC3D,MAAM,GAAG,GAAG,GAAG,EAAE;QACf,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,OAAQ,CAAqB,CAAC,IAAI,KAAK,UAAU;YAC3D,CAAC,CAAE,CAAqB;YACxB,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC,CAAC;IACrF,CAAC,CAAC;IACF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,GAAG,CAAC,KAAK;YAAE,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;;YACvD,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,eAAe,CAAC,IAAiB,EAAE,SAAsB;IAC7E,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;IACnC,MAAM,IAAI,GAAsB,IAAI,CAAC,MAAM,IAAI,SAAS,CAAC;IACzD,MAAM,GAAG,GAAG,CAAC,SAAS,CAAC,aAAa,EAAE,WAAW,IAAI,UAAU,CAAmB,CAAC;IAEnF,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IAEvD,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,WAAW;YACd,OAAO,cAAc,CAAC,GAAmB,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACvE,KAAK,UAAU;YACb,OAAO,aAAa,CAAC,GAAkB,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACrE,KAAK,WAAW;YACd,OAAO,gBAAgB,CAAC,GAAoB,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QAC1E,KAAK,cAAc,CAAC;QACpB,KAAK,sBAAsB;YACzB,OAAO,gBAAgB,CAAC,GAAoB,EAAE,OAAO,CAAC,CAAC;QACzD;YACE,MAAM,IAAI,KAAK,CAAC,YAAY,QAAQ,oBAAoB,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Providers that need the user's handle collected BEFORE the redirect (the
|
|
2
|
+
* handle resolves their server): AT-Proto / Bluesky. */
|
|
3
|
+
export declare const HANDLE_PROVIDERS: Set<string>;
|
|
4
|
+
/** Static, first-party chevrons for the carousel scroll buttons. */
|
|
5
|
+
export declare const CHEVRON_LEFT = "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M15 18l-6-6 6-6\"/></svg>";
|
|
6
|
+
export declare const CHEVRON_RIGHT = "<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M9 18l6-6-6-6\"/></svg>";
|
|
7
|
+
export declare const COPY: {
|
|
8
|
+
readonly 'sign-in': {
|
|
9
|
+
readonly title: "Sign in";
|
|
10
|
+
readonly submit: "Continue";
|
|
11
|
+
};
|
|
12
|
+
readonly 'sign-up': {
|
|
13
|
+
readonly title: "Create your account";
|
|
14
|
+
readonly submit: "Continue";
|
|
15
|
+
};
|
|
16
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.COPY = exports.CHEVRON_RIGHT = exports.CHEVRON_LEFT = exports.HANDLE_PROVIDERS = void 0;
|
|
4
|
+
/** Providers that need the user's handle collected BEFORE the redirect (the
|
|
5
|
+
* handle resolves their server): AT-Proto / Bluesky. */
|
|
6
|
+
exports.HANDLE_PROVIDERS = new Set(['bluesky']);
|
|
7
|
+
/** Static, first-party chevrons for the carousel scroll buttons. */
|
|
8
|
+
exports.CHEVRON_LEFT = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6"/></svg>';
|
|
9
|
+
exports.CHEVRON_RIGHT = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 18l6-6-6-6"/></svg>';
|
|
10
|
+
exports.COPY = {
|
|
11
|
+
'sign-in': { title: 'Sign in', submit: 'Continue' },
|
|
12
|
+
'sign-up': { title: 'Create your account', submit: 'Continue' },
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=constants.js.map
|