@arbidocs/client

中文文档
0.5.133发布于 2026/11/07MITTypeScriptESM + CJS近 30 天下载 6.2万

TypeScript SDK for the ARBI API — zero-knowledge auth, E2E encryption, and type-safe REST client

原始使用文档
文档目录18 节
  1. Install
  2. Quick Start
  3. Examples
  4. Register + Login (Node.js)
  5. Create a Workspace and Upload a Document
  6. Subscribe to Session State Changes
  7. API Reference
  8. createArbiClient(options)
  9. Auth Methods
  10. Middleware (Auto-Applied)
  11. Standalone Exports
  12. Logger
  13. How It Works
  14. Zero-Knowledge Auth
  15. Workspace Encryption
  16. IndexedDB Storage
  17. Requirements
  18. Releases

@arbidocs/client

TypeScript client for the ARBI API — zero-knowledge auth, E2E encryption, and type-safe REST client.

Install

npm install @arbidocs/client

For Node.js environments (no browser), also install the IndexedDB polyfill:

npm install fake-indexeddb

Quick Start

import { createArbiClient } from '@arbidocs/client'

const arbi = createArbiClient({
  baseUrl: 'https://www.arbidocs.com',
  deploymentDomain: 'www.arbidocs.com',
})

// Login
const session = await arbi.auth.login({
  email: '[email protected]',
  password: 'mypassword',
})

// Type-safe API calls — auto-injects Bearer token
const { data: workspaces } = await arbi.fetch.GET('/api/user/workspaces')
console.log(workspaces) // WorkspaceResponse[]

Examples

Register + Login (Node.js)

// Polyfill IndexedDB for Node.js (must be first import)
require('fake-indexeddb/auto')
const { createArbiClient } = require('@arbidocs/client')

async function main() {
  const arbi = createArbiClient({
    baseUrl: 'https://www.arbidocs.com',
    deploymentDomain: 'www.arbidocs.com',
    credentials: 'omit', // No cookies in Node.js
  })

  // Register a new user
  const { signingPrivateKey } = await arbi.auth.register({
    email: '[email protected]',
    password: 'SecurePass123!',
    verificationCode: '...', // From /api/user/verify-email flow
    firstName: 'Alice',
    lastName: 'Smith',
  })

  // Login
  const session = await arbi.auth.login({
    email: '[email protected]',
    password: 'SecurePass123!',
  })

  console.log('Logged in as', session.userExtId)
  console.log('Token:', session.accessToken.slice(0, 20) + '...')
}

main()

Create a Workspace and Upload a Document

require('fake-indexeddb/auto')
const fs = require('fs')
const {
  createArbiClient,
  getSession,
  sealedBoxDecrypt,
  deriveEncryptionKeypairFromSigning,
  createWorkspaceKeyHeader,
} = require('@arbidocs/client')

async function main() {
  const arbi = createArbiClient({
    baseUrl: 'https://www.arbidocs.com',
    deploymentDomain: 'www.arbidocs.com',
    credentials: 'omit',
  })

  // Login
  const { accessToken, serverSessionKey } = await arbi.auth.login({
    email: '[email protected]',
    password: 'SecurePass123!',
  })

  // Create workspace
  const { data: workspace } = await arbi.fetch.POST('/api/workspace/create_protected', {
    body: {
      name: 'My Case Files',
      description: 'Documents for Case #1234',
      is_public: false,
    },
  })

  // Decrypt the workspace key and generate the auth header.
  // The server wraps the workspace symmetric key with your public key
  // during creation. You unwrap it here to prove you own the workspace.
  const session = await getSession()
  const pubKey = session.signingPrivateKey.slice(32, 64)
  const encKP = deriveEncryptionKeypairFromSigning({
    publicKey: pubKey,
    secretKey: session.signingPrivateKey,
  })
  const workspaceKey = sealedBoxDecrypt(workspace.wrapped_key, encKP.secretKey)
  const wsHeader = await createWorkspaceKeyHeader(workspaceKey, serverSessionKey)

  // Tell the SDK which workspace is active (middleware auto-injects the header)
  arbi.session.setSelectedWorkspace(workspace.external_id)
  arbi.session.setCachedWorkspaceHeader(workspace.external_id, wsHeader)

  // Upload a PDF (multipart — use raw fetch since openapi-fetch
  // doesn't handle multipart/form-data)
  const formData = new FormData()
  const pdf = fs.readFileSync('./contract.pdf')
  formData.append('files', new Blob([pdf], { type: 'application/pdf' }), 'contract.pdf')

  const uploadRes = await fetch(
    `https://www.arbidocs.com/api/document/upload?workspace_ext_id=${workspace.external_id}`,
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${accessToken}`,
        'workspace-key': wsHeader,
      },
      body: formData,
    }
  )

  const { doc_ext_ids } = await uploadRes.json()
  console.log('Uploaded document:', doc_ext_ids[0])

  // List documents (workspace-key header injected automatically by middleware)
  const { data: docs } = await arbi.fetch.GET(
    '/api/workspace/{workspace_ext_id}/documents',
    { params: { path: { workspace_ext_id: workspace.external_id } } }
  )

  for (const doc of docs) {
    console.log(`  ${doc.file_name} — ${doc.status}`)
  }
}

main()

Subscribe to Session State Changes

The SDK's SessionManager supports a subscribe pattern for reactive updates:

const arbi = createArbiClient({ baseUrl, deploymentDomain })

// React to session changes (useful for custom UI frameworks)
const unsubscribe = arbi.session.subscribe((state) => {
  console.log('Session changed:', {
    loggedIn: !!state.accessToken,
    email: state.userEmail,
    workspace: state.selectedWorkspaceId,
  })
})

// Later: stop listening
unsubscribe()

API Reference

createArbiClient(options)

Creates an SDK client instance.

interface ArbiClientOptions {
  baseUrl: string              // ARBI API URL (e.g. "https://www.arbidocs.com")
  deploymentDomain: string     // Used for deterministic key derivation
  credentials?: RequestCredentials  // Default: 'include'. Use 'omit' for Node.js
  session?: SessionManager           // The host's session state; default: in-memory
  persistence?: SessionPersistence   // Keys between page loads; default: encrypted IndexedDB
  sso?: SsoTokenSource | null        // The SSO provider's token, asked only in SSO mode
  middleware?: Middleware[]          // Host middleware, run before the re-login middleware
  reloginExcludePatterns?: string[]  // Paths whose 401 never re-logs in
  onReloginSuccess?: (data) => void
  onRetryCloneFailed?: () => void
}

The client owns the session recovery: a 401 re-logs in once for every request that failed (single-flight, a 5s cooldown), a 400 "No active workspace" re-opens the workspace, and a re-login the server refused ends the session (session.clear(), then auth.onSessionExpired listeners). A login still finishing or an unreachable server leaves the session alone.

Returns an ArbiClient with these namespaces:

NamespaceDescription
arbi.fetchType-safe openapi-fetch client with auto-auth middleware
arbi.sessionIn-memory session state (token, user, workspace)
arbi.authHigh-level auth: register, login, loginWithKey, logout, changePassword, relogin
arbi.cryptoCrypto utilities: key derivation, signing, encryption, base64
arbi.storageIndexedDB session persistence: getSession, saveSession

Auth Methods

// Register
await arbi.auth.register({ email, password, verificationCode, firstName?, lastName? })

// Login with password
await arbi.auth.login({ email, password })

// Login with recovery key (Uint8Array)
await arbi.auth.loginWithKey({ email, signingPrivateKey })

// Change password
await arbi.auth.changePassword({ email, currentPassword, newPassword })

// Logout (clears session + IndexedDB)
await arbi.auth.logout()

// Re-login using stored private key (auto-called by middleware on 401)
await arbi.auth.relogin()

// "The session could not be recovered": show the sign-in screen
const off = arbi.auth.onSessionExpired(() => showSignIn())

// Seal every workspace key to this server session (before deriving an MCP session from it)
await arbi.auth.openAllWorkspaces()

Middleware (Auto-Applied)

The arbi.fetch client has three middleware layers applied automatically:

  1. Bearer Auth — injects Authorization: Bearer <token> on every request
  2. Workspace Key — injects workspace-key header for workspace-scoped endpoints
  3. Auto-Relogin — on 401, re-derives credentials from stored key and retries

Standalone Exports

All building blocks are individually importable for advanced use:

import {
  // Crypto primitives
  initSodium, signMessage, sealedBoxDecrypt, sealedBoxEncrypt,
  deriveEncryptionKeypairFromSigning, createWorkspaceKeyHeader,
  generateUserKeypairs, generateLoginCredentials,
  encryptMessage, decryptMessage,
  base64Encode, base64Decode, base64ToBytes, bytesToBase64,

  // Storage
  getSession, saveSession, clearAllData, hasSession,

  // Middleware (compose your own client)
  createBearerAuthMiddleware, createWorkspaceKeyMiddleware, createAutoReloginMiddleware,

  // Relogin handler
  createReloginHandler,

  // Session manager
  createSessionManager,

  // OpenAPI schema types
  type paths, type components, type operations,
} from '@arbidocs/client'

Logger

import { configureLogger, logger } from '@arbidocs/client'

configureLogger({ verbose: import.meta.env.DEV }) // once, at startup
logger.error('always printed')
logger.warn('printed only when verbose') // likewise info, debug

How It Works

Zero-Knowledge Auth

ARBI uses Ed25519 signatures for authentication. Your password never leaves the client:

  1. Key derivation: Argon2id(email + password + deploymentDomain) → Ed25519 seed → signing keypair
  2. Registration: Public key sent to server, private key stays local
  3. Login: Client signs a timestamp, server verifies the signature
  4. Session: Server returns an encrypted session key for workspace operations

Workspace Encryption

Each workspace has a symmetric key (NaCl SecretBox). The key is:

  • Generated server-side on workspace creation
  • Wrapped with your X25519 public key (derived from your Ed25519 signing key)
  • Unwrapped client-side using sealedBoxDecrypt
  • Used to generate per-request workspace-key headers (HMAC proof of key possession)

IndexedDB Storage

The SDK stores the signing private key and session key in IndexedDB, encrypted with a non-extractable AES-GCM key from the Web Crypto API. In Node.js, use fake-indexeddb as a polyfill.

Requirements

  • Browser: Any modern browser (Chrome, Firefox, Safari, Edge)
  • Node.js: v18+ (needs fetch, FormData, Blob globals). Install fake-indexeddb for IndexedDB.
  • TypeScript: 5.0+ (optional — works with plain JS too)

Releases

Release notes for every version are on GitHub releases.

版本与发布记录

@arbidocs/client 共发布 480 个版本,首次发布于 2026/02/18;过去 12 个月发布了 480 个版本。

版本发布日期距今
0.5.133latest2026/11/071 天前
0.5.1322026/11/061 天前
0.5.1312026/11/061 天前
0.5.1302026/11/052 天前
0.5.1292026/11/052 天前
0.5.1282026/11/052 天前

关于 @arbidocs/client 的常见问题

@arbidocs/client 是什么?

TypeScript SDK for the ARBI API — zero-knowledge auth, E2E encryption, and type-safe REST client

如何安装 @arbidocs/client?

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

@arbidocs/client 支持 TypeScript 吗?

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

@arbidocs/client 支持 ESM 还是 CommonJS?

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

@arbidocs/client 可以商用吗?

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

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

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

@arbidocs/client 还在维护吗?

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