@edgespark/web 0.0.0-beta.8b6c080

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 ADDED
@@ -0,0 +1,294 @@
1
+ # `@edgespark/web`
2
+
3
+ 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
+ Recommended browser auth path:
14
+
15
+ - use `@edgespark/web` for all browser auth
16
+ - prefer `authUI.mount()` for managed auth UI
17
+ - use `es.auth` for custom flows
18
+ - do not implement app auth by calling `/api/_es/auth/*` manually
19
+
20
+ ## Use in this repo
21
+
22
+ ```bash
23
+ pnpm install
24
+ pnpm --filter @edgespark/web build
25
+ ```
26
+
27
+ ## Quick start
28
+
29
+ ```ts
30
+ import { createEdgeSpark } from "@edgespark/web";
31
+ import "@edgespark/web/styles.css";
32
+
33
+ const es = createEdgeSpark();
34
+ const container = document.getElementById("auth")!;
35
+
36
+ es.authUI.mount(container, {
37
+ redirectTo: "/dashboard",
38
+ });
39
+ ```
40
+
41
+ Auto-detected labels are used by default. The managed UI picks the best preset
42
+ from the browser's language preferences automatically.
43
+
44
+ ## Public surface
45
+
46
+ ```ts
47
+ const es = createEdgeSpark();
48
+ const container = document.getElementById("auth")!;
49
+
50
+ es.auth;
51
+ es.api.fetch("/api/todos");
52
+ es.authUI.mount(container, { redirectTo: "/dashboard" });
53
+ ```
54
+
55
+ ### `auth`
56
+
57
+ `es.auth` keeps better-auth's API shape and adds two EdgeSpark helpers:
58
+
59
+ ```ts
60
+ const session = await es.auth.requireSession();
61
+
62
+ const unsubscribe = es.auth.onSessionChange((nextSession) => {
63
+ console.log(nextSession?.user.email ?? "signed out");
64
+ });
65
+ ```
66
+
67
+ `requireSession()` throws `UNAUTHENTICATED` only when no session exists. If the
68
+ session lookup itself fails, it preserves the upstream auth error code/status
69
+ so route guards can distinguish backend failures from a real sign-out.
70
+
71
+ `onSessionChange()` is hydration-safe: it waits for the first session lookup to
72
+ complete before emitting, so apps do not mistake initial loading for a real
73
+ signed-out state. Successful sign-in/sign-up/sign-out mutations also prompt
74
+ other tabs to refetch their session state.
75
+
76
+ Current platform note:
77
+ - `changeEmail()`
78
+ - `deleteUser()`
79
+ - `deleteUserCallback()`
80
+
81
+ still appear on the familiar better-auth-shaped surface, but EdgeSpark does
82
+ not currently enable those platform capabilities. These methods fail fast with
83
+ `UNSUPPORTED_AUTH_METHOD`. For normal browser auth flows, stay on the supported
84
+ paths: `authUI.mount()` for managed UI and `es.auth` for supported custom flows.
85
+
86
+ Use the familiar auth methods directly:
87
+
88
+ ```ts
89
+ await es.auth.signIn.email({ email, password });
90
+ await es.auth.signUp.email({ name, email, password });
91
+ await es.auth.signOut();
92
+ await es.auth.getSession();
93
+ ```
94
+
95
+ The platform-managed `/api/_es/auth/*` routes exist underneath the SDK, but normal app code should not call them directly. Treat those routes as implementation details unless you are debugging auth at the HTTP layer.
96
+
97
+ ### `api`
98
+
99
+ `es.api.fetch(...)` is the same-origin fetch wrapper for authenticated app requests.
100
+
101
+ ```ts
102
+ const response = await es.api.fetch("/api/todos");
103
+ const todos = await response.json();
104
+ ```
105
+
106
+ The wrapper always uses cookie credentials, rejects cross-origin URLs, and does not accept a `credentials` override in its `init` type.
107
+
108
+ ### `authUI`
109
+
110
+ The managed auth UI supports exactly two post-auth strategies.
111
+
112
+ #### Redirect mode
113
+
114
+ ```ts
115
+ const container = document.getElementById("auth")!;
116
+
117
+ const ui = es.authUI.mount(container, {
118
+ redirectTo: "/dashboard",
119
+ });
120
+ ```
121
+
122
+ #### Controlled mode
123
+
124
+ ```ts
125
+ const container = document.getElementById("auth")!;
126
+
127
+ const ui = es.authUI.mount(container, {
128
+ onSuccess(event) {
129
+ if (event.action === "password-reset") {
130
+ return;
131
+ }
132
+
133
+ window.location.assign("/dashboard");
134
+ },
135
+ });
136
+ ```
137
+
138
+ `ui.destroy()` removes the mounted UI and clears timers/listeners.
139
+
140
+ `onError` always receives a normalized `EdgeSparkAuthError`, so callback code
141
+ can safely read `error.code`, `error.message`, and `error.status` without
142
+ defensive `unknown` handling.
143
+
144
+ ```ts
145
+ es.authUI.mount(container, {
146
+ onSuccess(event) {
147
+ if (event.action !== "password-reset") {
148
+ window.location.assign("/dashboard");
149
+ }
150
+ },
151
+ onError(error) {
152
+ console.error(error.code, error.message);
153
+ },
154
+ });
155
+ ```
156
+
157
+ Password reset has one special rule:
158
+
159
+ - in controlled mode, `onSuccess({ action: "password-reset" })` fires and the caller owns the next step
160
+ - in redirect mode, the UI returns to sign-in with a success notice instead of redirecting to an authenticated page
161
+ - if the platform keeps JWT session cookie cache enabled, reset-time session revocation remains eventually consistent for already-open browser contexts and may take up to the cache TTL to become visible
162
+
163
+ In controlled mode, `authUI` stops mutating the container after `onSuccess`
164
+ runs. If the app wants to close, replace, or reuse that container, it owns that
165
+ teardown explicitly.
166
+
167
+ #### Appearance
168
+
169
+ Managed auth supports a small, explicit appearance contract. If `appearance` is
170
+ omitted, the UI keeps its built-in default styling.
171
+
172
+ - `theme` selects the built-in starting preset
173
+ - `variables` override specific design tokens on top of that preset
174
+ - `className` adds a stable root hook for app-scoped CSS
175
+
176
+ ```ts
177
+ import {
178
+ AUTH_UI_APPEARANCE_VARIABLE,
179
+ AUTH_UI_THEME,
180
+ type AuthUIAppearance,
181
+ } from "@edgespark/web";
182
+
183
+ const appearance = {
184
+ theme: AUTH_UI_THEME.DARK,
185
+ className: "my-auth-surface",
186
+ variables: {
187
+ [AUTH_UI_APPEARANCE_VARIABLE.PRIMARY]: "#d97d4a",
188
+ [AUTH_UI_APPEARANCE_VARIABLE.FONT]: '"Manrope", sans-serif',
189
+ },
190
+ } satisfies AuthUIAppearance;
191
+
192
+ es.authUI.mount(container, {
193
+ redirectTo: "/dashboard",
194
+ appearance,
195
+ });
196
+ ```
197
+
198
+ Supported appearance fields:
199
+
200
+ - `theme`: `"light"` or `"dark"` (`"light"` is the default)
201
+ - `className`: applied to the managed auth UI root container
202
+ - `variables`: stable CSS custom properties for light-touch theming
203
+
204
+ `appearance.variables` are applied as inline CSS custom properties on the
205
+ managed auth root, so explicit overrides win over the built-in stylesheet
206
+ defaults without requiring consumers to manage CSS load order.
207
+
208
+ When the appearance object is assigned to a variable before calling
209
+ `authUI.mount(...)`, prefer `satisfies AuthUIAppearance` so `tsc` rejects
210
+ unsupported theme values, unsupported token keys, and misspelled appearance
211
+ fields during type-checking.
212
+
213
+ Supported `appearance.variables` keys:
214
+
215
+ | Variable | Controls | Light default |
216
+ |----------|----------|---------------|
217
+ | `--edgespark-auth-bg` | Page/input background, status mix base | `#ffffff` |
218
+ | `--edgespark-auth-bg-muted` | Secondary button hover, muted status bg, modal close hover | `#f7f7f8` |
219
+ | `--edgespark-auth-card-bg` | Card background (accepts gradient or flat color) | `linear-gradient(180deg, #ffffff, #fcfcfc)` |
220
+ | `--edgespark-auth-border` | Input border, secondary button border, divider line | `rgba(15,23,42,0.08)` |
221
+ | `--edgespark-auth-text` | All body text | `#111827` |
222
+ | `--edgespark-auth-text-muted` | Subtitles, meta text, divider label, modal close icon | `#6b7280` |
223
+ | `--edgespark-auth-divider-bg` | Divider pill background (masks the line behind the label) | inherits `--edgespark-auth-bg` |
224
+ | `--edgespark-auth-primary` | Primary button bg, links, input focus ring + border | `#0f766e` |
225
+ | `--edgespark-auth-primary-hover` | Primary button hover bg | `#115e59` |
226
+ | `--edgespark-auth-primary-contrast` | Primary button text | `#ffffff` |
227
+ | `--edgespark-auth-error` | Error status text + tinted bg | `#b91c1c` |
228
+ | `--edgespark-auth-info` | Info status text + tinted bg | `#0f766e` |
229
+ | `--edgespark-auth-radius-card` | Card border radius | `20px` |
230
+ | `--edgespark-auth-radius-control` | Input border radius | `12px` |
231
+ | `--edgespark-auth-shadow` | Card box shadow | `0 24px 60px rgba(15,23,42,0.08)` |
232
+ | `--edgespark-auth-font` | Root font family | `"IBM Plex Sans", "Inter", ui-sans-serif, system-ui, sans-serif` |
233
+ | `--edgespark-auth-modal-backdrop` | OAuth modal overlay background | `rgba(15,23,42,0.22)` |
234
+ | `--edgespark-auth-modal-card-bg` | OAuth modal card background | `#ffffff` |
235
+ | `--edgespark-auth-modal-shadow` | OAuth modal card box shadow | `0 24px 60px rgba(15,23,42,0.18)` |
236
+
237
+ Only the root container hook, the theme attribute, and the documented CSS
238
+ variables above are part of the managed auth appearance contract. Internal
239
+ `.edgespark-auth__*` classes remain implementation details and should not be
240
+ treated as a stable public API.
241
+
242
+ ## Flows covered by the managed UI
243
+
244
+ - email/password sign-in
245
+ - email/password sign-up
246
+ - OAuth sign-in
247
+ - email verification instructions + resend
248
+ - forgot password
249
+ - reset password with email OTP
250
+
251
+ The forgot-password path only appears when the project config enables email-OTP password reset.
252
+
253
+ ## Locale presets
254
+
255
+ Managed auth UI labels auto-detect by default, so most apps do not need to
256
+ import locale helpers manually.
257
+
258
+ Import a preset only when the app wants to force a specific language or build
259
+ on top of a known base:
260
+
261
+ ```ts
262
+ import { createEdgeSpark, fr } from "@edgespark/web";
263
+
264
+ const es = createEdgeSpark();
265
+
266
+ es.authUI.mount(container, {
267
+ redirectTo: "/dashboard",
268
+ labels: fr,
269
+ });
270
+ ```
271
+
272
+ `detectLocale()` is exported for advanced cases that want the same browser
273
+ language matching outside the managed auth UI.
274
+
275
+ ## Design notes
276
+
277
+ - `createEdgeSpark()` takes no options
278
+ - current page origin is used internally
279
+ - auth base path is internal
280
+ - credentials mode is internal
281
+ - local dev with separate web/backend ports should use a dev proxy so the browser still sees same-origin requests
282
+
283
+
284
+ ### Managed sign-in presentation
285
+
286
+ Optional `labels.signIn` overrides include `emailPlaceholder`, `codePlaceholder`,
287
+ `sendingCode`, and `signingIn`. `labels.oauth.redirecting` accepts a `{provider}`
288
+ placeholder. These values are escaped as text and default to the selected SDK locale.
289
+ The resend action is beside the code label; its countdown updates without replacing
290
+ focused inputs. Changing the email resets the code step. Busy operations disable all
291
+ controls and render progress in the active button (`aria-busy="true"`), while errors
292
+ use `role="alert"` at the top of the card. Config loading uses a noninteractive skeleton.
293
+ The root `data-auth-action` is `idle`, `loading`, `send-code`, `resend-code`, `sign-in`,
294
+ or `oauth:<provider>` for styling; it is a presentation hook, not an authentication signal.
@@ -0,0 +1,384 @@
1
+ [data-edgespark-auth] {
2
+ --edgespark-auth-bg: #ffffff;
3
+ --edgespark-auth-bg-muted: #f7f7f8;
4
+ --edgespark-auth-card-bg: linear-gradient(180deg, #ffffff 0%, #fcfcfc 100%);
5
+ --edgespark-auth-border: rgba(15, 23, 42, 0.08);
6
+ --edgespark-auth-text: #111827;
7
+ --edgespark-auth-text-muted: #6b7280;
8
+ --edgespark-auth-divider-bg: var(--edgespark-auth-bg);
9
+ --edgespark-auth-primary: #0f766e;
10
+ --edgespark-auth-primary-hover: #115e59;
11
+ --edgespark-auth-primary-contrast: #ffffff;
12
+ --edgespark-auth-error: #b91c1c;
13
+ --edgespark-auth-info: #0f766e;
14
+ --edgespark-auth-radius-card: 20px;
15
+ --edgespark-auth-radius-control: 12px;
16
+ --edgespark-auth-shadow: 0 24px 60px rgba(15, 23, 42, 0.08);
17
+ --edgespark-auth-font: "IBM Plex Sans", "Inter", ui-sans-serif, system-ui, sans-serif;
18
+ --edgespark-auth-modal-backdrop: rgba(15, 23, 42, 0.22);
19
+ --edgespark-auth-modal-card-bg: #ffffff;
20
+ --edgespark-auth-modal-shadow: 0 24px 60px rgba(15, 23, 42, 0.18);
21
+ color: var(--edgespark-auth-text);
22
+ font-family: var(--edgespark-auth-font);
23
+ }
24
+
25
+ [data-edgespark-auth][data-edgespark-auth-theme="dark"] {
26
+ --edgespark-auth-bg: #14181f;
27
+ --edgespark-auth-bg-muted: #1d2330;
28
+ --edgespark-auth-card-bg: linear-gradient(180deg, #171d27 0%, #121823 100%);
29
+ --edgespark-auth-border: rgba(226, 232, 240, 0.12);
30
+ --edgespark-auth-text: #f8fafc;
31
+ --edgespark-auth-text-muted: #94a3b8;
32
+ --edgespark-auth-divider-bg: var(--edgespark-auth-bg);
33
+ --edgespark-auth-primary: #14b8a6;
34
+ --edgespark-auth-primary-hover: #0f9488;
35
+ --edgespark-auth-primary-contrast: #f8fafc;
36
+ --edgespark-auth-error: #fca5a5;
37
+ --edgespark-auth-info: #5eead4;
38
+ --edgespark-auth-shadow: 0 24px 60px rgba(2, 6, 23, 0.32);
39
+ --edgespark-auth-modal-backdrop: rgba(2, 6, 23, 0.56);
40
+ --edgespark-auth-modal-card-bg: #101722;
41
+ --edgespark-auth-modal-shadow: 0 24px 60px rgba(2, 6, 23, 0.48);
42
+ }
43
+
44
+ [data-edgespark-auth] *,
45
+ [data-edgespark-auth] *::before,
46
+ [data-edgespark-auth] *::after {
47
+ box-sizing: border-box;
48
+ }
49
+
50
+ .edgespark-auth__shell {
51
+ width: 100%;
52
+ }
53
+
54
+ .edgespark-auth__card {
55
+ display: grid;
56
+ gap: 16px;
57
+ width: 100%;
58
+ max-width: 420px;
59
+ padding: 24px;
60
+ border: 1px solid var(--edgespark-auth-border);
61
+ border-radius: var(--edgespark-auth-radius-card);
62
+ background: var(--edgespark-auth-card-bg);
63
+ box-shadow: var(--edgespark-auth-shadow);
64
+ }
65
+
66
+ .edgespark-auth__section,
67
+ .edgespark-auth__form,
68
+ .edgespark-auth__stack {
69
+ display: grid;
70
+ gap: 14px;
71
+ }
72
+
73
+ .edgespark-auth__header {
74
+ display: grid;
75
+ gap: 8px;
76
+ }
77
+
78
+ .edgespark-auth__title {
79
+ margin: 0;
80
+ font-size: 1.75rem;
81
+ line-height: 1.1;
82
+ font-weight: 700;
83
+ letter-spacing: -0.03em;
84
+ }
85
+
86
+ .edgespark-auth__copy,
87
+ .edgespark-auth__meta {
88
+ margin: 0;
89
+ color: var(--edgespark-auth-text-muted);
90
+ font-size: 0.95rem;
91
+ line-height: 1.5;
92
+ }
93
+
94
+ .edgespark-auth__field {
95
+ display: grid;
96
+ gap: 8px;
97
+ }
98
+
99
+ .edgespark-auth__tabs {
100
+ display: grid;
101
+ grid-template-columns: repeat(2, minmax(0, 1fr));
102
+ gap: 4px;
103
+ min-height: 44px;
104
+ padding: 4px;
105
+ border: 1px solid var(--edgespark-auth-border);
106
+ border-radius: var(--edgespark-auth-radius-control);
107
+ background: var(--edgespark-auth-bg-muted);
108
+ }
109
+
110
+ .edgespark-auth__tab {
111
+ min-width: 0;
112
+ border: 0;
113
+ border-radius: calc(var(--edgespark-auth-radius-control) - 4px);
114
+ background: transparent;
115
+ color: var(--edgespark-auth-text-muted);
116
+ cursor: pointer;
117
+ font: inherit;
118
+ font-size: 0.92rem;
119
+ font-weight: 600;
120
+ }
121
+
122
+ .edgespark-auth__tab--active {
123
+ background: var(--edgespark-auth-bg);
124
+ color: var(--edgespark-auth-text);
125
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
126
+ }
127
+
128
+ .edgespark-auth__tab:disabled {
129
+ cursor: not-allowed;
130
+ opacity: 0.6;
131
+ }
132
+
133
+ .edgespark-auth__label {
134
+ font-size: 0.9rem;
135
+ font-weight: 600;
136
+ }
137
+
138
+ .edgespark-auth__input {
139
+ width: 100%;
140
+ min-height: 48px;
141
+ padding: 0 14px;
142
+ border: 1px solid var(--edgespark-auth-border);
143
+ border-radius: var(--edgespark-auth-radius-control);
144
+ background: var(--edgespark-auth-bg);
145
+ color: var(--edgespark-auth-text);
146
+ font: inherit;
147
+ }
148
+
149
+ .edgespark-auth__input:focus {
150
+ outline: 2px solid color-mix(in srgb, var(--edgespark-auth-primary) 35%, transparent);
151
+ outline-offset: 1px;
152
+ border-color: var(--edgespark-auth-primary);
153
+ }
154
+
155
+ .edgespark-auth__button,
156
+ .edgespark-auth__link,
157
+ .edgespark-auth__text-action {
158
+ font: inherit;
159
+ }
160
+
161
+ .edgespark-auth__button {
162
+ display: inline-flex;
163
+ align-items: center;
164
+ justify-content: center;
165
+ min-height: 48px;
166
+ padding: 0 16px;
167
+ border-radius: 999px;
168
+ border: 1px solid transparent;
169
+ cursor: pointer;
170
+ transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease, opacity 120ms ease;
171
+ }
172
+
173
+ .edgespark-auth__button:disabled,
174
+ .edgespark-auth__link:disabled,
175
+ .edgespark-auth__text-action:disabled {
176
+ cursor: not-allowed;
177
+ opacity: 0.6;
178
+ }
179
+
180
+ .edgespark-auth__button--primary {
181
+ background: var(--edgespark-auth-primary);
182
+ color: var(--edgespark-auth-primary-contrast);
183
+ }
184
+
185
+ .edgespark-auth__button--primary:hover:not(:disabled) {
186
+ background: var(--edgespark-auth-primary-hover);
187
+ }
188
+
189
+ .edgespark-auth__button--secondary {
190
+ border-color: var(--edgespark-auth-border);
191
+ background: var(--edgespark-auth-bg);
192
+ color: var(--edgespark-auth-text);
193
+ }
194
+
195
+ .edgespark-auth__button--secondary:hover:not(:disabled) {
196
+ background: var(--edgespark-auth-bg-muted);
197
+ }
198
+
199
+ .edgespark-auth__actions {
200
+ display: grid;
201
+ gap: 10px;
202
+ }
203
+
204
+ .edgespark-auth__link {
205
+ justify-self: start;
206
+ padding: 0;
207
+ border: 0;
208
+ background: transparent;
209
+ color: var(--edgespark-auth-primary);
210
+ cursor: pointer;
211
+ }
212
+
213
+ .edgespark-auth__text-action {
214
+ justify-self: center;
215
+ padding: 2px 4px;
216
+ border: 0;
217
+ background: transparent;
218
+ color: var(--edgespark-auth-primary);
219
+ cursor: pointer;
220
+ font-size: 0.95rem;
221
+ font-weight: 600;
222
+ line-height: 1.4;
223
+ }
224
+
225
+ .edgespark-auth__text-action:hover:not(:disabled) {
226
+ color: var(--edgespark-auth-primary-hover);
227
+ }
228
+
229
+ .edgespark-auth__text-action:disabled {
230
+ color: var(--edgespark-auth-text-muted);
231
+ opacity: 1;
232
+ }
233
+
234
+ .edgespark-auth__divider {
235
+ position: relative;
236
+ text-align: center;
237
+ color: var(--edgespark-auth-text-muted);
238
+ font-size: 0.85rem;
239
+ }
240
+
241
+ .edgespark-auth__divider::before {
242
+ content: "";
243
+ position: absolute;
244
+ left: 0;
245
+ right: 0;
246
+ top: 50%;
247
+ border-top: 1px solid var(--edgespark-auth-border);
248
+ }
249
+
250
+ .edgespark-auth__divider span {
251
+ position: relative;
252
+ padding: 0 10px;
253
+ background: var(--edgespark-auth-divider-bg);
254
+ }
255
+
256
+ .edgespark-auth__status {
257
+ padding: 12px 14px;
258
+ border-radius: 14px;
259
+ font-size: 0.92rem;
260
+ line-height: 1.5;
261
+ }
262
+
263
+ .edgespark-auth__status--error {
264
+ color: var(--edgespark-auth-error);
265
+ background: color-mix(in srgb, var(--edgespark-auth-error) 10%, var(--edgespark-auth-bg));
266
+ }
267
+
268
+ .edgespark-auth__status--info {
269
+ color: var(--edgespark-auth-info);
270
+ background: color-mix(in srgb, var(--edgespark-auth-info) 10%, var(--edgespark-auth-bg));
271
+ }
272
+
273
+ .edgespark-auth__status--muted {
274
+ color: var(--edgespark-auth-text-muted);
275
+ background: var(--edgespark-auth-bg-muted);
276
+ }
277
+
278
+ .edgespark-auth__iframe-modal {
279
+ position: fixed;
280
+ inset: 0;
281
+ z-index: 9999;
282
+ display: flex;
283
+ align-items: center;
284
+ justify-content: center;
285
+ padding: 24px;
286
+ background: var(--edgespark-auth-modal-backdrop);
287
+ }
288
+
289
+ .edgespark-auth__iframe-modal-card {
290
+ position: relative;
291
+ display: grid;
292
+ gap: 16px;
293
+ width: min(520px, 100%);
294
+ padding: 28px;
295
+ border-radius: 24px;
296
+ background: var(--edgespark-auth-modal-card-bg);
297
+ box-shadow: var(--edgespark-auth-modal-shadow);
298
+ }
299
+
300
+ .edgespark-auth__iframe-modal-close {
301
+ position: absolute;
302
+ top: 12px;
303
+ right: 12px;
304
+ width: 36px;
305
+ height: 36px;
306
+ border: 0;
307
+ border-radius: 999px;
308
+ background: transparent;
309
+ color: var(--edgespark-auth-text-muted);
310
+ font-size: 1.5rem;
311
+ cursor: pointer;
312
+ }
313
+
314
+ .edgespark-auth__iframe-modal-close:hover {
315
+ background: var(--edgespark-auth-bg-muted);
316
+ }
317
+
318
+ .edgespark-auth__dev-banner {
319
+ background: #fef3c7;
320
+ color: #92400e;
321
+ padding: 0.5rem 1rem;
322
+ border-radius: 6px;
323
+ font-size: 0.8rem;
324
+ text-align: center;
325
+ margin-bottom: 0.75rem;
326
+ }
327
+
328
+ @media (max-width: 480px) {
329
+ .edgespark-auth__card {
330
+ padding: 18px;
331
+ border-radius: 16px;
332
+ }
333
+
334
+ .edgespark-auth__title {
335
+ font-size: 1.45rem;
336
+ }
337
+ }
338
+
339
+ /* Labels and their optional actions share a row without nesting buttons in labels. */
340
+ .edgespark-auth__field-heading {
341
+ display: flex;
342
+ align-items: center;
343
+ justify-content: space-between;
344
+ gap: 12px;
345
+ }
346
+
347
+ .edgespark-auth__field-heading .edgespark-auth__text-action {
348
+ flex-shrink: 0;
349
+ }
350
+
351
+ .edgespark-auth__sr-only {
352
+ position: absolute;
353
+ width: 1px;
354
+ height: 1px;
355
+ padding: 0;
356
+ margin: -1px;
357
+ overflow: hidden;
358
+ clip-path: inset(50%);
359
+ white-space: nowrap;
360
+ border: 0;
361
+ }
362
+
363
+ .edgespark-auth__skeleton-label {
364
+ width: 20%;
365
+ height: 20px;
366
+ border-radius: 4px;
367
+ background: var(--edgespark-auth-bg-muted);
368
+ }
369
+
370
+ .edgespark-auth__skeleton-input,
371
+ .edgespark-auth__skeleton-button {
372
+ min-height: 48px;
373
+ border: 1px solid var(--edgespark-auth-border);
374
+ border-radius: var(--edgespark-auth-radius-control);
375
+ background: var(--edgespark-auth-bg-muted);
376
+ }
377
+
378
+ .edgespark-auth__skeleton-button {
379
+ border-radius: 999px;
380
+ }
381
+
382
+ .edgespark-auth__skeleton-button--secondary {
383
+ background: var(--edgespark-auth-bg);
384
+ }
@@ -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,18 @@
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
+ * The exposed API intentionally keeps better-auth's shape so existing upstream
8
+ * knowledge transfers directly. EdgeSpark adds session helpers and sync
9
+ * semantics on top of that familiar surface.
10
+ */
11
+ export declare function createEdgeSparkAuth(): EdgeSparkAuth;
12
+ /**
13
+ * Creates the internal auth client used by the managed UI.
14
+ *
15
+ * Redirect handling is intentionally disabled here so `authUI` can own
16
+ * callback timing, loading state, and post-auth navigation semantics.
17
+ */
18
+ export declare function createManagedAuthUIClient(): BetterAuthClientInstance;