@evumi/berth-config
v0.0.63
Published
Client for Berth project Config: cached reads with background long-poll refresh.
Readme
@evumi/berth-config
读取 Berth 项目 Config 的客户端。第一次读取时拉一次整份配置,之后都从内存读;后台用一个长轮询保持最新。零运行时依赖,支持 Node ≥ 18、Bun、Deno 和不带 Node 内置模块的 edge 运行时。
不要放机密。 Config 对组织成员可读。密码、密钥放到服务变量里。
此 SDK 用于服务端(包括服务端组件、API 路由和 edge Worker)。BERTH_CONFIG_TOKEN 是服务端凭据,同时用于环境的 KV 和队列等接口,不能下发到浏览器。前端需要配置或 KV 数据时,由应用后端校验权限并仅返回所需字段。不要把 Token 复制到 NEXT_PUBLIC_*、VITE_*、打包器环境变量映射或客户端 props;不要在客户端组件里使用此 SDK。
用法
Berth 会给容器注入 BERTH_CONFIG_URL 和 BERTH_CONFIG_TOKEN,直接用默认导出:
import { config } from '@evumi/berth-config';
const enabled = await config.get<boolean>('newCheckout');
const all = await config.getAll();
const unsubscribe = config.subscribe((doc) => console.log('config changed', doc));自己建客户端:
import { createClient } from '@evumi/berth-config';
const cfg = createClient({
url: process.env.BERTH_CONFIG_URL, // 默认读环境变量
token: process.env.BERTH_CONFIG_TOKEN,
fallback: './config.local.json', // 本地开发或启动时连不上服务器时使用
timeoutMs: 5000,
onError: (err) => console.warn('config:', err.code, err.message), // 默认什么都不打
});| 方法 | 说明 |
|---|---|
| get<T>(key) | 某个顶层字段,不存在时返回 undefined |
| getAll<T>(keys?) | 整份文档,或只取给出的字段 |
| has(key) | 字段是否存在 |
| digest() | 当前 ETag(不带引号);用的是 fallback 时为 "" |
| version() | 当前版本号;还没有配置或用的是 fallback 时为 0 |
| subscribe(cb) | 配置变化时回调,返回取消函数 |
| close() | 停止长轮询并中断进行中的请求 |
返回值是副本,改了不影响缓存。
行为
- 第一次调用任意方法时拉取
GET /v1/config,并发调用共用这一次请求;失败了下次调用会重试。 - 之后后台只有一个长轮询(
?wait=55+If-None-Match),变化时立即更新并通知订阅者。 - 出错时按指数退避重试(带抖动,最长 30 秒);429 按
Retry-After等待。 - Node 里长轮询的连接和定时器都已
unref,不会让进程退出不了;脚本里也不需要手动close()。 - 错误都是
BerthConfigError,带code(unauthorized、not_found、unavailable、rate_limited、timeout、network、missing_env、fallback等)和status。令牌不会出现在错误或日志里。
Next.js
// app/page.tsx(服务端组件)
import { config } from '@evumi/berth-config';
export default async function Page() {
const banner = await config.get<string>('banner');
return <main>{banner && <p>{banner}</p>}</main>;
}Edge 运行时也能用,fallback 只能传对象(edge 没有文件系统)。
Express
import express from 'express';
import { config } from '@evumi/berth-config';
const app = express();
app.get('/', async (_req, res) => {
const { maxItems = 20 } = await config.getAll<{ maxItems?: number }>();
res.json({ maxItems });
});
app.listen(3000);本地开发
本地没有 BERTH_CONFIG_URL / BERTH_CONFIG_TOKEN 时读 fallback:
const cfg = createClient({ fallback: './config.local.json' }); // 文件(Node/Bun/Deno)
const cfg2 = createClient({ fallback: { newCheckout: true } }); // 或直接给对象没有环境变量也没有 fallback 时,读取会抛 missing_env。
开发
pnpm install
pnpm test # 先构建,再跑 vitest(含 ESM/CJS 子进程退出测试)
pnpm build # dist/esm + dist/cjs