# @broberg/lens 中文文档

规范地址：https://npmcn.com/package/%40broberg/lens/

版本：0\.3\.3
npm 元数据更新时间：2026-10-28T09:48:06.893Z
中文译文更新时间：2026-11-08T16:59:38.439Z
软件包许可：MIT

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

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

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

- 包体积（解压后）：274 kB，50 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- 运行依赖：0 个
- peer 依赖：hono \>=4，next-auth \>=5\.0\.0-beta\.0
- 首次发布：2026-06-11T11:17:58.028Z
- 最新版本发布：2026-10-28T09:48:06.660Z
- 近 30 天下载量：2041（2026-10-09 至 2026-11-07，npm 官方下载统计）
- 已发布版本数：8（近 12 个月 8 个）

## 中文简介

使任何符合Cardmem-Lens的应用程序:无头 POST /api/lens-session mint end point (narrow Bearer – 短寿命,只读 Playwright 存储状态),使Lens能够登录过认证墙并截取真实的被认证的表面,包括被prod. 框架不可知性

## 安装

```sh
npm install @broberg/lens
```

也可以使用：`pnpm add @broberg/lens`、`yarn add @broberg/lens`、`bun add @broberg/lens`

## README

# @broberg/ 连

做任何应用 **符合卡通要求** 几行:暴露出舰队标准 **薄荷终点** 这样一来Lens就能登录 _通过认证墙_ 并截图 **真实 认证表面** ——包括生产中——而不是登录页.

```bash
npm i @broberg/lens
```

## 这是什么

纸牌 **镜头** 验证表面用户实际看到, 这几乎总是 **登录后**镜头不能硬编码每个应用程序的认证,所以每个应用程序都暴露一个 薄荷a的终点 **寿命短,只读** 按需开会; Lens 称之为 在捕捉之前,使用会话,然后丢弃它.

合同在每次回购中都是相同的——只有 _如何薄荷会话_ 不一样. 所以这个包裹 装上制服,安全敏感 **~80%** 作为无头核; 您提供了特定认证 **20%** 联合国 `createLensSession` 钩上薄荷和标志 自己的会话饼干.

> 执行机队F098.1 薄荷标准(卡通) `docs/LENS-MINT-ENDPOINT.md`) (中文(简体) ). `components` 拥有 + 发布; cardem 拥有 spec.

## 合同

`POST /api/lens-session`标题 `Authorization: Bearer <LENS_MINT_SECRET>` → **200个** 有剧作家 **存储状态** JSON Lens守护进程逐字注射:

```json
{ "cookies": [ { "name": "<session-cookie>", "value": "<signed>", "domain": "<host>",
    "path": "/", "httpOnly": true, "secure": true, "sameSite": "Lax",
    "expires": 1733430000 } ],
  "origins": [] }
```

`expires` 实值 **unix 秒**。核心将填补每个字段,但 `name`编号 :`value`。 。 。 .

## Next.js 16 (Stack A) (英语)

`app/api/lens-session/route.ts`数字 :

```ts
import { createLensRoute } from "@broberg/lens/next";
import { signLensCookie } from "@/lib/auth"; // your app's signing

export const { POST } = createLensRoute({
  principal: "lens@myapp.local", // dedicated, read-only — NEVER cb@webhouse.dk
  async createSession({ principal, expiresAt }) {
    const value = await signLensCookie(principal, expiresAt);
    return { name: "myapp.session_token", value };
  },
});
```

## 霍诺(B级)

```ts
import { Hono } from "hono";
import { lensSessionHandler } from "@broberg/lens/hono";

const app = new Hono();
app.post("/api/lens-session", lensSessionHandler({
  principal: "lens@myapp.local",
  async createSession({ principal, expiresAt }) {
    return { name: "myapp_session", value: await mintSession(principal, expiresAt) };
  },
}));
```

## 任何其他框架

直接用普通请求调用核心处理器 :

```ts
import { createLensMintHandler } from "@broberg/lens";

const handle = createLensMintHandler({ principal, createSession });
const res = await handle({ authorization, host, secure }); // → { status, body }
```

## 计算会话( 您写的部分)

> ⚠️ **发布一个真实的会话 cookie , 您自己接受** - 同一个你 SPA的认证门检查( P)`getSession()` /你的JWT中间软件。 合成符号 可以认证 API 路由( 因此) `curl` 似乎绿色),但SPA仍然反弹到 登录,而Lens抓取一个登录墙. 通过您框架的真实会话薄荷 机制.

-   **更好的认证 :** 饼干是签名的(`<token>.<sig>`) (中文(简体) ). 通过 `auth.$context → internalAdapter.createSession(userId, ctx)`时,将其过期限制为 `expiresAt`,然后序列化签名的饼干。 饼干名= `<prefix>.session_token`。 。 。 .
-   **苏帕克:** 薄荷服务器侧面, 有专用镜头的服务作用键 用户; 返回 `sb-<ref>-auth-token` 饼干 `@supabase/ssr` 读取.
-   **自定义JWT(吉斯/HS256):** 为镜头签出短寿命只读 JWT 校长; 还原为您的会话饼干 .

## 核心保障

-   **暗舰:** `503` 直至 `LENS_MINT_SECRET` 设置( 逐项阅读 —— 翻转) 打开而不重新启动.
-   **`401` + 常数** 无记名比较( e)`crypto.timingSafeEqual` 超过SHA-256 文摘——长度独立,从不掷出.
-   **从不cb@ :** 以 `principal: "cb@webhouse.dk"` (或空白) 校长) **掷出** ——镜头身份必须是专用的只读用户.
-   **TTL 夹口** 改为 `[60s, 10min]` (违约10min); `expiresAt` 交给你 钩子,所以你夹住你的会话行 到相同的TTL.
-   **基本费率限额** (违约30分) `429`。 。 。 .
-   **Cookie 域名** =: `cookieDomain ?? LENS_COOKIE_DOMAIN ?? request host` (开口) 剥了,所以 `localhost:3000` dev 构建生成一个有效的 `localhost` (域名)—— 绝不是绑定的套接字地址( n)`0.0.0.0` 在飞行/代用主机上),从不 端口范围值。 在一个 **网页** 解决当地宿主家庭的要求 没有明确覆盖的主机,核心 **`console.warn`大声点** - 后面一个 反向代理是一个沉默的假绿色( set) `LENS_COOKIE_DOMAIN`) (中文(简体) ). 真实 http localhost dev stay system, 所以一个Docker dev-building sandbox只是工作.

## 写会话——仅在要求时,明确(0.2.1,F036.6)

有些表面需要Lens来ACT,而不只是看(helpdesk:Lens playing the 接待员和现场聊天时写作). 卡梅的标准:薄荷门给 (a) 默认的READ本金 每张薄荷纸都要经过审核.

| 请求正文 | 结果 |
| --- | --- |
| 无/ 为空 / `{}` 编号 : `{"mode":"read"}` | 改为本金,完全为0.1.x |
| `{"mode":"write","writes":true}` | 写入主笔, 如果应用程序允许写入 |
| `{"mode":"write"}` • 妇女 `{"writes":true}` 未知模式 ^ 坏 JSON | `400`,没有薄荷 |
| 写入请求, 应用程序没有 `writePrincipal` | `403`,没有薄荷 |

两种密钥, 目的: 一个字段是 一种字段, 或者默认不写 访问.

```ts
createLensRoute({
  principal: "lens@myapp.local",              // read
  writePrincipal: "lens-writer@myapp.local",  // write: a SEPARATE identity
  async onWriteMint({ principal, host, expiresAt, at }) {
    await audit.insert({ kind: "lens.write-mint", principal, host, expiresAt, at });
  },
  async createSession({ principal, mode, expiresAt }) {
    // mint a write-capable role ONLY when mode === "write"
    const value = await signMySessionCookie(principal, expiresAt, { role: mode === "write" ? "reception" : "readonly" });
    return { name: "myapp_session", value };
  },
});
```

-   **写字关了** 直至 `writePrincipal` 已经设定。 它必须与 `principal` (审计必须告诉阅读 从写),它永远不会 [cb@webhouse.dk 页面存档备份,存于互联网档案馆](mailto:cb@webhouse.dk)。设置它不 `onWriteMint` 启动时抛出.
-   **审计未能结束.** `onWriteMint` 运行后 `createSession` 财务报告和审定财务报表 在饼干还原之前 如果抛出,反应是 `500` 没有 cookie left; 薄荷行仅与 TTL (最大为 10 min) 相通而过期.
-   `ctx.mode` (单位:千美元)`"read"` | `"write"`)是新开的 `LensSessionContext`密码 只有 READS 上下文不受影响; 代码为 BUILDS 1( 测试固定) 必须添加 `mode`。 。 。 .

## 只读由您的应用程序执行

此包薄荷表示会话; 它确实 **没有** 从内部执行只读 终点 添加服务器侧 **书写护卫**: 如果经认证的本金是 镜头用户,拒绝每个突变请求(`POST`编号 :`PUT`编号 :`PATCH`编号 :`DELETE` + 写入 (RPC/工具)与 `403`。给镜头本金足够 _读取_ 获取 目标表面(常读取行政级别)。 PII 表面, 捕获 `no_diff` 烟 -- -- 从未有存储像素基线.

> 运行在 **节点运行时间** (核心用途) `node:crypto`)——不是"下一个边缘" 运行时间。 Mint 端点击中一个 DB 来创建会话,所以节点是正确的.

## 测试-gaps——CI的F086检查(0.3.0,F036.7)

每个互动要素必须包含 `data-testid` (舰队的F086规则). 这个 Cardmem 守护进程在本地检查(`POST /lens/testid-gaps`); CI 无法达到 守护进程,所以同样的扫描仪船在这里:

```bash
npx -p @broberg/lens testid-gaps src     # or: testid-gaps src, with the package installed
```

| 退出 | 表示 |
| --- | --- |
| `0` | 已扫描文件, 没有空白—— 打印它读取的文件数 |
| `1` | 差距——1个 `file:line <tag> without data-testid …` 每个差距 |
| `2` | 使用错误,或 **无源文件扫描** (一条错误的道路不能通过一扇门) |

文件扫描仪携带处理器时无法查看(hyperscript, `h('button', { onClick })`)在 stderr 上命名为 _未审查_他们不是 空白,它们也不干净.

在代码中 : `import { inventoryInteractiveGaps, gapsInFile } from "@broberg/lens/testid-gaps"` (只有节点).

**规矩是卡梅伦的**,逐字移动 `manifest.ts` @ cc3ad9e) 与 他们的测试。 何谓互动是在那里决定的;一个变化经过 所以守护进程和CI从来不会有分歧.

## API 密码

```ts
interface LensCookie { name: string; value: string; domain?: string; path?: string;
  httpOnly?: boolean; secure?: boolean; sameSite?: "Lax" | "Strict" | "None"; expires?: number; }
interface LensSessionContext { principal: string; mode: "read" | "write"; host: string; secure: boolean; ttlMs: number; expiresAt: number; }
type CreateLensSession = (ctx: LensSessionContext) => Promise<LensCookie | LensCookie[]> | LensCookie | LensCookie[];
interface LensMintOptions { secret?: string; createSession: CreateLensSession; principal: string;
  ttlMs?: number; cookieDomain?: string; maxPerMinute?: number;
  writePrincipal?: string; onWriteMint?: (e: { principal: string; host: string; expiresAt: number; at: number }) => void | Promise<void>; }

function createLensMintHandler(opts: LensMintOptions): (req: { authorization: string | null; host: string; secure: boolean; body?: string | null }) => Promise<{ status: number; body: unknown }>;
// @broberg/lens/next → createLensRoute(opts): { POST(req: Request): Promise<Response> }
// @broberg/lens/hono → lensSessionHandler(opts): (c: Context) => Promise<Response>
// @broberg/lens/next-auth → nextAuthLensSession(opts): (ctx: LensSessionContext) => Promise<LensCookie>
```

## 下一个 Auth 应用程序 - `@broberg/lens/next-auth`

一个现成的 `createSession` 钩子 **下一个 Auth( v5)** 应用,所以你不用手卷 jWT 编码 + 微妙的饼干 得到了每个下Auth-behind-login回波:

```ts
import { createLensMintHandler } from "@broberg/lens";
import { nextAuthLensSession } from "@broberg/lens/next-auth";

const handler = createLensMintHandler({
  principal: "lens@myapp.local",
  createSession: nextAuthLensSession({
    authSecret: process.env.AUTH_SECRET!,
    claims: { id: "lens-user-id", email: "lens@myapp.local", name: "Lens" },
    // `secure` defaults to NODE_ENV === "production" (NextAuth's useSecureCookies)
  }),
});
```

`next-auth` 是一个 **可选同伴依赖** (进口懒散;通过习惯) `encode` 为了避免它。 帮手抓了3个经过战斗测试的人 所以你不能:

1.  **`salt` 必须等于饼干名称** 下一个 Auth `getToken` 生成 JWE 键 与 `salt = cookie name`; 不匹配的无声解码为 `null`。 。 。 .
2.  **`__Secure-` 前缀 qq `secure: true`** 安全模式驱动 cookie 名称 (单位:千美元)`__Secure-authjs.session-token` 对 `authjs.session-token`盐,和 安全旗帜,一起.
3.  **铬拒绝安全饼干 `{domain}` 一个人** ——卡德明连锁店 守护进程合成一个 https source- URL 这样 `{domain, path}` 全舰作业 非守护天洞,设置 `url` 而不是饼干.

麻省理工学院的一部分 [`@broberg/*`](https://github.com/broberg-ai/components) 共享-图书馆家庭.

## 常见问题

### @broberg/lens 是什么？

使任何符合Cardmem-Lens的应用程序:无头 POST /api/lens-session mint end point \(narrow Bearer – 短寿命,只读 Playwright 存储状态\),使Lens能够登录过认证墙并截取真实的被认证的表面,包括被prod\. 框架不可知性

### 如何安装 @broberg/lens？

在项目目录运行 npm install @broberg/lens，或使用 pnpm add / yarn add / bun add。它还提供命令行工具 testid-gaps，可以用 npx testid-gaps 直接运行。

### @broberg/lens 支持 TypeScript 吗？

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

### @broberg/lens 支持 ESM 还是 CommonJS？

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

### @broberg/lens 可以商用吗？

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

### @broberg/lens 的下载量有多少？

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

### @broberg/lens 还在维护吗？

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

