@edgespark/web 0.0.1 → 0.0.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # `@edgespark/web`
2
2
 
3
- Parallel rewrite of the EdgeSpark browser SDK.
3
+ EdgeSpark browser SDK.
4
4
 
5
5
  This package is intentionally:
6
6
 
@@ -10,6 +10,13 @@ This package is intentionally:
10
10
  - zero-config
11
11
  - optimized for agent-written code
12
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
+
13
20
  ## Use in this repo
14
21
 
15
22
  ```bash
@@ -73,7 +80,8 @@ Current platform note:
73
80
 
74
81
  still appear on the familiar better-auth-shaped surface, but EdgeSpark does
75
82
  not currently enable those platform capabilities. These methods fail fast with
76
- `UNSUPPORTED_AUTH_METHOD`.
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.
77
85
 
78
86
  Use the familiar auth methods directly:
79
87
 
@@ -84,6 +92,8 @@ await es.auth.signOut();
84
92
  await es.auth.getSession();
85
93
  ```
86
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
+
87
97
  ### `api`
88
98
 
89
99
  `es.api.fetch(...)` is the same-origin fetch wrapper for authenticated app requests.
@@ -154,6 +164,81 @@ In controlled mode, `authUI` stops mutating the container after `onSuccess`
154
164
  runs. If the app wants to close, replace, or reuse that container, it owns that
155
165
  teardown explicitly.
156
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
+
157
242
  ## Flows covered by the managed UI
158
243
 
159
244
  - email/password sign-in
@@ -1,21 +1,46 @@
1
1
  [data-edgespark-auth] {
2
2
  --edgespark-auth-bg: #ffffff;
3
3
  --edgespark-auth-bg-muted: #f7f7f8;
4
+ --edgespark-auth-card-bg: linear-gradient(180deg, #ffffff 0%, #fcfcfc 100%);
4
5
  --edgespark-auth-border: rgba(15, 23, 42, 0.08);
5
6
  --edgespark-auth-text: #111827;
6
7
  --edgespark-auth-text-muted: #6b7280;
8
+ --edgespark-auth-divider-bg: var(--edgespark-auth-bg);
7
9
  --edgespark-auth-primary: #0f766e;
8
10
  --edgespark-auth-primary-hover: #115e59;
11
+ --edgespark-auth-primary-contrast: #ffffff;
9
12
  --edgespark-auth-error: #b91c1c;
10
13
  --edgespark-auth-info: #0f766e;
11
14
  --edgespark-auth-radius-card: 20px;
12
15
  --edgespark-auth-radius-control: 12px;
13
16
  --edgespark-auth-shadow: 0 24px 60px rgba(15, 23, 42, 0.08);
14
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);
15
21
  color: var(--edgespark-auth-text);
16
22
  font-family: var(--edgespark-auth-font);
17
23
  }
18
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
+
19
44
  [data-edgespark-auth] *,
20
45
  [data-edgespark-auth] *::before,
21
46
  [data-edgespark-auth] *::after {
@@ -34,7 +59,7 @@
34
59
  padding: 24px;
35
60
  border: 1px solid var(--edgespark-auth-border);
36
61
  border-radius: var(--edgespark-auth-radius-card);
37
- background: linear-gradient(180deg, #ffffff 0%, #fcfcfc 100%);
62
+ background: var(--edgespark-auth-card-bg);
38
63
  box-shadow: var(--edgespark-auth-shadow);
39
64
  }
40
65
 
@@ -118,7 +143,7 @@
118
143
 
119
144
  .edgespark-auth__button--primary {
120
145
  background: var(--edgespark-auth-primary);
121
- color: #ffffff;
146
+ color: var(--edgespark-auth-primary-contrast);
122
147
  }
123
148
 
124
149
  .edgespark-auth__button--primary:hover:not(:disabled) {
@@ -168,7 +193,7 @@
168
193
  .edgespark-auth__divider span {
169
194
  position: relative;
170
195
  padding: 0 10px;
171
- background: var(--edgespark-auth-bg);
196
+ background: var(--edgespark-auth-divider-bg);
172
197
  }
173
198
 
174
199
  .edgespark-auth__status {
@@ -180,12 +205,12 @@
180
205
 
181
206
  .edgespark-auth__status--error {
182
207
  color: var(--edgespark-auth-error);
183
- background: color-mix(in srgb, var(--edgespark-auth-error) 10%, #ffffff);
208
+ background: color-mix(in srgb, var(--edgespark-auth-error) 10%, var(--edgespark-auth-bg));
184
209
  }
185
210
 
186
211
  .edgespark-auth__status--info {
187
212
  color: var(--edgespark-auth-info);
188
- background: color-mix(in srgb, var(--edgespark-auth-info) 10%, #ffffff);
213
+ background: color-mix(in srgb, var(--edgespark-auth-info) 10%, var(--edgespark-auth-bg));
189
214
  }
190
215
 
191
216
  .edgespark-auth__status--muted {
@@ -201,7 +226,7 @@
201
226
  align-items: center;
202
227
  justify-content: center;
203
228
  padding: 24px;
204
- background: rgba(15, 23, 42, 0.22);
229
+ background: var(--edgespark-auth-modal-backdrop);
205
230
  }
206
231
 
207
232
  .edgespark-auth__iframe-modal-card {
@@ -211,8 +236,8 @@
211
236
  width: min(520px, 100%);
212
237
  padding: 28px;
213
238
  border-radius: 24px;
214
- background: #ffffff;
215
- box-shadow: 0 24px 60px rgba(15, 23, 42, 0.18);
239
+ background: var(--edgespark-auth-modal-card-bg);
240
+ box-shadow: var(--edgespark-auth-modal-shadow);
216
241
  }
217
242
 
218
243
  .edgespark-auth__iframe-modal-close {
@@ -68,7 +68,8 @@ export type EdgeSparkAuth = BetterAuthClientInstance & {
68
68
  *
69
69
  * The method remains present to preserve the familiar better-auth API shape,
70
70
  * but the facade throws `UNSUPPORTED_AUTH_METHOD` until the platform enables
71
- * this capability.
71
+ * this capability. For browser auth, prefer supported `client.auth` methods
72
+ * or `authUI.mount()` instead of dropping to raw `/api/_es/auth/*` routes.
72
73
  */
73
74
  changeEmail(...args: Parameters<BetterAuthClientInstance["changeEmail"]>): ReturnType<BetterAuthClientInstance["changeEmail"]>;
74
75
  /**
@@ -76,7 +77,8 @@ export type EdgeSparkAuth = BetterAuthClientInstance & {
76
77
  *
77
78
  * The method remains present to preserve the familiar better-auth API shape,
78
79
  * but the facade throws `UNSUPPORTED_AUTH_METHOD` until the platform enables
79
- * this capability.
80
+ * this capability. Handle account removal through your own app flow until the
81
+ * platform adds first-class support.
80
82
  */
81
83
  deleteUser(...args: Parameters<BetterAuthClientInstance["deleteUser"]>): ReturnType<BetterAuthClientInstance["deleteUser"]>;
82
84
  };
@@ -0,0 +1,64 @@
1
+ export declare const AUTH_UI_THEME: {
2
+ readonly LIGHT: "light";
3
+ readonly DARK: "dark";
4
+ };
5
+ export declare const AUTH_UI_THEME_LIGHT: "light";
6
+ export declare const AUTH_UI_THEME_DARK: "dark";
7
+ export declare const AUTH_UI_THEMES: readonly ["light", "dark"];
8
+ export declare const AUTH_UI_DEFAULT_THEME: "light";
9
+ export type AuthUITheme = (typeof AUTH_UI_THEME)[keyof typeof AUTH_UI_THEME];
10
+ export declare const AUTH_UI_APPEARANCE_VARIABLE: {
11
+ readonly BACKGROUND: "--edgespark-auth-bg";
12
+ readonly BACKGROUND_MUTED: "--edgespark-auth-bg-muted";
13
+ readonly CARD_BACKGROUND: "--edgespark-auth-card-bg";
14
+ readonly BORDER: "--edgespark-auth-border";
15
+ readonly TEXT: "--edgespark-auth-text";
16
+ readonly TEXT_MUTED: "--edgespark-auth-text-muted";
17
+ readonly DIVIDER_BACKGROUND: "--edgespark-auth-divider-bg";
18
+ readonly PRIMARY: "--edgespark-auth-primary";
19
+ readonly PRIMARY_HOVER: "--edgespark-auth-primary-hover";
20
+ readonly PRIMARY_CONTRAST: "--edgespark-auth-primary-contrast";
21
+ readonly ERROR: "--edgespark-auth-error";
22
+ readonly INFO: "--edgespark-auth-info";
23
+ readonly CARD_RADIUS: "--edgespark-auth-radius-card";
24
+ readonly CONTROL_RADIUS: "--edgespark-auth-radius-control";
25
+ readonly SHADOW: "--edgespark-auth-shadow";
26
+ readonly FONT: "--edgespark-auth-font";
27
+ readonly MODAL_BACKDROP: "--edgespark-auth-modal-backdrop";
28
+ readonly MODAL_CARD_BACKGROUND: "--edgespark-auth-modal-card-bg";
29
+ readonly MODAL_SHADOW: "--edgespark-auth-modal-shadow";
30
+ };
31
+ export type AuthUIAppearanceVariableName = (typeof AUTH_UI_APPEARANCE_VARIABLE)[keyof typeof AUTH_UI_APPEARANCE_VARIABLE];
32
+ export declare const AUTH_UI_APPEARANCE_VARIABLE_NAMES: readonly AuthUIAppearanceVariableName[];
33
+ export type AuthUIAppearanceVariables = Partial<Record<AuthUIAppearanceVariableName, string>>;
34
+ export interface AuthUIAppearance {
35
+ /**
36
+ * Built-in visual preset for the managed auth UI root.
37
+ *
38
+ * Defaults to `"light"` when omitted. Variable overrides apply on top of the
39
+ * selected preset instead of replacing the entire theme contract.
40
+ */
41
+ theme?: AuthUITheme;
42
+ /**
43
+ * Optional class applied to the managed auth UI root container.
44
+ *
45
+ * Use this to scope additional app-level CSS around the stable root element.
46
+ */
47
+ className?: string;
48
+ /**
49
+ * Stable CSS custom properties supported by the managed auth UI.
50
+ *
51
+ * This is the supported appearance contract for light-touch theming. When a
52
+ * variable is omitted, the current theme preset keeps its default value.
53
+ */
54
+ variables?: AuthUIAppearanceVariables;
55
+ }
56
+ interface NormalizedAuthUIAppearance {
57
+ theme: AuthUITheme;
58
+ className: string;
59
+ variables: AuthUIAppearanceVariables;
60
+ }
61
+ export declare function isAuthUITheme(value: unknown): value is AuthUITheme;
62
+ export declare function isAuthUIAppearanceVariableName(value: string): value is AuthUIAppearanceVariableName;
63
+ export declare function normalizeAuthUIAppearance(appearance?: AuthUIAppearance): NormalizedAuthUIAppearance;
64
+ export {};
@@ -1,2 +1,4 @@
1
1
  export { createAuthUIModule } from './mount';
2
+ export { AUTH_UI_APPEARANCE_VARIABLE, AUTH_UI_APPEARANCE_VARIABLE_NAMES, AUTH_UI_THEME, AUTH_UI_DEFAULT_THEME, AUTH_UI_THEME_DARK, AUTH_UI_THEME_LIGHT, AUTH_UI_THEMES, } from './appearance';
3
+ export type { AuthUIAppearance, AuthUIAppearanceVariableName, AuthUIAppearanceVariables, AuthUITheme, } from './appearance';
2
4
  export type { AuthSuccessEvent, AuthUILabelOverrides, AuthUILabels, ControlledAuthUIOptions, EdgeSparkAuthUI, MountAuthUIOptions, MountedAuthUI, RedirectAuthUIOptions, } from './types';
@@ -2,6 +2,7 @@ import { AuthSession, AuthUser } from '../auth/types';
2
2
  import { EdgeSparkAuthError } from '../errors';
3
3
  import { RedirectTarget } from '../redirect';
4
4
  import { DeepPartial } from '../shared/types';
5
+ import { AuthUIAppearance } from './appearance';
5
6
  /** Sign-in or sign-up completed with an authenticated session. */
6
7
  export type AuthenticatedAuthSuccessEvent = {
7
8
  action: "sign-in";
@@ -91,6 +92,17 @@ export interface AuthUILabels {
91
92
  };
92
93
  }
93
94
  export type AuthUILabelOverrides = DeepPartial<AuthUILabels>;
95
+ interface BaseAuthUIOptions {
96
+ onError?: (error: EdgeSparkAuthError) => void;
97
+ labels?: AuthUILabelOverrides;
98
+ /**
99
+ * Optional, constrained visual customization for the managed auth UI.
100
+ *
101
+ * This is intentionally smaller than the headless auth path. Use
102
+ * `client.auth` when the app needs full UI ownership.
103
+ */
104
+ appearance?: AuthUIAppearance;
105
+ }
94
106
  /**
95
107
  * Redirect mode — use when the auth UI is a full-page login gate.
96
108
  *
@@ -99,11 +111,9 @@ export type AuthUILabelOverrides = DeepPartial<AuthUILabels>;
99
111
  *
100
112
  * `onSuccess` is optional and fires *before* the redirect (useful for analytics).
101
113
  */
102
- export interface RedirectAuthUIOptions {
114
+ export interface RedirectAuthUIOptions extends BaseAuthUIOptions {
103
115
  redirectTo: RedirectTarget;
104
116
  onSuccess?: (event: AuthSuccessEvent) => void;
105
- onError?: (error: EdgeSparkAuthError) => void;
106
- labels?: AuthUILabelOverrides;
107
117
  }
108
118
  /**
109
119
  * Controlled mode — use when the app owns what happens after auth.
@@ -114,11 +124,9 @@ export interface RedirectAuthUIOptions {
114
124
  *
115
125
  * Use this for: modals, embedded auth widgets, SPA routing, onboarding flows.
116
126
  */
117
- export interface ControlledAuthUIOptions {
127
+ export interface ControlledAuthUIOptions extends BaseAuthUIOptions {
118
128
  onSuccess: (event: AuthSuccessEvent) => void;
119
129
  redirectTo?: never;
120
- onError?: (error: EdgeSparkAuthError) => void;
121
- labels?: AuthUILabelOverrides;
122
130
  }
123
131
  /**
124
132
  * Two post-auth strategies — pick one:
@@ -145,3 +153,4 @@ export interface EdgeSparkAuthUI {
145
153
  */
146
154
  mount(container: HTMLElement, options: MountAuthUIOptions): MountedAuthUI;
147
155
  }
156
+ export {};