# @broberg/forms-turnstile 中文文档

规范地址：https://npmcn.com/package/%40broberg/forms-turnstile/

版本：0\.4\.0
npm 元数据更新时间：2026-10-22T21:54:57.343Z
中文译文更新时间：2026-11-08T15:52:47.301Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%40broberg/forms-turnstile
原文对照：https://npmcn.com/package/%40broberg/forms-turnstile/?lang=original
项目主页：https://github.com/broberg-ai/components#readme
源码仓库：https://github.com/broberg-ai/components

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

- 包体积（解压后）：191 kB，38 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- 运行依赖：0 个
- peer 依赖：hono \>=4，react \>=18，preact \>=10\.0\.0
- 首次发布：2026-07-01T19:25:18.926Z
- 最新版本发布：2026-10-22T21:54:56.986Z
- 近 30 天下载量：6584（2026-10-09 至 2026-11-07，npm 官方下载统计）
- 已发布版本数：5（近 12 个月 5 个）

## 中文简介

为broberg.ai车队提供垃圾邮件保护的公有形式原始:蜂窝检测,一个在流程中的IP速率限制器,和Cloudflare Turnstile服务器侧校验. 无头核心 + 一个用于 React and Preact + a Hono 中间软件的部件钩.

## 安装

```sh
npm install @broberg/forms-turnstile
```

也可以使用：`pnpm add @broberg/forms-turnstile`、`yarn add @broberg/forms-turnstile`、`bun add @broberg/forms-turnstile`

## README

# @broberg/形式-翻转

垃圾邮件保护 **公开形式** broberg.ai车队的原始:蜂窝检测,一个在流程中的IP速率限制器,和Cloudflare Turnstile服务器侧校验——外加Preact部件钩和Hono中件. 从 `webhouse/cms`(无头核心)与 `xrt81`'铅形式 (Preact/Hono e2e).

```bash
npm i @broberg/forms-turnstile      # exact-pin for prod-auth deps
```

## 核心项目`@broberg/forms-turnstile` 编号 : `@broberg/forms-turnstile/server`(中文(简体) )

框架不可知性, `node:crypto` 只有这个.

```ts
import { applySpamGauntlet, hashIp, getSitekeyResponse } from "@broberg/forms-turnstile/server";

const ipHash = hashIp(clientIp); // GDPR-friendly — never store the raw IP

const result = await applySpamGauntlet({
  honeypot: { body },                                          // omit to skip this layer
  rateLimit: { ipHash, formName: "contact", maxPerHour: 5 },    // omit to skip this layer
  turnstile: { token: body.token, secret: env.TURNSTILE_SECRET_KEY, remoteip: clientIp },
});
if (result.blocked) {
  // result.reason: "honeypot" | "rate-limit" | "turnstile"
}
```

每层都是 **选入** ——只通过您想要的检查的选项密钥;它们按蜜壶的顺序运行故障快活 —— 速率限制 —— Turnstile.

个人支票也出口(`isHoneypotTriggered`, (中文(简体) ), `isRateLimited`, (中文(简体) ), `validateTurnstile`, (中文(简体) ), `HONEYPOT_FIELD`如果你宁愿自己叫他们.

### "你是一个机器人","我们不能问"是不同的答案(v0.3.0)

`verifyTurnstile()` 返回时 **3个** 结果,因为可能发生三件事:

```ts
import { verifyTurnstile } from "@broberg/forms-turnstile/server";

const r = await verifyTurnstile(token, secret, { remoteip, timeoutMs: 10_000 });
// { ok: true }
// { ok: false, reason: "rejected",    errorCodes: [...] }   ← Cloudflare said no
// { ok: false, reason: "unavailable", detail: "…" }         ← we never got an answer
```

这个 **从不扔** 5xx、 HTML 错误页、 已丢弃的连接和 暂停 全部回来作为 `unavailable` 和可读的细节.

**为什么会这样.** 在v. 3. 0 之前 , 请检查url=值 (帮助), `unavailable` 联系到的呼叫者 _两个 视云雀失败的形状而定不同的频道_一个非JSON 机构 **丢弃**而JSON的尸体没有 `success` 返回字段 **`false`**。 。 。 . 没有一个呼叫者能够一致地处理它 `false` 树枝是有害的 一个 它变成了 `reason: "turnstile"`,它使得 _你辜负了机器人 检查"_真实的人说她不是人类,"再试"是她唯一的 选择,她没有问题 日志上说 `turnstile` 同样,命名 错误的原因.

以出产为度量. 同样的缺陷 [v. 2.0 固定在浏览器上 边](https://npmcn.com/package/%40broberg/forms-turnstile/#readme-gate-the-submit-button-on-status-not-on-token-v020) ——当客户端/服务器 双人有一个半个,先检查另一个半个.

**没有判决不是判决.**

#### 选择政策

`applySpamGauntlet` 默认为 **关闭失败**数字 :

```ts
await applySpamGauntlet({ turnstile: { token, secret } });
// Cloudflare unreachable → THROWS. An unguarded route 500s. Nothing gets through.
```

这是蓄意的:它保留了这一一揽子方案已经为主导者所做的一切 所以升级从不会默默地把一个人的500元转换成400元 指责一个人类。 由你自己决定:

```ts
const r = await applySpamGauntlet({
  turnstile: { token, secret, onUnavailable: "block" },
});
if (r.blocked && r.reason === "turnstile-unavailable") {
  // Say what is true: "We can't check that right now — try shortly, or call us."
  // NOT "you failed the bot check".
}
```

`turnstile-unavailable` 独立成员 `SpamBlockReason`,所以一个 `switch` 在编译时会告诉你 你有一个新案子 手柄。 这两个选项也都放在Hono中间软件上(`timeoutMs`, (中文(简体) ), `onUnavailable`) (中文(简体) ).

**现在暂停了** (违约10s). 没有了,一个被绞死的"云花" 连接将请求维持到平台停止引用为止——更糟糕的是 与本地相比,飞行/服务器无所适从,用户只看回旋器.

**`validateTurnstile()` 已贬值但未作改变.** 这是损失——它不能 分辨出两个失败, 并仍然把它们分成一个返回值和一个 掷出. 它的准确行为被测试所支撑 所以现有的呼叫者看不到任何变化 升级。 迁移到 `verifyTurnstile` 当你触摸呼叫站点.

**费率限制提醒 :** 仅限进程(a) `Map`——保护单机部署(飞来单机,一班机工人),但每个实例都有自己的计数器,所以它确实 **没有** 保护多层/无服务器。 对于共享的可插件存储限制器(Turso/Redis-backed),可访问范围为 `@broberg/apikey`是因为 `SlidingWindowRateLimiter` 换句话说.

> **测量您的实例数, 然后在常数旁边写 .** 带 N 实例 `maxPerHour × N`没有 `maxPerHour` 所以说 数字在你的代码里是谎言 任何人阅读它下一个,除非评论说 所以说. `flyctl scale show -a <app>` 用一行来回答 测量的fd- sundhed 2 机器对 `maxPerHour: 5` 并将实际限值记录为10 **地点 常数**而不是在承诺的信息, 这是正确的地方.
> 
> 这是重复的刹车 不是门 蜜壶和花心肠 保护; 如果利率限制是您所依赖的地层, 您需要一个 共享商店.

### 本地 dev / CI ——不需要真正的密钥

```ts
import { TURNSTILE_TEST_SITE_KEY, TURNSTILE_TEST_SECRET_KEY } from "@broberg/forms-turnstile/server";
```

云浮的官职 **总是通过** 测试密钥 – 安全可执行, 安全默认, 因此流的末端工作没有真正的 Turnstile 部件 .

> #### 不要用 E2E 装入 _未解决_ 状态与测试键相对
> 
> 他们解决了 **几乎马上**因此,支票喜欢 _"提交按钮是 在用户解决挑战之前已禁用“_ 正在比赛部件: 您正在尝试证明 存在在一秒钟.
> 
> fd-sundhed在收养时就打到这个了——同一说法在一页中传出 对方失败了, **不是因为应用程序行为不同 而是因为 种族主张**。他们删除了支票,而不是增加一个等待,即 正确的呼唤:通过或失败的测试 都无法证明 向上转 这不是片面测试,而是状态测试 测试键不是 别动.
> 
> 坚持坚持下去的各州—— `solved` 解决后, `failed` 与 联合国 `error` 当你屏蔽脚本。 注意这个陷阱 _我们_ 已经建成, 总是通过键作为默认:方便和比赛 同样的特征.

### 运行时间现场钥匙交付

```ts
// GET /config route — serves the (public) site key at runtime so rotating it
// is a secret change, never a rebuild.
app.get("/config", (c) => c.json(getSitekeyResponse(env.TURNSTILE_SITE_KEY)));
```

## 部件悬钩- 反应( S)`/react`)或预告(`/preact`(中文(简体) )

懒惰地装入 Turnstile 脚本( cached + dedudeed) , 使部件一次成为 站点密钥可用. **两个适配器都是一样的** - 他们 钩子从哪一个包里取出不同,所以一个固定到达两个包.

```tsx
import { useTurnstile } from "@broberg/forms-turnstile/react";   // or /preact

function ContactForm() {
  const { widgetRef, token, status, error, reset } = useTurnstile(siteKey);

  return (
    <form onSubmit={onSubmit}>
      {/* ...fields... */}
      <div ref={widgetRef} data-testid="contact-form-captcha" />
      <button type="submit" disabled={status !== "solved"}>Send</button>
      {status === "failed" && <p role="alert">Spam-tjekket kunne ikke indlæses. {error}</p>}
    </form>
  );
}
```

`siteKey` 也许 `null`编号 :`undefined` 运行时 `/config` 获取在 飞行——读作: `loading`。 。 。 .

### 低键框——渲染选项(v. 4.0)

```tsx
useTurnstile(siteKey, { appearance: "interaction-only", theme: "light", size: "flexible" });
```

未经更改改为 `turnstile.render`数字 : `appearance` (单位:千美元)`"always" | "execute" | "interaction-only"`, (中文(简体) ), `theme` (单位:千美元)`"light" | "dark" | "auto"`, (中文(简体) ), `size` (单位:千美元)`"normal" | "flexible" | "compact"`, (中文(简体) ), `language`与 `"interaction-only"` 仅框 当 Cloudflare 想要点击时出现; 否则 `status` 继续直走 改为 `solved` 无所可见. 选项无法替换站点密钥或 钩回召相. 没有选项, 部件就和0. 3. 0 完全一样.

### 打开提交按钮 `status`,没有打开 `token` (v.2.0) (中文(简体) )

| `status` | 含义 |
| --- | --- |
| `loading` | 没有站点密钥, 或脚本仍在装入中 |
| `ready` | 部件已打开等待用户 |
| `solved` | `token` 有效 —— 这是您唯一需要提交的国家 |
| `failed` | 没有干预,它就无法运作; `error` 说为什么 |

**为什么这很重要.** 在v.2.0之前,钩子只暴露在 `token`空时 符号有两个原因: _用户尚未解析_,以及 _这永远不会 工作_。表单标注在 `!token` 因此显示一个从未提交过的按钮 启用了, 没有任何地方说为什么。 转弯被普通挡住 隐私扩展往往足以使这是普通用户的经验,而不是 边框.

在同一沉默中结束的三种不同的道路——这个脚本未能 装入,一个脚本 **已装入** 时 `window.turnstile` 从来没有出现过 `widgetRef` 从来没有被附着。 三个现在都结束了 `failed` 带有 独立 `error`由fd-sundhed提起,他发现三个人中的第一个由 读取焦油丸.

`reset()` 返回已解开的部件到 `ready`它会 **没有** 移动 a `failed` 部件退出 `failed` 重置一个从未装入的部件无法修复, 和洗钱 进入一个充满希望的状态 会抹去唯一的证据 真正的问题.

### 反应笔记

`react` 是一个可选的对等点( e)`>=18`) (中文(简体) ). 捆绑带 `"use client"`,所以一个 Next.js App路由器项目可以在不包含客户端组件的情况下从客户端组件导入它 标记任何额外的东西——只有反应包携带; `/server` 财务报告和审定财务报表 `/hono` 保持服务器安全.

## Hono 中间软件(`@broberg/forms-turnstile/hono`(中文(简体) )

读取JSON体本身(检查蜂蜜壶场+Turnstile sorder),运行起伏装置,并使用短路 `400` 在街区。 顺便说一句 解剖后的尸体被藏在上下文上 `spamCheckedBody` 因此,你的处理器不重读(已经消耗)请求流.

```ts
import { honoTurnstileMiddleware } from "@broberg/forms-turnstile/hono";

app.post(
  "/api/contact",
  honoTurnstileMiddleware({ secret: env.TURNSTILE_SECRET_KEY, formName: "contact", maxPerHour: 5 }),
  (c) => {
    const body = c.get("spamCheckedBody");
    // ...persist + notify...
    return c.json({ ok: true });
  },
);
```

## 常见问题

### @broberg/forms-turnstile 是什么？

为broberg\.ai车队提供垃圾邮件保护的公有形式原始:蜂窝检测,一个在流程中的IP速率限制器,和Cloudflare Turnstile服务器侧校验\. 无头核心 \+ 一个用于 React and Preact \+ a Hono 中间软件的部件钩\.

### 如何安装 @broberg/forms-turnstile？

在项目目录运行 npm install @broberg/forms-turnstile，或使用 pnpm add / yarn add / bun add。

### @broberg/forms-turnstile 支持 TypeScript 吗？

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

### @broberg/forms-turnstile 支持 ESM 还是 CommonJS？

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

### @broberg/forms-turnstile 可以商用吗？

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

### @broberg/forms-turnstile 的下载量有多少？

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

### @broberg/forms-turnstile 还在维护吗？

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

