@cedvict/http-guardian

中文文档
0.5.0发布于 2026/10/23MITTypeScriptESM + CJS零依赖近 30 天下载 3468

TypeScript 的配置 HTTP 客户端: auth (career/cookie), 守护,缓存+dedupe, 动态信封解析预设, 通知, 重定向处理 .

中文使用文档
文档目录18 节
  1. 安装
  2. 快例( REST 经典信封)
  3. 二进制回复
  4. 预设
  5. Auth 模式
  6. 警卫
  7. bearerRefreshGuard({ refreshFn, onAuthFailed?, ... })
  8. csrfGuard({ tokenFromCookie?, tokenFromStorage?, tokenFromH…
  9. correlationIdGuard({ headerName?, generator?, readResponseH…
  10. idempotencyGuard({ headerName?, applyTo?, respectExisting? …
  11. 食谱:映射器 le traceId 后端 QQ 哨兵/ OTel 前端
  12. 重定向
  13. 测试
  14. Cookie 会话刷新后卫(单飞、冷却、反跳动)
  15. 通知编辑( 可选)
  16. 预设构建器和别名( sable DX)
  17. 稳定认证刷新策略
  18. 插件( 配置时间)

@cedvict/http-guardian (中文(简体) )

HTTP 核心客户端( 承诺) fetch。 。 。 .

安装

运行时间要求 : 节点 第22页 (或任何现代浏览器/更新运行时间) fetch) (中文(简体) ).

如果你在一个没有全局的环境运行 fetch,明确通过 createHttpClient({ fetch })。 。 。 .

释放(0.0.1-next.*发表于 next dis. 直至a 稳定版本已出炉, latest 指向更旧的预放:从 next,或者将准确的版本标出.

npm i @cedvict/http-guardian@next

快例( REST 经典信封)

import { createHttpClient, bearerAuth, createApiParserRestClassic, memoryCache } from "@cedvict/http-guardian";

const api = createHttpClient({
  baseUrl: "https://api.example.com",
  auth: bearerAuth(() => localStorage.getItem("token")),
  parser: createApiParserRestClassic(),
  cache: memoryCache({ maxEntries: 500 })
});

const res = await api.get<{ id: string; name: string }>("/me", {
  cache: { ttlMs: 30_000, tags: ["me"] }
});

if (res.ok) console.log(res.data.name);
else console.error(res.errors);

二进制回复

使用明确的响应类型进行下载. 成功响应绕过 JSON信封解析器 他们的字节保持不变 错误响应仍然使用 普通内容类型解码器,它保存结构化的 API 错误.

const report = await api.get<Blob>("/reports/monthly", {
  responseType: "blob",
  headers: { accept: "application/pdf" },
});

if (report.ok) {
  const url = URL.createObjectURL(report.data);
  // Open or download the URL, then call URL.revokeObjectURL(url).
} else {
  console.error(report.errors);
}

responseType: "arrayBuffer" 还可用于以字节为导向的处理. 二进制请求未收到默认 Accept: application/json 标题, 并保存缓存/调用密钥 blob 财务报告和审定财务报表 arrayBuffer 答复分开.

预设

  • createApiParserRestClassic()
  • createApiParserLaravel()
  • createApiParserNest()
  • createApiParserGraphQL({ allowPartialData? })
  • createApiParserStructured({ exposeMeta?, honorRetryableHint? }) - 解析器 帮助打字: { success, code, message, details, retryable, issues[], i18n, meta }。 。 。 . {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}那又怎样 AppError 端口 code, (中文(简体) ), message 和布伦打字 retryable后台和冠军category, (中文(简体) ), severity, (中文(简体) ), i18n, (中文(简体) ), traceId, (中文(简体) ), spanId, (中文(简体) ), envelope) 不安不负责任 details. 发布 peut 重置器 retryable (原始内容存档于2017-09-21). de l'enveloppe.
  • createApiParserProblem({ codeMembers?, requestIdHeader? }) - 问题细节 (RFC 9457, application/problem+json) (中文(简体) ). 来自 detail,则 title· ; code 从第一个非空白成员 codeMembers (默认) ["code"], (中文(简体) ), 譬如说. ["rule", "code"]);每个扩展保存在 details.extensions (单位:千美元)ProblemErrorDetails),与 retryAfter (单位:千美元) Retry-After 标题)和 requestId布尔 retryable 成员变成 AppError.retryable。 。 。 . 验证问题给出一个 AppError 每个输入,从 errors (单位:千美元){ detail, pointer })或(或) invalid-params (单位:千美元){ name, reason }) (中文(简体) ).
  • createApiParserDetail({ requestIdHeader? }) ——API不成功 信封:成功体是数据,错误是 { detail, code?, retryAfter?, requestId? }。 。 。 . 字符串 detail 给一个 AppError (单位:千美元)code 保留);422项验证 detail 数组给出一个 AppError 每个输入 field (单位:千美元)loc 无 联合国 body编号 :query/...来源)和 code (议题) type) (中文(简体) ). details (单位:千美元)DetailErrorDetails载体 retryAfter (单位:千美元) Retry-After 标题, 延迟或 HTTP 日期,否则身体字段,以整秒计), requestId (躯体,其他) X-Request-ID, (中文(简体) ), loc 和生的 body。 。 。 .

解析器可以读取一个失败的响应的页眉 : getErrors(raw, status, response?) 接收 { headers } 作为可选的第三个论据.

没有 notifier,客户端使用 consoleNotifier,它每写 失败到控制台。 过 silentNotifier 当应用程序显示时 自有信件来源 result.errors。 。 。 .

Auth 模式

  • bearerAuth(() => token)
  • cookieAuth({ credentials: "include", csrf: { headerName, getToken } })
  • noAuth()

警卫

守卫在无中相器械中.

bearerRefreshGuard({ refreshFn, onAuthFailed?, ... })

刷新 JWT 显示器 单机 : sur 401, 无人知晓 refreshFn() 时间轴:; {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}401 reçues pendant sce sreaking sententent dans une 队列 存档副本 (原始内容存档于2013-10-10). Partagée puis sont rejouées avec le nouveau sord. 刷新音乐, onAuthFailed(error) 排队时的法语和法语 (原始内容存档于2017-10-21) (英语). vorient le 401 d'grounde.

import { createHttpClient, bearerAuth, bearerRefreshGuard } from "@cedvict/http-guardian";

createHttpClient({
  auth: bearerAuth(() => tokenStore.access()),
  guards: [
    bearerRefreshGuard({
      refreshFn: async () => {
        const { accessToken, refreshToken } = await api.refresh();
        tokenStore.set(accessToken, refreshToken);
        return { accessToken, refreshToken };
      },
      onAuthFailed: () => router.navigate("/login"),
      skipRefreshFor: (ctx) => ctx.url.includes("/auth/"),
    }),
  ],
});

csrfGuard({ tokenFromCookie?, tokenFromStorage?, tokenFromHeader?, headerName?, applyTo? })

弹出去头 X-CSRF-Token (défaut) 变种动物 (单位:千美元)POST编号 :PUT编号 :PATCH编号 :DELETE) (中文(简体) ). 安保部门安全 页:1 tokenFromCookie 使用率最高 浏览器( r)typeof document === 'undefined'),le guard shippe silencieusement. (原始内容存档于2018-09-31).

csrfGuard(
  isPlatformBrowser(platformId)
    ? { tokenFromCookie: "csrf_token" }
    : { tokenFromStorage: () => null },
);

correlationIdGuard({ headerName?, generator?, readResponseHeader? })

弹出去头 X-Correlation-Id (UUID v4 par défaut,倒计时) Math.random()+时间戳 si crypto.randomUUID 页:1 (原始内容存档于2018-09-21) (英语). L'ID est 稳定 请求重试 (法语). (原始内容存档于2018-09-26) (英语). Si la réponse contient le même header, 仅此而已 ctx.meta.correlationId 下游插件倾斜 (伐木工,哨兵,奥特尔).

💡 模式“ X- request-Id ” : si ton后端(ops, ELK, Datadog...) 使用 le 页眉 X-Request-Id plut-t que (英语) X-Correlation-Id 倒接缝线 日志前/后端, 更改正则 headerName 数字 :

correlationIdGuard({ headerName: "X-Request-Id" })

(原始内容存档于2018-10-21) (英语). Pas besoin d'OpenTelemetery côté front, ni d'un 二等后卫: C'est le même (原始内容存档于2012-10-12). Mécanisme avec un nom différent. 可组合的avec un OTel traceparent 数字 : {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}什么? {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}什么.

idempotencyGuard({ headerName?, applyTo?, respectExisting? })

弹出去头 X-Idempotency-Key (乌伊德·德福特) POST编号 :PUT编号 :PATCH。 。 。 . 拉克莱斯特 稳定企业重试 ——活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活. 尊重 une Clé 四分卫通过 RequestOptions.idempotencyKey, (中文(简体) ), ctx.idempotencyKey,通过头来指导.

食谱:映射器 le traceId 后端 QQ 哨兵/ OTel 前端

Quand le 后端 renvoie une enveloppe avec un 页面存档备份,存于互联网档案馆 meta.traceId 页:1 spanId(中文(简体) ) - 通过类型 createApiParserStructured 缝纫机用具 追踪空地/活动前端的克恩事件 vers la trace后端对应(et 反向).

{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}蓝色的胸口

  1. correlationIdGuard 格内尔·恩 X-Correlation-Id 支持恢复, 和lit le header de réponse pour exploser l'ID dans 相接(英文) ctx.meta.correlationId。 。 。 .
  2. createApiParserStructured 储存 traceId 编号 : spanId 达斯 errors[].details (en cas d'erreur) (法语). 圣克萨斯圣克萨斯圣克萨斯 通过无宠卫士 dédié.
import {
  createHttpClient,
  bearerAuth,
  correlationIdGuard,
  createApiParserStructured,
  type Guard,
} from "@cedvict/http-guardian";
import { sentryPlugin } from "@cedvict/http-guardian-plugin-sentry";
import { otelPlugin } from "@cedvict/http-guardian-plugin-otel";
import * as Sentry from "@sentry/browser";

// Lit X-Trace-Id / X-Span-Id de la réponse et les pose sur ctx.meta pour
// les plugins downstream (sentry, otel, logger).
const traceMetaGuard: Guard = async (ctx, next) => {
  const res = await next(ctx);
  const traceId = res.headers.get("x-trace-id");
  const spanId = res.headers.get("x-span-id");
  if (traceId || spanId) {
    ctx.meta ??= {};
    if (traceId) ctx.meta.traceId = traceId;
    if (spanId) ctx.meta.spanId = spanId;
  }
  return res;
};

const api = createHttpClient({
  baseUrl: "https://api.example.com",
  auth: bearerAuth(() => token),
  parser: createApiParserStructured(),
  guards: [correlationIdGuard(), traceMetaGuard],
  plugins: [
    sentryPlugin({
      // shouldCapture reçoit le ctx → on lit ctx.meta pour tagger l'event Sentry.
      shouldCapture: ({ status, ctx }) => {
        if (status && status >= 500) {
          Sentry.getCurrentScope().setTags({
            "backend.trace_id": ctx.meta?.traceId ?? "",
            correlation_id: ctx.meta?.correlationId ?? "",
          });
          return true;
        }
        return false;
      },
    }),
    otelPlugin({
      setAttributes: (span, ctx) => {
        if (ctx.meta?.traceId) span.setAttribute("backend.trace_id", ctx.meta.traceId);
        if (ctx.meta?.spanId) span.setAttribute("backend.span_id", ctx.meta.spanId);
        if (ctx.meta?.correlationId) span.setAttribute("correlation_id", ctx.meta.correlationId);
      },
    }),
  ],
});

科特错失,我被贬 createApiParserStructured 揭发déjà traceId 编号 : spanId 达斯 result.errors[0].details 数字 :

const r = await api.post("/orders", { json: payload });
if (!r.ok) {
  const d = r.errors[0]?.details as { traceId?: string; spanId?: string } | undefined;
  console.error("backend trace:", d?.traceId, d?.spanId);
}

重定向

  • redirects: { mode: "follow" | "manual" | "error", maxHops, onRedirect }

测试

见 TESTING.md。 。 。 .

如果您使用基于 cookie 的 auuth , 而您的后端会显示一个更新的端点, 您可以添加安全更新的 guard :

import { createHttpClient, cookieAuth, cookieSafeRefreshGuard, createApiParserRestClassic } from "@cedvict/http-guardian";

const api = createHttpClient({
  baseUrl: "https://api.example.com",
  auth: cookieAuth({ credentials: "include" }),
  parser: createApiParserRestClassic(),
  guards: [
    cookieSafeRefreshGuard({
      refreshPath: "/auth/refresh", // or absolute: "https://auth.example.com/refresh"
      maxRetry: 1,                  // anti-loop (default)
      honorRetryAfter: true         // cooldown on 429 (default)
    })
  ],
});

您可以按请求禁用任何认证/ 新鲜行为 noAuth: true数字 :

await api.get("/public/health", { noAuth: true });

通知编辑( 可选)

如果您提供了自定义通知符并记录/发送事件到遥测,您可以添加一个 redact 函数 从事件有效载荷中去除秘密(例如: url:

import type { Notifier, NotifyEvent } from "@cedvict/http-guardian";

const notifier: Notifier = {
  redact: (e: NotifyEvent) => ({ ...e, url: e.url.replace(/\?.*$/, "") }),
  notify: (e) => console.error(e),
};

预设构建器和别名( sable DX)

如果您想要一个1行设置而不失去访问权限 createHttpClient,使用预设构建器 :

import { createPresetBuilder } from "@cedvict/http-guardian";

const api = createPresetBuilder({ baseUrl: "https://api.example.com" })
  .cookie({ credentials: "include" })
  // opt-in only:
  // .cookieRefresh({ refreshPath: "/auth/refresh", mePath: "/auth/me" })
  .create();

异名( 围绕预设构建器的薄膜):

  • createCookieClient({ baseUrl, refresh?: CookieSafeRefreshGuardOptions | null, ... })
  • createBearerClient({ baseUrl, getToken, ... })

稳定认证刷新策略

  • Cookie 认证: 刷新为 选入 (编) cookieRefresh(...) 或通过 refresh 改为 createCookieClient) (中文(简体) ).
  • 持有者认证: 刷新为 默认禁用。 。 。 .

插件( 配置时间)

plugins 被同步应用于 createHttpClient(...) 以更改选项草案对象.

合同:

  • 应用 顺序
  • 由 name (第一胜负)

版本与发布记录

@cedvict/http-guardian 共发布 22 个版本,首次发布于 2026/02/09;过去 12 个月发布了 22 个版本。

版本发布日期距今
0.5.0latest2026/10/2317 天前
0.4.02026/10/081 个月前
0.2.12026/10/081 个月前
0.2.02026/10/011 个月前
0.1.02026/10/011 个月前
0.0.1-next.17next2026/10/011 个月前

其他发布通道:legacy → 0.3.1,next → 0.0.1-next.17。

关于 @cedvict/http-guardian 的常见问题

@cedvict/http-guardian 是什么?

TypeScript 的配置 HTTP 客户端: auth (career/cookie), 守护,缓存+dedupe, 动态信封解析预设, 通知, 重定向处理 .

如何安装 @cedvict/http-guardian?

在项目目录运行 npm install @cedvict/http-guardian,或使用 pnpm add / yarn add / bun add。

@cedvict/http-guardian 支持 TypeScript 吗?

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

@cedvict/http-guardian 支持 ESM 还是 CommonJS?

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

@cedvict/http-guardian 需要什么版本的 Node.js?

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

@cedvict/http-guardian 可以商用吗?

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

@cedvict/http-guardian 的下载量有多少?

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

@cedvict/http-guardian 还在维护吗?

最新版本 0.5.0 发布于 2026/10/23(17 天前);至今共发布 22 个版本,其中过去 12 个月发布了 22 个;首次发布于 2026/02/09。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。