@edgespark/client

中文文档
1.0.4发布于 2026/03/18MITTypeScriptESM + CJS近 30 天下载 3076

edgespark umbrella client: auth UI/APIs plus HTTP utilities.

原始使用文档
文档目录29 节
  1. Install
  2. Quick Start
  3. Managed Login UI
  4. Container Requirements
  5. Basic Usage
  6. React Example
  7. Custom Labels
  8. Custom Styles
  9. Custom UI
  10. Sign In
  11. Sign Up
  12. Get User Info
  13. Sign Out
  14. Forgot Password / Reset Password
  15. Listen to Auth State Changes
  16. API Module
  17. Lifecycle & Best Practices
  18. Check Login Status Before Rendering
  19. Get User Info on Target Page
  20. onLogin vs getSession vs onAuthStateChange
  21. React Best Practice
  22. Cleanup
  23. API Reference
  24. createEdgeSpark(options)
  25. client.auth
  26. client.api
  27. LoginUIOptions
  28. RedirectOptions
  29. Build

@edgespark/client

Browser SDK for EdgeSpark BaaS - authentication + API client in one package.

Install

npm i @edgespark/client

Note: This SDK is browser-only. It does not support Node.js or SSR environments.

Quick Start

import { createEdgeSpark } from "@edgespark/client";
import "@edgespark/client/styles.css";

const client = createEdgeSpark({ baseUrl: "https://api.example.com" });

// Option 1: Use managed login UI (recommended)
await client.auth.renderAuthUI(document.getElementById("auth")!, {
  redirectTo: "/dashboard",
  onLogin: (user) => console.log("Logged in!", user.email),
});

// Option 2: Build custom UI with direct API calls
await client.auth.signIn.email({ email, password });
const session = await client.auth.getSession();

// API requests (auth token auto-injected)
const res = await client.api.fetch("/api/users");

Managed Login UI

renderAuthUI provides a complete out-of-the-box authentication flow:

FlowDescription
Sign InEmail/password login, OAuth (Google, etc.), anonymous temporary account
Sign UpEmail/password registration (handles email verification automatically)
Forgot PasswordSend reset code → Enter code → Set new password
Email VerificationSend verification email after signup, supports resend

Container Requirements

The managed login UI is optimized for these container dimensions:

DimensionValueNotes
Optimal width420pxMatches --edgespark-auth-card-width
Minimum width320pxMobile devices
Responsivewidth: 100%; max-width: 420pxRecommended
<div id="auth" style="width: 100%; max-width: 420px; margin: 0 auto;"></div>

Basic Usage

import { createEdgeSpark } from "@edgespark/client";
import "@edgespark/client/styles.css";  // Required

const client = createEdgeSpark({ baseUrl: "https://api.example.com" });

// Check if already logged in
const session = await client.auth.getSession();
if (session.data?.user) {
  window.location.href = "/dashboard";
} else {
  // Render login UI
  await client.auth.renderAuthUI(document.getElementById("auth")!, {
    redirectTo: "/dashboard",
    onError: (error) => console.error("Auth error:", error),
  });
}

React Example

import { useRef, useEffect } from "react";
import { createEdgeSpark } from "@edgespark/client";
import "@edgespark/client/styles.css";

// Create client outside component (singleton)
const client = createEdgeSpark({ baseUrl: "https://api.example.com" });

export function AuthPage() {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!ref.current) return;

    // Check login status first
    client.auth.getSession().then((session) => {
      if (session.data?.user) {
        window.location.href = "/dashboard";
        return;
      }
      client.auth.renderAuthUI(ref.current!, {
        redirectTo: "/dashboard",
      });
    });
  }, []);

  return <div ref={ref} />;
}

// Dashboard page - get user info here
export function DashboardPage() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    client.auth.getSession().then((session) => {
      if (!session.data?.user) {
        window.location.href = "/login";
        return;
      }
      setUser(session.data.user);
    });
  }, []);

  if (!user) return <div>Loading...</div>;
  return <div>Welcome, {user.name}!</div>;
}

Custom Labels

Override default labels via the labels parameter (partial override supported):

await client.auth.renderAuthUI(container, {
  labels: {
    // Common labels
    common: {
      continueButton: "Continue",
      backButton: "Back",
      unknownError: "Something went wrong, please try again",
    },
    // Sign in page
    signIn: {
      title: "Welcome Back",
      subtitle: "Sign in to continue",
      emailPlaceholder: "Enter your email",
      passwordPlaceholder: "Enter your password",
      loginButton: "Sign In",
      forgotPassword: "Forgot password?",
      signUpPrompt: "Don't have an account?",
      toggleToSignUp: "Sign up now",
      guestPrompt: "Not ready to sign up?",
      guestLinkText: "Continue with temporary account",
    },
    // Sign up page
    signUp: {
      title: "Create Account",
      subtitle: "Sign up to get started",
      emailPlaceholder: "Enter your email",
      passwordPlaceholder: "Enter your password",
      confirmPasswordPlaceholder: "Confirm your password",
      signUpButton: "Sign Up",
      toggleToSignIn: "Already have an account?",
      toggleToSignInLink: "Sign in now",
    },
    // Verification code page
    verifyCode: {
      title: "Enter Verification Code",
      sentTo: "Code sent to",
      resendCode: "Resend",
      resendCountdown: "Resend in {seconds}s",  // {seconds} is a placeholder
    },
    // Reset password
    resetPassword: {
      title: "Reset Password",
      subtitle: "Enter your email to receive a reset code",
      sendResetButton: "Send Reset Code",
      backToSignIn: "Back to sign in",
    },
    // Set new password
    setNewPassword: {
      title: "Set New Password",
      subtitle: "Code sent to",
      newPasswordLabel: "New password",
      confirmPasswordLabel: "Confirm password",
      setPasswordButton: "Set Password",
    },
    // OAuth buttons
    oauth: {
      googleBtnName: "Continue with Google",
      githubBtnName: "Continue with GitHub",
    },
    // Error messages (override backend error codes)
    errors: {
      INVALID_EMAIL_OR_PASSWORD: "Invalid email or password",
      INVALID_EMAIL: "Invalid email format",
      PASSWORD_MISMATCH: "Passwords do not match",
      PASSWORD_TOO_SHORT: "Password is too short",
      USER_ALREADY_EXISTS: "This email is already registered",
      EMAIL_NOT_VERIFIED: "Email not verified, please check your inbox",
      INVALID_OTP: "Invalid verification code",
      OTP_EXPIRED: "Verification code has expired",
      TOO_MANY_ATTEMPTS: "Too many attempts, please try again later",
    },
    // Success page
    success: {
      title: "Success!",
      continueButton: "Continue",
    },
    // iframe OAuth modal (for preview page limitations)
    iframeModal: {
      titleTemplate: "Sign in with {provider}",  // {provider} is a placeholder
      messageTemplate: "Due to preview limitations, {provider} sign-in must be completed in a new tab.",
      actionButton: "Open in New Tab",
    },
  },
});

Custom Styles

All styles are scoped under [data-edgespark-auth]. Import the CSS and override via CSS variables:

/* In your CSS file */
[data-edgespark-auth] {
  /* Colors */
  --edgespark-auth-color-primary: rgba(0, 0, 0, 0.95);
  --edgespark-auth-color-secondary: rgba(0, 0, 0, 0.65);
  --edgespark-auth-color-tertiary: rgba(0, 0, 0, 0.45);
  --edgespark-auth-color-text: rgba(0, 0, 0, 0.95);
  --edgespark-auth-color-text-inverse: #ffffff;
  --edgespark-auth-color-error: #dc2626;
  --edgespark-auth-color-border: rgba(0, 0, 0, 0.12);
  --edgespark-auth-color-bg: #ffffff;
  --edgespark-auth-color-bg-secondary: rgba(0, 0, 0, 0.05);
  --edgespark-auth-color-bg-link: #0077ff;

  /* Typography */
  --edgespark-auth-font-family: 'Roboto Flex', -apple-system, sans-serif;
  --edgespark-auth-font-size-title: 32px;
  --edgespark-auth-font-size-body: 14px;
  --edgespark-auth-font-size-small: 12px;

  /* Sizing & Border Radius */
  --edgespark-auth-radius-card: 30px;
  --edgespark-auth-radius-input: 12px;
  --edgespark-auth-radius-button: 999px;
  --edgespark-auth-spacing: 16px;
  --edgespark-auth-input-height: 48px;
  --edgespark-auth-button-height: 48px;
  --edgespark-auth-card-width: 420px;
}

Custom UI

If the managed UI doesn't meet your needs, call client.auth APIs directly to build your own UI.

client.auth is based on better-auth client and supports all better-auth methods.

Sign In

// Email/password sign in
const result = await client.auth.signIn.email({
  email: "[email protected]",
  password: "password",
});

if (result.error) {
  console.error("Sign in failed:", result.error.message);
} else {
  console.log("Sign in successful:", result.data);
}

// OAuth sign in (redirects to third-party page)
await client.auth.signIn.social({
  provider: "google",
  callbackURL: "/dashboard",  // Redirect after success
});

// Anonymous temporary account (requires backend support)
await client.auth.signIn.anonymous();

Sign Up

const result = await client.auth.signUp.email({
  name: "John Doe",
  email: "[email protected]",
  password: "password",
  callbackURL: "/verify-email",  // Link destination in verification email
});

if (result.error) {
  console.error("Sign up failed:", result.error.message);
}

Get User Info

// Get current logged-in user's session
const session = await client.auth.getSession();

if (session.data) {
  console.log("User info:", session.data.user);
  console.log("Session:", session.data.session);
} else {
  console.log("Not logged in");
}

Sign Out

await client.auth.signOut();

Forgot Password / Reset Password

// 1. Send reset code to email
await client.auth.forgetPassword.emailOtp({
  email: "[email protected]",
});

// 2. Set new password with verification code
const result = await client.auth.emailOtp.resetPassword({
  email: "[email protected]",
  otp: "123456",
  password: "newpassword",
});

if (result.error) {
  console.error("Reset failed:", result.error.message);
}

Listen to Auth State Changes

// Listen to auth state changes (login/logout/token refresh)
const unsubscribe = client.auth.onAuthStateChange(() => {
  // Get latest session when state changes
  client.auth.getSession().then((session) => {
    if (session?.data?.user) {
      console.log("Logged in", session.data.user);
    } else {
      console.log("Logged out");
    }
  });
});

// Unsubscribe
unsubscribe();

API Module

client.api.fetch is fetch with auto-injected auth token:

// GET
const res = await client.api.fetch("/api/users");
const users = await res.json();

// POST
const res = await client.api.fetch("/api/posts", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ title: "Hello" }),
});

Security Note: Token is only injected for same-origin requests (same domain as baseUrl). Cross-origin requests will not include the token automatically.


Lifecycle & Best Practices

Check Login Status Before Rendering

Always check if user is already logged in before rendering the login UI:

const session = await client.auth.getSession();
if (session.data?.user) {
  // Already logged in, redirect to dashboard
  window.location.href = "/dashboard";
  return;
}

// Not logged in, render login UI
await client.auth.renderAuthUI(container, {
  redirectTo: "/dashboard",
});

Get User Info on Target Page

After redirect, use getSession() on the target page - this is the recommended way to get user info:

// /dashboard page
const session = await client.auth.getSession();
if (!session.data?.user) {
  window.location.href = "/login";
  return;
}
console.log("Welcome", session.data.user.name);

onLogin vs getSession vs onAuthStateChange

APIUse CaseNotes
getSession()Recommended: Get current user infoWorks everywhere, async
onLoginQuick sync ops (analytics, logging)Not called for OAuth; fires before redirect
onAuthStateChangeAdvanced: real-time auth monitoringNeed manual unsubscribe; fires on any token change

React Best Practice

// ✅ Create client outside component (singleton)
const client = createEdgeSpark({ baseUrl: "..." });

function AuthPage() {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!ref.current) return;

    // Check if already logged in
    client.auth.getSession().then((session) => {
      if (session.data?.user) {
        window.location.href = "/dashboard";
        return;
      }
      // Render login UI
      client.auth.renderAuthUI(ref.current!, {
      redirectTo: "/dashboard",
      });
    });

    // No need to destroy - renderAuthUI auto-cleans previous UI
  }, []);

  return <div ref={ref} />;
}

Cleanup

// Destroy client (cleanup UI, event listeners, etc.)
client.destroy();

// Also clear locally stored token
client.destroy({ clearToken: true });

Note: Only call destroy() when the entire app unmounts, not on component unmount.


API Reference

createEdgeSpark(options)

Creates an EdgeSpark client. Initialization is synchronous.

OptionTypeDescription
baseUrlstringBackend URL (required)
fetchCredentialsRequestCredentialsFetch credentials mode, default "include"

client.auth

Authentication client based on better-auth. Main methods:

MethodDescription
signIn.email({ email, password })Email/password sign in
signIn.social({ provider, callbackURL })OAuth sign in
signIn.anonymous()Anonymous sign in
signUp.email({ name, email, password })Email sign up
signOut()Sign out
getSession()Get current session/user info
forgetPassword.emailOtp({ email })Send password reset code
emailOtp.resetPassword({ email, otp, password })Reset password with code
renderAuthUI(container, options)Render managed login UI
onAuthStateChange(listener)Listen to token changes

client.api

MethodDescription
fetch(path, options?)Fetch with auth token

LoginUIOptions

Options for renderAuthUI:

OptionTypeDescription
redirectTostringRedirect URL after login (simple mode)
redirectsRedirectOptionsFine-grained redirect config (overrides redirectTo)
labelsPartial<ExtendedLabels>Custom labels
onLogin(user) => voidOptional: for quick sync ops like analytics (not called for OAuth)
onError(error) => voidError callback

RedirectOptions

interface RedirectOptions {
  oauth?: {
    success?: string;   // OAuth login success
    newUser?: string;   // OAuth first-time signup
    error?: string;     // OAuth error
  };
  emailPassword?: string;     // Email/password login success
  emailVerification?: string; // Email verification link destination
  anonymous?: string;         // Anonymous login success
}

Build

npm run build      # Build
npm run dev        # Run example (examples/dev)

版本与发布记录

@edgespark/client 共发布 32 个版本,首次发布于 2025/12/04;过去 12 个月发布了 32 个版本。

版本发布日期距今
1.0.4latest2026/03/187 个月前
1.0.32026/01/0610 个月前
1.0.22025/12/1710 个月前
1.0.12025/12/1610 个月前
1.0.02025/12/1610 个月前
0.0.272025/12/1610 个月前

其他发布通道:beta → 0.0.2。

关于 @edgespark/client 的常见问题

@edgespark/client 是什么?

edgespark umbrella client: auth UI/APIs plus HTTP utilities.

如何安装 @edgespark/client?

在项目目录运行 npm install @edgespark/client,或使用 pnpm add / yarn add / bun add。

@edgespark/client 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@edgespark/client 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@edgespark/client 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@edgespark/client 可以商用吗?

@edgespark/client 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@edgespark/client 的下载量有多少?

根据 npm 官方下载统计,@edgespark/client 在 2026/10/13 – 2026/11/11 这 30 天内被下载 3,076 次,日均约 103 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@edgespark/client 还在维护吗?

最新版本 1.0.4 发布于 2026/03/18(7 个月前);至今共发布 32 个版本,其中过去 12 个月发布了 32 个;首次发布于 2025/12/04。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。