# @cipherstash/protect-ffi 中文文档

规范地址：https://npmcn.com/package/%40cipherstash/protect-ffi/

版本：0\.33\.0
npm 元数据更新时间：2026-10-02T07:49:09.795Z
中文译文更新时间：2026-11-09T16:28:15.591Z
软件包许可：MIT

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

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

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

- 包体积（解压后）：8.7 MB，239 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- 运行依赖：1 个
- 首次发布：2025-02-17T18:29:59.197Z
- 最新版本发布：2026-10-02T07:49:09.429Z
- 近 30 天下载量：5206（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：50（近 12 个月 22 个）

## 中文简介

原始 FFI 绑定到 CipherStash 客户端 SDK — 权限@ cipherstash/stack

## 安装

```sh
npm install @cipherstash/protect-ffi
```

也可以使用：`pnpm add @cipherstash/protect-ffi`、`yarn add @cipherstash/protect-ffi`、`bun add @cipherstash/protect-ffi`

## README

# CipherStash 客户端 FFI (`@cipherstash/protect-ffi`(中文(简体) )

> \[大 如果您想要在您的应用程序中执行此软件包, 请使用官方 [`@cipherstash/stack` 软件包](https://github.com/cipherstash/stack)。 。 。 .

该项目为CepherStash客户端Rust SDK提供了JS装订,并被 [创建- 尼昂](https://www.npmjs.com/package/create-neon)。 。 。 .

## 大楼

大楼需要 [已支持的节点和rust版本](https://github.com/neon-bindings/neon#platform-support)。 。 。 .

要运行建设,运行:

```sh
$ npm run build
```

此命令使用 [@neon-rs/cli (英语)](https://www.npmjs.com/package/@neon-rs/cli) 用于从输出中组装二进制节点加通 `cargo`。 。 。 .

## 本地设置

认证 `stash` CLI来建立你的地方环境。 它通过 `npx` - 不需要单独安装:

```sh
npx stash auth login
```

您将被提示注册或创建账户 .

## 探索

建成后 `protect-ffi`,您可以在节点控制台探索其出口. 代表团必须出示全权证书,以便 `newClient()` 成功——从 CipherStash 剖面商店、环境变量或明确 `authStrategy` 转至 `newClient()`对于地方发展来说,最简单的选择是: `npx stash auth login`,它包含配置文件商店。 或者设置 `CS_WORKSPACE_CRN`, (中文(简体) ), `CS_ACCESS_KEY`,则 `CS_CLIENT_ID`编号 :`CS_CLIENT_KEY` 键盘在您的环境中 .

```sh
$ npm i
$ npm run build
$ node
> const addon = require(".");
> const client = await addon.newClient({ encryptConfig: {v: 1, tables: {users: {email: {indexes: {ore: {}, match: {}, unique: {}}}}}} });
> const ciphertext = await addon.encrypt(client, { plaintext: "plaintext", column: "email", table: "users" });
> const plaintext = await addon.decrypt(client, { ciphertext });
> console.log({ciphertext, plaintext});
```

## EQL 版本选择

`newClient` 接受一个 `eqlVersion` 选项选择线条格式 `encrypt` 编号 : `encryptBulk` 编号 : `encryptQuery` 发出 :

```js
// EQL v2 (default) — the `eql_v2_encrypted` payload format
const v2 = await addon.newClient({ encryptConfig })

// EQL v3 — payloads for the per-capability `eql_v3` domains
const v3 = await addon.newClient({ encryptConfig, eqlVersion: 3 })
```

与 `eqlVersion: 3`,每列有效载荷的目标 `eql_v3` 域名 来源于 `cast_as` 和索引:

每个公示栏域都包含一个 `eql_v3_` 前缀( 故为 SQL) 列类型是 `public.eql_v3_text_eq`; 仅词查询双胞胎不 (单位:千美元)`eql_v3.query_text_eq`),因为 `eql_v3` 计划已经把它们改了.

| `cast_as` | 家属 | 索引 | 域名 |
| --- | --- | --- | --- |
| `text` | `text` | `unique` + 键 `ore` + 键 `match` | `eql_v3_text_search_ore` |
| `text` | `text` | `unique` + 键 `ope` + 键 `match` | `eql_v3_text_search` |
| `text` | `text` | `unique` + 键 `ore` | `eql_v3_text_ord_ore` |
| `text` | `text` | `match` | `eql_v3_text_match` |
| `text` | `text` | `unique` | `eql_v3_text_eq` |
| `int` 编号 : `small_int` 编号 : `bigint` | `integer` 编号 : `smallint` 编号 : `bigint` | `ore` (有无) `unique`(中文(简体) ) | `eql_v3_<family>_ord_ore` |
| `int` 编号 : `small_int` 编号 : `bigint` | `integer` 编号 : `smallint` 编号 : `bigint` | `unique` | `eql_v3_<family>_eq` |
| `number` 编号 : `decimal` 编号 : `date` 编号 : `timestamp` | `double` 编号 : `numeric` 编号 : `date` 编号 : `timestamp` | 见上文 | 见上文 |
| 任意平面 | —— 说 | 无 | 仅存储域( e)`eql_v3_text`, (中文(简体) ), `eql_v3_integer`, (...) (中文(简体) ) |
| `boolean` | `boolean` | 不仅仅是 | `eql_v3_boolean` (只存储——任何索引错误) |
| `json` | `json` | `ste_vec` (必须, `compat` 模式) | `eql_v3_json_search` |

注释:

-   最富有的匹配域获胜,它必须覆盖每个配置 能力——一种沉默地放弃术语错误的组合 (例如:. `unique` + 键 `match` 或者说 `ore` + 键 `match` 文本:没有单个域 带有这些术语集,所以添加缺失的索引以达到搜索 域或将能力分出各列。 仅使用 Scalar 配置 可使用 `eqlVersion: 2`; 包含 `ste_vec` 需要v3时.
-   两个文本搜索域仅在它们携带的顺序名词上有所不同: `eql_v3_text_search_ore` 携带 `ob` (德文), `eql_v3_text_search` 携带 `op` (OPE) (英语). 配置两者 `ore` 财务报告和审定财务报表 `ope` 选择 ORE .
-   例外: 投出一个 _任期_ 等会就没事了 _能力_ 活下来了. 非文本排序域只携带 `ob`这样 `unique` + 键 `ore` 编号 数字列投放 `hm` ——平等仍然通过ORE操作者实现.
-   排序文本需要 `unique` 指数(`eql_v3_text_ord_ore` 编号 : `eql_v3_text_ord_ope` 携带 `hm` + 键 `ob`编号 :`op`(三); `ore`\- 只有文本错误.
-   A 类 `json` 栏 `ste_vec` 索引必须使用 `compat` 模式(模式) `cipherstash-client` 默认自0.40.0起). v3 命令 SteVec 条目 cLLW -OPE 游戏 `op` 任期;a `standard`\- 模式索引发布 CLLW- ORE `oc`,哪个 没有机械转换,所以在配置时出现这样的列错误.
-   包含 `ste_vec` 默认为 EQL v3. 明确 `eqlVersion: 2` 被拒绝,因为客户端不能代表新的 以 v2 有线格式选择绑定 SteVec 信封.
-   `decrypt` 接受 **两者** 格式,不论 `eqlVersion`,所以v2和 v3 数据可以在迁移期间共存.
-   v3 查询加密返回索引名词只操作符: scalar 查询 生成列域的查询双双 (`{v, i, <terms>}`,没有 `c` 密码)——与 `col = $1::jsonb::eql_v3.query_<name>` (或 命令操作员,或 `@@` (原始内容存档于2017-09-21). for match. 剧本总是带着所有的东西 列域术语,以 `indexType` 被问及。 贾森 封装查询生成 `eql_v3.query_json` 针和 `ste_vec_selector` 查询返回赤色选择器散列 页:1 `->` 编号 : `->>` 操作员。 JSON 路径中的精确平等 `ste_vec_value_selector` 与 `{path, value}` 返回一个输入 适合 GIN 支持的仅选取封闭针 `@>` 操作员.
-   `ope`\- 索引列映射到 `<family>_ord_ope` 并携带 `op` (CLLW-OPE)名词(从密码-客户0.38.

> \[注\] **正在中断类型脚本更改 :** `encrypt`编号 :`encryptBulk` 现在返回 `EncryptedPayload` (单位:千美元)`Encrypted | EncryptedV3`改为 `Encrypted`。 。 。 . V2 客户端的运行时输出没有变化, 但访问的代码 `.k` 或指定结果为 `Encrypted` 必须先缩小。 v3 分级带 没有 `k`在读它之前注意它的存在 `'k' in payload && payload.k === 'ct'` (a v2 scalar),或检查 `payload.v === 3` 以检测v3成员。 (赤裸的) `payload.k === 'ct'` 不汇编反对工会。 )

## 大缩略语

已加密 `cast_as: 'bigint'` 列存储已签名的64位整数 (PostgreSQL 存档副本) `bigint`) (中文(简体) ). `encrypt` 编号 : `encryptBulk` 编号 : `encryptQuery` 编号 : `encryptQueryBulk` 接受简写为JS `number` 或联合来文 `bigint`数字 :

-   `bigint` 输入是精确的,并对照完整的i64范围检查范围: **\-9223372036854775808至9223372036854775807** (2^63至2^63-1) (中文(简体) ). 区域外的值 `RangeError` 指定界限和界限 错误的方向。 没有 `code` 这是JS的边界检查, 而不是在下面描述的 Rust 错误之一 [错误](https://npmcn.com/package/%40cipherstash/protect-ffi/#readme-errors)搜索索引 条款(`hm`, (中文(简体) ), `ob`, (中文(简体) ), `op`)由同值产生,所以边界 请检查是否也覆盖索引- 期限生成 .
-   `number` 输入保留现有的精确整数 :分数, 非无限值, 或超出范围值出错, 而不是沉默 被截断了.
-   A 类 `bigint` 仅被接受为平面图的顶层平面文字 栏。 JSON没有大块头,所以大块头都在里面 `cast_as: 'json'` 文档(或 JSON 封装查询条款) 以 `TypeError` 尼昂和原子弹上 更一般地说, 简洁的文字如下 `JSON.stringify` 两个平台上的语义: `toJSON` 获得荣誉(a) `Date` 成为它的ISO字符串, `undefined` 属性被丢弃,以及 非无限数字成为 `null`。 。 。 .
-   内线形式 `{"__protect_ffi_bigint__": "<digits>"}` 实值 保留:a `cast_as: 'json'` 完全由这个构成的简单文字 单键形状(带有相距 i64 小数字符串) 读取为 a 比林特在边界,拒绝Json列。 嵌入 较大文档中键的发生不受影响.

```js
const ciphertext = await addon.encrypt(client, {
  plaintext: 9007199254740993n, // beyond Number.MAX_SAFE_INTEGER — stays exact
  column: 'score',
  table: 'users',
})
const decrypted = await addon.decrypt(client, { ciphertext })
// decrypted === 9007199254740993n (a JS bigint)
```

> \[尖叫声\] **中断更改 :** `decrypt` 编号 : `decryptBulk` 编号 : `decryptBulkFallible` 现在,总是返回一个JS `bigint` 页:1 `cast_as: 'bigint'` 栏-甚至 符合联署材料编号的价值观。 以前的释放返回a `number`,无声地失去精确度 `Number.MAX_SAFE_INTEGER` (2^53-1) (中文(简体) ). 代码比较或计算已解密的大意 列必须更新(例如:. `decrypted === 123n` 改为 `decrypted === 123`,或 `Number(decrypted)` 当值已知时 (小一点).

## 错误

两个条目都投出一个普通的JS `Error` 拥有稳定 `code`,设置在 Rust 中 从错误变体。 没有包装课 也没什么可拆的.

并不是每次失败都有代码——包裹密码-客户端错误 到达没有田. `'UNKNOWN'` 是那个案件的名称,而不是一个值 脏发作.

类型脚本类型 a `catch` 变量为 `unknown`,所以你缩小一次。 那个 此软件包不需要任何信息 :

```typescript
try {
  await addon.encryptQuery(client, opts)
} catch (err) {
  if (err instanceof Error && 'code' in err && err.code === 'INVALID_JSON_PATH') {
    // handle JSON path mistakes
  }
  throw err
}
```

要将代码作为打入的值环绕, 请使用 `isProtectErrorCode`检查中 价值而不是外地存在的重要性: 节点集 `code` 独自一人 错误,所以赤裸裸 `err?.code` 读取会让 `ECONNRESET` 其中一个通过 这些.

```typescript
import {
  isProtectErrorCode,
  type ProtectErrorCode,
} from '@cipherstash/protect-ffi'

function errorCode(err: unknown): ProtectErrorCode | undefined {
  const { code } = err as { code?: unknown }
  return isProtectErrorCode(code) ? code : undefined
}
```

密码本身是 `PROTECT_ERROR_CODES` 内 `src/errors.ts`,哪个 `src/errorCodes.test.ts` 检查 `#[diagnostic(code(..))]` 属性 `Error` 内 `crates/protect-ffi/src/lib.rs` - 一个地方一个代码 决定.

## 可用的脚本

在工程目录中,您可以运行:

由于这个包裹被吸收到 `cipherstash/stack` 单雷波, 货物被故意避免违约 `build` 财务报告和审定财务报表 `test` 路径: 根 `pnpm test` 到达每个包裹,在这条道路上的货运过程会给每个贡献者的机器上放上一个Rust工具链. 鲁斯特活佛以自己的剧本为后盾,以下列举. `packages/protect-ffi/src/lintWiring.test.ts` 执行分法.

#### `pnpm run build`

编译类型脚本 `lib/` 与 `tsc`。这是软件包 `main`,它被生成——一个工作空间的消费者解决一个空包直到运行. **它不会建造土生土长的**; 使用 `build:native` 对于这个.

#### `pnpm run build:native`

构建节点加通( N)`index.node`)来源,生成一个发布库 `cargo --release`。 。 。 .

额外 [`cargo build`](https://doc.rust-lang.org/cargo/commands/cargo-build.html) 可附上论据。 注意没有 `--` 分隔符 – pnpm 向前 `--` 逐字记录( npm 剥去它),这些脚本结束于 `> cargo.log`因此,一个分离的旗地 _之后_ 重新定向和货物拒绝作为立场论据。 例如,允许 [货物特性](https://doc.rust-lang.org/cargo/reference/features.html)数字 :

```sh
pnpm run build:native --feature=beetle
```

#### `pnpm run debug`

类似 `build:native` 但生成调试构建 `cargo`。 。 。 .

#### `pnpm run cross`

类似 `build:native` 但使用时 [交叉键](https://github.com/cross-rs/cross) 要交叉编译到另一个平台。 使用 [`CARGO_BUILD_TARGET`](https://doc.rust-lang.org/cargo/reference/config.html#buildtarget) 用于选择构建目标的环境变量. `pnpm run zigbuild` 通过 [货物建造](https://github.com/rust-cross/cargo-zigbuild),这是释放矩阵用来给glibc打针.

#### `pnpm run build:wasm`

构建 `wasm32-unknown-unknown` 输出到 `dist/wasm/` 与 `wasm-pack`,然后插入 `.wasm` 作为基准64 `./wasm-inline` 输入。 需要原32目标 `wasm-pack` (两者均由 `mise install` 在此目录中).

#### `pnpm test`

类型检查TypeScript,运行单元测试(tsc + vitest),并用提示和格式检查TypeScript. **无货物**,按设计——见上文.

注:这也没有进行整合测试. 对于这些,见 [下级](https://npmcn.com/package/%40cipherstash/protect-ffi/#readme-integration-tests)。 。 。 .

#### `pnpm run test:cargo`

测试套房的一分之一: `cargo test --locked` 加号 `cargo fmt --check`。运行时使用路径过滤的根工作流程,而不是 `pnpm test`。 。 。 . `--locked` 而不是悄悄地再生出一个僵尸 `Cargo.lock` ——建筑脚本没有通过,所以一个合法地更新锁的局部建筑仍然有效.

#### `pnpm run test:typecheck:wasm`

类型检查 `type-tests/` 与原子弹产生的声明相对应 `dist/wasm/`需求 `build:wasm` 财务报告和审定财务报表 `build` 因此它不属于 `pnpm test` ——必须经过一个新的克隆人,没有 `dist/`。 。 。 .

#### `mise run lint:rust`

每个Rust检查CI门上:对宿主目标进行剪接,对接 `wasm32-unknown-unknown`,以及 `cargo fmt --check`。 。 。 . `pnpm test` 把这些都抛出,所以在推动rust改变之前先跑.

原臂需要安装一次目标:

```sh
rustup target add wasm32-unknown-unknown
```

`mise run fmt` 修正检查报告的格式.

## 项目布局

该项目的目录结构是:

```
protect-ffi/
├── Cargo.toml
├── README.md
├── integration-tests/
├── lib/
├── src/
|   ├── index.mts
|   └── index.cts
├── crates/
|   └── protect-ffi/
|       └── src/
|           └── lib.rs
├── platforms/
├── docs/
├── scripts/
├── package.json
└── target/
```

| 条目 | 目的 |
| --- | --- |
| `Cargo.toml` | 货物 [显示文件](https://doc.rust-lang.org/cargo/reference/manifest.html),通知 `cargo` 命令. |
| `README.md` | 这个文件. |
| `integration-tests/` | 包含整合测试的目录. |
| `lib/` | 包含生成输出的目录 [页:1](https://typescriptlang.org/)。 。 。 . |
| `src/` | 包含 TypeScript 源文件的目录. |
| `index.mts` | 此库通过时的输入点 [无害环境管理 `import`](https://nodejs.org/api/esm.html#modules-ecmascript-modules) 语法. |
| `index.cts` | 此库通过时的输入点 [司法理事会 `require`](https://nodejs.org/api/modules.html#requireid)。 。 。 . |
| `crates/` | 包含项目 Rust 源代码的目录树 . |
| `lib.rs` | Rust源代码的入站点. |
| `platforms/` | 包含此库所支持的每个平台的二进制附加后端的分布的目录. |
| `docs/` | JSONB 整合文件和移民说明. |
| `scripts/` | 构建帮助脚本( 如 WASM inlineing) . |
| `package.json` | 时间 [显示文件](https://docs.npmjs.com/cli/v7/configuring-npm/package-json),通知 `npm` 命令. |
| `target/` | 二进制由罗斯特建筑所出产的文物. |

## 融合测试

融合测试现场 `./integration-tests` 目录. 这些测试利用了 Rust 和 JavaScript 的本地建筑来测试 `@cipherstash/protect-ffi` 正如API消费者那样.

目录是它自己的私人 pnpm 工作空间成员(`@cipherstash/ffi-integration-tests`),这样 `pnpm install` 在回波根安装它, 两者 `@cipherstash/protect-ffi` 财务报告和审定财务报表 `@cipherstash/eql` 解决此树上的包而不是公布柏油丸。 它已经 **没有 `test` 脚本** 目标——根 `pnpm test` 否则会尝试运行一个套房 需要多克和现场证明。 使用 `mise` 任务.

这些测试依赖于:

-   要配置的 CipherStash (通过) `.toml` 配置或环境变量),以及
-   待设定的 Postgres 环境变量

示例环境变量:

```
CS_CLIENT_ID=
CS_CLIENT_KEY=
# The integration tests read CS_CLIENT_ACCESS_KEY; the library itself reads CS_ACCESS_KEY
CS_CLIENT_ACCESS_KEY=
CS_WORKSPACE_CRN=
# Must match the port used by the integration-test setup (mise.toml)
PGPORT=5436
PGDATABASE=cipherstash
PGUSER=cipherstash
PGPASSWORD=password
PGHOST=localhost
```

进行整合测试:

```sh
mise setup
mise test:integration
```

也可以以"观察"模式运行集成测试:

```sh
mise test:integration --watch
```

默认的锁上下文测试不包含在内,因为无效锁上下文在ZeroKMS中的火灾安全警告. 包括这些,运行:

```sh
mise test:integration:all
```

## 释放

这个包被吸收到 [`cipherstash/stack`](https://github.com/cipherstash/stack) 和它的放出现在运行在该存储器的 [更改](https://github.com/changesets/changesets) 流动而不是a `workflow_dispatch` 触发。 不再有手取的版本: 包装及其6个平台软件包共享更改器 `fixed` 组,所以 释放所有7个在锁步.

写入更改列表( E)`pnpm changeset` 在 repo 根) 命名 `@cipherstash/protect-ffi`; 固定组向平台传播凸起 软件包。 变形体变成 `CHANGELOG.md` 条目 - 更改器拥有 现在的文件,所以 `[Unreleased]` 标题和 `version` npm 生命周期 用来宣传它的钩子不见了.

**出版的剪接已经完成,随之而来的是更改的禁运.** 无 现在所有七个软件包都值得信任的出版 `cipherstash/stack`绑定 改为 `release.yml`名称 `@cipherstash/protect-ffi` 释放 通常情况下。 挡住一个的警卫`scripts/lint-no-ffi-changeset.mjs`和 页:1 `.changeset/<name>.md.deferred` 将它停到切口的公约 都不见了.

**而路径被证明, 不只是配置** —— 说 `0.32.0` 发表于 此仓库 。 Npm 名称上的 SLSA 出处 `https://github.com/cipherstash/stack` 财务报告和审定财务报表 `.github/workflows/release.yml`, (中文(简体) ), 这是解决问题的唯一证据: 这一段说,FFI的释放没有从这里流出,写着0.31.0 当0.32.0 降落时,最新鲜的 也从未重温过.

如果您发现一个文件仍然带有扩展名, 该文件是写在 封锁,目前为惰性而非停放: `@changesets/read` 选择在 `.endsWith('.md')`,因此它描述的更改会空出 更改日志条目和无内容 `changeset version` 或者说 `changeset publish` 将 说吧 重命名为 `git mv`,或者如果更改从未运出,则删除. `scripts/__tests__/no-parked-changesets.test.mjs` 不管是哪一种方式都失败了.

上一个存储器的 GitHub 动作工作流程保存在 `.github/` 在此目录中由子树导入并保存为参考 那个港口 他们走了,六个平台的建构矩阵 现在生活在 仓库根 `.github/workflows/_build-ffi-artifacts.yml`,与 `ffi-preflight.yml` 作为它的人工分发干线。 没有包裹 目录由 GitHub 执行, 它从仓库读取工作流程 仅根- `src/lintWiring.test.ts` 如果有人回来了,就失败了.

## 学习更多

更多地了解:

-   [霓虹](https://neon-bindings.com/)。 。 。 .
-   [锈](https://www.rust-lang.org/)。 。 。 .
-   [节点](https://nodejs.org/)。 。 。 .

## 常见问题

### @cipherstash/protect-ffi 是什么？

原始 FFI 绑定到 CipherStash 客户端 SDK — 权限@ cipherstash/stack

### 如何安装 @cipherstash/protect-ffi？

在项目目录运行 npm install @cipherstash/protect-ffi，或使用 pnpm add / yarn add / bun add。

### @cipherstash/protect-ffi 支持 TypeScript 吗？

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

### @cipherstash/protect-ffi 支持 ESM 还是 CommonJS？

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

### @cipherstash/protect-ffi 可以商用吗？

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

### @cipherstash/protect-ffi 的下载量有多少？

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

### @cipherstash/protect-ffi 还在维护吗？

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

