field-guard

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

field-guard skill

field-guard技能使用指南

field-guard is a lightweight, fully type-safe, field-level access control library for TypeScript with zero runtime dependencies.
field-guard是一个轻量级、完全类型安全的TypeScript字段级访问控制库,无任何运行时依赖。

Import

导入

ts
import { defineGuard, combineGuards, mergeFieldVerdicts } from "field-guard";
ts
import { defineGuard, combineGuards, mergeFieldVerdicts } from "field-guard";

Define a Guard

定义Guard

ts
type Ctx = { userId: string; role: "admin" | "user" };
type User = { id: string; email: string; name: string };

const userGuard = defineGuard<Ctx>()({
  fields: ["id", "email", "name"],
  policy: {
    owner: true,                     // all fields allowed
    admin: true,                     // all fields allowed
    other: { id: true, name: true }, // whitelist — only id and name
    banned: false,                   // no fields allowed
  },
});
ts
type Ctx = { userId: string; role: "admin" | "user" };
type User = { id: string; email: string; name: string };

const userGuard = defineGuard<Ctx>()({
  fields: ["id", "email", "name"],
  policy: {
    owner: true,                     // 允许所有字段
    admin: true,                     // 允许所有字段
    other: { id: true, name: true }, // 白名单 — 仅允许id和name
    banned: false,                   // 拒绝所有字段
  },
});

Policy Modes

策略模式

ValueBehavior
true
Allow all fields for this level
false
Deny all fields for this level
{ id: true, name: true }
Whitelist — only listed fields allowed
{ secret: false }
Blacklist — all fields except listed ones
fields
and
policy
are both optional. You can omit either or both.
取值行为
true
此权限等级下允许所有字段
false
此权限等级下拒绝所有字段
{ id: true, name: true }
白名单 — 仅允许列出的字段
{ secret: false }
黑名单 — 除列出字段外允许所有字段
fields
policy
都是可选参数。你可以省略其中一个或全部。

.withCheck<Target>()

.withCheck<Target>()

Resolves the access level based on context and a target object. Use
verdictMap[level]
to return the correct
FieldVerdict
.
ts
const userGuard = defineGuard<Ctx>()({
  fields: ["id", "email", "name"],
  policy: {
    owner: true,
    other: { id: true, name: true },
  },
}).withCheck<User>()(({ ctx, target, verdictMap }) => {
  const level = ctx.userId === target.id ? "owner" : "other";
  return verdictMap[level];
});

// Evaluate
const g = userGuard.for({ userId: "1", role: "user" });
const verdict = g.check({ id: "1", email: "me@example.com", name: "Me" });
verdict.allowedFields; // ["id", "email", "name"]
根据上下文和目标对象解析访问权限等级。使用
verdictMap[level]
返回正确的
FieldVerdict
ts
const userGuard = defineGuard<Ctx>()({
  fields: ["id", "email", "name"],
  policy: {
    owner: true,
    other: { id: true, name: true },
  },
}).withCheck<User>()(({ ctx, target, verdictMap }) => {
  const level = ctx.userId === target.id ? "owner" : "other";
  return verdictMap[level];
});

// 评估权限
const g = userGuard.for({ userId: "1", role: "user" });
const verdict = g.check({ id: "1", email: "me@example.com", name: "Me" });
verdict.allowedFields; // ["id", "email", "name"]

.withDerive()

.withDerive()

Computes extra properties from context. Can also produce row-level filter conditions.
ts
import { eq } from "drizzle-orm";

type Post = { id: string; content: string; authorId: string };

const postGuard = defineGuard<Ctx>()({
  fields: ["id", "content", "authorId"],
  policy: {
    owner: true,
    other: { id: true, content: true },
  },
})
  .withDerive(({ ctx }) => ({
    // Row-level filter (e.g. for Drizzle ORM WHERE clause)
    where: ctx.role === "admin"
      ? undefined
      : eq(posts.authorId, ctx.userId),
  }))
  .withCheck<Post>()(({ ctx, target, verdictMap }) => {
    const level = ctx.userId === target.authorId ? "owner" : "other";
    return verdictMap[level];
  });

// Usage
const g = postGuard.for({ userId: "1", role: "user" });
const rows = await db.select().from(posts).where(g.where); // row-level
const results = rows.map((row) => g.check(row).pick(row)); // field-level
根据上下文计算额外属性。也可生成行级过滤条件。
ts
import { eq } from "drizzle-orm";

type Post = { id: string; content: string; authorId: string };

const postGuard = defineGuard<Ctx>()({
  fields: ["id", "content", "authorId"],
  policy: {
    owner: true,
    other: { id: true, content: true },
  },
})
  .withDerive(({ ctx }) => ({
    // 行级过滤条件(例如用于Drizzle ORM的WHERE子句)
    where: ctx.role === "admin"
      ? undefined
      : eq(posts.authorId, ctx.userId),
  }))
  .withCheck<Post>()(({ ctx, target, verdictMap }) => {
    const level = ctx.userId === target.authorId ? "owner" : "other";
    return verdictMap[level];
  });

// 使用示例
const g = postGuard.for({ userId: "1", role: "user" });
const rows = await db.select().from(posts).where(g.where); // 行级过滤
const results = rows.map((row) => g.check(row).pick(row)); // 字段级过滤

combineGuards

combineGuards

Bundles multiple guards for different resources and binds them all at once with a single
.for()
call.
ts
const guards = combineGuards<Ctx>()({
  users: userGuard,
  posts: postGuard,
});

const g = guards.for({ userId: "1", role: "user" });

g.users.check({ id: "1", email: "a@b.com", name: "A" });
g.posts.check({ id: "p1", content: "hello", authorId: "1" });
将多个针对不同资源的Guard捆绑在一起,只需一次
.for()
调用即可为所有Guard绑定上下文。
ts
const guards = combineGuards<Ctx>()({
  users: userGuard,
  posts: postGuard,
});

const g = guards.for({ userId: "1", role: "user" });

g.users.check({ id: "1", email: "a@b.com", name: "A" });
g.posts.check({ id: "p1", content: "hello", authorId: "1" });

FieldVerdict
Helpers

FieldVerdict
辅助方法

ts
verdict.allowedFields;              // string[] of allowed field names
verdict.coversAll(["id", "name"]);  // true if all given fields are allowed
verdict.coversSome(["email"]);      // true if any given field is allowed
verdict.pick(obj);                  // returns object with only allowed fields
ts
verdict.allowedFields;              // 允许访问的字段名称数组
verdict.coversAll(["id", "name"]);  // 如果所有指定字段都被允许则返回true
verdict.coversSome(["email"]);      // 如果任一指定字段被允许则返回true
verdict.pick(obj);                  // 返回仅包含允许字段的对象

mergeFieldVerdicts

mergeFieldVerdicts

Merges multiple verdicts with
"union"
(any-of) or
"intersection"
(all-of) strategy.
ts
// Union: field allowed if ANY verdict allows it
mergeFieldVerdicts("union", [verdictA, verdictB], fields);

// Intersection: field allowed only if ALL verdicts allow it
mergeFieldVerdicts("intersection", [verdictA, verdictB], fields);
Also available as
mergeVerdicts
on every guard instance:
ts
const verdict = guard.mergeVerdicts("union", { owner: true, admin: false });
使用"union"(任一允许即允许)或"intersection"(全部允许才允许)策略合并多个裁决结果。
ts
// 并集:任一裁决允许该字段则允许
mergeFieldVerdicts("union", [verdictA, verdictB], fields);

// 交集:所有裁决都允许该字段才允许
mergeFieldVerdicts("intersection", [verdictA, verdictB], fields);
每个Guard实例也提供了
mergeVerdicts
方法:
ts
const verdict = guard.mergeVerdicts("union", { owner: true, admin: false });

Type Safety

类型安全

All fields, levels, and verdicts are inferred from your definitions. The TypeScript compiler immediately flags:
  • Unknown field names in
    policy
  • Unknown levels in
    verdictMap
    lookups
  • Incorrect return types from
    .withCheck()
Type errors from AI-generated code become instant feedback — no runtime surprises.
所有字段、权限等级和裁决结果都会从你的定义中自动推导。TypeScript编译器会立即标记以下错误:
  • policy
    中使用未知字段名称
  • verdictMap
    查找中使用未知权限等级
  • .withCheck()
    返回错误的类型
AI生成代码中的类型错误会即时反馈 — 不会出现运行时意外问题。