@andbridge/glowpod
v3.3.1
Published
A axios-based HTTP client library with cross-platform AES encrypt/decrypt support (Browser / Node.js / React Native)
Maintainers
Readme
@andbridge/glowpod
基于 axios 的跨平台 HTTP 客户端:统一拦截器、AES 加解密、结构化错误派发。Node.js / Browser / React Native 三端零配置开箱即用。
Cross-platform axios-based HTTP client: unified interceptors, AES encrypt/decrypt, structured error dispatching. Zero-config out-of-the-box for Node.js / Browser / React Native.
目录 / Table of Contents
- @andbridge/glowpod
- 目录 / Table of Contents
- 简介 / Introduction
- 安装 / Installation
- 30 秒上手 / 30-Second Quick Start
- 平台支持 / Platform Support
- 核心能力 / Core Capabilities
- 进阶 / Advanced
- API 参考 / API Reference
- 常见问题 / FAQ
- License
简介 / Introduction
@andbridge/glowpod 在 axios 之上提供三个核心能力,让前端 / Node 业务侧只写成功路径:
- 拦截器开箱即用:一行
setupInterceptors()自动注册请求加密、响应解密、业务码解包、错误分类派发 - 透明加解密:设置
encryptKey即可启用 AES 加解密,无需关心底层算法 - 结构化错误:HTTP 失败 / 业务码失败 / 网络断开 / 主动取消 自动分类到不同回调
A drop-in enhancement over axios that provides three core capabilities, letting business code focus only on the success path:
- Out-of-the-box interceptors: One-line
setupInterceptors()auto-registers request encryption, response decryption, business code unwrapping, and error categorization - Transparent encryption: Set
encryptKeyto enable AES encryption/decryption, no need to know the underlying algorithm - Structured errors: HTTP failures, business code failures, network errors, and cancellations are auto-dispatched to different callbacks
// 业务侧只需 3 行 / Business code needs only 3 lines
import { GlowPod, setupInterceptors } from "@andbridge/glowpod";
const pod = new GlowPod({ baseURL: "https://api.example.com" });
setupInterceptors({ instance: pod.getInstance() });
await pod.Get<User>({ url: "/users/1" }); // 直接拿到 User 类型 / Get User typed directly安装 / Installation
按平台安装对应依赖:
Install platform-specific dependencies:
Node.js
pnpm add @andbridge/glowpod
# 或 / or npm install / yarn addNode 端无需额外 peer 依赖(使用内置 node:crypto)。
No extra peer dependencies needed on Node.js (uses built-in node:crypto).
Browser
pnpm add @andbridge/glowpod @noble/ciphers浏览器使用纯 JS 的 @noble/ciphers(Web Crypto API 不支持 ECB)。
Browser uses pure-JS @noble/ciphers (Web Crypto API doesn't support ECB).
React Native
pnpm add @andbridge/glowpod react-native-quick-crypto
cd ios && pod installRN 端使用原生 AES 实现的 react-native-quick-crypto(性能比纯 JS 快 3-5x)。
RN uses react-native-quick-crypto for native AES (3-5x faster than pure JS).
⚠️ 每个平台的 peer 是编译期硬绑定的。bundle 内仅引用本端 peer,未安装时直接抛
Cannot find module,无运行时 fallback。Each platform's peer is compile-time hard-bound. The bundle only references its own peer; if not installed, it throws
Cannot find moduledirectly — no runtime fallback.
30 秒上手 / 30-Second Quick Start
// main.ts
import { GlowPod, setupInterceptors } from "@andbridge/glowpod";
// 1. 创建实例 / Create instance
const pod = new GlowPod({ baseURL: "https://api.example.com" });
// 2. 配置拦截器 / Setup interceptors
setupInterceptors({
instance: pod.getInstance(),
networkFailedCaller: (response) => {
if (response?.status === 401) location.href = "/login";
else console.error("请求失败", response);
},
});
// 3. 发起请求 / Make request
const users = await pod.Get<{ list: User[] }>({
url: "/api/v1/users",
params: { page: 1 },
});
console.log(users.list);完成。拦截器、错误处理、加解密都已就绪。
Done. Interceptors, error handling, and encryption are all ready.
平台支持 / Platform Support
| 平台 / Platform | 加密实现 / Crypto Backend | 最低版本 / Min Version | 必装 peer / Required Peer |
| --------------- | ----------------------------- | ---------------------- | ------------------------------- |
| Node.js | node:crypto (AES-ECB) | Node 16+ | 无 / None |
| Browser | @noble/ciphers/aes.js | 现代浏览器 / Modern | @noble/ciphers |
| React Native | react-native-quick-crypto | RN 0.74+ | react-native-quick-crypto |
axios 已内置打包(~42KB),消费方无需安装。
axios is bundled internally (~42KB); consumers don't need to install it.
统一密文格式:AES-ECB + PKCS7 + Base64(三端互通)。
Unified ciphertext format: AES-ECB + PKCS7 + Base64 (interoperable across platforms).
核心能力 / Core Capabilities
基本 CRUD / Basic CRUD
GlowPod 提供 Get / Post / Put / Delete 四个泛型方法,所有 axios AxiosRequestConfig 字段平铺在 Options 中。
GlowPod provides Get / Post / Put / Delete as four generic methods; all axios AxiosRequestConfig fields are flattened in Options.
// GET - params 作为 query
const res = await pod.Get<PageResult<User>>({
url: "/api/v1/users",
params: { page: 1, size: 20 },
});
// POST - params 作为 body
const created = await pod.Post<User>({
url: "/api/v1/users",
params: { name: "Alice", age: 18 },
});
// PUT
await pod.Put<User>({ url: "/api/v1/users/1", params: { name: "Bob" } });
// DELETE
await pod.Delete<null>({ url: "/api/v1/users/1" });默认 params 行为:GET / DELETE → query string;POST / PUT → request body。
Default params behavior: GET / DELETE → query string; POST / PUT → request body.
统一错误处理 / Unified Error Handling
setupInterceptors 按场景自动派发到三个回调,无需业务侧写 try/catch。
setupInterceptors auto-dispatches to three callbacks by scenario — no try/catch in business code.
| 触发条件 / Trigger Condition | 派发目标 / Dispatched To |
| ---------------------------------------------------------------- | ------------------------------------------------- |
| 网络层错误(断网 / 超时 / DNS 失败 / CORS 拒绝 / 后端无响应) | serverKillCaller(error) — 错误对象本身 |
| HTTP 失败(response.status 不在 [200,299]) | networkFailedCaller(error.response) |
| 业务码失败(data[codeName] !== businessSuccessCode) | networkFailedCaller(error.response) |
| 拦截器内部抛错(加解密失败 / setHeaders / beforeRequest 等) | networkFailedCaller(error.response) |
| 主动取消(AbortController.abort()) | requestCancelCaller(error)(默认静默) |
setupInterceptors({
instance: pod.getInstance(),
responseStruct: {
codeName: "code", // 业务码字段名 / Business code field name
dataName: "data", // 业务数据字段名 / Business data field name
messageName: "message", // 业务消息字段名(仅用于回调内自取)
},
networkFailedCaller: (response) => {
// response 是 AxiosError.response(HTTP 非 2xx / 业务码失败 / 内部抛错)
if (response?.status === 401 || response?.status === 403) {
location.href = "/login";
return;
}
const msg = (response?.data as any)?.message ?? "请求失败";
toast.error(msg);
},
serverKillCaller: (error) => {
// error 是 AxiosError(含 request 字段、无 response 字段)
toast.error("网络异常,请检查连接");
},
requestCancelCaller: (error) => {
console.log("请求已取消", error);
},
});响应解包规则(HTTP 2xx 时按 responseStruct 解包):
Response unwrapping rules (HTTP 2xx unwrapped by responseStruct):
| 场景 / Scenario | 返回 / Returns |
| --------------------------------------------------------------------- | --------------------------------------- |
| data[codeName] == businessSuccessCode 且 data[dataName] 存在 | response.data[dataName] |
| data[codeName] == businessSuccessCode 且 data[dataName] 缺失 | response.data |
| data[codeName] 字段不存在 | response.data |
| responseStruct: null | response.data(不解包) |
| data[codeName] 存在但 !== businessSuccessCode | 抛 BusinessError → networkFailedCaller |
短路默认处理:在 catch 中调用 hiddenDefaultErrorHandler() 可阻止默认回调:
Bypass default handling: Call hiddenDefaultErrorHandler() in .catch() to suppress default callbacks:
import type { GlowPodRequestError } from "@andbridge/glowpod";
pod.Get({ url: "/users/1" }).catch((err: GlowPodRequestError) => {
err.hiddenDefaultErrorHandler();
// 自定义处理 / Custom handling
toast.warning(err.data?.message ?? "请求失败");
});自动加解密 / Automatic Encryption
设置 encryptKey 即启用自动加解密:
Set encryptKey to enable automatic encryption/decryption:
pod.Post({
url: "/api/v1/users",
params: { name: "Alice" },
encryptKey: "16-byte-key-here", // 16/24/32 字节 / 16/24/32 bytes
});执行顺序 / Execution Order:
请求拦截器: setHeaders → beforeRequest → 加密 params/data → 发送
Response: 接收 → 解密响应 → afterResponse → 响应解包 → 返回 T默认行为约定 / Default Behavior:
- POST / PUT:
params加密后作为 request body - GET / DELETE:
params加密后包装为{ [encryptPayloadField]: encrypted }作为 query - 响应:字符串响应体自动解密为对象,后续
code/data解包在明文上执行 - 跳过条件:
responseType === "blob"或响应体不是字符串
GET 加密字段名可通过 encryptPayloadField 自定义(默认 "data"):
GET query field name can be customized via encryptPayloadField (default "data"):
pod.Get({
url: "/search",
params: { q: "alice" },
encryptKey: "my-key",
encryptPayloadField: "payload", // → ?payload=<encrypted>
});跳过加解密:支持单独关闭请求或响应阶段。
Skip encryption: Support closing request or response phase separately.
pod.Get({
url: "/raw",
encryptKey: "shared-key",
closeRequestEncrypt: true, // 不加密请求(params 明文传输)
});
pod.Post({
url: "/legacy",
params: { raw: "data" },
encryptKey: "shared-key",
closeResponseEncrypt: true, // 不解密响应(密文原样返回)
});自定义加解密算法 / Custom Encryption Algorithm
实例级配置 / Instance-Level Config
import CryptoJS from "crypto-js";
const IV = CryptoJS.enc.Utf8.parse("0102030405060708");
setupInterceptors({
instance: pod.getInstance(),
encryptCaller: async (data, key) =>
CryptoJS.AES.encrypt(JSON.stringify(data), key, {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
iv: IV,
}).toString(),
decryptCaller: async (encrypted, key) => {
const bytes = CryptoJS.AES.decrypt(encrypted, key, {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
iv: IV,
});
return JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
},
});单请求覆盖 / Single Request Override
import type { EncryptCaller, DecryptCaller } from "@andbridge/glowpod";
const sm4Encrypt: EncryptCaller = async (data, key) => "...";
const sm4Decrypt: DecryptCaller = async (encrypted, key) => "...";
pod.Post({
url: "/special",
params: { id: 1 },
encryptKey: "sm4-key",
encryptCaller: sm4Encrypt,
decryptCaller: sm4Decrypt,
});优先级链 / Priority Chain: 请求级 Options > 实例级 setupInterceptors > 内置默认实现。
动态 Token 注入 / Dynamic Token Injection
两种钩子,按场景选择:
Two hooks, choose by scenario:
| 钩子 / Hook | 时机 / Timing | 适用场景 / Use Case |
| ------------------ | ---------------- | ------------------------------------------------- |
| setHeaders | 加密之前 | Token 也会被加密传输(推荐用于鉴权 Token) |
| beforeRequest | 加密之前 | 改 config.params / config.data(加密会覆盖) |
// 方式一:setHeaders(Token 会被加密传输)
setupInterceptors({
instance: pod.getInstance(),
setHeaders: async (headers) => {
const token = await getToken();
headers.set("Authorization", `Bearer ${token}`);
},
});
// 方式二:beforeRequest(修改 params,但 GET 会被加密覆盖)
setupInterceptors({
instance: pod.getInstance(),
beforeRequest: async (config) => {
// 添加非加密参数(如 _t 时间戳防缓存)
config.params = { ...config.params, _t: Date.now() };
return config;
},
});响应后处理 / Response Post-Processing
afterResponse 在解密后、解包前执行,可短路或自定义响应。
afterResponse runs after decryption but before unwrapping; can short-circuit or customize.
setupInterceptors({
instance: pod.getInstance(),
afterResponse: async (data, raw) => {
// data: 已解密明文 / decrypted plain data
// raw: 解密前原始 response(未启用解密时为 undefined)
if ((data as any)?.code === 403) {
router.push("/forbidden");
}
// 返回值语义 / Return value semantics:
// - undefined: 继续默认解包 / continue default unwrapping
// - 任意值: 短路,直接作为响应返回 / short-circuit, return as response
return data;
},
});解密失败的降级处理:解密抛错时 data 为 undefined,raw 是密文。可在 afterResponse 内提前 try/catch 降级。
Graceful degradation on decryption failure: When decryption throws, data is undefined and raw holds the ciphertext. Try/catch inside afterResponse for fallback.
afterResponse: async (data, raw) => {
if (!data && raw) {
return { code: -1, data: null, message: "解密失败" };
}
return data;
},文件下载 / File Download
设置 responseType: "blob" 跳过加解密和解包:
Set responseType: "blob" to skip encryption/decryption and unwrapping:
const response = await pod.Get({
url: "/api/v1/download",
params: { fileId: "123" },
responseType: "blob",
});
// response 是原始 AxiosResponse
const blob = response.data as Blob;
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "file.zip";
a.click();
URL.revokeObjectURL(url);请求取消 / Request Cancellation
使用 axios 原生 AbortController:
Use axios native AbortController:
const controller = new AbortController();
pod.Get({
url: "/api/v1/users",
signal: controller.signal,
});
// 取消
controller.abort();取消时的派发:取消会抛 AxiosError(code: "ERR_CANCELED"),默认静默。需要在取消时执行清理时,配置 requestCancelCaller:
Cancellation dispatching: Cancellation throws AxiosError (code: "ERR_CANCELED"), silently by default. Configure requestCancelCaller for cleanup:
// 全局配置 / Global
setupInterceptors({
instance: pod.getInstance(),
requestCancelCaller: (error) => console.log("已取消", error),
});
// 或单请求覆盖 / Or per-request override
pod.Get({
url: "/users",
signal: controller.signal,
requestCancelCaller: () => {/* 覆盖全局 */},
});未配置 requestCancelCaller 时,错误仍向上抛,可在 .catch() 中通过 err.code === "ERR_CANCELED" 自判:
Without requestCancelCaller, error still propagates; check in .catch():
try {
await pod.Get({ url: "/users", signal: controller.signal });
} catch (err: any) {
if (err?.code === "ERR_CANCELED") return;
throw err;
}自定义业务错误 / Custom Business Errors
业务侧在 setHeaders / beforeRequest 主动抛错会被分类到 networkFailedCaller。如需在 catch 处单独处理:
Business-side errors thrown from setHeaders / beforeRequest are dispatched to networkFailedCaller. To handle separately in .catch():
beforeRequest: async (config) => {
if (!config.params?.userId) {
const err: any = new Error("缺少 userId");
err.business = true; // 打标记
throw err;
}
return config;
},
// catch 处
pod.Get({ url, params }).catch((err: any) => {
if (err.business) {
err.hiddenDefaultErrorHandler?.();
toast("参数错误");
}
});进阶 / Advanced
TypeScript 类型 / TypeScript Types
完整类型导出:
Complete type exports:
import type {
GlowPod, // class
Options, // 单请求配置 / Per-request config
InterceptorsOptions, // 全局拦截器配置 / Global interceptor config
ResponseStruct, // { codeName?, dataName?, messageName? }
EncryptCaller, // (data: unknown, key: string) => Promise<string>
DecryptCaller, // (encrypted: string, key: string) => Promise<unknown>
RequestCancelCaller, // (error: AxiosError) => void
GlowPodRequestError, // AxiosError & { hiddenDefaultErrorHandler(): void }
} from "@andbridge/glowpod";泛型推断 / Generic inference:
const user = await pod.Get<User>({ url: "/users/1" });
// ^? User
interface PageResult<T> { list: T[]; total: number; }
const page = await pod.Get<PageResult<User>>({ url: "/users", params: { page: 1 } });
// ^? PageResult<User>多实例 / Multiple Instances
不要对同一 instance 多次调用 setupInterceptors(错误派发状态会污染)。多套拦截器需求下创建多个实例:
Don't call setupInterceptors multiple times on the same instance (error dispatch state will be polluted). Use multiple instances for multi-tenant / multi-backend:
const mainPod = new GlowPod({ baseURL: BASE_URL_A });
const adminPod = new GlowPod({ baseURL: BASE_URL_B });
setupInterceptors({ instance: mainPod.getInstance(), /* callbacks A */ });
setupInterceptors({ instance: adminPod.getInstance(), /* callbacks B */ });手动加解密 / Manual Encryption
不通过拦截器,直接调用底层加解密工具:
Without interceptors, directly call the underlying encryption utility:
import { encrypt, decrypt } from "@andbridge/glowpod";
const ciphertext = await encrypt("16-byte-key-here", { name: "Alice" });
const plaintext = await decrypt("16-byte-key-here", ciphertext);默认实现:AES-128/192/256-ECB + PKCS7 + Base64。key 必须是 16/24/32 字节(UTF-8 编码后)。
Default implementation: AES-128/192/256-ECB + PKCS7 + Base64. key must be 16/24/32 bytes (UTF-8 encoded).
明文格式:默认实现先把对象用 Jackson 风格 JSON.stringify(obj, null, 2) 格式化(键与冒号间带空格:{"a" : 1}),再走 AES-ECB 加密。如需与 crypto-js 或其它后端互通,请确保明文格式一致,或整体替换为自定义 encryptCaller / decryptCaller。
Plaintext format: The default implementation first formats objects in Jackson-style JSON.stringify(obj, null, 2) (space between key and colon: {"a" : 1}), then encrypts with AES-ECB. For interop with crypto-js or other backends, ensure plaintext format consistency, or replace with custom encryptCaller / decryptCaller.
__PLATFORM__ 公共常量 / Public Constant
每个 bundle 都导出 __PLATFORM__: "nodejs" | "browser" | "reactnative",值为编译期确定的字符串字面量。
Each bundle exports __PLATFORM__: "nodejs" | "browser" | "reactnative", the value of which is a compile-time determined string literal.
import { __PLATFORM__ } from "@andbridge/glowpod";
if (__PLATFORM__ === "browser") {
// 浏览器专属逻辑 / Browser-only logic
}
console.log("Running on:", __PLATFORM__); // 用于埋点 / 调试 / Used for analytics / debuggingAPI 参考 / API Reference
GlowPod 类 / Class
import { GlowPod } from "@andbridge/glowpod";
class GlowPod {
constructor(options: CreateAxiosDefaults); // 与 axios.create() 同参
getInstance(): AxiosInstance;
Get<T>(options: Options): Promise<T>;
Post<T>(options: Options): Promise<T>;
Put<T>(options: Options): Promise<T>;
Delete<T>(options: Options): Promise<T>;
}Options 字段 / Options fields:
| 字段 / Field | 类型 / Type | 默认 / Default | 说明 / Description |
| ------------------------- | ------------------------------------------------- | ----------------------- | ------------------------------------------------------------------ |
| url | string | 必填 / required | 请求路径 / Request path |
| params | object \| null | undefined | GET/DELETE → query; POST/PUT → body |
| headers | RawAxiosRequestHeaders \| AxiosHeaders | undefined | 顶层平铺,不做合并 / Flat, no merge |
| encryptKey | string | undefined | 启用自动加解密 / Enable automatic encryption |
| encryptCaller | EncryptCaller | undefined | 单请求覆盖加密 / Per-request encryption override |
| decryptCaller | DecryptCaller | undefined | 单请求覆盖解密 / Per-request decryption override |
| encryptPayloadField | string | "data" | GET 加密后 query 字段名 / GET query field after encryption |
| requestCancelCaller | RequestCancelCaller | undefined | 覆盖全局取消回调 / Override global cancellation callback |
| closeRequestEncrypt | boolean | false | 跳过请求加密 / Skip request encryption |
| closeResponseEncrypt | boolean | false | 跳过响应解密 / Skip response decryption |
| 其他 / Others | - | - | 顶层平铺 AxiosRequestConfig:responseType / timeout / signal / baseURL 等 |
setupInterceptors 函数 / Function
import { setupInterceptors } from "@andbridge/glowpod";
function setupInterceptors(options: InterceptorsOptions & { instance: AxiosInstance }): void;InterceptorsOptions 字段 / InterceptorsOptions fields:
| 字段 / Field | 类型 / Type | 默认 / Default | 说明 / Description |
| --------------------------- | -------------------------------------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------- |
| instance | AxiosInstance | 必填 / required | pod.getInstance() |
| serverKillCaller | (error: AxiosError) => void | noop | 网络层错误 / Network-layer errors |
| networkFailedCaller | (response: AxiosResponse \| undefined) => void | noop | HTTP 非 2xx / 业务码失败 / 内部抛错 |
| requestCancelCaller | (error: AxiosError) => void | undefined | 主动取消 / Manual cancellation |
| responseStruct | ResponseStruct \| null | { codeName: "code", dataName: "data" } | 传 null 跳过解包 / Pass null to skip unwrapping |
| businessSuccessCode | string | "0" | 业务码成功判定值 / Business success code value |
| encryptCaller | EncryptCaller | 内置 AES-ECB / Built-in AES-ECB | 实例级默认加密 / Instance-level default encryption |
| decryptCaller | DecryptCaller | 内置 AES-ECB / Built-in AES-ECB | 实例级默认解密 / Instance-level default decryption |
| encryptPayloadField | string | "data" | 实例级 GET query 字段名 / Instance-level GET query field |
| setHeaders | (headers: AxiosRequestHeaders) => Promise<void> | noop | 加密之前执行 / Runs before encryption |
| beforeRequest | (config: InternalAxiosRequestConfig) => Promise<config> | noop | 加密之前执行 / Runs before encryption |
| afterResponse | (data, raw?) => Promise<any> | noop | data 已解密;raw 是原始密文 response / data decrypted; raw is original ciphertext response |
encrypt / decrypt 函数 / Functions
import { encrypt, decrypt } from "@andbridge/glowpod";
function encrypt<T>(key: string, data: T): Promise<string>;
function decrypt(key: string, encrypted: string): Promise<unknown>;默认实现:AES-ECB + PKCS7 + Base64。
Default implementation: AES-ECB + PKCS7 + Base64.
__PLATFORM__ 常量 / Constant
import { __PLATFORM__ } from "@andbridge/glowpod";
const platform: "nodejs" | "browser" | "reactnative" = __PLATFORM__;值在编译期固定,由 rollup replace 插件注入。
Value is fixed at compile time, injected by rollup's replace plugin.
GlowPodRequestError 类型 / Type
import type { GlowPodRequestError } from "@andbridge/glowpod";
type GlowPodRequestError = AxiosError & {
hiddenDefaultErrorHandler(): void; // 调用以阻止默认错误回调
};常见问题 / FAQ
各平台必装什么?/ What peer dependencies per platform?
| 平台 / Platform | 必装 / Required | 何时 / When |
| --------------- | ------------------------ | ---------------------------------------- |
| Node.js | 无 / None | 默认使用 node:crypto |
| Browser | @noble/ciphers | 浏览器 bundle 静态引用 noble |
| React Native | react-native-quick-crypto | RN bundle 静态引用 quick-crypto(推荐,原生 AES 性能) |
每个平台的 peer 是编译期硬绑定的——未安装直接抛 Cannot find module,无运行时 fallback。
Each platform's peer is compile-time hard-bound — uninstalled peers throw Cannot find module directly; no runtime fallback.
Metro 需要配置吗?/ Does Metro need any configuration?
不需要。3.3.0+ 使用 双轨入口声明,同时覆盖所有 Metro 版本:
No configuration needed. 3.3.0+ uses dual-rail entry declarations covering all Metro versions:
- 顶层
"react-native"字段:Metro 所有版本(0.60+ 至 0.83+)直接读取,最可靠的兜底入口 Top-level"react-native"field: Read directly by all Metro versions (0.60+ to 0.83+); the most reliable fallback entry exports["."].react-native条件:新版 Metro(0.79+)与 Node.js 解析器使用exports["."].react-nativecondition: Used by newer Metro (0.79+) and Node.js resolver
bundle 内静态 import react-native-quick-crypto,CJS bundle 零 import( 调用。
Bundles statically import react-native-quick-crypto; CJS bundles have zero import( calls.
历史说明:早期版本曾在
react-native.config.cjs里提供blockList: [/^node:.*/],实际无效(Metro 的blockList匹配的是已解析的文件绝对路径,node:protocol 在解析阶段就失败了)。如果你的metro.config.js里手写了这段配置,可以删除。Historical note: Earlier versions provided
blockList: [/^node:.*/]inreact-native.config.cjs, which was actually ineffective (Metro'sblockListmatches resolved absolute file paths;node:protocol fails at the resolve stage). If yourmetro.config.jshas this config, it can be removed.
Metro 报错 Unable to resolve module node:crypto 怎么办?/ Metro error Unable to resolve module node:crypto?
确认你使用的是 @andbridge/glowpod >= 3.3.0(含双轨入口声明)。如果升级后仍报错:
Verify you're using @andbridge/glowpod >= 3.3.0 (with dual-rail entry declarations). If the error persists after upgrading:
# 1. 清除 Metro 缓存
rm -rf $TMPDIR/metro-cache # macOS
# 或 / or
rm -rf node_modules/.cache/metro
# 2. 重新安装 pods
cd ios && pod install && cd ..
# 3. 重启 Metro
npx react-native start --reset-cache如果仍无法解决,可在 metro.config.js 显式配置 resolver:
If it still fails, configure the resolver explicitly in metro.config.js:
// metro.config.js
const { getDefaultConfig } = require("@react-native/metro-config");
const config = getDefaultConfig(__dirname);
config.resolver.resolveRequest = (context, moduleName, platform) => {
if (moduleName === "@andbridge/glowpod") {
return context.resolveRequest(
context,
"@andbridge/glowpod/build/glowpod.react-native.cjs",
platform,
);
}
return context.resolveRequest(context, moduleName, platform);
};
module.exports = config;与原生 axios 的核心差异?/ Key differences from vanilla axios?
| 维度 / Dimension | 原生 axios / Vanilla axios | @andbridge/glowpod |
| ----------------------- | --------------------------- | -------------------------------------------------------- |
| 拦截器 / Interceptors | 每个 instance 手动 use | 一行 setupInterceptors 搞定 / One-line setup |
| 业务码解包 / Code unwrap | 不支持 / Not supported | 内置 codeName / dataName 联动解包 |
| 错误分类 / Error type | 需手写 try/catch | 三回调自动派发 + 取消可选 / Three-callback auto-dispatch |
| 加解密 / Encryption | 完全不涉及 / Not involved | 一键启用 + 可替换算法 / One-key enable + replaceable |
| 鉴权跳转 / Auth redirect | 每个 instance 写一遍 | 在 networkFailedCaller 内自判 response.status |
| 业务代码 / Business code | 经常写 try/catch | 成功路径只写业务,失败由回调兜底 |
升级迁移 / Migration
跨版本迁移指南见 ARCHITECTURE.md → 升级迁移。
For cross-version migration guide, see ARCHITECTURE.md → Migration.
License
ISC
