@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 +294 -0
- package/auth/styles/styles.css +384 -0
- package/dist/api/fetch.d.ts +8 -0
- package/dist/api/types.d.ts +13 -0
- package/dist/auth/create-auth.d.ts +18 -0
- package/dist/auth/local-dev.d.ts +36 -0
- package/dist/auth/types.d.ts +84 -0
- package/dist/auth-ui/ManagedAuthUI.d.ts +105 -0
- package/dist/auth-ui/appearance.d.ts +64 -0
- package/dist/auth-ui/config.d.ts +54 -0
- package/dist/auth-ui/default-labels.d.ts +2 -0
- package/dist/auth-ui/google-one-tap.d.ts +47 -0
- package/dist/auth-ui/index.d.ts +4 -0
- package/dist/auth-ui/mount.d.ts +22 -0
- package/dist/auth-ui/prompt-google-one-tap.d.ts +43 -0
- package/dist/auth-ui/runtime.d.ts +7 -0
- package/dist/auth-ui/types.d.ts +187 -0
- package/dist/browser.d.ts +2 -0
- package/dist/client.d.ts +43 -0
- package/dist/errors.d.ts +32 -0
- package/dist/index.cjs +177 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.es.js +2774 -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 +53 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is the web app running in local-dev mode?
|
|
3
|
+
*
|
|
4
|
+
* Reads a `<meta name="edgespark-local-dev" content="true">` tag that
|
|
5
|
+
* the `edgespark dev` reverse proxy splices into every HTML response.
|
|
6
|
+
* The dev proxy only runs under `edgespark dev`; the meta tag is absent
|
|
7
|
+
* on deployed apps (served through the CF dispatcher, which doesn't
|
|
8
|
+
* inject).
|
|
9
|
+
*
|
|
10
|
+
* Uses a <meta> tag rather than an inline <script> so the signal works
|
|
11
|
+
* under strict CSP — `script-src 'self'` without `'unsafe-inline'`
|
|
12
|
+
* would silently drop a script-based marker. Meta tags carry no
|
|
13
|
+
* script-src constraint.
|
|
14
|
+
*
|
|
15
|
+
* No server round-trip, no build-time macro, no hostname heuristic, no
|
|
16
|
+
* framework assumptions. Survives SDK pre-bundling — the SDK reads the
|
|
17
|
+
* DOM at call time, not a value captured at build time.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isLocalDev(): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Auto-verify email in local dev. The server reads the pending verification
|
|
22
|
+
* token from D1, drives Better Auth's verify-email handler, and returns with
|
|
23
|
+
* the session cookie already set. Single round-trip — no token round-trip on
|
|
24
|
+
* the client.
|
|
25
|
+
*/
|
|
26
|
+
export declare function autoVerifyLocalDev(email: string): Promise<boolean>;
|
|
27
|
+
/**
|
|
28
|
+
* Fetch the pending OTP for password reset in local dev.
|
|
29
|
+
* Returns the OTP string or null if not available.
|
|
30
|
+
*/
|
|
31
|
+
export declare function fetchOtpLocalDev(email: string): Promise<string | null>;
|
|
32
|
+
/**
|
|
33
|
+
* Fetch the pending OTP for email sign-in in local dev.
|
|
34
|
+
* Returns the OTP string or null if not available.
|
|
35
|
+
*/
|
|
36
|
+
export declare function fetchSignInOtpLocalDev(email: string): Promise<string | null>;
|
|
@@ -0,0 +1,84 @@
|
|
|
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 the auth API shape aligned with better-auth while adding a
|
|
21
|
+
* small set of session helpers on top.
|
|
22
|
+
*
|
|
23
|
+
* Most upstream auth methods remain available on the familiar better-auth
|
|
24
|
+
* shape. Some upstream methods may still be intentionally unsupported by the
|
|
25
|
+
* current EdgeSpark platform capability set even though they exist on the
|
|
26
|
+
* underlying better-auth client surface. In those cases, the facade throws an
|
|
27
|
+
* `EdgeSparkAuthError` with code `UNSUPPORTED_AUTH_METHOD` instead of deferring
|
|
28
|
+
* to a platform-specific 400/404 response.
|
|
29
|
+
*/
|
|
30
|
+
export type EdgeSparkAuth = BetterAuthClientInstance & {
|
|
31
|
+
/**
|
|
32
|
+
* Resolves with the current session, or throws when the browser is not authenticated.
|
|
33
|
+
*
|
|
34
|
+
* Use this for route guards and other flows where nullable session checks add noise.
|
|
35
|
+
*
|
|
36
|
+
* If the session lookup itself fails (offline, 5xx, etc.), this throws an
|
|
37
|
+
* `EdgeSparkAuthError` that preserves the upstream error code/status instead
|
|
38
|
+
* of collapsing the failure into `UNAUTHENTICATED`.
|
|
39
|
+
*/
|
|
40
|
+
requireSession(): Promise<AuthSession>;
|
|
41
|
+
/**
|
|
42
|
+
* Subscribes to session changes for the current browser context.
|
|
43
|
+
*
|
|
44
|
+
* The first emission is hydration-safe: it waits for the initial server
|
|
45
|
+
* session lookup to complete before firing, so subscribers never receive a
|
|
46
|
+
* premature `null` while a cookie session is still settling.
|
|
47
|
+
*
|
|
48
|
+
* After hydration, successful session atom updates are forwarded immediately.
|
|
49
|
+
* Successful auth mutations that create or clear a session in the current
|
|
50
|
+
* tab also prompt other tabs to refetch their session state, so logout and
|
|
51
|
+
* same-tab sign-in/sign-up converge quickly across tabs.
|
|
52
|
+
*
|
|
53
|
+
* Transient lookup failures that temporarily clear the atom to `null` are
|
|
54
|
+
* ignored so subscribers do not mistake a backend error for a real sign-out.
|
|
55
|
+
*
|
|
56
|
+
* Returns an unsubscribe function.
|
|
57
|
+
*/
|
|
58
|
+
onSessionChange(callback: SessionChangeCallback): () => void;
|
|
59
|
+
/**
|
|
60
|
+
* Signs out and then best-effort synchronizes session state for the current
|
|
61
|
+
* tab and other open tabs.
|
|
62
|
+
*
|
|
63
|
+
* The underlying better-auth result is returned unchanged.
|
|
64
|
+
*/
|
|
65
|
+
signOut(...args: Parameters<BetterAuthClientInstance["signOut"]>): ReturnType<BetterAuthClientInstance["signOut"]>;
|
|
66
|
+
/**
|
|
67
|
+
* Currently unsupported by EdgeSpark platform auth.
|
|
68
|
+
*
|
|
69
|
+
* The method remains present to preserve the familiar better-auth API shape,
|
|
70
|
+
* but the facade throws `UNSUPPORTED_AUTH_METHOD` until the platform enables
|
|
71
|
+
* this capability. For browser auth, prefer supported `client.auth` methods
|
|
72
|
+
* or `authUI.mount()` instead of dropping to raw `/api/_es/auth/*` routes.
|
|
73
|
+
*/
|
|
74
|
+
changeEmail(...args: Parameters<BetterAuthClientInstance["changeEmail"]>): ReturnType<BetterAuthClientInstance["changeEmail"]>;
|
|
75
|
+
/**
|
|
76
|
+
* Currently unsupported by EdgeSpark platform auth.
|
|
77
|
+
*
|
|
78
|
+
* The method remains present to preserve the familiar better-auth API shape,
|
|
79
|
+
* but the facade throws `UNSUPPORTED_AUTH_METHOD` until the platform enables
|
|
80
|
+
* this capability. Handle account removal through your own app flow until the
|
|
81
|
+
* platform adds first-class support.
|
|
82
|
+
*/
|
|
83
|
+
deleteUser(...args: Parameters<BetterAuthClientInstance["deleteUser"]>): ReturnType<BetterAuthClientInstance["deleteUser"]>;
|
|
84
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { BetterAuthClientInstance } from '../auth/types';
|
|
2
|
+
import { NormalizedAuthUIConfig } from './config';
|
|
3
|
+
import { AuthUIModuleRuntimeOptions } from './mount';
|
|
4
|
+
import { MountAuthUIOptions, MountedAuthUI } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* Internal managed auth UI implementation.
|
|
7
|
+
*
|
|
8
|
+
* The public API only exposes `authUI.mount(...)`; this class owns the DOM,
|
|
9
|
+
* form flows, and lifecycle.
|
|
10
|
+
*
|
|
11
|
+
* DOM ownership: the instance owns a single root element it appends to the
|
|
12
|
+
* mount container. It never writes to `container.innerHTML`. This keeps
|
|
13
|
+
* tear-down safe when the container is a DOM node a reconciling framework
|
|
14
|
+
* (React/Vue/Solid) reuses across conditional branches — wiping the
|
|
15
|
+
* container would erase sibling content the framework just rendered.
|
|
16
|
+
*/
|
|
17
|
+
export declare class ManagedAuthUI implements MountedAuthUI {
|
|
18
|
+
private readonly container;
|
|
19
|
+
private readonly auth;
|
|
20
|
+
private readonly options;
|
|
21
|
+
private readonly loadConfig;
|
|
22
|
+
private readonly runtimeOptions?;
|
|
23
|
+
private readonly fieldIdPrefix;
|
|
24
|
+
private readonly labels;
|
|
25
|
+
private readonly root;
|
|
26
|
+
private readonly state;
|
|
27
|
+
private destroyed;
|
|
28
|
+
private terminalActionInProgress;
|
|
29
|
+
private config;
|
|
30
|
+
private countdownUntil;
|
|
31
|
+
private countdownTimer;
|
|
32
|
+
private pendingFocusSnapshot;
|
|
33
|
+
private focusRestoreTimer;
|
|
34
|
+
private verificationCompletionAction;
|
|
35
|
+
constructor(container: HTMLElement, auth: BetterAuthClientInstance, options: MountAuthUIOptions, loadConfig: () => Promise<NormalizedAuthUIConfig>, runtimeOptions?: AuthUIModuleRuntimeOptions | undefined);
|
|
36
|
+
destroy(): void;
|
|
37
|
+
private initialize;
|
|
38
|
+
private redirectAfterManagedReturnLookupFailure;
|
|
39
|
+
private navigateToRedirect;
|
|
40
|
+
private readMountSession;
|
|
41
|
+
private render;
|
|
42
|
+
private renderLoading;
|
|
43
|
+
private renderDevModeBanner;
|
|
44
|
+
private renderStatus;
|
|
45
|
+
private renderCurrentView;
|
|
46
|
+
private renderSignInView;
|
|
47
|
+
private renderSignUpView;
|
|
48
|
+
private renderVerifyEmailView;
|
|
49
|
+
private renderForgotPasswordView;
|
|
50
|
+
private renderResetPasswordView;
|
|
51
|
+
private renderOAuthProviders;
|
|
52
|
+
private renderInput;
|
|
53
|
+
private bindDOM;
|
|
54
|
+
private switchView;
|
|
55
|
+
private handleSignIn;
|
|
56
|
+
private handleEmailOtpSignIn;
|
|
57
|
+
private sendSignInOtp;
|
|
58
|
+
private resendSignInCode;
|
|
59
|
+
private handleSignUp;
|
|
60
|
+
private handleOAuthSignIn;
|
|
61
|
+
private handleForgotPassword;
|
|
62
|
+
private resendResetCode;
|
|
63
|
+
private handleResetPassword;
|
|
64
|
+
private resendVerificationEmail;
|
|
65
|
+
/**
|
|
66
|
+
* Drive the local-dev auto-verify endpoint and, on success, complete the
|
|
67
|
+
* auth flow. Returns whether auto-verify completed — callers decide what
|
|
68
|
+
* to do on `false` (sign-up falls through to a retry UI; resend throws).
|
|
69
|
+
*/
|
|
70
|
+
private tryDevAutoVerify;
|
|
71
|
+
private sendVerificationEmail;
|
|
72
|
+
private completeAuth;
|
|
73
|
+
private retryAuthCompletionDataIfNeeded;
|
|
74
|
+
private syncPublicSessionBestEffort;
|
|
75
|
+
private resolveAuthCompletionData;
|
|
76
|
+
private completePasswordReset;
|
|
77
|
+
private handleExistingSessionOnMount;
|
|
78
|
+
private runAction;
|
|
79
|
+
private maybeHandleVerificationRequired;
|
|
80
|
+
private handleVerificationEmailSent;
|
|
81
|
+
private invokeRedirectModeSuccessHook;
|
|
82
|
+
private handleError;
|
|
83
|
+
private failValidation;
|
|
84
|
+
private getManagedReturnURL;
|
|
85
|
+
private consumeManagedReturnContext;
|
|
86
|
+
private startCountdown;
|
|
87
|
+
private updateCountdownControls;
|
|
88
|
+
private clearCountdown;
|
|
89
|
+
private captureFocusSnapshot;
|
|
90
|
+
private scheduleFocusRestore;
|
|
91
|
+
private clearFocusRestoreTimer;
|
|
92
|
+
private passwordResetEndpoints;
|
|
93
|
+
private emailOtpSignInEndpoints;
|
|
94
|
+
private resolveInitialSignInMethod;
|
|
95
|
+
private setSignInMethod;
|
|
96
|
+
private canUseEmailOtpSignIn;
|
|
97
|
+
private extractRedirectURL;
|
|
98
|
+
private extractUser;
|
|
99
|
+
private canUsePasswordResetEmailOtp;
|
|
100
|
+
private handleSessionlessManagedReturn;
|
|
101
|
+
private isCountdownActive;
|
|
102
|
+
private getCountdownSecondsRemaining;
|
|
103
|
+
private countdownText;
|
|
104
|
+
private escape;
|
|
105
|
+
}
|
|
@@ -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 {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export type OAuthProviderId = "google" | "apple" | "microsoft" | "github" | "gitlab" | "discord";
|
|
2
|
+
interface OAuthProviderConfig {
|
|
3
|
+
/**
|
|
4
|
+
* OAuth Client ID resolved server-side. Present only when both the Client
|
|
5
|
+
* ID var and the corresponding secret resolve, so a non-empty value is
|
|
6
|
+
* the "this provider is fully wired browser-side" signal.
|
|
7
|
+
*/
|
|
8
|
+
clientId?: string;
|
|
9
|
+
}
|
|
10
|
+
interface OAuthProviderBlock {
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
config?: OAuthProviderConfig;
|
|
13
|
+
}
|
|
14
|
+
interface ProviderEmailPassword {
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
config?: {
|
|
17
|
+
minPasswordLength?: number;
|
|
18
|
+
requireEmailVerification?: boolean;
|
|
19
|
+
requirePasswordResetEmailVerification?: boolean;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
interface ProviderEmailOtp {
|
|
23
|
+
enabled?: boolean;
|
|
24
|
+
}
|
|
25
|
+
interface AuthConfigResponse {
|
|
26
|
+
disableSignUp?: boolean;
|
|
27
|
+
providerEmailPassword?: ProviderEmailPassword;
|
|
28
|
+
providerEmailOtp?: ProviderEmailOtp;
|
|
29
|
+
providerGoogle?: OAuthProviderBlock;
|
|
30
|
+
providerApple?: OAuthProviderBlock;
|
|
31
|
+
providerMicrosoft?: OAuthProviderBlock;
|
|
32
|
+
providerGithub?: OAuthProviderBlock;
|
|
33
|
+
providerGitlab?: OAuthProviderBlock;
|
|
34
|
+
providerDiscord?: OAuthProviderBlock;
|
|
35
|
+
}
|
|
36
|
+
export interface NormalizedAuthUIConfig {
|
|
37
|
+
disableSignUp: boolean;
|
|
38
|
+
emailPasswordEnabled: boolean;
|
|
39
|
+
emailOtpSignInEnabled: boolean;
|
|
40
|
+
minPasswordLength: number;
|
|
41
|
+
requireEmailVerification: boolean;
|
|
42
|
+
passwordResetEmailOtpEnabled: boolean;
|
|
43
|
+
oauthProviders: OAuthProviderId[];
|
|
44
|
+
/**
|
|
45
|
+
* Resolved Google OAuth Client ID when Google is enabled and both
|
|
46
|
+
* credentials resolved server-side; `null` otherwise. Signals that
|
|
47
|
+
* Google client-side wiring is ready.
|
|
48
|
+
*/
|
|
49
|
+
googleClientId: string | null;
|
|
50
|
+
isLocalDev: boolean;
|
|
51
|
+
}
|
|
52
|
+
export declare function loadNormalizedAuthUIConfig(): Promise<NormalizedAuthUIConfig>;
|
|
53
|
+
export declare function normalizeAuthUIConfig(config: AuthConfigResponse, isLocalDev?: boolean): NormalizedAuthUIConfig;
|
|
54
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal helpers for driving Google Identity Services (One Tap) from the
|
|
3
|
+
* managed auth UI. Loads the GIS client script on demand and exposes a tiny
|
|
4
|
+
* typed surface around the global `google.accounts.id` namespace.
|
|
5
|
+
*
|
|
6
|
+
* GIS is treated as a UX enhancement: any load failure falls back silently
|
|
7
|
+
* to the regular "Continue with Google" OAuth button.
|
|
8
|
+
*/
|
|
9
|
+
interface GsiCredentialResponse {
|
|
10
|
+
credential: string;
|
|
11
|
+
select_by?: string;
|
|
12
|
+
}
|
|
13
|
+
interface GsiInitializeOptions {
|
|
14
|
+
client_id: string;
|
|
15
|
+
callback: (response: GsiCredentialResponse) => void;
|
|
16
|
+
auto_select?: boolean;
|
|
17
|
+
cancel_on_tap_outside?: boolean;
|
|
18
|
+
use_fedcm_for_prompt?: boolean;
|
|
19
|
+
context?: "signin" | "signup" | "use";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Subset of Google's `PromptMomentNotification`. Methods are optional because
|
|
23
|
+
* GIS's FedCM-era roadmap retires some of them, and EdgeSpark does not depend
|
|
24
|
+
* on `@types/google.accounts` for the full type surface.
|
|
25
|
+
*/
|
|
26
|
+
interface GsiPromptNotification {
|
|
27
|
+
isNotDisplayed?: () => boolean;
|
|
28
|
+
isSkippedMoment?: () => boolean;
|
|
29
|
+
isDismissedMoment?: () => boolean;
|
|
30
|
+
getDismissedReason?: () => string | undefined;
|
|
31
|
+
}
|
|
32
|
+
interface GsiIdAccounts {
|
|
33
|
+
initialize(options: GsiInitializeOptions): void;
|
|
34
|
+
prompt(notificationCallback?: (notification: GsiPromptNotification) => void): void;
|
|
35
|
+
cancel(): void;
|
|
36
|
+
disableAutoSelect(): void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Loads `accounts.google.com/gsi/client` once per page and resolves with
|
|
40
|
+
* the `google.accounts.id` namespace. Subsequent calls return the same
|
|
41
|
+
* promise; load failures reject and reset so a caller can retry next mount.
|
|
42
|
+
*/
|
|
43
|
+
export declare function loadGoogleIdentityServices(): Promise<GsiIdAccounts>;
|
|
44
|
+
export type GoogleIdentityServices = GsiIdAccounts;
|
|
45
|
+
export type GoogleCredentialResponse = GsiCredentialResponse;
|
|
46
|
+
export type GooglePromptNotification = GsiPromptNotification;
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
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';
|
|
4
|
+
export type { AuthenticatedAuthSuccessEvent, AuthSuccessEvent, AuthUILabelOverrides, AuthUILabels, ControlledAuthUIOptions, EdgeSparkAuthUI, MountAuthUIOptions, MountedAuthUI, OneTapPromptHandle, PromptGoogleOneTapOptions, RedirectAuthUIOptions, } from './types';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BetterAuthClientInstance } from '../auth/types';
|
|
2
|
+
import { EdgeSparkAuthUI } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Internal runtime config for the auth-UI module factory. Threads cross-cutting
|
|
5
|
+
* auth wiring into `ManagedAuthUI` (modal/redirect flows) and
|
|
6
|
+
* `promptGoogleOneTap` (page-load One Tap prompt).
|
|
7
|
+
*/
|
|
8
|
+
export interface AuthUIModuleRuntimeOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Best-effort hook to nudge the public auth session after the managed UI
|
|
11
|
+
* completes a sign-in. Used by `ManagedAuthUI`'s email and OAuth flows.
|
|
12
|
+
*/
|
|
13
|
+
syncPublicSession?: () => Promise<void> | void;
|
|
14
|
+
/**
|
|
15
|
+
* The public auth client used by `promptGoogleOneTap` for credential
|
|
16
|
+
* exchange. Routing One Tap through the same client as `signIn`/`signUp`
|
|
17
|
+
* keeps session updates and `onSessionChange` consistent across all
|
|
18
|
+
* sign-in methods.
|
|
19
|
+
*/
|
|
20
|
+
publicAuthClient: BetterAuthClientInstance;
|
|
21
|
+
}
|
|
22
|
+
export declare function createAuthUIModule(auth: BetterAuthClientInstance, runtimeOptions: AuthUIModuleRuntimeOptions): EdgeSparkAuthUI;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { BetterAuthClientInstance } from '../auth/types';
|
|
2
|
+
import { EdgeSparkAuthError } from '../errors';
|
|
3
|
+
import { NormalizedAuthUIConfig } from './config';
|
|
4
|
+
import { AuthenticatedAuthSuccessEvent } from './types';
|
|
5
|
+
export interface PromptGoogleOneTapOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Fires when Google One Tap sign-in completes.
|
|
8
|
+
*
|
|
9
|
+
* **Most apps should prefer `client.auth.onSessionChange`** as the single
|
|
10
|
+
* source of truth — it fires for One Tap, modal sign-in, sign-out, and
|
|
11
|
+
* cross-tab session changes. Use `onSuccess` only for flow-local concerns
|
|
12
|
+
* scoped to this specific prompt (e.g., per-flow analytics).
|
|
13
|
+
*/
|
|
14
|
+
onSuccess?: (event: AuthenticatedAuthSuccessEvent) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Fires on credential-exchange or post-sign-in errors.
|
|
17
|
+
*/
|
|
18
|
+
onError?: (error: EdgeSparkAuthError) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Fires when Google declines to display the prompt — e.g., FedCM cooldown,
|
|
21
|
+
* no eligible accounts, or browser permission denied. Use this to render a
|
|
22
|
+
* fallback "Sign in with Google" button.
|
|
23
|
+
*/
|
|
24
|
+
onUnavailable?: () => void;
|
|
25
|
+
}
|
|
26
|
+
export interface OneTapPromptHandle {
|
|
27
|
+
/**
|
|
28
|
+
* Dismiss the prompt and suppress any pending callbacks. Idempotent and
|
|
29
|
+
* safe to call after the prompt has already closed.
|
|
30
|
+
*/
|
|
31
|
+
cancel(): void;
|
|
32
|
+
}
|
|
33
|
+
interface PromptRuntime {
|
|
34
|
+
/**
|
|
35
|
+
* The wrapped public auth client. Calls flow through its path-proxy so
|
|
36
|
+
* `oneTap.callback` triggers the same session-atom refresh and
|
|
37
|
+
* `onSessionChange` emit as `signIn`/`signUp`.
|
|
38
|
+
*/
|
|
39
|
+
authClient: BetterAuthClientInstance;
|
|
40
|
+
loadConfig: () => Promise<NormalizedAuthUIConfig>;
|
|
41
|
+
}
|
|
42
|
+
export declare function createPromptGoogleOneTap(runtime: PromptRuntime): (options?: PromptGoogleOneTapOptions) => OneTapPromptHandle;
|
|
43
|
+
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
|
+
}>;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { AuthSession, AuthUser } from '../auth/types';
|
|
2
|
+
import { EdgeSparkAuthError } from '../errors';
|
|
3
|
+
import { RedirectTarget } from '../redirect';
|
|
4
|
+
import { DeepPartial } from '../shared/types';
|
|
5
|
+
import { AuthUIAppearance } from './appearance';
|
|
6
|
+
import { OneTapPromptHandle, PromptGoogleOneTapOptions } from './prompt-google-one-tap';
|
|
7
|
+
export type { OneTapPromptHandle, PromptGoogleOneTapOptions } from './prompt-google-one-tap';
|
|
8
|
+
/** Sign-in or sign-up completed with an authenticated session. */
|
|
9
|
+
export type AuthenticatedAuthSuccessEvent = {
|
|
10
|
+
action: "sign-in";
|
|
11
|
+
user: AuthUser;
|
|
12
|
+
session: AuthSession;
|
|
13
|
+
} | {
|
|
14
|
+
action: "sign-up";
|
|
15
|
+
user: AuthUser;
|
|
16
|
+
session: AuthSession;
|
|
17
|
+
};
|
|
18
|
+
/** Password was reset. No session — the user must sign in again. */
|
|
19
|
+
export interface PasswordResetSuccessEvent {
|
|
20
|
+
action: "password-reset";
|
|
21
|
+
user: null;
|
|
22
|
+
session: null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Payload emitted by `onSuccess` after a managed auth flow completes.
|
|
26
|
+
*
|
|
27
|
+
* Switch on `event.action` to narrow `user` and `session`:
|
|
28
|
+
* - `"sign-in"` / `"sign-up"` → `user: AuthUser`, `session: AuthSession`
|
|
29
|
+
* - `"password-reset"` → `user: null`, `session: null`
|
|
30
|
+
*/
|
|
31
|
+
export type AuthSuccessEvent = AuthenticatedAuthSuccessEvent | PasswordResetSuccessEvent;
|
|
32
|
+
export interface AuthUILabels {
|
|
33
|
+
common: {
|
|
34
|
+
loading: string;
|
|
35
|
+
redirecting: string;
|
|
36
|
+
backToSignIn: string;
|
|
37
|
+
};
|
|
38
|
+
signIn: {
|
|
39
|
+
title: string;
|
|
40
|
+
subtitle: string;
|
|
41
|
+
emailLabel: string;
|
|
42
|
+
emailPlaceholder?: string;
|
|
43
|
+
codePlaceholder?: string;
|
|
44
|
+
sendingCode?: string;
|
|
45
|
+
signingIn?: string;
|
|
46
|
+
passwordLabel: string;
|
|
47
|
+
codeLabel?: string;
|
|
48
|
+
submitButton: string;
|
|
49
|
+
sendCodeButton?: string;
|
|
50
|
+
verifyCodeButton?: string;
|
|
51
|
+
resendCodeButton?: string;
|
|
52
|
+
resendCountdown?: string;
|
|
53
|
+
passwordTab?: string;
|
|
54
|
+
codeTab?: string;
|
|
55
|
+
forgotPassword: string;
|
|
56
|
+
switchToSignUp: string;
|
|
57
|
+
signUpPrompt: string;
|
|
58
|
+
};
|
|
59
|
+
signUp: {
|
|
60
|
+
title: string;
|
|
61
|
+
subtitle: string;
|
|
62
|
+
nameLabel: string;
|
|
63
|
+
emailLabel: string;
|
|
64
|
+
passwordLabel: string;
|
|
65
|
+
confirmPasswordLabel: string;
|
|
66
|
+
submitButton: string;
|
|
67
|
+
switchToSignIn: string;
|
|
68
|
+
signInPrompt: string;
|
|
69
|
+
};
|
|
70
|
+
verifyEmail: {
|
|
71
|
+
title: string;
|
|
72
|
+
subtitle: string;
|
|
73
|
+
sentToLabel: string;
|
|
74
|
+
resendButton: string;
|
|
75
|
+
resendCountdown: string;
|
|
76
|
+
changeEmailButton: string;
|
|
77
|
+
};
|
|
78
|
+
forgotPassword: {
|
|
79
|
+
title: string;
|
|
80
|
+
subtitle: string;
|
|
81
|
+
emailLabel: string;
|
|
82
|
+
submitButton: string;
|
|
83
|
+
};
|
|
84
|
+
resetPassword: {
|
|
85
|
+
title: string;
|
|
86
|
+
subtitle: string;
|
|
87
|
+
codeLabel: string;
|
|
88
|
+
newPasswordLabel: string;
|
|
89
|
+
confirmPasswordLabel: string;
|
|
90
|
+
submitButton: string;
|
|
91
|
+
resendCodeButton: string;
|
|
92
|
+
resendCountdown: string;
|
|
93
|
+
};
|
|
94
|
+
oauth: {
|
|
95
|
+
dividerText: string;
|
|
96
|
+
continueWith: string;
|
|
97
|
+
/** Use {provider} for the provider display name. */
|
|
98
|
+
redirecting?: string;
|
|
99
|
+
};
|
|
100
|
+
errors: {
|
|
101
|
+
invalidEmail: string;
|
|
102
|
+
requiredField: string;
|
|
103
|
+
passwordTooShort: string;
|
|
104
|
+
passwordMismatch: string;
|
|
105
|
+
invalidCode: string;
|
|
106
|
+
generic: string;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export type AuthUILabelOverrides = DeepPartial<AuthUILabels>;
|
|
110
|
+
interface BaseAuthUIOptions {
|
|
111
|
+
onError?: (error: EdgeSparkAuthError) => void;
|
|
112
|
+
labels?: AuthUILabelOverrides;
|
|
113
|
+
/**
|
|
114
|
+
* Optional, constrained visual customization for the managed auth UI.
|
|
115
|
+
*
|
|
116
|
+
* This is intentionally smaller than the headless auth path. Use
|
|
117
|
+
* `client.auth` when the app needs full UI ownership.
|
|
118
|
+
*/
|
|
119
|
+
appearance?: AuthUIAppearance;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Redirect mode — use when the auth UI is a full-page login gate.
|
|
123
|
+
*
|
|
124
|
+
* After successful auth the SDK navigates to `redirectTo` automatically.
|
|
125
|
+
* This is the common case: dedicated `/login` page → redirect to `/dashboard`.
|
|
126
|
+
*
|
|
127
|
+
* `onSuccess` is optional and fires *before* the redirect (useful for analytics).
|
|
128
|
+
*/
|
|
129
|
+
export interface RedirectAuthUIOptions extends BaseAuthUIOptions {
|
|
130
|
+
redirectTo: RedirectTarget;
|
|
131
|
+
onSuccess?: (event: AuthSuccessEvent) => void;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Controlled mode — use when the app owns what happens after auth.
|
|
135
|
+
*
|
|
136
|
+
* The SDK will not navigate. Instead, `onSuccess` fires with the auth event
|
|
137
|
+
* and the app decides the next step (close a modal, update state, navigate
|
|
138
|
+
* programmatically via a router, etc.).
|
|
139
|
+
*
|
|
140
|
+
* Use this for: modals, embedded auth widgets, SPA routing, onboarding flows.
|
|
141
|
+
*/
|
|
142
|
+
export interface ControlledAuthUIOptions extends BaseAuthUIOptions {
|
|
143
|
+
onSuccess: (event: AuthSuccessEvent) => void;
|
|
144
|
+
redirectTo?: never;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Two post-auth strategies — pick one:
|
|
148
|
+
*
|
|
149
|
+
* - **Redirect mode** (`{ redirectTo }`) — SDK navigates after auth.
|
|
150
|
+
* Best for dedicated login pages.
|
|
151
|
+
*
|
|
152
|
+
* - **Controlled mode** (`{ onSuccess }`) — app handles post-auth behavior.
|
|
153
|
+
* Best for modals, embedded widgets, or SPA routing.
|
|
154
|
+
*/
|
|
155
|
+
export type MountAuthUIOptions = RedirectAuthUIOptions | ControlledAuthUIOptions;
|
|
156
|
+
export interface MountedAuthUI {
|
|
157
|
+
/**
|
|
158
|
+
* Removes the mounted UI and clears internal timers/listeners.
|
|
159
|
+
*/
|
|
160
|
+
destroy(): void;
|
|
161
|
+
}
|
|
162
|
+
export interface EdgeSparkAuthUI {
|
|
163
|
+
/**
|
|
164
|
+
* Mounts the managed auth UI into a container element.
|
|
165
|
+
*
|
|
166
|
+
* Pass `redirectTo` for the common login-page case, or `onSuccess` when the
|
|
167
|
+
* surrounding app wants to control routing/state after auth completes.
|
|
168
|
+
*/
|
|
169
|
+
mount(container: HTMLElement, options: MountAuthUIOptions): MountedAuthUI;
|
|
170
|
+
/**
|
|
171
|
+
* Triggers a Google One Tap prompt. Calling the function is the opt-in —
|
|
172
|
+
* there is no separate server config or option flag.
|
|
173
|
+
*
|
|
174
|
+
* The prompt shows only when Google is fully wired server-side (the
|
|
175
|
+
* project's `auth-config.yaml` enables Google with resolvable credentials).
|
|
176
|
+
* Otherwise the call logs a clear error to the console and returns an
|
|
177
|
+
* inert handle.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* useEffect(() => {
|
|
181
|
+
* if (session) return;
|
|
182
|
+
* const prompt = client.authUI.promptGoogleOneTap();
|
|
183
|
+
* return () => prompt.cancel();
|
|
184
|
+
* }, [session]);
|
|
185
|
+
*/
|
|
186
|
+
promptGoogleOneTap(options?: PromptGoogleOneTapOptions): OneTapPromptHandle;
|
|
187
|
+
}
|