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

@longzai-intelligence-typescript/rolldown-plugin-type-boundary

v0.1.26

Published

构建期 rolldown 插件,把 schema 派生(omit/pick/extend)展开为独立 z.object 字面量,阻断字段名向 JS bundle 泄漏

Readme

@longzai-intelligence-typescript/rolldown-plugin-type-boundary

构建期 rolldown 插件,把 schema / 类型派生在打包阶段展开为独立字面量,阻断被删除的字段名向产物泄漏。

解决的问题

schema 派生(omit/pick/extend)与 TS 原生类型派生(Omit<T,'x'> / Pick<T,'x'>)会让被删除的字段名以字面量形态残留在构建产物中,即便字段在类型层与运行时确已移除。泄漏分两条通道:

| 通道 | 派生写法 | 残留形态 | 后果 | | --------- | ------------------------------ | ---------------------------------- | ------------------------------------ | | JS bundle | Schema.omit({ phone: true }) | omit({ phone: true }) 实参硬编码 | 敏感字段名出现在发布的 bundle 源码中 | | .d.ts | type B = Omit<A, 'phone'> | Omit<A, 'phone'> 字面量 | 敏感字段名出现在发布的类型声明中 |

本插件在构建期对源 .ts 做静态重写,把派生表达式展开为扁平字面量,并切断对源定义的引用,让被删字段名从产物中彻底消失——同时保持开发态推断链连通(开发体验不变)。

作用范围

在构建期(transform order:'pre')识别并改写以下两类派生:

通道 1:JS bundle(zod 派生)

识别 X.omit({...}) / X.pick({...}) / X.extend({...})X 为 zod ZodObject),静态重写为独立 z.object({...}) 字面量。

// 改写前(源码,开发态保持推断链连通)
const SourceSchema = z.object({ id: z.number(), phone: z.string(), name: z.string() });
export const PublicUserSchema = SourceSchema.omit({ phone: true });

// 改写后(JS 产物)
export const PublicUserSchema = z.object({
  id: z.number(),
  name: z.string(),
});

源 schema 断链后若不再被引用,其声明语句一并删除(敏感字段名随之消失)。

通道 2:.d.ts(TS 原生类型派生)

识别 type B = Omit<A, 'x'> / type B = Pick<A, 'x'>,展开为 inline 对象类型,消除 Omit< 与字段名字面量。

// 改写前(源码)
type A = { phone: string; name: string; id: number };
type B = Omit<A, 'phone'>;

// 改写后(产物)
type B = {
  name: string;
  id: number;
};

支持源类型为 type alias 指向 TSTypeLiteralinterface 两种形态。

源定义的位置

两条通道均支持:

  • 同文件定义:源 schema / 源类型与派生在同一文件内
  • 跨文件定义:源通过 import 引入(基于 rolldown 的 this.resolve + 文件读取,自动解析)

用法

作为 rolldown / lzi-builder 插件,注入构建配置的 plugins 字段:

// lzi-builder.config.ts(或 rolldown.config.ts)
import { defineConfig } from '@longzai-intelligence-builder/esm';
import { typeBoundaryPlugin } from '@longzai-intelligence-typescript/rolldown-plugin-type-boundary';

export default defineConfig({
  plugins: [typeBoundaryPlugin()],
});

配置项

export interface TypeBoundaryOptions {
  /** 全局开关,默认 true */
  enabled?: boolean;
  /** 命中派生但无法静态重写时的策略,默认 'warn' */
  onUnsupported?: 'warn' | 'error' | 'silent';
  /** 强制保护的字段名/模式清单(敏感字段意图登记,CI 守门) */
  protect?: Array<string | RegExp>;
  /** 是否启用 .d.ts 通道(TS 原生 Omit/Pick 改写),默认 true */
  dts?: boolean;
  /** 是否打印改写日志 */
  debug?: boolean;
}

| 选项 | 说明 | | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | enabled | 全局开关,false 时插件直接返回不处理 | | onUnsupported | 命中派生但因能力边界无法静态重写时的策略:warn(默认,输出不阻断)、error(抛错阻断构建,CI 守门用)、silent(静默,不推荐,易造成假阳性安全感) | | protect | 敏感字段意图清单。声明后:成功断链中被删字段命中清单时输出确认日志;存在 unsupported 派生时,无视 onUnsupported 一律升级为 error(敏感字段不允许静默泄漏) | | dts | 是否启用 .d.ts 通道,默认 true | | debug | 打印每个命中文件的改写原因与替换文本 |

典型配置

// 严格守门场景:敏感字段登记 + 不可解析派生直接失败
typeBoundaryPlugin({
  protect: ['password', 'phone', 'idCard', /secret/i],
  onUnsupported: 'error',
});

能力边界

当前版本支持与不支持:

支持

  • zod omit / pick / extend 三种派生(单次调用)
  • TS 原生 Omit<T,K> / Pick<T,K> 类型派生(含联合类型 key 'a' | 'b'
  • 同文件源定义(z.object 字面量 / type alias / interface
  • 跨文件源定义(基于 import 解析)
  • export const / export type 包裹的派生

不支持(命中时按 onUnsupported 处理,默认 warn)

  • zod 链式多次派生 S.omit({}).omit({}).extend({})
  • zod partial / merge / intersection / discriminatedUnion / refine / transform
  • TS 原生 Omit 链的嵌套展开(如 Omit<Omit<A,'x'>,'y'>,当前只展开最外层)
  • 源 schema 经工厂函数 / as const / 变量赋值生成
  • 嵌套对象字段级 omit

不支持的场景不会静默放行:会被收集为 unsupported 记录,按 onUnsupported 策略 warn / error。这是本插件的安全设计——避免开发者误以为已断链实则未断。

原理

  1. transform order:'pre' 拦截源 .ts,取 rolldown 提供的 meta.ast(oxc Program)
  2. 粗筛:文件是否含 zod 派生调用 / TS 类型派生
  3. 遍历变量声明与类型别名,匹配派生表达式
  4. 静态解析源定义(同文件直接查找;跨文件经 this.resolve + 文件读取 + oxc parse)
  5. 计算派生后字段集合,用 meta.magicString.overwrite 重写为扁平字面量
  6. 同文件源定义断链后若不再被引用,删除其声明语句

跨文件解析带缓存(同一 import 说明符只读盘解析一次)。

测试

bun test

覆盖 zod 派生、TS 原生类型派生、跨文件解析、能力边界契约等场景。

依赖

  • rolldownpeerDependencies,构建期运行,要求 ^1.2.0
  • 不依赖 zod(仅做 AST 静态分析,不 import zod 运行时;对消费方使用的 zod 版本无要求,语义上仅对 zod v4 有意义)