field-guard
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesefield-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
策略模式
| Value | Behavior |
|---|---|
| Allow all fields for this level |
| Deny all fields for this level |
| Whitelist — only listed fields allowed |
| Blacklist — all fields except listed ones |
fieldspolicy| 取值 | 行为 |
|---|---|
| 此权限等级下允许所有字段 |
| 此权限等级下拒绝所有字段 |
| 白名单 — 仅允许列出的字段 |
| 黑名单 — 除列出字段外允许所有字段 |
fieldspolicy.withCheck<Target>()
.withCheck<Target>().withCheck<Target>()
.withCheck<Target>()Resolves the access level based on context and a target object. Use to return the correct .
verdictMap[level]FieldVerdictts
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]FieldVerdictts
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().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
combineGuardscombineGuards
combineGuardsBundles multiple guards for different resources and binds them all at once with a single call.
.for()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捆绑在一起,只需一次调用即可为所有Guard绑定上下文。
.for()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
FieldVerdictFieldVerdict
辅助方法
FieldVerdictts
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 fieldsts
verdict.allowedFields; // 允许访问的字段名称数组
verdict.coversAll(["id", "name"]); // 如果所有指定字段都被允许则返回true
verdict.coversSome(["email"]); // 如果任一指定字段被允许则返回true
verdict.pick(obj); // 返回仅包含允许字段的对象mergeFieldVerdicts
mergeFieldVerdictsmergeFieldVerdicts
mergeFieldVerdictsMerges multiple verdicts with (any-of) or (all-of) strategy.
"union""intersection"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 on every guard instance:
mergeVerdictsts
const verdict = guard.mergeVerdicts("union", { owner: true, admin: false });使用"union"(任一允许即允许)或"intersection"(全部允许才允许)策略合并多个裁决结果。
ts
// 并集:任一裁决允许该字段则允许
mergeFieldVerdicts("union", [verdictA, verdictB], fields);
// 交集:所有裁决都允许该字段才允许
mergeFieldVerdicts("intersection", [verdictA, verdictB], fields);每个Guard实例也提供了方法:
mergeVerdictsts
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 lookups
verdictMap - Incorrect return types from
.withCheck()
Type errors from AI-generated code become instant feedback — no runtime surprises.
所有字段、权限等级和裁决结果都会从你的定义中自动推导。TypeScript编译器会立即标记以下错误:
- 中使用未知字段名称
policy - 查找中使用未知权限等级
verdictMap - 返回错误的类型
.withCheck()
AI生成代码中的类型错误会即时反馈 — 不会出现运行时意外问题。