@broberg/lens

中文文档
0.3.3发布于 2026/10/28MITTypeScriptESM + CJS零依赖近 30 天下载 2041

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

中文使用文档
文档目录12 节
  1. 这是什么
  2. 合同
  3. Next.js 16 (Stack A) (英语)
  4. 霍诺(B级)
  5. 任何其他框架
  6. 计算会话( 您写的部分)
  7. 核心保障
  8. 写会话——仅在要求时,明确(0.2.1,F036.6)
  9. 只读由您的应用程序执行
  10. 测试-gaps——CI的F086检查(0.3.0,F036.7)
  11. API 密码
  12. 下一个 Auth 应用程序 - @broberg/lens/next-auth

@broberg/ 连

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

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守护进程逐字注射:

{ "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数字 :

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

export const { POST } = createLensRoute({
  principal: "[email protected]", // dedicated, read-only — NEVER [email protected]
  async createSession({ principal, expiresAt }) {
    const value = await signLensCookie(principal, expiresAt);
    return { name: "myapp.session_token", value };
  },
});

霍诺(B级)

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

const app = new Hono();
app.post("/api/lens-session", lensSessionHandler({
  principal: "[email protected]",
  async createSession({ principal, expiresAt }) {
    return { name: "myapp_session", value: await mintSession(principal, expiresAt) };
  },
}));

任何其他框架

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

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: "[email protected]" (或空白) 校长) 掷出 ——镜头身份必须是专用的只读用户.
  • 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} 未知模式 ^ 坏 JSON400,没有薄荷
写入请求, 应用程序没有 writePrincipal403,没有薄荷

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

createLensRoute({
  principal: "[email protected]",              // read
  writePrincipal: "[email protected]",  // 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 (审计必须告诉阅读 从写),它永远不会 [email protected] 页面存档备份,存于互联网档案馆。设置它不 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 无法达到 守护进程,所以同样的扫描仪船在这里:

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 密码

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回波:

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

const handler = createLensMintHandler({
  principal: "[email protected]",
  createSession: nextAuthLensSession({
    authSecret: process.env.AUTH_SECRET!,
    claims: { id: "lens-user-id", email: "[email protected]", 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/* 共享-图书馆家庭.

版本与发布记录

@broberg/lens 共发布 8 个版本,首次发布于 2026/06/11;过去 12 个月发布了 8 个版本。

版本发布日期距今
0.3.3latest2026/10/2811 天前
0.3.22026/10/1227 天前
0.3.02026/10/1029 天前
0.2.12026/10/021 个月前
0.1.32026/07/044 个月前
0.1.22026/06/134 个月前

关于 @broberg/lens 的常见问题

@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 和提交记录判断。