# @cloudhal/comments 中文文档

规范地址：https://npmcn.com/package/%40cloudhal/comments/

版本：0\.6\.5
npm 元数据更新时间：2026-11-08T22:22:09.595Z
中文译文更新时间：2026-11-10T03:34:46.929Z
软件包许可：Apache-2\.0

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

原始 npm 页面：https://www.npmjs.com/package/%40cloudhal/comments
原文对照：https://npmcn.com/package/%40cloudhal/comments/?lang=original
项目主页：https://cloudhal.dev/
源码仓库：https://github.com/cloudhal-dev/cloudhal

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

- 包体积（解压后）：442 kB，48 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=24
- 运行依赖：3 个
- peer 依赖：vitest \>=3\.0\.0
- 首次发布：2026-10-20T10:50:11.045Z
- 最新版本发布：2026-11-08T22:22:09.265Z
- 近 30 天下载量：4509（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：26（近 12 个月 26 个）

## 中文简介

Cloudhal阅读器评论(RFC-0007):设置和线程状态,文本常态化和内容规则,链接,工作第一人证,提交管道,清洗计划,保留计划和SQLite/D1商店. 工人安全无虞.

## 安装

```sh
npm install @cloudhal/comments
```

也可以使用：`pnpm add @cloudhal/comments`、`yarn add @cloudhal/comments`、`bun add @cloudhal/comments`

## README

# @cloudhal/评论

对Cloudhal网站的第一读者评论[RFC-0007 (英语)](https://npmcn.com/package/docs/rfcs/0007-reader-comments.md), (中文(简体) ), [ADR-0022 (英语)](https://npmcn.com/package/docs/adr/0022-reader-comments.md)) (中文(简体) ). 特邀评论为节制,在D1直播 **站点外图**,被服务器送入缓存文章页面并被标签清除(`cloudhal-comments-<nodeId>`在自由清洗预算范围内。 Bots支付时附有工作第一人证;Turnstile是选入.

这里的一切 **工人安全**: 任何条目均不只导入节点。 存储和返回的记录是 `Comment` 合同合同 `@cloudhal/core-contracts`; 没有 API 返回电子邮件地址、 IP 散列或外部的挑战 `commenter` 访问器(invariant C1).

| 条目 | 什麽 |
| --- | --- |
| `@cloudhal/comments` | 设置模式和 `threadState`、规范化、内容规则、链接、工作证明、提交/报告/质疑管道及其答案、钥匙和散列、隔离限制、清洗规划员、保留计划。 不依赖存储 . |
| `@cloudhal/comments/sql` | `SqlCommentStore` 结束 `SqlDriver` (SQLite 本地和节点,D1 Cloudflare)用于迁移 `0016_comments`。 。 。 . |
| `@cloudhal/comments/client` | 浏览器来源( 只有节点构建输入) : 注释模块 (`/_cloudhal/c.<hash>.js`, 4 KB gzipped)及其工作证明工人(`/_cloudhal/cw.<hash>.js`,纯-JS SHA-256, \* \* 1 KB),由该建筑公司建造 `cloudhal()` 整合;终点名称和预算. |
| `@cloudhal/comments/testing` | `describeCommentStoreConformance`, (中文(简体) ), `describeCommentPipelineSuite`, (中文(简体) ), `countingDriver`, (中文(简体) ), `expectContractComment` (vitest) (英语). |
| `@cloudhal/comments/fixtures/comment-text.json` | 共同的链接规则定律`parseCommentText` 用于渲染器和工作室的块. |

## 设置和线程状态(§4.5)

```ts
import { applySettingsInput, defaultCommentSettings, threadClosesAt, threadState } from "@cloudhal/comments";

const settings = await store.settings(siteId); // the row, or the defaults (off, rev 0)
threadState({ settings, entry: { nodeId, collection: "posts", publishedAt }, thread, hasApproved, now });
// → "open" | "closed" | "hidden" | "off"   (90 days after first publication by default)
```

`CommentSettingsInput` 由 `applySettingsInput` (拒收未知密钥; `CommentSettingsError`代码 `invalid_settings`) (中文(简体) ). `collectionEnabled`, (中文(简体) ), `closeAfterDaysFor`, (中文(简体) ), `threadVisible` 财务报告和审定财务报表 `DEFAULT_RETENTION` 也输出.

## 案文(第6.4节,第8.1节)

-   `normalizeText`, (中文(简体) ), `normalizeName`, (中文(简体) ), `normalizeEmail`, (中文(简体) ), `checkText`, (中文(简体) ), `checkName`, (中文(简体) ), `checkEmail`, (中文(简体) ), `codePoints`, (中文(简体) ), `cutCodePoints`, (中文(简体) ), `CONTRACT_FORBIDDEN` ——NFC,CRLF → LF, Control and bidi字符被去除(合同拒绝),代码分出长度.
-   `evaluateContent({ name, text }, { linkLimit, watchWords, blockedWords })` → `{ ok, status: "pending" | "spam", labels }` 或者说 `{ ok: false, field, problem }`· ; `compileWordList`, (中文(简体) ), `matchesWordList`, (中文(简体) ), `wordTokens`, (中文(简体) ), `foldForMatch` (NFKC,土耳其语) `İ`编号 :`I`编号 :`ı` 叠成一团, `countLinks`, (中文(简体) ), `instructionLikeRules`编号 :`isInstructionLike` (单位:千美元)`INSTRUCTION_RULES_VERSION`页:1, `ruleLabels`。 。 。 .
-   **链接 :** `parseCommentText(text)` 页:1 `{kind:"text"}|{kind:"link", href}` 段; `safeCommentHref`, (中文(简体) ), `linkifyCommentLine`, (中文(简体) ), `linkify`· ; `commentTextHtml(text)` 用于服务器交付的 HTML(已退出, `<p>`编号 :`<br>`, (中文(简体) ), `rel="ugc nofollow noopener noreferrer"` =: `COMMENT_LINK_REL`) (中文(简体) ). 仅 `https:` 有被点化的主机且没有用户信息成为链接的 URL 。 规矩相符 `@cloudhal/ui`临时 `comment-model.ts`; 固定针接住他们.

## 工作证明(第7条)

```ts
import { deriveCommentKeys, difficultyFor, issueChallenge, verifyChallenge, ReplayCache } from "@cloudhal/comments";
const keys = await deriveCommentKeys(env.CLOUDHAL_COMMENTS_KEY); // HKDF per purpose: pow, emailHmac, email, secrets
const { challenge, bits, expiresAt } = await issueChallenge(keys.pow, { siteId, target: nodeId, kind: "comment", bits: difficultyFor(today, cap), now });
```

`v1.<payload>.<mac>`无国籍者 10分钟 3秒 满分 18分升到20分21. `solveChallenge` 是参考解析器(测试,浏览器工人的算法). 单一用途: `ReplayCache` 每个隔离加唯一索引 `challenge_id`。 。 。 .

## 输油管(第6.2-6.6节)

```ts
import { handleCommentSubmission, handleReportSubmission, handleChallengeRequest,
         submissionResponse, challengeResponse, CommentLimiter, DailyCount, ReplayCache, memoize } from "@cloudhal/comments";
import { SqlCommentStore } from "@cloudhal/comments/sql";

// once per isolate
const limiter = new CommentLimiter(), daily = new DailyCount(), replay = new ReplayCache();
const settings = memoize(() => store.settings(env.CLOUDHAL_SITE_ID)); // 60 s

const deps: PipelineDeps = {
  siteId: env.CLOUDHAL_SITE_ID, available: true, hosts, clientAddress: request.headers.get("cf-connecting-ip")!,
  keys, now: () => new Date(), settings, resolveEntry, store, limiter, daily, replay,
  rateLimit?: (key) => …,           // optional Rate Limiting binding
  turnstile?: ({ token, remoteip, idempotencyKey }) => siteverify(…), // when the site uses Turnstile
};
return submissionResponse(await handleCommentSubmission(request, deps));
// reports: submissionResponse(await handleReportSubmission(request, deps), { pendingFragment: "comment-reported" })
// challenges: challengeResponse(await handleChallengeRequest(request, deps))
```

`resolveEntry(nodeId)` 返回时 **已出版** 条目(`{ nodeId, collection, path, publishedAt }`)或(或) `null`。结果 `{ kind: "pending", stored, dropped? }` (蜂窝、糟糕的证明、重放、重复和禁止与存储的评论无法区分)或 `{ kind: "error", status, error, fields?, retryAfter? }`; 表格状态 `303` 改为 `#comment-pending`, (中文(简体) ), `#comment-invalid`, (中文(简体) ), `#comment-closed`, (中文(简体) ), `#comment-script-required`, (中文(简体) ), `#comment-slow-down`, (中文(简体) ), `#comment-try-again`, (中文(简体) ), `#comment-paused`。在步骤12之前,没有任何文字;在步骤12的批次内插入一个月的第一个IP盐.

## 储存`@cloudhal/comments/sql`(中文(简体) )

`new SqlCommentStore(sql, { now? })`数字 :

| 区域 | 方法 |
| --- | --- |
| 设置、秘密 | `settings`, (中文(简体) ), `putSettings(siteId, input, rev, by)`, (中文(简体) ), `putSecret`, (中文(简体) ), `readSecret`, (中文(简体) ), `secretInfo`, (中文(简体) ), `deleteSecret` |
| 线索 | `thread`, (中文(简体) ), `putThread(siteId, nodeId, { mode, closesAt? }, rev, by, { purge?, audit? })` |
| 管道( R)`SubmissionStore`(中文(简体) ) | `submit`, (中文(简体) ), `report`, (中文(简体) ), `approvedComment`, (中文(简体) ), `readSalt`, (中文(简体) ), `clientActivity`, (中文(简体) ), `isDuplicate`, (中文(简体) ), `storedSince`, (中文(简体) ), `pendingOnEntry`, (中文(简体) ), `isBanned`, (中文(简体) ), `reportActivity` |
| 读取 | `get`, (中文(简体) ), `list(siteId, { status | "reported", nodeId, nodeIds, label, since, cursor, limit ≤ 50, order })`, (中文(简体) ), `threadComments`, (中文(简体) ), `renderPage(siteId, nodeId, page)`, (中文(简体) ), `counts`, (中文(简体) ), `history`, (中文(简体) ), `stateHash(siteId, ids, threads)` (单位:千美元)`cm1:`(中文(简体) ) |
| 调和 | `moderate(siteId, { ids ≤ 50, action, actor, reasonCode?, note?, expectedRevs, approvalId?, ownNodeIds?, audit? })` · 每项评论结果; `label(siteId, { items, actor, source, expectedRevs?, ownNodeIds?, audit? })`· ; `reply(siteId, StaffReply)`· ; `editReply` |
| 只有一个人 | `findCommenter`, (中文(简体) ), `commenter(siteId, id, keys)` (解密电子邮件), `commenters(siteId, ids, keys)` (三个读任何数字), `commenterComments(siteId, id)` (一读), `hideApprovedOf(siteId, commentId, { actor, reasonCode })` (范围, `more`, (中文(简体) ), `erase(siteId, { commenterId } | { commentIds }, "anonymise" | "delete-text", person, { note?, audit? })`, (中文(简体) ), `ban`, (中文(简体) ), `listBans(siteId, { commentId? })` (评论员禁止发表评论之一), `liftBan` |
| 报告 | `listReports`, (中文(简体) ), `resolveReport` |
| `purgeQueue` | `queued`, (中文(简体) ), `sites`, (中文(简体) ), `enqueue`, (中文(简体) ), `recent`, (中文(简体) ), `log` (出版路径的清洗日志行), `sent`, (中文(简体) ), `failed` ——饲料 `planPurge` |
| `retention` | `run(siteId, retentionPlan(settings.retention, now))`, (中文(简体) ), `deleteEntries(siteId, deletedEntriesDue(…))`, (中文(简体) ), `runDeployment(now, oldestSaltMonthToKeep(now))` |

`approve`, (中文(简体) ), `restore`, (中文(简体) ), `delete`答复、消除和禁止需要一个人类行为者(`CommentForbiddenError`代码 `forbidden`); stale (中文(简体) ) `rev`这是 `CommentConflictError` (单位:千美元)`conflict`)用于单笔写作和评论 `conflict` 批量写作的结果. `audit` 声明附在同一批,最后一批。 每条路径的报表计数在店面上,并由合规套件申明.

## 清洗计划和保留(第8.4节,第9.3节)

`planPurge({ rows, recent, now })` → `{ requests, nextCheckAt, takedownWaiting }`:一次性取出(通常的标记会沿去取出),20 s后正常的标记,而最后一分钟显示的清除请求还不到3个(包括了),100个标记会请求. `purgeBackoffMs`, (中文(简体) ), `settingsPurgeTags` (或者300个条目以上的"一切"), `commentsCacheTag`, (中文(简体) ), `PURGE_LIMITS`。 。 。 . `retentionPlan` 列出受约束的步数(每跑500行).

## 常见问题

### @cloudhal/comments 是什么？

Cloudhal阅读器评论\(RFC-0007\):设置和线程状态,文本常态化和内容规则,链接,工作第一人证,提交管道,清洗计划,保留计划和SQLite/D1商店\. 工人安全无虞\.

### 如何安装 @cloudhal/comments？

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

### @cloudhal/comments 支持 TypeScript 吗？

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

### @cloudhal/comments 支持 ESM 还是 CommonJS？

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

### @cloudhal/comments 需要什么版本的 Node\.js？

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

### @cloudhal/comments 可以商用吗？

@cloudhal/comments 使用 Apache-2\.0 许可。宽松许可：允许商业使用、修改和再分发，并附带明确的专利授权；分发时需保留许可与 NOTICE 文件，修改过的文件要注明改动。以上为概括，不构成法律意见，请以许可原文为准。

### @cloudhal/comments 的下载量有多少？

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

### @cloudhal/comments 还在维护吗？

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

