@classic-homes/auth

中文文档
1.47.0发布于 2026/11/10MITTypeScriptES Module零依赖近 30 天下载 1736

经典主题应用程序的认证服务和 Svelte 绑定

中文使用文档
文档目录34 节
  1. 特征
  2. 安装
  3. 快速启动
  4. 1. 初始化认证
  5. 2. 登录自动设定 Auth
  6. 3. 使用 Auth 存储器( Svelte)
  7. 4. 包含重定向 URL 的 SSO 登录
  8. 5. 外交部挑战核查
  9. 6. 保护路线
  10. 作用
  11. 显示一个角色
  12. 管理另一个用户的角色
  13. 托肯刷新
  14. API 错误和访问调试
  15. 访问探测器
  16. API 参考
  17. 核心出口
  18. Svelte 导出
  19. 配置选项
  20. Auth 动作
  21. Auth 存储状态
  22. 使用时使用@ classic- homes/ some- svelte
  23. 自动刷新
  24. 测试公用事业
  25. 安装
  26. 快速启动
  27. 测试出口
  28. 雾取
  29. Mock Auth 存储器
  30. 预定义情景
  31. 自定义请求
  32. 孤立的试验背景
  33. 用户固定
  34. 许可证

@classic-homes/ auth (英语)

为经典主题设计系统配有斯维尔特捆绑的框架-不可知认证核心.

特征

  • 基于 JWT 并自动刷新令牌的认证
  • 可配置提供者的SSO(单一签名)支持和登录
  • 多要素认证(MFA/TOTP)支持类型守护
  • 成功登录时自动设置认证状态
  • 插件存储适配器( localStorage, sessionStorage, 或自定义)
  • 认证状态的 Svelte 反应存储
  • 保护页面的路由守卫
  • TypeScript 第一种具有完全类型安全性

安装

npm install @classic-homes/auth

快速启动

1. 初始化认证

在您的应用程序的切入点( 例如 ), hooks.client.ts 用于斯维尔特基特:

import { initAuth } from '@classic-homes/auth';
import { goto } from '$app/navigation';
import { base } from '$app/paths';

initAuth({
  baseUrl: 'https://api.example.com',
  storage: {
    getItem: (key) => localStorage.getItem(key),
    setItem: (key, value) => localStorage.setItem(key, value),
    removeItem: (key) => localStorage.removeItem(key),
  },
  // ⚠️ SECURITY: localStorage (also the default when `storage` is omitted)
  // persists the long-lived refresh token where any XSS can read it.
  // Safer options exported from '@classic-homes/auth/core':
  //   storage: createSessionStorage()  // per-tab, cleared on close
  //   storage: createMemoryStorage()   // never persisted, lost on reload
  // or keep tokens out of JS entirely with an httpOnly-cookie flow.
  // SSO configuration (optional)
  sso: {
    enabled: true,
    provider: 'authentik',
  },
  // Callback when auth errors occur
  onAuthError: (error) => {
    console.error('Auth error:', error);
  },
  // Callback when tokens are refreshed
  onTokenRefresh: (tokens) => {
    console.log('Tokens refreshed');
  },
  // Callback when user is logged out
  onLogout: () => {
    goto(`${base}/auth/login`);
  },
});

2. 登录自动设定 Auth

这个 authService.login() 方法在成功登录时自动设置认证状态 :

import {
  authService,
  isMfaChallengeResponse,
  getMfaToken,
  getAvailableMethods,
} from '@classic-homes/auth/core';
import { goto } from '$app/navigation';

async function handleLogin(email: string, password: string) {
  const response = await authService.login({
    username: email,
    password: password,
  });

  // Check if MFA is required
  if (isMfaChallengeResponse(response)) {
    const mfaToken = getMfaToken(response);
    const methods = getAvailableMethods(response);
    // Redirect to MFA challenge page
    goto(`/auth/mfa-challenge?token=${mfaToken}&methods=${methods.join(',')}`);
    return;
  }

  // Auth state is automatically set - redirect to dashboard
  goto('/dashboard');
}

Email-MFA账户通过电子邮件获得代码: 发送它与挑战符, 然后与 method: 'email'冷却(一分钟一个代码,五分钟一个) 小时)抛出 MFAChallengeEmailError 与 retryAfterSeconds。 。 。 .

import { authService, MFAChallengeEmailError } from '@classic-homes/auth';

try {
  const { email } = await authService.sendMFAChallengeEmail(mfaToken); // "jo***@example.com"
} catch (error) {
  if (error instanceof MFAChallengeEmailError && error.retryAfterSeconds) {
    // Show "Resend in Ns"
  }
}

await authService.verifyMFAChallenge({ mfaToken, code, method: 'email' });

要禁用自动设置认证( 用于手动控制) :

const response = await authService.login(credentials, { autoSetAuth: false });
// Manually set auth state
authActions.setAuth(response.accessToken, response.refreshToken, response.user);

3. 使用 Auth 存储器( Svelte)

<script lang="ts">
  import { authStore, authActions, isAuthenticated, currentUser } from '@classic-homes/auth/svelte';

  // Using derived stores
  // $isAuthenticated - boolean
  // $currentUser - User | null

  async function handleLogout() {
    // SSO-aware logout
    const result = await authActions.logoutWithSSO();
    if (result.ssoLogoutUrl) {
      // Redirect to SSO provider logout
      window.location.href = result.ssoLogoutUrl;
    } else {
      goto('/auth/login');
    }
  }
</script>

{#if $isAuthenticated}
  <p>Welcome, {$currentUser?.firstName}</p>
  <button onclick={handleLogout}>Logout</button>
{:else}
  <a href="/auth/login">Login</a>
{/if}

4. 包含重定向 URL 的 SSO 登录

import { authService } from '@classic-homes/auth/core';

function handleSSOLogin(redirectUrl: string) {
  // Specify where to redirect after SSO callback
  authService.initiateSSOLogin({
    callbackUrl: `${window.location.origin}/auth/sso-callback`,
    redirectUrl: redirectUrl, // Final destination after auth
  });
}

initiateSSOLogin 也可以随机发送 client_state,保留在这个标签的 sessionStorage(单用,10分钟TTL). API在回电中回响 散列( 散列)client_state- 检查 这个 在此之前 存储散列中的任何符号,所以一个精心设计的回调 URL 无法 将用户签入他人账户( 登录 CSRF ):

import { verifySsoClientState } from '@classic-homes/auth/core';

const params = new URLSearchParams(window.location.hash.slice(1));
const { accepted, result } = verifySsoClientState(params); // 'ok' | 'mismatch' | 'missing'
if (!accepted) {
  window.history.replaceState({}, '', window.location.pathname);
  throw new Error('This sign-in link was not started from this browser tab. Please sign in again.');
}

设定 sso.requireClientState: true 一旦API回响 client_state 实值 部署在那个环境中 在此之前(默认),每个结果都是 已接受,所以日志 result 以观看推出.

5. 外交部挑战核查

import { authService } from '@classic-homes/auth/core';

async function handleMFAVerify(mfaToken: string, code: string, trustDevice: boolean) {
  // Auto-sets auth state on success
  const response = await authService.verifyMFAChallenge({
    mfaToken,
    code,
    method: 'totp',
    trustDevice,
  });

  // Auth state is automatically set - redirect to dashboard
  goto('/dashboard');
}

6. 保护路线

// src/routes/dashboard/+page.ts
import { checkAuth, requireRole } from '@classic-homes/auth/svelte';
import { redirect } from '@sveltejs/kit';
import { browser } from '$app/environment';

export function load({ url }) {
  if (browser) {
    const result = checkAuth();
    if (!result.allowed) {
      throw redirect(302, `/auth/login?redirect=${encodeURIComponent(url.pathname)}`);
    }
  }
  return {};
}

// For role-based access:
export function load({ url }) {
  if (browser) {
    const result = checkAuth({ roles: ['admin', 'manager'] });
    if (!result.allowed) {
      if (result.reason === 'not_authenticated') {
        throw redirect(302, `/auth/login?redirect=${encodeURIComponent(url.pathname)}`);
      }
      if (result.reason === 'missing_role') {
        throw redirect(302, '/unauthorized');
      }
    }
  }
  return {};
}

作用

用户持有 设定 作用问题user.roles) (中文(简体) ). 他们的许可是工会 因此用户的访问比任何单一的角色都要广泛.

import { authStore } from '@classic-homes/auth/svelte';

// A manager who is also an agent
authStore.user?.roles; // ['manager', 'agent']

authStore.hasRole('manager'); // true
authStore.hasRole('agent'); // true — secondary roles count
authStore.hasAnyRole(['admin', 'agent']); // true
authStore.hasAllRoles(['manager', 'agent']); // true

角色检查是准确的成员. 持有 admin 并不意味着 agent数字 :

authStore.user?.roles; // ['admin']
authStore.hasRole('agent'); // false

这个镜像了API,所以UI不能提供服务器拒绝的东西. 也让管理员通过,列出你接受的角色: checkAuth({ roles: ['admin', 'agent'] })。 。 。 .

显示一个角色

computePrimaryRole 解决用于显示的最高级别角色。 这是一个标签, 不是一种能力,绝不是任何一种能力:

import { computePrimaryRole, getEffectiveLevel } from '@classic-homes/auth/core';

computePrimaryRole(['user', 'admin']); // 'admin'
computePrimaryRole([]); // 'guest'
getEffectiveLevel(['user', 'manager']); // 3

// Or format the whole set
import { formatUserRoles } from '@classic-homes/auth/core';
formatUserRoles(user); // 'Manager, Agent'
formatUserRoles(user, { max: 1 }); // 'Manager +1 more'

管理另一个用户的角色

要求数 users:read 列表和 users:update 改为:

import { authApi } from '@classic-homes/auth/core';

const { roles, primaryRole } = await authApi.getUserRoles(userId);

await authApi.grantUserRole(userId, 'agent'); // idempotent
await authApi.revokeUserRole(userId, 'agent');
await authApi.setUserRoles(userId, ['manager', 'agent']); // replaces the set

围绕设计的两个服务器规则:

  • 角色变化不影响该用户的活会话. 权限是 在铸造一个符号时计算,所以补助金在他们的下个符号上生效 刷新——不是他们的下个请求.
  • 服务器拒绝删除用户的 最后一个 作用,或授予在 高于代理用户自己的级别。 两者都是错误的.

托肯刷新

访问令牌是短寿命并刷新令牌旋转(单用),所以 a 刷新永远不能为同一目的运行两次. refreshSession() 是那个 刷新路径——客户端自己的401重试使用它,任何代码也应该使用 主动刷新 :

import { refreshSession } from '@classic-homes/auth/core';

// Before a route guard reads claims (roles/permissions live in the JWT):
const outcome = await refreshSession({ onlyIfExpired: true });
// 'fresh'       — token still valid, nothing sent
// 'refreshed'   — tokens stored and the Svelte store already updated
// 'rejected'    — session cleared (handleSessionExpired/onSessionExpired/onLogout ran)
// 'unavailable' — server unreachable/5xx even after retries; session kept, try later

保证:每分页单飞(目前呼叫者共用一个呼叫), 以 Web 锁定在标签间序列化, 如果旋转了另一个标签, 则调节 首先 短暂的失败 refreshRetryDelays 备份 (默认) [1000, 3000]) (中文(简体) ). 不要打电话 authApi.refreshToken + 键 updateStoredTokens 绕过上面所有的东西 并让一个旋转的孤儿 符号 .

打电话 initCrossTabSync() 一旦在应用中开始这样一个刷新或签出另一个 选项卡将这个标签的斯维尔特商店补水.

API 错误和访问调试

非-2xx的答复 apiRequest 编号 : api.* 丢出一个 ApiError。 。 message 是服务器的消息(如以前),加 status,《宪章》 code, (中文(简体) ), details, (中文(简体) ), method 财务报告和审定财务报表 endpoint数字 :

import { isApiError } from '@classic-homes/auth/core';

try {
  await api.get('/homes', true);
} catch (e) {
  if (isApiError(e) && e.status === 403) {
    console.log(e.details?.required); // e.g. ['homes:read']
  }
}

访问探测器

钩子,比如搜索器 他们是惰性的 直到安装,在服务器上什么都不做.

  • setAccessProbe(fn) 收到每个访问决定:核心 hasRole*存储 has*, (中文(简体) ), checkAuth (单位:千美元)source: 'route', (中文(简体) ), filterByAccess编号 :canAccess (单位:千美元)'nav')和API 403s (英语:'http',只有路径,没有查询字符串). 每次活动都有要求, allowed 和什么情况 missing。 。 。 .
  • 在安装探测器时, 请求 API 源头( 从来没有其他主机) 发送 X-Access-Debug: 1。CHAPI 回复管理会话 X-Access-Redacted: taxId=vendors:read:sensitive, notes=*,作为 资料 source: 'redacted' 活动(allowed: true,只有路径) fields: { name, permission }[]· ; * 表示没有配置权限 (默认-否定). 它反映了真正的象征, 从来没有"视同"。 没有头,没有头 活动。 自定义头条使跨源 API 请求在飞行前完成 .
  • setAccessSubject(userId, { roles, permissions }) 将 UI 预览为另一个 角色集 (“ 视图为” ) 仅针对该用户。 其他用户的检查是 未受影响, authStore.user 从未更改,预览结束于任意 登录、登录或会话过期。 签入角色标注( E)allowedRoles总是 使用真正的角色.
  • evaluateAccess(subject, requirement) 是共享的任意/所有决定 checkAuth 用途; checkAuth 成果现在包括 missing 拒绝.

这些改变只涉及UI的决定. API 授权每个请求与真实 符号,所以预览永远不能允许访问 .

API 参考

核心出口

import {
  // Initialization
  initAuth,
  getConfig,
  isInitialized,

  // Service
  authService,
  AuthService,

  // API
  authApi,

  // MFA Guards
  isMfaChallengeResponse,
  isLoginSuccessResponse,
  getMfaToken,
  getAvailableMethods,

  // JWT Utilities
  decodeJWT,
  isTokenExpired,
  getTokenRemainingTime,

  // Roles
  ROLE_NAMES,
  ROLE_HIERARCHY,
  getUserRoles,
  computePrimaryRole,
  getEffectiveLevel,
  hasRole,
  hasAnyRole,
  hasAllRoles,

  // Errors
  ApiError,
  isApiError,

  // Access probe (see "API errors and access debugging")
  setAccessProbe,
  setAccessSubject,
  clearAccessSubject,
  evaluateAccess,

  // Types
  type User,
  type UserRolesResponse,
  type RoleName,
  type AuthState,
  type LoginCredentials,
  type LoginResponse,
  type LogoutResponse,
  type RegisterData,
  type AuthConfig,
  type LoginOptions,
  type MFAVerifyOptions,
} from '@classic-homes/auth/core';

Svelte 导出

import {
  // Store
  authStore,
  isAuthenticated,
  currentUser,

  // Actions
  authActions,

  // Guards
  checkAuth,
  createAuthGuard,
  requireAuth,
  requireRole,
  requirePermission,
  protectedLoad,
} from '@classic-homes/auth/svelte';

配置选项

interface AuthConfig {
  /** Base URL for the auth API */
  baseUrl: string;

  /** Custom fetch implementation (useful for SSR or testing) */
  fetch?: typeof fetch;

  /** Storage adapter for token persistence */
  storage?: StorageAdapter;

  /** Storage key prefix for auth data */
  storageKey?: string;

  /** SSO configuration */
  sso?: {
    enabled: boolean;
    provider: string;
    authorizeUrl?: string;
    /** Reject SSO callbacks whose client_state is missing or wrong (default false) */
    requireClientState?: boolean;
  };

  /** Callback when auth errors occur */
  onAuthError?: (error: Error) => void;

  /** Callback when tokens are refreshed */
  onTokenRefresh?: (tokens: { accessToken: string; refreshToken: string }) => void;

  /** Callback when user is logged out */
  onLogout?: () => void;
}

Auth 动作

这个 authActions 对象提供认证操作的方法:

// Set auth data after login
authActions.setAuth(accessToken, refreshToken, user, sessionToken);

// Update tokens after refresh
authActions.updateTokens(accessToken, refreshToken);

// Update user profile
authActions.updateUser(user);

// Clear auth state (local logout)
authActions.logout();

// SSO-aware logout (calls API, returns SSO logout URL if applicable)
const result = await authActions.logoutWithSSO();
if (result.ssoLogoutUrl) {
  window.location.href = result.ssoLogoutUrl;
}

// Permission and role checks
authActions.hasPermission('users:read');
authActions.hasRole('admin');
authActions.hasAnyRole(['admin', 'manager']);
authActions.hasAllRoles(['admin', 'manager']);
authActions.hasAnyPermission(['users:read', 'users:write']);
authActions.hasAllPermissions(['users:read', 'users:write']);

// Reload auth from storage
authActions.rehydrate();

Auth 存储状态

interface AuthState {
  accessToken: string | null;
  refreshToken: string | null;
  user: User | null;
  isAuthenticated: boolean;
}

使用时使用@ classic- homes/ some- svelte

认证软件包与窗体验证结合 @classic-homes/theme-svelte数字 :

<script lang="ts">
  import { useForm, loginSchema } from '@classic-homes/theme-svelte';
  import { authService, isMfaChallengeResponse, getMfaToken } from '@classic-homes/auth/core';
  import { goto } from '$app/navigation';

  const form = useForm({
    schema: loginSchema,
    initialValues: {
      email: '',
      password: '',
      rememberMe: false,
    },
    onSubmit: async (data) => {
      const response = await authService.login({
        username: data.email,
        password: data.password,
      });

      if (isMfaChallengeResponse(response)) {
        const mfaToken = getMfaToken(response);
        goto(`/auth/mfa-challenge?token=${mfaToken}`);
        return;
      }

      // Auth state automatically set
      goto('/dashboard');
    },
  });
</script>

<form onsubmit={form.handleSubmit}>
  <input bind:value={form.data.email} />
  {#if form.errors.email}
    <span class="error">{form.errors.email}</span>
  {/if}
  <!-- ... -->
</form>

自动刷新

当 :

  • API 请求返回 401 未授权
  • 刷新令牌是有效的

Svelte 商店在刷新符号时会自动更新, 因此您的 UI 保持同步 .

测试公用事业

认证包包括用于单位的综合测试设施和整合测试.

安装

# The testing utilities are included in the main package
npm install @classic-homes/auth

快速启动

import { describe, it, beforeEach, afterEach, expect } from 'vitest';
import {
  setupTestAuth,
  mockUser,
  configureMFAFlow,
  assertAuthenticated,
} from '@classic-homes/auth/testing';
import { authService, isMfaChallengeResponse } from '@classic-homes/auth/core';

describe('Login Flow', () => {
  let cleanup: () => void;
  let mockFetch;

  beforeEach(() => {
    const ctx = setupTestAuth();
    cleanup = ctx.cleanup;
    mockFetch = ctx.mockFetch;
  });

  afterEach(() => cleanup());

  it('handles successful login', async () => {
    const response = await authService.login({
      username: '[email protected]',
      password: 'password',
    });

    expect(response.user).toMatchObject(mockUser);
    mockFetch.assertCalled('/auth/login');
  });

  it('handles MFA flow', async () => {
    configureMFAFlow(mockFetch);

    const response = await authService.login({
      username: '[email protected]',
      password: 'password',
    });

    expect(isMfaChallengeResponse(response)).toBe(true);
  });
});

测试出口

import {
  // Fixtures - Pre-defined test data
  mockUser,
  mockAdminUser,
  mockSSOUser,
  mockMFAUser,
  mockAccessToken,
  mockRefreshToken,
  mockLoginSuccess,
  mockMFARequired,
  createMockUser,
  createMockTokenPair,
  createMockLoginSuccess,

  // Mocks - Test doubles for dependencies
  MockStorageAdapter,
  MockFetchInstance,
  MockAuthStore,
  createMockStorage,
  createMockFetch,
  createMockAuthStore,

  // Setup Helpers
  setupTestAuth,
  createTestAuthHelpers,
  quickSetupAuth,
  withTestAuth,

  // State Simulation
  authScenarios,
  applyScenario,
  configureMFAFlow,
  configureTokenRefresh,
  configureSSOLogout,
  simulateLogin,
  simulateLogout,

  // Assertions
  assertAuthenticated,
  assertUnauthenticated,
  assertHasPermissions,
  assertHasRoles,
  assertTokenValid,
  assertApiCalled,
  assertStoreMethodCalled,
  assertRequiresMFA,
} from '@classic-homes/auth/testing';

雾取

这个 MockFetchInstance 提供带有预先定义的认证路径的可配置的模拟获取 :

const ctx = setupTestAuth();
const { mockFetch } = ctx;

// Default routes are pre-configured for all auth endpoints

// Customize responses
mockFetch.requireMFA(); // Login requires MFA
mockFetch.failLogin('Invalid credentials'); // Login fails
mockFetch.enableSSOLogout(); // Logout returns SSO URL

// Add custom routes
mockFetch.addRoute({
  method: 'GET',
  path: '/custom/endpoint',
  response: { data: 'custom response' },
});

// Fail specific endpoints
mockFetch.failEndpoint('GET', '/auth/profile', 403, 'Forbidden');

// Check call history
expect(mockFetch.wasCalled('/auth/login')).toBe(true);
mockFetch.assertCalled('/auth/profile');
mockFetch.assertNotCalled('/auth/logout');

Mock Auth 存储器

这个 MockAuthStore 模仿 Svelte 认证商店 :

const store = createMockAuthStore();

// Simulate states
store.simulateAuthenticated(mockAdminUser);
store.simulateUnauthenticated();

// Direct state manipulation
store.setState({ isAuthenticated: true, user: mockUser });

// Check method calls
store.assertMethodCalled('setAuth');
store.assertMethodNotCalled('logout');

// Get call history
const calls = store.getCallsFor('setAuth');

预定义情景

应用常见的认证方案进行测试 :

import { authScenarios, applyScenario } from '@classic-homes/auth/testing';

// Available scenarios:
// - 'unauthenticated'
// - 'authenticated'
// - 'admin'
// - 'ssoUser'
// - 'mfaEnabled'
// - 'unverifiedEmail'
// - 'inactive'
// - 'expiredToken'

const store = createMockAuthStore();
applyScenario(store, 'admin');
expect(store.user?.roles).toContain('admin');

自定义请求

使用内置断言进行常见检查:

import {
  assertAuthenticated,
  assertHasPermissions,
  assertTokenValid,
  assertApiCalled,
  assertRequiresMFA,
} from '@classic-homes/auth/testing';

// Auth state assertions
assertAuthenticated(store.getState());
assertUnauthenticated(store.getState());

// Permission assertions
assertHasPermissions(user, ['read:profile', 'write:profile']);
assertHasRoles(user, ['admin']);

// Token assertions
assertTokenValid(accessToken);
assertTokenExpired(oldToken);

// API call assertions
assertApiCalled(mockFetch, 'POST', '/auth/login', {
  times: 1,
  body: { username: 'test', password: 'pass' },
});

// MFA assertions
assertRequiresMFA(loginResponse);
assertNoMFARequired(loginResponse);

孤立的试验背景

在孤立的认证背景中运行测试 :

import { withTestAuth } from '@classic-homes/auth/testing';

// Automatic setup and cleanup
await withTestAuth(async ({ mockFetch, mockStore }) => {
  mockFetch.requireMFA();
  const response = await authService.login({ username: 'test', password: 'pass' });
  expect(response.requiresMFA).toBe(true);
});

用户固定

创建自定义测试用户 :

import {
  mockUser,
  mockAdminUser,
  createMockUser,
  createMockUserWithRoles,
} from '@classic-homes/auth/testing';

// Use pre-defined users
expect(mockUser.roles).toEqual(['user']);
expect(mockAdminUser.permissions).toContain('manage:system');

// Create custom users
const customUser = createMockUser({
  email: '[email protected]',
  firstName: 'Custom',
});

// Create users with specific RBAC
const managerUser = createMockUserWithRoles(['manager', 'user'], ['read:reports', 'write:reports']);

许可证

麻省理工学院

版本与发布记录

@classic-homes/auth 共发布 76 个版本,首次发布于 2026/01/09;过去 12 个月发布了 76 个版本。

版本发布日期距今
1.47.0latest2026/11/10今天
1.46.02026/11/073 天前
1.45.02026/11/073 天前
1.42.12026/11/055 天前
1.42.02026/11/055 天前
1.41.02026/11/055 天前

关于 @classic-homes/auth 的常见问题

@classic-homes/auth 是什么?

经典主题应用程序的认证服务和 Svelte 绑定

如何安装 @classic-homes/auth?

在项目目录运行 npm install @classic-homes/auth,或使用 pnpm add / yarn add / bun add。

@classic-homes/auth 支持 TypeScript 吗?

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

@classic-homes/auth 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@classic-homes/auth 可以商用吗?

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

@classic-homes/auth 的下载量有多少?

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

@classic-homes/auth 还在维护吗?

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