# @foir/sdk 中文文档

规范地址：https://npmcn.com/package/%40foir/sdk/

版本：0\.34\.0
npm 元数据更新时间：2026-11-09T13:56:46.874Z
中文译文更新时间：2026-11-15T04:16:03.188Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%40foir/sdk
原文对照：https://npmcn.com/package/%40foir/sdk/?lang=original
项目主页：https://github.com/eidestudio/foir-clients#readme
源码仓库：https://github.com/eidestudio/foir-clients

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

- 包体积（解压后）：1.7 MB，150 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=18\.0\.0
- 运行依赖：1 个
- peer 依赖：ai ^7\.0\.0，yjs ^13\.6\.0，react ^18\.0\.0 \|\| ^19\.0\.0，hls\.js ^1\.5\.0，graphql ^16\.0\.0，blurhash ^2\.0\.0，y-websocket ^3\.0\.0，@bufbuild/protobuf ^2\.12\.0，@graphql-codegen/cli ^5\.0\.0，@graphql-codegen/client-preset ^4\.0\.0
- 首次发布：2026-06-23T16:05:12.221Z
- 最新版本发布：2026-11-09T13:56:46.711Z
- 近 30 天下载量：2477（2026-10-15 至 2026-11-13，npm 官方下载统计）
- 已发布版本数：35（近 12 个月 35 个）

## 中文简介

官方 Foir SDK – 编辑 Iframe 客户端 + 服务器侧平台帮助器

## 安装

```sh
npm install @foir/sdk
```

也可以使用：`pnpm add @foir/sdk`、`yarn add @foir/sdk`、`bun add @foir/sdk`

## README

# 翻译:

官方Foir SDK——客户端-不可知的GraphQL传输,为您的应用用户签名, 进步媒体、内容渲染、实时协作和编辑/服务器 用于创建应用程序的助手 [转弯](https://foir.dev/) 平台.

SDK被分割为独立的子路径输出,所以消费者只拉什么 他们使用。 所有重物 (`react`, (中文(简体) ), `graphql`, (中文(简体) ), `yjs`, (中文(简体) ), `hls.js`, )是一个 可选的对等依赖性被锁定到需要它的子路径上.

## 安装

```sh
pnpm add @foir/sdk
# plus the peers for the subpaths you use, e.g.:
pnpm add graphql            # for @foir/sdk/client + @foir/sdk/auth
pnpm add react              # for @foir/sdk (editor), /media, /content
pnpm add yjs y-websocket    # for @foir/sdk/realtime
```

## 子路径导出

| 导入 | 这是什么 | 同伴 |
| --- | --- | --- |
| `@foir/sdk` | 编辑器 iframe 客户端 - `EditorProvider`, (中文(简体) ), `useEditor`, (中文(简体) ), `useAutoResize`, (中文(简体) ), `createPlatformClient`,主机编辑器信件协议 | `react` |
| `@foir/sdk/client` | SSR/接地装载器的客户-不可知图QL运输 - `foirRequest`, (中文(简体) ), `isFoirPreview`, (中文(简体) ), `absolutiseFoirMediaUrl` | `graphql` |
| `@foir/sdk/data` | 输入数据客户端结束 `/graphql` —— 说 `createFoirClient`每个键形行动, `include`编号 :`omit`, (中文(简体) ), `fetchFoirManifest`; 为您打印文档, 所以仍然没有 `graphql` | **无** |
| `@foir/sdk/auth` | 为您的应用程序用户提供框架不可知的签名:登录/登记/OTP/提供者流量、象征性生命周期(`SessionAdapter`, (中文(简体) ), `createFoirSessionClient` (fir主持饼干会), `authedRequest`警卫 | `graphql` |
| `@foir/sdk/react` | Drop- in React 认证 - `<FoirProvider>` (电话或 `hostedSession` 模式), `useFoirSession`编号 :`useFoirUser`编号 :`useFoirAuth`, (中文(简体) ), `<SignIn>`, (中文(简体) ), `<OAuthButtons>`, (中文(简体) ), `<UserButton>` | `react`, (中文(简体) ), `graphql` |
| `@foir/sdk/next` | Next.js App 路由器 BFF 认证 : `createFoirCookieAdapter`, (中文(简体) ), `getFoirSession`, (中文(简体) ), `foirSignIn*`, (中文(简体) ), `startFoirProviderSignIn`, (中文(简体) ), `finishFoirProviderSignIn`, (中文(简体) ), `createFoirMiddleware` | `graphql` |
| `@foir/sdk/server` | 用于扩展/配置 API 后端的服务器端助手 —— `createServerClient`, (中文(简体) ), `verifyWebhookSignature`, (中文(简体) ), `verifyScopedToken`客户端 | —— 说 |
| `@foir/sdk/live` | Live reads——福尔公司无效图中的客户端:标签索引,活载量商店, `seq` 命令、 更改记录 ~ 标记映射 | **无** |
| `@foir/sdk/live/react` | `<FoirLive>` 一个套接字,每个模型重新计算,连接到您已有的查询客户端 | `react` |
| `@foir/sdk/media` | 进步媒体—— `ProgressiveMedia`, (中文(简体) ), `BlurhashCanvas`, (中文(简体) ), `useProgressiveLoad`,变位适配器 | `react` (单位:千美元)`hls.js`, (中文(简体) ), `blurhash` 可选) |
| `@foir/sdk/content` | 内容的原始人—— `createContentRenderer` 块发送 + Lexical `RichTextRenderer` | `react` |
| `@foir/sdk/realtime` | 细串联帮助结束 `y-websocket` - 警犬室地址、存在、装饰品刷新 | `yjs`, (中文(简体) ), `y-websocket` |
| `@foir/sdk/codegen` | 构建时的图克l-代码源预设( T)`foirCodegen`\+ 详尽无遗的块注册插件 | `@graphql-codegen/cli`, (中文(简体) ), `@graphql-codegen/client-preset` (滴答声) |

## 使用量

### 已键入数据客户端( 没有 GraphQL, 没有代码阶梯)

`@foir/sdk/data` 执行相同 `/graphql` 结束点 其他消费者 使用——一个执行表面,一个执行者,一组范围检查,一个答复 缓存。 您跳过的是写入文档: 客户端加载选择 树 `GET /v1/manifest` 发布操作,应用您的 `include`编号 :`omit`,以及 打印一个.

打印一个预先解开的树是字符串结构, 因此软件包仍然需要 **没有 `graphql` 完全依赖** 没有 `graphql`,没有 `@graphql-typed-document-node/core`,没有 `@graphql-codegen/*`,没有新的对等点.

```ts
import { createFoirClient } from '@foir/sdk/data';
// generated by `foir types` from GET /v1/manifest
import * as generated from './foir.generated';

const foir = createFoirClient({
  apiUrl: process.env.PLATFORM_API_URL!,   // a bare origin or the /graphql URL
  apiKey: process.env.PLATFORM_API_KEY!,
  generated,                               // the whole module: types, trees, version
});

const post = await foir.post.findUnique({
  naturalKey: 'hello',
  include: { author: true },   // relations opt in
  omit: ['bodyRichtext'],      // drop the heavy field
});

const posts = await foir.post.findMany({ first: 10 });   // a Relay connection
const hits  = await foir.ns.search.searchRecords({ query: 'shoes' });
const other = await foir.raw('{ designTokens { colors } }');  // any document, any root field
```

**一个导入, 没有类型参数 .** `generated` 载着一半的 `foir types` 写作—— `FoirSchema` 类型和运行时间选择树 文件的印制来自 `FoirSchema` 推定而不是重述, 并且一个添加到发电机上的文物 之后花费你的呼叫站点 什么也没有。 A 类 模块中缺少一个导出是一个编译错误,而不是安静 贬:通通取出,或不通取出.

它替换的字段( :`surfaceVersion`, (中文(简体) ), `surfaceHashes`, (中文(简体) ), `selections`, (中文(简体) ), `namespaceSelections`, (中文(简体) ), `selectionUnions`)仍然工作,并且仍然赢得了两者 已经给出.

**漂流被地表按键.** 模块记录 `surfaceVersion` 这个 产生自: 散起于什么 `foir types` 读取,仅移动 当再生成会写出不同的文件时。 推动操作、角色或 键会移动服务器的无效计数器而不是表面,所以不是 漂来漂去。 在类型包括的模型上发布字段,客户端 报告一次,命名模型.

**漂流不会停止通话.** 已经长大的地表仍然能回答每一个问题 文档模块打印。 拒绝打入的电话 `FoirSchemaDriftError`, (中文(简体) ), 只有在它所依赖的东西不再作为生成服务时: 选择名称、其行动、其论点之一或关系的目标 包含。 信件名称 `drift.incompatible` 列表中 ,作为路径(`models.order.selection.status`) (中文(简体) ). 补救措施是: `foir types`· ; `strictSchemaDrift: false` 从来没有.

**斯卡勒总是完整的**这样 `post.title` 键入到当前和 _实值_ 目前没有部分`select` 脚枪. `include` 添加关系密钥, `omit` 移除了平地,多形态的地块在 `__typename` 在详尽无遗的情况下 `switch` 没有铸造物.

**客户是由你的证书塑造的.** 生成的 `FoirSchema` 包含 所以只读可公布的密钥没有 `create` 改为 电话 :

```ts
foir.post.create({ data });   // ✗ compile error under a publishable key: not in its manifest
```

`generated` 是可选的。 没有它,也没有 `selections` - 客户 获取 `GET /v1/manifest` 第一次使用 并读取同样的树 从它。 A 类 倒计时,所以一个零码的开始工作, 成本在前方的往返 每一个寒冷引用的第一个问题。 上面说,一次,当它建成的时候 那样的话 安静 `announceManifestFallback: false`。 。 。 .

可共享的读取器发送到 `GET /cdn/graphql` 仅限 在 URL 中坚持- query 散列, 这样您前面的 CDN 可以服务下一个 访问者未到达原籍地。 这是一个查询,一个可公布的密钥,没有 用户无记名, 在已公布的频道上; 其他的都是 `POST /graphql`。 。 。 .

`foir.raw()` 是逃生舱门,它需要一个 **图QL 文档** - 这个 达到任何证书所能达到的,包括根字段,没有模型或命名空间 索赔。 整个都回来了 `data` 对象.

命名空间( N)`search`, (中文(简体) ), `users`, (中文(简体) ), `notifications`(活在地下) `foir.ns` 因为 客户端没有运行时间计划从模型中告诉命名空间. `ns` 财务报告和审定财务报表 `raw` 是唯一的保留名称.

带会话的读取 `bearerToken`,作为字符串或已解决的函数 请:

```ts
const foir = createFoirClient({
  apiUrl,
  apiKey,
  generated,
  bearerToken: () => session.get('foir_access_token'),
});
```

已经 `PLATFORM_API_URL` 编号 : `PLATFORM_API_KEY` 编号 : `FOIR_PREVIEW` 电线 图QL 路径 ? `createFoirClientFromEnv(env, { generated })` 重新使用它们, 包括预览密钥和预览频道草稿.

一份以组织名称为基础的产品,其中要求与 `orgId`, (中文(简体) ), 客户端发送的 `X-Org-Id` 每一个请求。 这是为项目 证书——后台,或刚刚解决子域的应用程序 一个组织; 存储面继续使用仅限于其自身的证书 组织 `orgId` 无法向别处行驶 :

```ts
const foir = createFoirClient({ apiUrl, apiKey, generated, orgId });
```

### 从服务器加载器输入的图形QL

`foirRequest` 是输入的 `fetch` 包装器, 不是 GraphQL 客户端: 它携带 B. 与特定地点有关的运输问题`x-api-key`,预览计划通道,出错 形状)和运行生成 `TypedDocumentNode`s. 带来客户 (Apollo/urql/react-query)直接使用生成的文档并复制 那些头.

```ts
import { foirRequest, type FoirEnv } from '@foir/sdk/client';
import { HomePageDocument } from './generated/graphql';

export async function loader({ context }: LoaderArgs) {
  const env: FoirEnv = {
    PLATFORM_API_URL: context.env.PLATFORM_API_URL,
    PLATFORM_API_KEY: context.env.PLATFORM_API_KEY,
  };
  const data = await foirRequest(env, HomePageDocument, { slug: 'home' });
  return data.homePage;
}
```

### 将用户签名

```ts
import {
  signInWithPassword,
  authedRequest,
  type SessionAdapter,
} from '@foir/sdk/auth';
import { MyProfileDocument } from './generated/graphql';

// SessionAdapter = { get, set, delete } over your session store (cookie, KV…).
const user = await signInWithPassword(adapter, env, { email, password });

// Bearer-authed request with automatic refresh-and-retry on expired sessions.
const { myProfile } = await authedRequest(adapter, env, MyProfileDocument);
```

### 反应

`@foir/sdk/react` 将反应层建在 `@foir/sdk/auth`组合 您的应用程序一次,然后用钩子读取会话或者在组件中掉入. 这个 默认会话存储为 `localStorage`; 通过自己的 `adapter` 饼干.

```tsx
'use client';
import { FoirProvider, SignIn, UserButton, useFoirSession } from '@foir/sdk/react';

const env = {
  PLATFORM_API_URL: process.env.NEXT_PUBLIC_PLATFORM_API_URL,
  PLATFORM_API_KEY: process.env.NEXT_PUBLIC_PLATFORM_API_KEY, // a publishable key
};

export function App() {
  return (
    <FoirProvider env={env}>
      <Header />
      <SignIn afterSignInUrl="/dashboard" />
    </FoirProvider>
  );
}

function Header() {
  const { isAuthenticated, isLoading } = useFoirSession();
  if (isLoading) return null;
  return isAuthenticated ? <UserButton afterSignOutUrl="/" /> : null;
}
```

对于自定义表单, 使用 `useFoirAuth()` (单位:千美元)`signIn`, (中文(简体) ), `register`, (中文(简体) ), `signInWithOtp`, (中文(简体) ), `requestOtp`, (中文(简体) ), `signInWithProvider`, (中文(简体) ), `signOut`, (中文(简体) ), `getToken`和 `useFoirUser()`。 。 。 .

### 非后端SPA(Foir主持会议)

默认 `<FoirProvider>` 存储符号在 `localStorage`如果您的 SPA 没有 后端,但你有一个 **已验证自定义登录域** 同一可登记 域( 选项)`app.acme.com` + 键 `login.acme.com`),更喜欢 `hostedSession`: 持久 证书会变成你的域名上的Htp Only Cookie Foir 和15分钟 访问符只在记忆中存在。 没有与认证相关的内容可以达到 因此,安全局没有什么可告密的——BFF的安全姿态没有 后端 .

```tsx
<FoirProvider
  env={{ PLATFORM_API_URL: 'https://api.foir.dev/graphql', PLATFORM_API_KEY: 'foir_pub_…' }}
  hostedSession={{ loginOrigin: 'https://login.acme.com' }}
>
  <App />
</FoirProvider>
```

签入是一个方向,而不是一种形式—— `useFoirAuth().hostedSignInUrl()` 给你 uRL(在此模式中投放的密码/OTP动作). `getToken()` 交换 饼干和在过期前悄悄地再交换; `signOut()` 撤销会话 服务器侧并立即关闭当前客户端,甚至下线. 之后 下个主机的登录重定向回来, 创建新客户端( 或重挂载) 提供方)。 一个被关闭或被摧毁的客户从不恢复交换. 其它的都一样.. `useFoirSession`, (中文(简体) ), `useFoirUser`, (中文(简体) ), `<UserButton>` —— 说 行为也一样.

框架不可预见等同性:

```ts
import { createFoirSessionClient } from '@foir/sdk/auth';

const session = createFoirSessionClient({ loginOrigin: 'https://login.acme.com' });
const token = await session.getToken(); // null when signed out
```

### Next.js (App路由器, BFF) (英语)

`@foir/sdk/next` 将符号保存在 HttpOnly 饼干服务器侧。 保护 用中间软件的路由,在服务器组件中读取会话,并签名于 从路由处理器.

```ts
// middleware.ts
import { createFoirMiddleware } from '@foir/sdk/next';
export const middleware = createFoirMiddleware({ signInUrl: '/sign-in' });
export const config = { matcher: ['/dashboard/:path*'] };
```

```ts
// app/dashboard/page.tsx (Server Component)
import { cookies } from 'next/headers';
import { getFoirSession } from '@foir/sdk/next';

export default async function Dashboard() {
  const user = await getFoirSession(await cookies());
  return <p>Signed in as {user?.email}</p>;
}
```

```ts
// app/sign-in/route.ts (Route Handler)
import { cookies } from 'next/headers';
import { foirSignInWithPassword } from '@foir/sdk/next';

export async function POST(req: Request) {
  const { email, password } = await req.json();
  await foirSignInWithPassword({ env, store: await cookies(), email, password });
  return Response.json({ ok: true });
}
```

### 代码

```ts
// codegen.ts
import { foirCodegen } from '@foir/sdk/codegen';

export default foirCodegen({
  apiUrl: process.env.PLATFORM_API_URL!,
  apiKey: process.env.PLATFORM_API_KEY!,
  // documents: 'app/graphql/foir/**/*.graphql' (default)
  // outputDir: './app/gql/foir/' (default)
});
```

### 编辑器扩展

```tsx
import { EditorProvider, useEditor } from '@foir/sdk';

function App() {
  return (
    <EditorProvider>
      <MyEditor />
    </EditorProvider>
  );
}

function MyEditor() {
  const { init, client } = useEditor(); // record context + platform GraphQL client
  // ...
}
```

### 现场阅读

`@foir/sdk/live` 将已读取的缓存绑定到服务器自身的依赖图中。 每个 可缓存的响应已经告诉你它取决于哪些记录和收藏 上,变化流已经告诉你 当其中之一移动;这是 来.

```tsx
import { FoirLive, useFoirLive, useFoirLiveStatus } from '@foir/sdk/live/react';

<FoirLive
  queryClient={queryClient}
  subscribe={(model, options, handlers) => foir[model].onChange({ ...handlers, ...options })}
>
  <ProductList />
</FoirLive>;

function ProductList() {
  useFoirLive('product');                       // watch, for as long as this is mounted
  const { newCounts } = useFoirLiveStatus();    // "3 new products — show"
  // ...
}
```

一个 **适配器**,不是商店:您保留您的查询库,它拥有 你的数据。 它拥有的是哪个条目的更改无效 记录,而不是模型,所以一个编辑不会反射每个列表.

活着 **每个模式的选入**。没有订阅,因为提供者 挂载:共享的CDN车道是主要资产,毛毯活地推取读取 把车开到原地.

#### 它不是,也不会变成

**没有本地突变日志, 没有离线模式, 没有冲突解决, 没有乐观 倒转.** 服务器是权威的: 变异通过线和 无效还原 .

这是一个产品边界,而不是一个阶段。 加上乐观的突变 回滚将使这个图书馆成为另一个类别,有自己的行进图 和永久的支撑表面。 如果你需要,就用一个——这不是它 它的缺失不是一个错误.

#### 以及改变反应的部署

批量进口或出版物的扫射会给一个模型起爆. `<FoirLive>` 连接它: 一个 25 ms 窗口内一个查询的五十个无效是一个 调试,最多4个调试一次,查询再次失效 中后退还差一点 调色符 `coalesceWindowMs` 财务报告和审定财务报表 `maxConcurrentRefetches`。 。 。 .

斯克马漂流是分开的,很容易错过,因为没有别的东西抓住它. A 类 部署重命名字段时, 每个缓存条目都会流出并悄悄错误 ——记录没有改变,没有出现出错,字节描述的是一种图谋 这已经不复存在。 连线一次:

```ts
import { createFoirDriftHandler } from '@foir/sdk/live';

createFoirClient({
  // ...
  checkSchemaDrift: true,
  onSchemaDrift: createFoirDriftHandler({
    resync: () => {
      store.resync();
      queryClient.clear();
    },
  }),
});
```

#### 已知限制:可变排序键

由可变键命令的查询.. `updatedAt`,或一个 `position` 用户拖动 - 更新时可以在页面之间有记录移动,因此可能出现两次 或消失,直到下一个完全反射.

故意没有检测到:这需要客户方的知识 排序字段,且完全正确的选项将常规编辑转换为 N- page refetch on most feed. (原始内容存档于2018-09-21). 过 `mode="notify"` 在屏幕上,它很重要 并按用户的条件进行调试.

#### 刺痛,以及为什么这三种情况不同

Foir 是纯中继光标的无 `offset`,因此加载的页面保持 他们的秩序和 `endCursor` 保持正确的。 只有三样东西破了,每样 获得数据支持的处理:

-   **删除** ——行被id删除,与 **无获取**。该活动带有 id,所以这是立即正确的.
-   **创建** a 包括: **计数**不是反弹 创建无法被分解( 记录) 事件没有尸体,你不能在不知道之前放置行 排序),并重排头页预放在读取器中卷下.
-   **更新** 查证记录的查询 成员为: 没有改变,所以光标保持.

`totalCount` 被标记为大致而非调整:它是服务器的 在查询的过滤器下计数,猜测会使一个 stale 数字变成一个 错了.

## 许可证

麻省理工学院

## 常见问题

### @foir/sdk 是什么？

官方 Foir SDK – 编辑 Iframe 客户端 \+ 服务器侧平台帮助器

### 如何安装 @foir/sdk？

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

### @foir/sdk 支持 TypeScript 吗？

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

### @foir/sdk 支持 ESM 还是 CommonJS？

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

### @foir/sdk 需要什么版本的 Node\.js？

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

### @foir/sdk 可以商用吗？

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

### @foir/sdk 的下载量有多少？

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

### @foir/sdk 还在维护吗？

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

