# @datafn/client 中文文档

规范地址：https://npmcn.com/package/%40datafn/client/

版本：0\.1\.2
npm 元数据更新时间：2026-11-10T21:06:18.426Z
中文译文更新时间：2026-11-11T14:36:31.994Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40datafn/client
原文对照：https://npmcn.com/package/%40datafn/client/?lang=original
项目主页：https://github.com/21nCo/superfunctions#readme
源码仓库：https://github.com/21nCo/superfunctions

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：2.5 MB，8 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- 运行依赖：3 个
- 首次发布：2026-01-24T14:34:03.094Z
- 最新版本发布：2026-11-10T21:06:17.663Z
- 近 30 天下载量：2401（2026-10-12 至 2026-11-10，npm 官方下载统计）
- 已发布版本数：6（近 12 个月 6 个）

## 中文简介

DataFn 有事件总线和变异支持的客户端

## 安装

```sh
npm install @datafn/client
```

也可以使用：`pnpm add @datafn/client`、`yarn add @datafn/client`、`bun add @datafn/client`

## README

# @ datafn/ 客户端

DataFn 的离线第一反应客户端. 提供流利表和 KV API, 用于 UI 绑定的反应信号, 浏览器拥有的索引DB 存储, 本地支持的核心数据存储通过 `@datafn/swift-bridge`,双向同步,一个事件总线,交易,插件,以及多用户数据隔离.

## 安装

```bash
npm install @datafn/client @datafn/core
```

对于想要斯威夫特拥有的持久和同步的苹果Webview主机:

```bash
npm install @datafn/client @datafn/core @datafn/swift-bridge
```

## 地形矩阵

| 地形学 | 地方持久性 | 远距离持久性 | 同步所有者 |
| --- | --- | --- | --- |
| 浏览器拥有 | 索引数据库 | DataFn 服务器 | Java 脚本 |
| 本地支持的数据Fn 服务器 | 核心数据 | DataFn 服务器 | 挥发 |
| 本地备份 CloudKit | 核心数据 | CloudKit 私人数据库 | 挥发 |

原生后盾模式是明确的. 如果桥没了,它不会悄悄倒回索引数据库.

## 特征

| 特性 | 说明 |
| --- | --- |
| **流利的表格 API** | `client.table("resource")` ——范围查询,变异,信号 |
| **反应信号** | 当数据变化时自动更新的实时查询 |
| **KV 存储** | 包含信号支持的内置密钥值 API(`client.kv`(中文(简体) ) |
| **离线存储** | 有基于更动日志的离线变异的索引DB和内存适配器 |
| **同步** | 克隆、拉、推、克隆、调和——与水分计划 |
| **仅线下模式** | `sync.mode: "local-only"` - 不需要服务器 |
| **活动总线** | 用于突变和同步生命周期的全球事件流 |
| **交易** | 跨资源原子多步骤行动 |
| **插件系统** | 截取询问、突变和自定义逻辑同步 |
| **日期编码c** | 日期字段的自动序列化/分页 |
| **多用户隔离** | 用户索引数据库 `authContext` + 储存厂 |
| **扩展适配器** | 浏览器扩展支持通过 `remoteAdapter` 有远程订阅 |
| **类型安全** | 从您的计划中得出完整的脚本推论 |

* * *

## 快速启动

```typescript
import { createDatafnClient, IndexedDbStorageAdapter } from "@datafn/client";
import type { DatafnSchema } from "@datafn/core";

const schema: DatafnSchema = {
  resources: [
    {
      name: "tasks",
      version: 1,
      fields: [
        { name: "id", type: "string", required: true, unique: true },
        { name: "title", type: "string", required: true },
        { name: "completed", type: "boolean", required: true, default: false },
      ],
    },
  ],
};

const client = createDatafnClient({
  schema,
  clientId: "device-" + crypto.randomUUID(),
  storage: new IndexedDbStorageAdapter("my-app-db"),
  sync: {
    offlinability: true,
    remote: "http://localhost:3000/datafn",
  },
});

// Start sync (clone + pull + push engine)
await client.sync.start();

// Insert a record
await client.table("tasks").mutate({
  operation: "insert",
  record: { title: "Hello DataFn", completed: false },
});

// Create a reactive signal
const signal = client.table("tasks").signal({
  filters: { completed: false },
  sort: ["-createdAt"],
});

signal.subscribe((result) => {
  console.log("Active tasks:", result.data);
});
```

## 本地支持的苹果网络视频模式

当同一网页应用程序被嵌入 SwiftUI 主机中时, 配置客户端 :

```typescript
import { createDatafnClient } from "@datafn/client";
import {
  createNativeBackedRemoteAdapter,
  createNativeBackedSearchProvider,
  createNativeBackedStorageAdapter,
  createNativeSyncController,
  createWKWebViewBridgeBus,
} from "@datafn/swift-bridge";

const bus = createWKWebViewBridgeBus({ handlerName: "datafn" });

const client = createDatafnClient({
  schema,
  clientId: "apple-webview-device",
  namespace: "org-1:user-1",
  storage: createNativeBackedStorageAdapter(bus),
  searchProvider: createNativeBackedSearchProvider(bus),
  sync: {
    owner: "native",
    mode: "sync",
    offlinability: true,
    remoteAdapter: createNativeBackedRemoteAdapter(bus),
    native: {
      syncController: createNativeSyncController(bus),
      remoteMode: "datafn-server",
      expectedSchemaHash: "todo-app-example-v1",
      failIfUnavailable: true,
      remoteProfile: "default",
    },
  },
});
```

对于 CloudKit 支持的个人应用程序, 更改 `remoteMode` 改为 `"icloud"`。在两种本地支持的模式中:

-   斯威夫特拥有持久性和同步性.
-   斯威夫特还拥有由SearchFn支撑的本地索引.
-   Java 脚本 `SyncEngine` 必须保持不活动.
-   DataFn 如果桥不可用, 则在启动持久性前必须失败 .
-   索引DB不得用作倒置的持久性或搜索-索引层.
-   CloudKit只同步记录. 搜索索引文件仍为每个设备的本地状态 .

* * *

## 客户端配置

### 创建 DatafnClient( 配置)

```typescript
interface DatafnClientConfig<S extends DatafnSchema> {
  /** Your DataFn schema definition */
  schema: S;

  /** Stable client/device identifier — required for offline + idempotency */
  clientId: string;

  /** Sync configuration (see below) */
  sync?: DatafnSyncConfig;

  /**
   * Local persistence adapter.
   * Can be a direct adapter instance or a factory function for multi-user isolation.
   */
  storage?: DatafnStorageAdapter | DatafnStorageFactory;

  /** Auth context for multi-user/multi-tenant data isolation */
  authContext?: AuthContext | AuthContextProvider;

  /** Optional plugins for hook execution */
  plugins?: DatafnPlugin[];

  /** Custom timestamp function (for testing) */
  getTimestamp?: () => number;

  /**
   * Custom ID generator for insert operations.
   * Default: `${idPrefix || resource}:${crypto.randomUUID()}`
   */
  generateId?: (params: { resource: string; idPrefix?: string }) => string;
}
```

### Datafn 同步配置

```typescript
interface DatafnSyncConfig {
  /**
   * Explicit mode selection.
   * - "sync": requires remote or remoteAdapter
   * - "local-only": no server required; all tables start as "ready"
   */
  mode?: "sync" | "local-only";

  /** Enable offline support (requires storage) */
  offlinability?: boolean;

  /** Remote server URL for the default HTTP transport */
  remote?: string;

  /** Injected remote adapter (takes precedence over remote URL) */
  remoteAdapter?: DatafnRemoteAdapter;

  /** Enable WebSocket for real-time server-push updates */
  ws?: boolean;

  /** WebSocket URL (derived from remote if omitted) */
  wsUrl?: string;

  /** Push engine: interval between batches (ms). Default 2000. */
  pushInterval?: number;

  /** Push engine: records per batch. Default 100. */
  pushBatchSize?: number;

  /** Push engine: max retries per mutation. Default 3. */
  pushMaxRetries?: number;

  /** Hydration plan for large datasets */
  hydration?: {
    /** Resources that MUST be cloned before the app is considered "ready" */
    bootResources?: string[];
    /** Resources that hydrate in the background after boot */
    backgroundResources?: string[];
    /** Per-resource clone page size (or a single number for all) */
    clonePageSize?: number | Record<string, number>;
  };
}
```

* * *

## 表 API 数据

这个 `client.table(name)` 方法返回特定资源的范围控件。 您也可以作为属性访问表格 : `client.tasks` 等同为 `client.table("tasks")`。 。 。 .

```typescript
const tasks = client.table("tasks");
// or equivalently:
const tasks = client.tasks;
```

### 表.query(碎片)

执行此资源的查询范围 .

```typescript
const result = await tasks.query({
  select: ["id", "title", "completed"],
  filters: { completed: false },
  sort: ["-createdAt"],
  limit: 20,
});
// result.data = [{ id: "task:...", title: "...", completed: false }, ...]
```

**查询特性 :**

-   `select` 编号 : `omit` - 外地选择
-   `filters` - 运算符: `eq`, (中文(简体) ), `ne`, (中文(简体) ), `gt`, (中文(简体) ), `gte`, (中文(简体) ), `lt`, (中文(简体) ), `lte`, (中文(简体) ), `like`, (中文(简体) ), `ilike`, (中文(简体) ), `is_null`, (中文(简体) ), `is_not_null`, (中文(简体) ), `in`, (中文(简体) ), `nin`, (中文(简体) ), `contains`
-   逻辑组 : `$and`, (中文(简体) ), `$or`
-   `sort` ——多领域: `["name", "-createdAt"]` (前缀) `-` = 下降数)
-   `limit` 编号 : `offset` ——以抵消为基础的平面图
-   `cursor` ——以光标为主的平面(`{ after: {...} }`(中文(简体) )
-   `count` ——只计算返回数
-   `groupBy` 编号 : `aggregations` 编号 : `having` ——汇总查询
-   `search` ——全文搜索

### 搜索( 提供备份, 本地第一)

DataFn 搜索在 `searchProvider` 已配置。 在同步模式下,在水合准备后搜索是本地第一.

地形特有搜索所有权 :

-   浏览器拥有模式: 使用 JavaScript 搜索Fn 提供者, 通常 `@searchfn/datafn-provider` 与 `@searchfn/adapter-indexeddb`。 。 。 .
-   本地支持的 Apple WebView 模式: 使用 `createNativeBackedSearchProvider(bus)` 因此Swift针对共享本地搜索Fn后端执行搜索.
-   原生后卫CloudKit模式:使用同一种原生的SearchFn后端,但CloudKit同步只记录;索引文件每设备重建并在当地维护.

`table.query()` 搜索块选项 :

```typescript
const result = await tasks.query({
  search: {
    query: "test",
    prefix: true,
    fuzzy: 0.2,
    fieldBoosts: { title: 2, description: 1 },
  },
});
```

具有明确来源路径的跨资源搜索 :

```typescript
const result = await client.search({
  query: "test",
  resources: ["tasks", "projects"],
  prefix: true,
  fuzzy: 0.2,
  fieldBoosts: { title: 2, name: 1 },
  source: "auto", // auto | local | remote
});
```

`source` 语义 :

-   `auto` (默认): 本地先; 如果本地提供者路径不可用, 返回到远程 .
-   `local`:强迫本地提供者执行.
-   `remote`: 强制远程 `/datafn/search` 执行.

如果找不到请求的来源, DataFn 返回 `DFQL_UNSUPPORTED`。 。 。 .

MiniSearch-仅限插件模式仍然支持兼容性,但提供者支持的模式是推荐的路径.

### 表格. mutate(碎片)

执行此资源的突变范围 .

```typescript
// Insert
await tasks.mutate({
  operation: "insert",
  record: { title: "New task", completed: false },
});

// Merge (partial update)
await tasks.mutate({
  operation: "merge",
  id: "task:abc",
  record: { completed: true },
});

// Replace (full update)
await tasks.mutate({
  operation: "replace",
  id: "task:abc",
  record: { title: "Updated", completed: true },
});

// Delete
await tasks.mutate({
  operation: "delete",
  id: "task:abc",
});
```

**变异操作 :**

| 运行 | 说明 |
| --- | --- |
| `insert` | 创建新记录 |
| `merge` | 部分更新( 仅指定字段) |
| `replace` | 完整更新( 替换整个记录) |
| `delete` | 删除记录 |

**关系业务** (使用时 `client.mutate()` 配备全部资源/版本:

| 运行 | 说明 |
| --- | --- |
| `relate` | 在记录之间建立关系 |
| `unrelate` | 删除记录之间的关系 |
| `modifyRelation` | 更新关系元数据 |

```typescript
// Tag a todo with a category (many-many relation)
await client.mutate({
  resource: "todos",
  version: 1,
  operation: "relate",
  id: "todo:1",
  relation: "tags",
  targetId: "cat:work",
});

// Remove a tag
await client.mutate({
  resource: "todos",
  version: 1,
  operation: "unrelate",
  id: "todo:1",
  relation: "tags",
  targetId: "cat:work",
});
```

**高级突变特征:**

-   **目 录**数字 : `clientId` + 键 `mutationId` 用于复制
-   **优化货币**数字 : `if` 警卫 预防冲突
-   **A. 背景情况**: 将任意上下文数据传递给插件和事件

### 表格. signal( 碎片)

创建反应信号——当数据变化时自动更新的活查询.

```typescript
const activeTasks = tasks.signal({
  filters: { completed: false },
  sort: ["-createdAt"],
});

// Get current value
console.log(activeTasks.get());

// Subscribe to changes
const unsub = activeTasks.subscribe((result) => {
  console.log("Tasks:", result.data);
  console.log("Loading:", result.loading);
});

// Check states
activeTasks.loading;     // true while initial fetch is in progress
activeTasks.error;       // non-null if last fetch failed
activeTasks.refreshing;  // true while background refresh is in progress
```

**信号特性 :**

-   懒取: 只在第一次订阅时加载数据
-   自动更新:突变影响查询脚印时重跑
-   跳出分批:多次快速突变触发单一刷新
-   缓存: 具有相同查询的信号共享一个缓存实例( 通过) `dfqlKey`(中文(简体) )

### 表格. 订阅( 处理器, 过滤器 ?)

只订阅此资源的事件 .

```typescript
tasks.subscribe((event) => {
  console.log(`${event.action} on ${event.resource}:`, event.ids);
});
```

* * *

## 韩国API

内置的密钥值商店提供了一个与您输入的资源同步的无计划存储层. 透过 `client.kv`。 。 。 .

```typescript
// Set a value
await client.kv.set("user:theme", "dark");

// Get a value
const theme = await client.kv.get<string>("user:theme");
// → "dark"

// Merge into an object value
await client.kv.set("user:prefs", { fontSize: 14, lang: "en" });
await client.kv.merge("user:prefs", { fontSize: 16 });
// → { fontSize: 16, lang: "en" }

// Delete a key
await client.kv.delete("user:theme");

// Reactive signal for a key
const themeSignal = client.kv.signal<string>("user:theme", {
  defaultValue: "dark",
});

themeSignal.subscribe((value) => {
  document.body.className = value; // Updates reactively
});
```

### KV API 参考文献

| 方法 | 签署 | 说明 |
| --- | --- | --- |
| `get` | `get<T>(key): Promise<T | null>` | 读取一个值 |
| `set` | `set<T>(key, value, params?): Promise<Result>` | 写入一个值( 替换语义) |
| `merge` | `merge(key, patch, params?): Promise<Result>` | 浅入已存在的对象 |
| `delete` | `delete(key, params?): Promise<Result>` | 删除密钥 |
| `signal` | `signal<T>(key, options?): DatafnSignal<T>` | 密钥的响应信号 |

KV 数据存储在内建 `kv` 和任何其他资源一样参与同步(clone/pull/push).

* * *

## 仅线下模式

在完全没有服务器的情况下运行客户端 。 所有数据只存在于本地存储中.

```typescript
const client = createDatafnClient({
  schema,
  clientId: "local-device",
  storage: new IndexedDbStorageAdapter("my-app-local"),
  sync: {
    mode: "local-only",
  },
});

// No sync.start() needed — all tables are immediately "ready"
// Queries and mutations work against local storage
await client.table("tasks").mutate({
  operation: "insert",
  record: { title: "Offline task" },
});
```

当在 `local-only` 模式 :

-   所有资源水分状态设定为 `"ready"` 马上
-   只对本地存储执行查询
-   对本地存储的突变表示乐观
-   没有网络通话
-   同步外观方法( E)`clone`, (中文(简体) ), `pull`, (中文(简体) ), `push`(如果调用,则丢出)

* * *

## 同步

### 同步外观

```typescript
client.sync.seed(payload)       // Seed data to server
client.sync.clone(payload)      // Full data download
client.sync.pull(payload)       // Incremental sync (cursor-based)
client.sync.push(payload)       // Upload local mutations
client.sync.cloneUp(options?)   // Upload local data to server
```

### 同步引擎

何时 `offlinability` 真实,同步引擎管理整个生命周期:

```typescript
await client.sync.start();       // Start sync engine (clone → pull loop → push loop)
client.sync.stop();              // Stop sync engine
await client.sync.pullNow();     // Trigger immediate pull
await client.sync.cloneNow();    // Trigger immediate clone
await client.sync.reconcileNow(); // Trigger reconcile
```

**同步引擎行为 :**

-   打开 `start()`: 克隆启动资源, 然后开始拉和推间隔
-   调取可见度变化: 标签可见时重排
-   按下批量:排队突变并分批地推到 `pushInterval`
-   重试: 重试失败后再重试 `pushMaxRetries` 时间

### 水分国

每个资源跟踪其水分状态:

| 状态 | 说明 |
| --- | --- |
| `notStarted` | 尚未复制数据 |
| `hydrating` | 克隆人正在进行中 |
| `ready` | 有数据可供查询 |

配置启动对背景资源来控制应用准备 :

```typescript
sync: {
  hydration: {
    bootResources: ["tasks", "projects"],     // Must clone before app is ready
    backgroundResources: ["audit_log"],        // Hydrates after boot
    clonePageSize: { tasks: 500, audit_log: 100 },
  },
}
```

### 克隆

上传本地数据到服务器( 例如下线工作后):

```typescript
const result = await client.sync.cloneUp({
  resources: ["tasks"],         // Which resources to upload (default: all)
  includeManyMany: true,        // Upload join rows too
  recordOperation: "merge",     // "merge" | "replace" | "insert"
  batchSize: 100,               // Records per batch
  maxRetries: 3,                // Retries per batch
  failFast: false,              // Stop on first error?
  clearChangelogOnSuccess: true, // Drain changelog after upload
  setGlobalCursorOnSuccess: true,// Update cursors
  pullAfter: true,              // Pull new data after upload
});

console.log(result.uploadedCount);
```

* * *

## 活动总线

订阅全局事件或过滤器, 按资源, 类型, 动作等等 .

```typescript
// Global subscription
const unsub = client.subscribe((event) => {
  console.log(event.type, event.resource, event.ids);
});

// Filtered subscription
const unsub2 = client.subscribe(
  (event) => console.log("Task mutated:", event),
  {
    type: "mutation_applied",
    resource: "tasks",
    action: ["insert", "merge"],
  },
);
```

### 事件过滤器

```typescript
type EventFilter = {
  type?: string | string[];
  resource?: string | string[];
  ids?: string | string[];
  mutationId?: string | string[];
  action?: string | string[];
  fields?: string | string[];
  contextKeys?: string[];
  context?: Record<string, unknown>;
};
```

### 匹配过滤器

用于检查事件在程序上是否匹配过滤器的功能 :

```typescript
import { matchesFilter } from "@datafn/client";

if (matchesFilter(event, { resource: "tasks", type: "mutation_applied" })) {
  // handle
}
```

* * *

## 交易

执行原子多步骤操作 :

```typescript
const result = await client.transact({
  transactionId: "tx-complete-all",
  atomic: true,
  steps: [
    {
      query: {
        resource: "tasks",
        version: 1,
        select: ["id"],
        filters: { completed: false },
      },
    },
    {
      mutation: {
        resource: "tasks",
        version: 1,
        operation: "merge",
        id: "task:1",
        record: { completed: true },
      },
    },
    {
      mutation: {
        resource: "tasks",
        version: 1,
        operation: "delete",
        id: "task:2",
      },
    },
  ],
});
```

* * *

## 存储适配器

### 已索引的DbStorageAdapter

由IndexedDB支持的持续浏览器存储. 支持多用户隔离.

```typescript
import { IndexedDbStorageAdapter } from "@datafn/client";

// Simple usage
const storage = new IndexedDbStorageAdapter("my-app-db");

// Multi-user isolation
const storage = IndexedDbStorageAdapter.createForUser(
  "my-app-db",
  userId,
  tenantId, // optional
);
// Creates database: "my-app-db_tenant-456_user-123"
```

### 内存图集Adapter

用于测试的内存存储——数据丢失于页面刷新.

```typescript
import { MemoryStorageAdapter } from "@datafn/client";

const storage = new MemoryStorageAdapter();
```

### DatafnStorageAdapter 接口

为自定义存储后端执行此接口 :

```typescript
interface DatafnStorageAdapter {
  // Records
  getRecord(resource: string, id: string): Promise<Record<string, unknown> | null>;
  listRecords(resource: string): Promise<Record<string, unknown>[]>;
  upsertRecord(resource: string, record: Record<string, unknown>): Promise<void>;
  deleteRecord(resource: string, id: string): Promise<void>;
  findRecords(resource: string, field: string, value: unknown): Promise<Record<string, unknown>[]>;
  countRecords(resource: string): Promise<number>;

  // Join rows (many-many relations)
  listJoinRows(relationKey: string): Promise<Array<Record<string, unknown>>>;
  getJoinRows(relationKey: string, fromId: string): Promise<Array<Record<string, unknown>>>;
  getJoinRowsInverse(relationKey: string, toId: string): Promise<Array<Record<string, unknown>>>;
  upsertJoinRow(relationKey: string, row: Record<string, unknown>): Promise<void>;
  setJoinRows(relationKey: string, rows: Array<Record<string, unknown>>): Promise<void>;
  deleteJoinRow(relationKey: string, from: string, to: string): Promise<void>;
  countJoinRows(relationKey: string): Promise<number>;

  // Sync state
  getCursor(resource: string): Promise<string | null>;
  setCursor(resource: string, cursor: string | null): Promise<void>;
  getHydrationState(resource: string): Promise<DatafnHydrationState>;
  setHydrationState(resource: string, state: DatafnHydrationState): Promise<void>;

  // Offline changelog
  changelogAppend(entry: Omit<DatafnChangelogEntry, "seq">): Promise<DatafnChangelogEntry>;
  changelogList(options?: { limit?: number }): Promise<DatafnChangelogEntry[]>;
  changelogAck(options: { throughSeq: number }): Promise<void>;
}
```

* * *

## 多用途/多用途隔离

在单独的索引数据库中孤立每个用户的数据.

### 备选案文1:提供(建议)

```typescript
import { createDatafnClient, IndexedDbStorageAdapter } from "@datafn/client";

const client = createDatafnClient({
  schema,
  clientId: "device-uuid",
  authContext: authClient.contextProvider, // implements { getContext(): AuthContext }
  storage: (ctx) =>
    IndexedDbStorageAdapter.createForUser("my-app", ctx.userId, ctx.tenantId),
  sync: { remote: "http://localhost:3000/datafn" },
});
```

### 备选案文2:直接认证

```typescript
const client = createDatafnClient({
  schema,
  clientId: "device-uuid",
  authContext: { userId: "user-123", tenantId: "tenant-456" },
  storage: (ctx) =>
    IndexedDbStorageAdapter.createForUser("my-app", ctx.userId, ctx.tenantId),
  sync: { remote: "http://localhost:3000/datafn" },
});
```

当用户登录和另一个日志输入时,创建新的客户端实例. 每个用户的数据仍被隔离在自己的索引数据库中.

* * *

## 日期编码c

自动序列化和解析 `date` 字段.

```typescript
import {
  serializeDateFields,
  parseDateFields,
  parseQueryResultDates,
} from "@datafn/client";

// Serialize Date objects to timestamps for mutations
const serialized = serializeDateFields(schema, "tasks", {
  title: "Hello",
  createdAt: new Date(),
});

// Parse timestamps back to Date objects
const parsed = parseDateFields(schema, "tasks", {
  title: "Hello",
  createdAt: 1707000000000,
});

// Parse all date fields in a query result
const result = parseQueryResultDates(schema, "tasks", queryResult);
```

* * *

## 插件

扩展客户端行为,以插件来拦截查询,突变,并同步.

```typescript
import type { DatafnPlugin } from "@datafn/core";

const loggingPlugin: DatafnPlugin = {
  name: "logger",
  runsOn: ["client"],

  afterMutation(ctx, mutation, result) {
    console.log("Mutation:", mutation, "Result:", result);
  },

  afterSync(ctx, phase, payload, result) {
    console.log(`Sync ${phase}:`, result);
  },
};

const client = createDatafnClient({
  schema,
  clientId: "...",
  plugins: [loggingPlugin],
  // ...
});
```

* * *

## 远程适应器

当您提供时使用默认的 HTTP 传输 `sync.remote`。对于自定义运输(仅WebSocket、浏览器扩展等),执行 `DatafnRemoteAdapter`数字 :

```typescript
interface DatafnRemoteAdapter {
  query(q: unknown): Promise<unknown>;
  mutation(m: unknown): Promise<unknown>;
  transact(t: unknown): Promise<unknown>;
  seed(payload: unknown): Promise<unknown>;
  clone(payload: unknown): Promise<unknown>;
  pull(payload: unknown): Promise<unknown>;
  push(payload: unknown): Promise<unknown>;
  reconcile(payload: unknown): Promise<unknown>;
}
```

### 扩展适配器

对于浏览器扩展,远程适配器可以包含事件订阅支持:

```typescript
const client = createDatafnClient({
  schema,
  clientId: "extension-popup",
  sync: {
    remoteAdapter: {
      ...transportMethods,
      onEvent(handler) { /* wire inbound events */ },
      subscribeRemote(filter) { /* register subscription */ },
      unsubscribeRemote(id) { /* remove subscription */ },
    },
  },
});
```

* * *

## 出口

```typescript
// Client factory and types
export { createDatafnClient, type DatafnClient, type DatafnClientConfig, type DatafnRemoteAdapter }

// Table API
export { type DatafnTable }

// Event system
export { EventBus, type EventHandler }
export { matchesFilter, type EventFilter }

// Storage
export { type DatafnStorageAdapter, type DatafnStorageFactory }
export { type DatafnHydrationState, type DatafnChangelogEntry }
export { MemoryStorageAdapter }
export { IndexedDbStorageAdapter }

// KV API
export type { DatafnKvApi }
export { kvId, KV_RESOURCE_NAME }

// CloneUp
export type { CloneUpOptions, CloneUpResult }

// Date Codec
export { serializeDateFields, parseDateFields, parseQueryResultDates }

// Auth (re-exported from @superfunctions/auth)
export type { AuthContext, AuthContextProvider }

// Errors
export { type DatafnClientError, createClientError }
export { unwrapRemoteSuccess }
```

## 许可证

麻省理工学院

## 常见问题

### @datafn/client 是什么？

DataFn 有事件总线和变异支持的客户端

### 如何安装 @datafn/client？

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

### @datafn/client 支持 TypeScript 吗？

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

### @datafn/client 支持 ESM 还是 CommonJS？

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

### @datafn/client 可以商用吗？

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

### @datafn/client 的下载量有多少？

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

### @datafn/client 还在维护吗？

最新版本 0\.1\.2 发布于 2026/11/11（今天）；至今共发布 6 个版本，其中过去 12 个月发布了 6 个；首次发布于 2026/01/24。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

