npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

yunzone-service-kit

v0.34.0

Published

云洲服务集群通用基础设施:插件式注册中心基座(/registry)、运行时配置层(/config)、SQL 数据库抽象(/db)、对象存储抽象(/storage)、共享原语(/core)、运维数据接口(/ops)、鉴权中心契约与客户端(/auth)与应用目录契约(/app)

Downloads

834

Readme

yunzone-service-kit · 云洲接口套件

云洲服务集群通用基础设施: 共享原语(/core) 插件式注册中心基座(/registry) 运行时配置层(/config) SQL 数据库抽象(/db) 对象存储抽象(/storage) 运维数据接口与管理后台认证(/ops) 鉴权中心契约与客户端(/auth) 应用目录契约(/app)

纯逻辑层零框架耦合,任何 Node 服务可直接引用。Next.js 路由适配层拆为独立入口(/ops/next、/app/next;next 为 optional peerDependency),不引入这些子路径即不产生框架依赖。

子模块

/core — 共享原语

集群共用的基础形状:

  • KitError:领域错误统一基类,提供 code(默认取类名)/ cause / toJSON()(结构化日志用,不泄露堆栈)。各领域错误族(StorageError / ObjectStoreError / AuthCenterError / AppManifestError)均继承它,消费方按类 catch,不依赖字符串匹配
  • LifecycleAware:init?() / disconnect?() 钩子形状,由组合根统一调度(SqlDb / ObjectStore 均继承)

/registry — 泛型 Provider 注册中心基座

提供插件式泛型注册中心模块:

  • KitProvider:最小接口(元信息 meta + 健康检查 isAvailable())
  • ProviderRegistry<T, P>:注册(重复 id 忽略)、优先级排序、健康检查优雅降级、getStatus() 状态快照
  • 领域注册中心通过继承复用调度逻辑,可扩展 describeProvider 追加领域字段
import { ProviderRegistry } from "yunzone-service-kit/registry";

class MyRegistry extends ProviderRegistry<MyProvider> {
  constructor() {
    super({ name: "MyRegistry" });
  }
}

const registry = new MyRegistry();
registry.register(myProvider);
const active = await registry.getActiveProvider();
const status = await registry.getStatus(); // { registeredCount, availableCount, activeProvider, providers }

/config — 运行时配置层

环境变量分面规范(Env Facets)

环境变量按"服务角色"分面声明,服务用 composeFacets 组合自己需要的面:

| 分面 | 变量 | 说明 | |------|------|------| | deploymentFacet | DEPLOY_ENV、PROJECT_DOMAIN_DEFAULT、PUBLIC_BASE_URL、DEPLOY_PROVIDER、HOSTNAME、PORT、WORKSPACE_PATH | 部署面(中立键,TICKET-001) | | adminFacet | ADMIN_KEYBOOK(密码本来源:本地路径或 http(s) URL)、ADMIN_PATH(默认 /admin)、ADMIN_SESSION_MAX_AGE(可选,默认 86400) | 管理后台(v2.0 密码本协议);未设置 = 禁用(503);ADMIN_PASSWORD / ADMIN_USERNAME 已于 0.33.0 退役 |

userCenterFacet(USER_CENTER_URL / USER_CENTER_API_KEY)已于 0.28.0 退役:计量与定价上移平台计量域后全集群无消费者。

import {
  adminFacet,
  composeFacets,
  deploymentFacet,
  requiredFacet,
} from "yunzone-service-kit/config";

// 组合分面
const envSchema = composeFacets(deploymentFacet, adminFacet);

// 模块级必填(如密码本来源)
const adminEnvSchema = requiredFacet(adminFacet, ["ADMIN_KEYBOOK"]);
const env = loadEnv(adminEnvSchema);

鉴权中心服务凭证内容不走环境变量:authCenterFacet 已退役,唯一装配渠道是凭证内容——本地文件或远端 URL,来源由 /auth 的来源指示键决定(见 loadAuthCenterCredential)。

aliases 选项:loadEnv(schema, { aliases }) 支持规范 key 未设置时回退别名(通用机制;诊断快照只列规范名、不回显值)。平台注入适配(TICKET-001)用预构建的 deploymentAliases 常量——永久机制(平台注入变量名不可改名,适配层在读取时回退,禁止服务代码裸读):

// COZE_PROJECT_ENV 等平台注入旧名经适配层回退到中立键(永久,禁止裸读)
loadEnv(deploymentFacet, { aliases: deploymentAliases });

本地 .env 文件加载

部署时只需拷贝一份 .env 到服务目录,启动时一次灌入,无需运维逐行设置环境变量:

// 方式一:独立加载(文件不存在默认静默跳过,开发环境无 .env 可启动)
loadDotEnv();

// 方式二:loadEnv 一键组合(先加载 .env 再校验)
const env = loadEnv(envSchema, { dotenv: true });

合并语义:默认不覆盖 process.env 已有变量(平台注入优先),override: true 显式覆盖;required: true 时文件缺失报错。

语法子集:KEY=VALUE、单/双引号值(双引号支持 \n \r \t 转义)、# 整行注释、空行、export 前缀;不支持行尾内联注释与多行值(含 # 的值按字面量保留)。解析失败抛 EnvFileError,只报行号、不回显变量值。

其他解析工具

| 导出 | 说明 | |---|---| | loadEnv / EnvConfigError / envSnapshot | zod 环境变量契约懒解析;失败抛出带诊断快照的错误(只显示 set/unset,不回显变量值) | | parseDotEnv / loadDotEnv / EnvFileError | 本地 .env 文件解析与加载(默认不覆盖平台注入变量);loadEnv 可传 { dotenv: true } 一键组合 | | resolveDatabaseUrl | 数据库连接 URL 解析:DATABASE_PROVIDER 显式选择 postgres(DATABASE_URL)/ coze(Coze 平台 PG 变量组),不自动嗅探 | | resolveStorageConfig | 对象存储凭证解析:STORAGE_PROVIDER 显式选择 s3(S3_ENDPOINT_URL / S3_BUCKET_NAME)/ coze(Coze 平台 COZE_BUCKET_* 变量组),凭证可选(缺省走 SDK 默认凭证链),不自动嗅探 | | resolveBaseUrl | 服务对外 baseUrl 解析链:PUBLIC_BASE_URL → BETTER_AUTH_URL(历史遗留兼容键)→(DEV 回退)→ PROJECT_DOMAIN_DEFAULT → fallback | | resolveListenAddress | 监听地址:HOSTNAME(默认 0.0.0.0)+ PORT(默认 5000;平台旧名 DEPLOY_RUN_PORT 经适配层回退) | | normalizeDeployEnv / deploymentAliases | 部署适配层(TICKET-001):按 DEPLOY_PROVIDER 分派(缺省 self 通用模式,中立键直读;Coze 部署需显式设置 DEPLOY_PROVIDER=coze),把平台注入旧名读取时回退映射到中立键(不写 process.env) |

配置键命名规范(TICKET-001)

环境变量命名遵循三层命名空间模型,新增配置键需经维护组评审:

  1. 通用契约命名空间(service-kit 声明的键):一律中立命名,禁止以任何平台名(coze / vercel / render / aws 等)作为键名的一部分;平台差异通过键值分派表达(DEPLOY_PROVIDER / DATABASE_PROVIDER)。
  2. 平台注入命名空间(COZE_*、PG* 等):平台私有。项目不写入(loadDotEnv 默认平台注入优先,私有值会被静默覆盖)、不提升进通用契约(语义污染)。
  3. 项目私有命名空间(服务自有 schema 扩展):自由命名,但不得占用平台注入命名空间。

裸读禁令:禁止直接 process.env.COZE_* 裸读平台注入变量,必须经中立键(通用契约 + deploymentAliases / normalizeDeployEnv)或自有 schema 显式声明(私有层)接入。

平台注入变量名本身不改名,由适配层在读取时映射:解析器内部调用 normalizeDeployEnv;loadEnv 路径传 deploymentAliases 常量。

/db — SQL 数据库抽象

集群统一数据库操作心智模型:SQL 字符串即真理,调用方直接书写参数化 SQL($1 占位),无字段映射层。

  • SqlDb:SQL 执行器接口——query(sql, params) 查询返回行、execute(sql, params) 写语句返回影响行数、withTransaction(fn) 事务强承诺(回调内全部写入要么整体提交、要么整体回滚);继承 LifecycleAware
  • 渠道三值域(DatabaseChannel):postgres(通用,凭证 DATABASE_URL)/ coze(按平台注入行为运行,PGDATABASE_URL / PG* 变量组)/ none(显式无库声明,createSqlDb 对其 fail-fast,消费方据此改用非库存储)——SQL 语义一致,仅连接凭证获取方式不同
  • PgSqlDb:kit 官方渠道适配器(postgres / coze 两渠道共用;真事务 BEGIN/COMMIT/ROLLBACK、事务代理禁止嵌套、pg 23505 归一化为 UniqueViolationError、支持 ssl / max / idleTimeoutMillis 连接池选项)
  • PoolProvider + isPoolProvider(db):连接池提供能力与类型守卫(宿主注入场景,如把池传给 omni-auth 等外部库),替代 (db as PgSqlDb) 类型断言;事务代理不实现该接口,守卫对其返回 false
  • resolveDatabaseChannel:渠道解析,唯一来源 DATABASE_PROVIDER,未设置默认 postgres(不自动嗅探)
  • createSqlDb({ channel }):渠道工厂——按 channel 分派凭证获取并返回 PgSqlDb 实例;需要自定义连接池选项(如 SSL)时直接 new PgSqlDb(url, options)
  • 错误族:StorageError 基类、UniqueViolationError(code = "UNIQUE_VIOLATION",与 omni-auth SDK 同语义错误码对齐)、StorageConfigError(配置错误)
import { createSqlDb, resolveDatabaseChannel } from "yunzone-service-kit/db";

const channel = resolveDatabaseChannel(); // DATABASE_PROVIDER → "postgres" | "coze" | "none"
const db = createSqlDb({ channel });     // postgres → DATABASE_URL;coze → 平台注入变量组;none → fail-fast

const rows = await db.query("SELECT id FROM users WHERE name = $1", ["alice"]);

事务边界约束:withTransaction 回调内禁止调用 omni-auth 等外部库的认证/写 API——跨抽象事务互相不可见(外部库在池上另开连接),其写入会静默逃逸本事务。

/storage — 对象存储抽象

集群统一对象存储操作心智模型,与 /db 的 SqlDb 抽象完全同构:key 即真理,调用方直接组织 storage key(如 avatars/user123.jpg),无字段映射层。

  • ObjectStore:存储执行器接口——upload(params) 上传返回 key、getSignedUrl(key, expireSeconds?) 生成读签名 URL(禁止自行拼接)、getUploadUrl(options) 生成写(PUT)预签名 URL(客户端直写场景,默认时效 300s)、delete(key) 删除、exists(key) 存在性检查;继承 LifecycleAware(init/disconnect 由组合根调度)
  • 渠道三值域(StorageChannel):s3(通用 S3,实现需额外获取连接凭证 S3_ENDPOINT_URL / S3_BUCKET_NAME)/ coze(按平台注入行为运行,COZE_BUCKET_* 变量组)/ none(显式无存储声明,任何存储操作对其 fail-fast)——操作语义一致,仅连接凭证获取方式不同
  • S3ObjectStore:kit 官方渠道适配器(@aws-sdk/client-s3 实现,s3 / coze 两渠道共用,渠道差异仅凭证解析;forcePathStyle 默认 true 以兼容 MinIO 等非 AWS 端点,阿里云 OSS 等需置 false;支持注入 S3Client 便于单测)
  • resolveStorageChannel:渠道解析,唯一来源 STORAGE_PROVIDER,未设置默认 s3(不自动嗅探)
  • createObjectStore({ channel }):渠道工厂——按 channel 分派凭证获取并返回 S3ObjectStore 实例;需要自定义 S3Client 时直接 new S3ObjectStore(config, options)
  • 错误族:ObjectStoreError 基类、ObjectStoreConfigError(配置错误;命名避开 db 已占用的 StorageError 族)
import { createObjectStore, resolveStorageChannel } from "yunzone-service-kit/storage";

const channel = resolveStorageChannel(); // STORAGE_PROVIDER → "s3" | "coze" | "none"
const store = createObjectStore({ channel }); // s3 → S3_* 变量;coze → 平台注入变量组;none → fail-fast

const key = await store.upload({ fileContent: buffer, fileName: "avatars/user123.jpg", contentType: "image/jpeg" });
const url = await store.getSignedUrl(key);

/ops — 运维数据接口与管理后台认证(纯逻辑层)

运维报告聚合:

  • createOpsReporter({ name, version, env? }):聚合服务信息、部署信息、注册中心状态快照、宿主扩展段、env set/unset 指纹(不含变量值)
  • reporter.addSection(id, provider):挂载宿主自有运维数据(API 调用热度、队列积压、卡片目录快照等 kit 无契约的数据)。provider 每次 report() 时调用,同步/异步皆可;抛错只把该段降级为 { ok: false, error }(只留消息不留堆栈),其余段与整份报告照常产出
  • 本入口不提供 HTTP 端点:服务自建路由,3 行封装 /ops:
import { createOpsReporter } from "yunzone-service-kit/ops";
import { facetKeys } from "yunzone-service-kit/config";

const reporter = createOpsReporter({ name: "user-center", version: "1.2.3" });
reporter.addRegistry("captcha", captchaRegistry);
reporter.addSection("apiHeat", () => heatCounter.snapshot()); // 形状由宿主定义
reporter.setEnvKeys(facetKeys(envSchema)); // 或手写白名单 ["DEPLOY_ENV", "ADMIN_KEYBOOK"]

export async function GET() {
  return Response.json(await reporter.report());
}

扩展段在报告中的形态(消费方按 ok 收窄后读 data):

{
  "sections": {
    "apiHeat": { "ok": true, "data": { /* 宿主自定义 */ } },
    "queue": { "ok": false, "error": "counter unavailable" }
  }
}

管理后台认证(Admin Keybook Protocol v2.0,详见 docs/admin-keybook-protocol.md)——集群内唯一实现,服务禁止自建 admin-auth。凭证 = 密码本(Ed25519 公钥清单,非秘密),登录为挑战应答(私钥本机签名、从不上网);v1.0 密码字符串链已退役:

| 导出 | 说明 | |---|---| | resolveAdminConfig(env?) | 同步读取管理面配置;ADMIN_KEYBOOK 未设置 → enabled: false(禁用态 503),无任何默认回退 | | parseAdminKeybook / loadAdminKeybook | 密码本 JSON 校验 / 本地路径 + http(s) URL 双源加载(启动一次性) | | initAdminRuntime(opts?) | 启动装配:加载密码本 → 进程内缓存指纹(换本须重启;加载失败 = 禁用态) | | createAdminChallenge / verifyAdminChallenge | 一次性 challenge 令牌(HMAC 无状态 + 120s 窗口 + host 通道绑定 + nonce 进程内一次性) | | buildAdminLoginMessage / verifyKeybookSignature | 登录规范串组装 v2.{key_id}.{nonce}.{host}.{ts} / Ed25519 查本验签(撤销条目一律拒绝) | | createSignedAdminSession / validateSignedAdminSession | HMAC-SHA256 签名会话(格式沿用 v1.0、无状态、多实例自动可用);第二参数为密码本指纹,校验返回会话主体 key_id 或 null | | deriveAdminSessionSecret | 由密码本指纹派生会话密钥——换本(指纹变化)后全部旧会话立即失效 | | ADMIN_SESSION_COOKIE / ADMIN_SESSION_DEFAULT_MAX_AGE_SECONDS | cookie 名(admin_session)与默认有效期(86400 秒) |

配套资产:ops-companion/admin-login.html(零依赖登录伴侣)、pnpm adminctl(genkey / keybook / demo 密钥与密码本运维)。

/ops/next — 管理后台路由守卫(Next.js 适配层)

独立入口,依赖 next/server(next 为 optional peerDependency),不污染纯逻辑层:

import { requireAdminAuth } from "yunzone-service-kit/ops/next";

// 禁用态 → 503 { code: "ADMIN_DISABLED" };未登录 / 会话无效 / 已过期 → 401 { code: "UNAUTHENTICATED" }
export const GET = requireAdminAuth(async (request, { params }) => {
  return Response.json({ /* ... */ });
});

守卫以宽松泛型原样透传服务侧 handler 签名,不引入 NextRequest 作为参数类型——规避 Next.js 内部私有标记在不同 node_modules 副本间的类型不兼容;cookie 按鸭子类型读取,兼容 getter 属性(Next.js 15.3+/16)与方法(旧版)两种形态。

/auth — 鉴权中心契约与客户端

Token Authority Service(鉴权中心)对接:契约副本 + 官方通信实现,单一事实源为 docs/token-contract.md。

  • 契约面:TOKEN_ENDPOINTS(issue / introspect / lookup / refresh / revoke / healthz)、TOKEN_ERROR_CODES、各端点请求/响应类型
  • createAuthCenterClient({ baseUrl, apiKey?, timeoutMs? }):
    • introspect(request):无效 token 由服务端统一返回 200 + { active: false }(防枚举),客户端原样透传不抛错,消费方按 active 判断
    • healthz():探活,可用(2xx)返回 true,不可达 / 非 2xx 一律 false(不抛错)
  • AuthCenterError:协议层失败(网络 / 超时 / 非 2xx / 响应体非法)统一归一化;响应体可解析出服务端错误码时保留至 error.code
  • 职责边界:kit 只封装通信协议;探活节奏、降级回退、claims → 业务身份映射等策略由消费方实现

凭证内容(服务凭证唯一装配渠道,详见 docs/credential-file.md):

服务凭证(apiKey / productId / 服务地址)不进环境变量;来源显式二选一——本地文件(默认)或远端 URL(集中托管,如 OSS)。签发或轮换凭证时从鉴权中心管理后台一次性下载凭证文件(明文 apiKey 仅存在于签发/轮换响应,服务端只存 hash,事后无补发路径):

import {
  createAuthCenterClient,
  loadAuthCenterCredential,
  refreshAuthCenterCredential,
} from "yunzone-service-kit/auth";

// 来源解析:AUTH_CENTER_CREDENTIAL_URL(远端)| AUTH_CENTER_CREDENTIAL_FILE(本地路径)
// → 默认 <cwd>/auth-center.credential.json;两键同设抛错;缺失即抛 CredentialFileError(fail-fast)
// 统一缓存快照:更新生效点 = 进程重启或 refreshAuthCenterCredential()
await refreshAuthCenterCredential(); // 部署约定:启动流程调用一次(远端取件 / 本地读入)
const credential = loadAuthCenterCredential();

const client = createAuthCenterClient({
  baseUrl: credential.authCenterUrl,
  apiKey: credential.apiKey,
});
  • 远端 fetch 失败与「缺失」同语义(optional → null,否则抛错);任何消息不回显 URL(视为凭证材料)
  • 文件含明文 apiKey:本地来源加载时做启发式检查,疑似被 git 跟踪时 console.warn 一次(提示非保证)
  • 文件内 expiresAt 仅为签发时快照(auto-renew 场景会失真),仅供展示;运行时有效性以 introspect 为准
  • 轮换后旧凭证立即失效(introspect 返回 active: false):更新来源内容 + 重启或调用 refresh 生效

/app — 应用目录契约(AppManifest v1)

Headless 纯数据层(零框架 / 零 React):子产品按契约自述"应用介绍",宿主聚合后标准渲染、随意编排。详见 docs/app-manifest.md。

  • parseAppManifest(input, source?) + appManifestSchema:契约校验与归一化(status → "live"、tags → []、apis 条目接入要点 request / response / errors 缺省 []);失败抛 AppManifestError
  • resolveAppCatalog(entries, { skipInvalid? }):把「子产品自述 + 宿主编排覆盖」合并为可直接渲染的 ResolvedApp[](order 升序在前、重复 id fail-fast)
  • APP_MANIFEST_WELL_KNOWN_PATH:标准端点路径约定 /.well-known/app-manifest.json

/app/next — 标准端点托管(Next.js 适配层)

// src/app/.well-known/app-manifest.json/route.ts
import { createAppManifestHandler } from "yunzone-service-kit/app/next";
import manifest from "@/manifest/app-manifest.json";

export const GET = createAppManifestHandler({ manifest });

每次请求经契约校验(非法 manifest → 500 { code: "APP_MANIFEST_INVALID" },不输出非法契约、不泄漏诊断细节),成功响应统一 Cache-Control: public, max-age=300;manifest 支持静态对象或函数形态(运行时动态状态)。完全静态时也可直接放置 public/.well-known/app-manifest.json(零代码,二选一)。

开发

pnpm install
pnpm test       # vitest 单元测试
pnpm ts-check   # 类型检查
pnpm build      # tsup 构建(ESM + CJS + DTS)

发布

  • 版本策略:0.x 快速迭代,手动 bump package.json 版本后打 GitHub Release(tag = v0.x.y)
  • 发布流程:GitHub Release published 触发 publish.yml,通过 OIDC 自动发布到 npmjs(public)
  • 消费者安装:pnpm add yunzone-service-kit
  • 本地开发联调:pnpm add link:../yunzone-service-kit(实时指向源目录 dist,发布后切回 ^0.x.y)

License

MIT