@edgespark/web 0.0.1-beta.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/README.md +130 -0
- package/auth/styles/styles.css +245 -0
- package/dist/api/fetch.d.ts +8 -0
- package/dist/api/types.d.ts +13 -0
- package/dist/auth/create-auth.d.ts +17 -0
- package/dist/auth/types.d.ts +55 -0
- package/dist/auth-ui/ManagedAuthUI.d.ts +80 -0
- package/dist/auth-ui/config.d.ts +33 -0
- package/dist/auth-ui/default-labels.d.ts +2 -0
- package/dist/auth-ui/index.d.ts +2 -0
- package/dist/auth-ui/mount.d.ts +7 -0
- package/dist/auth-ui/runtime.d.ts +7 -0
- package/dist/auth-ui/types.d.ts +147 -0
- package/dist/browser.d.ts +2 -0
- package/dist/client.d.ts +38 -0
- package/dist/errors.d.ts +23 -0
- package/dist/index.cjs +147 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.es.js +1726 -0
- package/dist/locales/ar.d.ts +2 -0
- package/dist/locales/de.d.ts +2 -0
- package/dist/locales/en-US.d.ts +2 -0
- package/dist/locales/es.d.ts +2 -0
- package/dist/locales/fr.d.ts +2 -0
- package/dist/locales/hi.d.ts +2 -0
- package/dist/locales/index.d.ts +21 -0
- package/dist/locales/it.d.ts +2 -0
- package/dist/locales/ja.d.ts +2 -0
- package/dist/locales/ko.d.ts +2 -0
- package/dist/locales/pt-BR.d.ts +2 -0
- package/dist/locales/ru.d.ts +2 -0
- package/dist/locales/zh-CN.d.ts +2 -0
- package/dist/redirect.d.ts +5 -0
- package/dist/shared/types.d.ts +6 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# `@edgespark/web`
|
|
2
|
+
|
|
3
|
+
Parallel rewrite of the EdgeSpark browser SDK.
|
|
4
|
+
|
|
5
|
+
This package is intentionally:
|
|
6
|
+
|
|
7
|
+
- browser-only
|
|
8
|
+
- same-origin only
|
|
9
|
+
- cookie-session only
|
|
10
|
+
- zero-config
|
|
11
|
+
- optimized for agent-written code
|
|
12
|
+
|
|
13
|
+
## Use in this repo
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm install
|
|
17
|
+
pnpm --filter @edgespark/web build
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { createEdgeSpark } from "@edgespark/web";
|
|
24
|
+
import "@edgespark/web/styles.css";
|
|
25
|
+
|
|
26
|
+
const es = createEdgeSpark();
|
|
27
|
+
const container = document.getElementById("auth")!;
|
|
28
|
+
|
|
29
|
+
es.authUI.mount(container, {
|
|
30
|
+
redirectTo: "/dashboard",
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Public surface
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
const es = createEdgeSpark();
|
|
38
|
+
const container = document.getElementById("auth")!;
|
|
39
|
+
|
|
40
|
+
es.auth;
|
|
41
|
+
es.api.fetch("/api/todos");
|
|
42
|
+
es.authUI.mount(container, { redirectTo: "/dashboard" });
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### `auth`
|
|
46
|
+
|
|
47
|
+
`es.auth` is the better-auth client, with two small EdgeSpark additions:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
const session = await es.auth.requireSession();
|
|
51
|
+
const unsubscribe = es.auth.onSessionChange((session) => {
|
|
52
|
+
console.log(session?.user.email ?? "signed out");
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Use the upstream better-auth auth methods directly:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
await es.auth.signIn.email({ email, password });
|
|
60
|
+
await es.auth.signUp.email({ name, email, password });
|
|
61
|
+
await es.auth.signOut();
|
|
62
|
+
await es.auth.getSession();
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### `api`
|
|
66
|
+
|
|
67
|
+
`es.api.fetch(...)` is the same-origin fetch wrapper for authenticated app requests.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
const response = await es.api.fetch("/api/todos");
|
|
71
|
+
const todos = await response.json();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The wrapper always uses cookie credentials, rejects cross-origin URLs, and does not accept a `credentials` override in its `init` type.
|
|
75
|
+
|
|
76
|
+
### `authUI`
|
|
77
|
+
|
|
78
|
+
The managed auth UI supports exactly two post-auth strategies.
|
|
79
|
+
|
|
80
|
+
#### Redirect mode
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
const container = document.getElementById("auth")!;
|
|
84
|
+
|
|
85
|
+
const ui = es.authUI.mount(container, {
|
|
86
|
+
redirectTo: "/dashboard",
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### Controlled mode
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
const container = document.getElementById("auth")!;
|
|
94
|
+
|
|
95
|
+
const ui = es.authUI.mount(container, {
|
|
96
|
+
onSuccess(event) {
|
|
97
|
+
if (event.action === "password-reset") {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
window.location.assign("/dashboard");
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`ui.destroy()` removes the mounted UI and clears timers/listeners.
|
|
107
|
+
|
|
108
|
+
Password reset has one special rule:
|
|
109
|
+
|
|
110
|
+
- in controlled mode, `onSuccess({ action: "password-reset" })` fires and the caller owns the next step
|
|
111
|
+
- in redirect mode, the UI returns to sign-in with a success notice instead of redirecting to an authenticated page
|
|
112
|
+
|
|
113
|
+
## Flows covered by the managed UI
|
|
114
|
+
|
|
115
|
+
- email/password sign-in
|
|
116
|
+
- email/password sign-up
|
|
117
|
+
- OAuth sign-in
|
|
118
|
+
- email verification instructions + resend
|
|
119
|
+
- forgot password
|
|
120
|
+
- reset password with email OTP
|
|
121
|
+
|
|
122
|
+
The forgot-password path only appears when the project config enables email-OTP password reset.
|
|
123
|
+
|
|
124
|
+
## Design notes
|
|
125
|
+
|
|
126
|
+
- `createEdgeSpark()` takes no options
|
|
127
|
+
- current page origin is used internally
|
|
128
|
+
- auth base path is internal
|
|
129
|
+
- credentials mode is internal
|
|
130
|
+
- local dev with separate web/backend ports should use a dev proxy so the browser still sees same-origin requests
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
[data-edgespark-auth] {
|
|
2
|
+
--edgespark-auth-bg: #ffffff;
|
|
3
|
+
--edgespark-auth-bg-muted: #f7f7f8;
|
|
4
|
+
--edgespark-auth-border: rgba(15, 23, 42, 0.08);
|
|
5
|
+
--edgespark-auth-text: #111827;
|
|
6
|
+
--edgespark-auth-text-muted: #6b7280;
|
|
7
|
+
--edgespark-auth-primary: #0f766e;
|
|
8
|
+
--edgespark-auth-primary-hover: #115e59;
|
|
9
|
+
--edgespark-auth-error: #b91c1c;
|
|
10
|
+
--edgespark-auth-info: #0f766e;
|
|
11
|
+
--edgespark-auth-radius-card: 20px;
|
|
12
|
+
--edgespark-auth-radius-control: 12px;
|
|
13
|
+
--edgespark-auth-shadow: 0 24px 60px rgba(15, 23, 42, 0.08);
|
|
14
|
+
--edgespark-auth-font: "IBM Plex Sans", "Inter", ui-sans-serif, system-ui, sans-serif;
|
|
15
|
+
color: var(--edgespark-auth-text);
|
|
16
|
+
font-family: var(--edgespark-auth-font);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
[data-edgespark-auth] *,
|
|
20
|
+
[data-edgespark-auth] *::before,
|
|
21
|
+
[data-edgespark-auth] *::after {
|
|
22
|
+
box-sizing: border-box;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.edgespark-auth__shell {
|
|
26
|
+
width: 100%;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.edgespark-auth__card {
|
|
30
|
+
display: grid;
|
|
31
|
+
gap: 16px;
|
|
32
|
+
width: 100%;
|
|
33
|
+
max-width: 420px;
|
|
34
|
+
padding: 24px;
|
|
35
|
+
border: 1px solid var(--edgespark-auth-border);
|
|
36
|
+
border-radius: var(--edgespark-auth-radius-card);
|
|
37
|
+
background: linear-gradient(180deg, #ffffff 0%, #fcfcfc 100%);
|
|
38
|
+
box-shadow: var(--edgespark-auth-shadow);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.edgespark-auth__section,
|
|
42
|
+
.edgespark-auth__form,
|
|
43
|
+
.edgespark-auth__stack {
|
|
44
|
+
display: grid;
|
|
45
|
+
gap: 14px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.edgespark-auth__header {
|
|
49
|
+
display: grid;
|
|
50
|
+
gap: 8px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.edgespark-auth__title {
|
|
54
|
+
margin: 0;
|
|
55
|
+
font-size: 1.75rem;
|
|
56
|
+
line-height: 1.1;
|
|
57
|
+
font-weight: 700;
|
|
58
|
+
letter-spacing: -0.03em;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.edgespark-auth__copy,
|
|
62
|
+
.edgespark-auth__meta {
|
|
63
|
+
margin: 0;
|
|
64
|
+
color: var(--edgespark-auth-text-muted);
|
|
65
|
+
font-size: 0.95rem;
|
|
66
|
+
line-height: 1.5;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.edgespark-auth__field {
|
|
70
|
+
display: grid;
|
|
71
|
+
gap: 8px;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.edgespark-auth__label {
|
|
75
|
+
font-size: 0.9rem;
|
|
76
|
+
font-weight: 600;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.edgespark-auth__input {
|
|
80
|
+
width: 100%;
|
|
81
|
+
min-height: 48px;
|
|
82
|
+
padding: 0 14px;
|
|
83
|
+
border: 1px solid var(--edgespark-auth-border);
|
|
84
|
+
border-radius: var(--edgespark-auth-radius-control);
|
|
85
|
+
background: var(--edgespark-auth-bg);
|
|
86
|
+
color: var(--edgespark-auth-text);
|
|
87
|
+
font: inherit;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.edgespark-auth__input:focus {
|
|
91
|
+
outline: 2px solid color-mix(in srgb, var(--edgespark-auth-primary) 35%, transparent);
|
|
92
|
+
outline-offset: 1px;
|
|
93
|
+
border-color: var(--edgespark-auth-primary);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.edgespark-auth__button,
|
|
97
|
+
.edgespark-auth__link {
|
|
98
|
+
font: inherit;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.edgespark-auth__button {
|
|
102
|
+
display: inline-flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
justify-content: center;
|
|
105
|
+
min-height: 48px;
|
|
106
|
+
padding: 0 16px;
|
|
107
|
+
border-radius: 999px;
|
|
108
|
+
border: 1px solid transparent;
|
|
109
|
+
cursor: pointer;
|
|
110
|
+
transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease, opacity 120ms ease;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.edgespark-auth__button:disabled,
|
|
114
|
+
.edgespark-auth__link:disabled {
|
|
115
|
+
cursor: not-allowed;
|
|
116
|
+
opacity: 0.6;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.edgespark-auth__button--primary {
|
|
120
|
+
background: var(--edgespark-auth-primary);
|
|
121
|
+
color: #ffffff;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.edgespark-auth__button--primary:hover:not(:disabled) {
|
|
125
|
+
background: var(--edgespark-auth-primary-hover);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.edgespark-auth__button--secondary {
|
|
129
|
+
border-color: var(--edgespark-auth-border);
|
|
130
|
+
background: var(--edgespark-auth-bg);
|
|
131
|
+
color: var(--edgespark-auth-text);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.edgespark-auth__button--secondary:hover:not(:disabled) {
|
|
135
|
+
background: var(--edgespark-auth-bg-muted);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.edgespark-auth__actions {
|
|
139
|
+
display: grid;
|
|
140
|
+
gap: 10px;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.edgespark-auth__link {
|
|
144
|
+
justify-self: start;
|
|
145
|
+
padding: 0;
|
|
146
|
+
border: 0;
|
|
147
|
+
background: transparent;
|
|
148
|
+
color: var(--edgespark-auth-primary);
|
|
149
|
+
cursor: pointer;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.edgespark-auth__divider {
|
|
153
|
+
position: relative;
|
|
154
|
+
text-align: center;
|
|
155
|
+
color: var(--edgespark-auth-text-muted);
|
|
156
|
+
font-size: 0.85rem;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.edgespark-auth__divider::before {
|
|
160
|
+
content: "";
|
|
161
|
+
position: absolute;
|
|
162
|
+
left: 0;
|
|
163
|
+
right: 0;
|
|
164
|
+
top: 50%;
|
|
165
|
+
border-top: 1px solid var(--edgespark-auth-border);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.edgespark-auth__divider span {
|
|
169
|
+
position: relative;
|
|
170
|
+
padding: 0 10px;
|
|
171
|
+
background: var(--edgespark-auth-bg);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.edgespark-auth__status {
|
|
175
|
+
padding: 12px 14px;
|
|
176
|
+
border-radius: 14px;
|
|
177
|
+
font-size: 0.92rem;
|
|
178
|
+
line-height: 1.5;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.edgespark-auth__status--error {
|
|
182
|
+
color: var(--edgespark-auth-error);
|
|
183
|
+
background: color-mix(in srgb, var(--edgespark-auth-error) 10%, #ffffff);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.edgespark-auth__status--info {
|
|
187
|
+
color: var(--edgespark-auth-info);
|
|
188
|
+
background: color-mix(in srgb, var(--edgespark-auth-info) 10%, #ffffff);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.edgespark-auth__status--muted {
|
|
192
|
+
color: var(--edgespark-auth-text-muted);
|
|
193
|
+
background: var(--edgespark-auth-bg-muted);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.edgespark-auth__iframe-modal {
|
|
197
|
+
position: fixed;
|
|
198
|
+
inset: 0;
|
|
199
|
+
z-index: 9999;
|
|
200
|
+
display: flex;
|
|
201
|
+
align-items: center;
|
|
202
|
+
justify-content: center;
|
|
203
|
+
padding: 24px;
|
|
204
|
+
background: rgba(15, 23, 42, 0.22);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.edgespark-auth__iframe-modal-card {
|
|
208
|
+
position: relative;
|
|
209
|
+
display: grid;
|
|
210
|
+
gap: 16px;
|
|
211
|
+
width: min(520px, 100%);
|
|
212
|
+
padding: 28px;
|
|
213
|
+
border-radius: 24px;
|
|
214
|
+
background: #ffffff;
|
|
215
|
+
box-shadow: 0 24px 60px rgba(15, 23, 42, 0.18);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.edgespark-auth__iframe-modal-close {
|
|
219
|
+
position: absolute;
|
|
220
|
+
top: 12px;
|
|
221
|
+
right: 12px;
|
|
222
|
+
width: 36px;
|
|
223
|
+
height: 36px;
|
|
224
|
+
border: 0;
|
|
225
|
+
border-radius: 999px;
|
|
226
|
+
background: transparent;
|
|
227
|
+
color: var(--edgespark-auth-text-muted);
|
|
228
|
+
font-size: 1.5rem;
|
|
229
|
+
cursor: pointer;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.edgespark-auth__iframe-modal-close:hover {
|
|
233
|
+
background: var(--edgespark-auth-bg-muted);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
@media (max-width: 480px) {
|
|
237
|
+
.edgespark-auth__card {
|
|
238
|
+
padding: 18px;
|
|
239
|
+
border-radius: 16px;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.edgespark-auth__title {
|
|
243
|
+
font-size: 1.45rem;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { EdgeSparkApi } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Creates the API module used by EdgeSpark apps.
|
|
4
|
+
*
|
|
5
|
+
* The wrapper keeps cookie credentials enabled and rejects cross-origin URLs so
|
|
6
|
+
* the SDK stays honest about its same-origin product boundary.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createApiModule(): EdgeSparkApi;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type EdgeSparkRequestInit = Omit<RequestInit, "credentials">;
|
|
2
|
+
/**
|
|
3
|
+
* Same-origin fetch wrapper for authenticated app requests.
|
|
4
|
+
*/
|
|
5
|
+
export interface EdgeSparkApi {
|
|
6
|
+
/**
|
|
7
|
+
* Same-origin `fetch` with cookie credentials always enabled.
|
|
8
|
+
*
|
|
9
|
+
* `credentials` is intentionally omitted from the accepted init shape because
|
|
10
|
+
* the SDK never allows callers to disable cookie auth.
|
|
11
|
+
*/
|
|
12
|
+
fetch(input: RequestInfo | URL, init?: EdgeSparkRequestInit): Promise<Response>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BetterAuthClientInstance, EdgeSparkAuth } from './types';
|
|
2
|
+
export declare const AUTH_BASE_PATH: "/api/_es/auth";
|
|
3
|
+
export declare function syncEdgeSparkAuthSession(authClient: BetterAuthClientInstance): Promise<void>;
|
|
4
|
+
/**
|
|
5
|
+
* Creates the headless auth module.
|
|
6
|
+
*
|
|
7
|
+
* This intentionally mirrors better-auth so agents can rely on upstream API
|
|
8
|
+
* knowledge instead of learning a second parallel auth surface.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createEdgeSparkAuth(): EdgeSparkAuth;
|
|
11
|
+
/**
|
|
12
|
+
* Creates the internal auth client used by the managed UI.
|
|
13
|
+
*
|
|
14
|
+
* Redirect handling is intentionally disabled here so `authUI` can own
|
|
15
|
+
* callback timing, loading state, and post-auth navigation semantics.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createManagedAuthUIClient(): BetterAuthClientInstance;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createAuthClient } from 'better-auth/client';
|
|
2
|
+
/**
|
|
3
|
+
* Direct better-auth client instance returned by createAuthClient().
|
|
4
|
+
*/
|
|
5
|
+
export type BetterAuthClientInstance = ReturnType<typeof createAuthClient>;
|
|
6
|
+
export type SessionResult = Awaited<ReturnType<BetterAuthClientInstance["getSession"]>>;
|
|
7
|
+
export type AuthSession = NonNullable<SessionResult["data"]>;
|
|
8
|
+
export type AuthUser = AuthSession["user"];
|
|
9
|
+
export type SessionChangeCallback = (session: AuthSession | null) => void;
|
|
10
|
+
export interface AuthEndpointError {
|
|
11
|
+
code?: string;
|
|
12
|
+
message?: string;
|
|
13
|
+
status?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface AuthEndpointResult<TData = unknown> {
|
|
16
|
+
data?: TData | null;
|
|
17
|
+
error?: AuthEndpointError | null;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* EdgeSpark keeps auth primitive usage aligned with better-auth.
|
|
21
|
+
*
|
|
22
|
+
* All better-auth client methods work directly. EdgeSpark adds three
|
|
23
|
+
* small conveniences on top: `requireSession`, `onSessionChange`, and
|
|
24
|
+
* a wrapped `signOut` that guarantees same-tab session invalidation.
|
|
25
|
+
*/
|
|
26
|
+
export type EdgeSparkAuth = BetterAuthClientInstance & {
|
|
27
|
+
/**
|
|
28
|
+
* Resolves with the current session, or throws when the browser is not authenticated.
|
|
29
|
+
*
|
|
30
|
+
* Use this for route guards and other flows where nullable session checks add noise.
|
|
31
|
+
*/
|
|
32
|
+
requireSession(): Promise<AuthSession>;
|
|
33
|
+
/**
|
|
34
|
+
* Subscribes to session changes for the current tab.
|
|
35
|
+
*
|
|
36
|
+
* The first emission is **hydration-safe**: it waits for the initial server
|
|
37
|
+
* session check to complete before firing, so subscribers never receive a
|
|
38
|
+
* premature `null` that looks like "not authenticated" while the cookie
|
|
39
|
+
* session is still being verified.
|
|
40
|
+
*
|
|
41
|
+
* After the initial emission, every session atom update is forwarded
|
|
42
|
+
* immediately (sign-in from another tab, sign-out, token refresh, etc.).
|
|
43
|
+
*
|
|
44
|
+
* Returns an unsubscribe function.
|
|
45
|
+
*/
|
|
46
|
+
onSessionChange(callback: SessionChangeCallback): () => void;
|
|
47
|
+
/**
|
|
48
|
+
* Signs out and invalidates the session for the current tab.
|
|
49
|
+
*
|
|
50
|
+
* This wraps better-auth's `signOut` and additionally forces the session
|
|
51
|
+
* atom to refetch, ensuring `onSessionChange` subscribers are notified
|
|
52
|
+
* synchronously after the call completes.
|
|
53
|
+
*/
|
|
54
|
+
signOut(...args: Parameters<BetterAuthClientInstance["signOut"]>): ReturnType<BetterAuthClientInstance["signOut"]>;
|
|
55
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { BetterAuthClientInstance } from '../auth/types';
|
|
2
|
+
import { NormalizedAuthUIConfig } from './config';
|
|
3
|
+
import { MountAuthUIOptions, MountedAuthUI } from './types';
|
|
4
|
+
interface ManagedAuthUIRuntimeOptions {
|
|
5
|
+
syncPublicSession?: () => Promise<void> | void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Internal managed auth UI implementation.
|
|
9
|
+
*
|
|
10
|
+
* The public API only exposes `authUI.mount(...)`; this class owns the DOM,
|
|
11
|
+
* form flows, and lifecycle.
|
|
12
|
+
*/
|
|
13
|
+
export declare class ManagedAuthUI implements MountedAuthUI {
|
|
14
|
+
private readonly container;
|
|
15
|
+
private readonly auth;
|
|
16
|
+
private readonly options;
|
|
17
|
+
private readonly loadConfig;
|
|
18
|
+
private readonly runtimeOptions?;
|
|
19
|
+
private readonly labels;
|
|
20
|
+
private readonly root;
|
|
21
|
+
private readonly state;
|
|
22
|
+
private destroyed;
|
|
23
|
+
private terminalActionInProgress;
|
|
24
|
+
private config;
|
|
25
|
+
private countdownUntil;
|
|
26
|
+
private countdownTimer;
|
|
27
|
+
private pendingFocusSnapshot;
|
|
28
|
+
private focusRestoreTimer;
|
|
29
|
+
private verificationCompletionAction;
|
|
30
|
+
constructor(container: HTMLElement, auth: BetterAuthClientInstance, options: MountAuthUIOptions, loadConfig: () => Promise<NormalizedAuthUIConfig>, runtimeOptions?: ManagedAuthUIRuntimeOptions | undefined);
|
|
31
|
+
destroy(): void;
|
|
32
|
+
private initialize;
|
|
33
|
+
private render;
|
|
34
|
+
private renderLoading;
|
|
35
|
+
private renderStatus;
|
|
36
|
+
private renderCurrentView;
|
|
37
|
+
private renderSignInView;
|
|
38
|
+
private renderSignUpView;
|
|
39
|
+
private renderVerifyEmailView;
|
|
40
|
+
private renderForgotPasswordView;
|
|
41
|
+
private renderResetPasswordView;
|
|
42
|
+
private renderOAuthProviders;
|
|
43
|
+
private renderInput;
|
|
44
|
+
private bindDOM;
|
|
45
|
+
private switchView;
|
|
46
|
+
private handleSignIn;
|
|
47
|
+
private handleSignUp;
|
|
48
|
+
private handleOAuthSignIn;
|
|
49
|
+
private handleForgotPassword;
|
|
50
|
+
private resendResetCode;
|
|
51
|
+
private handleResetPassword;
|
|
52
|
+
private resendVerificationEmail;
|
|
53
|
+
private sendVerificationEmail;
|
|
54
|
+
private completeAuth;
|
|
55
|
+
private resolveAuthCompletionData;
|
|
56
|
+
private completePasswordReset;
|
|
57
|
+
private handleExistingSessionOnMount;
|
|
58
|
+
private runAction;
|
|
59
|
+
private maybeHandleVerificationRequired;
|
|
60
|
+
private handleVerificationEmailSent;
|
|
61
|
+
private handleError;
|
|
62
|
+
private failValidation;
|
|
63
|
+
private getManagedReturnURL;
|
|
64
|
+
private consumeManagedReturnContext;
|
|
65
|
+
private startCountdown;
|
|
66
|
+
private clearCountdown;
|
|
67
|
+
private captureFocusSnapshot;
|
|
68
|
+
private scheduleFocusRestore;
|
|
69
|
+
private clearFocusRestoreTimer;
|
|
70
|
+
private passwordResetEndpoints;
|
|
71
|
+
private extractRedirectURL;
|
|
72
|
+
private extractUser;
|
|
73
|
+
private canUsePasswordResetEmailOtp;
|
|
74
|
+
private handleSessionlessManagedReturn;
|
|
75
|
+
private isCountdownActive;
|
|
76
|
+
private getCountdownSecondsRemaining;
|
|
77
|
+
private countdownText;
|
|
78
|
+
private escape;
|
|
79
|
+
}
|
|
80
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type OAuthProviderId = "google" | "apple" | "microsoft" | "github" | "gitlab" | "discord";
|
|
2
|
+
interface OAuthProviderBlock {
|
|
3
|
+
enabled?: boolean;
|
|
4
|
+
}
|
|
5
|
+
interface ProviderEmailPassword {
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
config?: {
|
|
8
|
+
minPasswordLength?: number;
|
|
9
|
+
requireEmailVerification?: boolean;
|
|
10
|
+
requirePasswordResetEmailVerification?: boolean;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
interface AuthConfigResponse {
|
|
14
|
+
disableSignUp?: boolean;
|
|
15
|
+
providerEmailPassword?: ProviderEmailPassword;
|
|
16
|
+
providerGoogle?: OAuthProviderBlock;
|
|
17
|
+
providerApple?: OAuthProviderBlock;
|
|
18
|
+
providerMicrosoft?: OAuthProviderBlock;
|
|
19
|
+
providerGithub?: OAuthProviderBlock;
|
|
20
|
+
providerGitlab?: OAuthProviderBlock;
|
|
21
|
+
providerDiscord?: OAuthProviderBlock;
|
|
22
|
+
}
|
|
23
|
+
export interface NormalizedAuthUIConfig {
|
|
24
|
+
disableSignUp: boolean;
|
|
25
|
+
emailPasswordEnabled: boolean;
|
|
26
|
+
minPasswordLength: number;
|
|
27
|
+
requireEmailVerification: boolean;
|
|
28
|
+
passwordResetEmailOtpEnabled: boolean;
|
|
29
|
+
oauthProviders: OAuthProviderId[];
|
|
30
|
+
}
|
|
31
|
+
export declare function loadNormalizedAuthUIConfig(): Promise<NormalizedAuthUIConfig>;
|
|
32
|
+
export declare function normalizeAuthUIConfig(config: AuthConfigResponse): NormalizedAuthUIConfig;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { BetterAuthClientInstance } from '../auth/types';
|
|
2
|
+
import { EdgeSparkAuthUI } from './types';
|
|
3
|
+
interface ManagedAuthUIRuntimeOptions {
|
|
4
|
+
syncPublicSession?: () => Promise<void> | void;
|
|
5
|
+
}
|
|
6
|
+
export declare function createAuthUIModule(auth: BetterAuthClientInstance, runtimeOptions?: ManagedAuthUIRuntimeOptions): EdgeSparkAuthUI;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { AuthSession, AuthUser, BetterAuthClientInstance } from '../auth/types';
|
|
2
|
+
import { EdgeSparkAuthError } from '../errors';
|
|
3
|
+
export declare function normalizeAuthError(error: unknown, fallbackMessage?: string): EdgeSparkAuthError;
|
|
4
|
+
export declare function readCurrentSession(auth: BetterAuthClientInstance): Promise<{
|
|
5
|
+
user: AuthUser | null;
|
|
6
|
+
session: AuthSession | null;
|
|
7
|
+
}>;
|