用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/tomevault-io/skills-registry --skill graplix命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
| Use when this capability is needed.
> Use when this capability is needed.
Review architecture and API design for the vfs-s3 project. Use when the user mentions @architect, asks to review an issue's design, discuss module boundaries, API shape, or architectural decisions for vfs-s3. Also trigger when the user wants to create an ADR (Architecture Decision Record) or evaluate a technical approach for the project. Intended for dispatch from Codex automation or Claude routines; GitHub trigger phrase: @vfs-s3-bot please prepare design doc Use when this capability is needed.
基于 SOC 职业分类
正在显示 SKILL.md
| name | graplix |
| description | > Use when this capability is needed. |
Build relation-based access control with Graplix. This skill teaches you how to find current documentation and write correct Graplix code.
Everything you know about Graplix is likely outdated or wrong. Never rely on memory. Your training data may contain obsolete APIs. Always verify against the documentation referenced in this skill.
Before writing code, verify package installation:
ls node_modules/@graplix/engine/
references/quick-start.md| Question | Resource | Purpose |
|---|---|---|
| Project setup / installation | references/quick-start.md | Installation and first permission check guide |
| Schema syntax / keywords | references/schema-syntax.md | .graplix file syntax and relation expressions |
| API usage / type signatures | references/embedded-docs.md | Look up via installed package docs and type declarations |
| Error resolution | references/common-errors.md | Troubleshooting solutions |
Embedded docs (if @graplix/engine is installed)
node_modules/@graplix/engine/dist/docs/*.mdnode_modules/@graplix/engine/dist/index.d.mtsSource code (if packages installed)
node_modules/@graplix/engine/dist/index.mjsGraplix has two runtime components:
.graplix schema → buildEngine() → engine.check() / engine.explain()
(relation model) (async factory) (permission evaluation)
Data flow for a permission check:
engine.check({ user, object, relation, context })
→ resolveType(user) → EntityRef
→ resolveType(object) → EntityRef
→ evaluate relation graph (resolver.relations callbacks)
→ resolver.load() for any entity IDs encountered
→ true | false
Graplix schemas are .graplix text files. See references/schema-syntax.md for the full reference.
type user
type repository
relations
define owner: [user]
define member: [user]
define admin: owner or member
define can_delete: owner from organization
Key rules:
snake_case[TypeA, TypeB] — direct relation (user must be one of these types)relation from source — transitive via another relation on sourceterm or term — union of multiple termsdefine = type with no relations (still valid)import { buildEngine } from "@graplix/engine";
// 1. Define your entity types
type User = { id: string };
type Repository = { id: string; ownerIds: string[] };
// 2. Set up data (replace with your real data source)
const users = new Map<string, User>([
["user-1", { id: "user-1" }],
["user-2", { id: "user-2" }],
]);
const repos = new Map<string, Repository>([
["repo-1", { id: "repo-1", ownerIds: ["user-1"] }],
]);
// 3. Write your schema
const schema = `
type user
type repository
relations
define owner: [user]
define can_delete: owner
`;
// 4. Build the engine (async — validates schema eagerly)
const engine = await buildEngine<object, User | Repository>({
schema,
resolveType: (value) => {
if (typeof value !== || value === ) ;
( value) ;
;
},
: {
: {
: user.,
() {
users.(id) ?? ;
},
},
: {
: repo.,
() {
repos.(id) ?? ;
},
: {
() {
repo.
.( users.(id))
.((u): u is => u !== );
},
},
},
},
});
engine.({
: users.()!,
: repos.()!,
: ,
: {},
});
result = engine.({
: users.()!,
: repos.()!,
: ,
: {},
});
result.;
result.;
result.;
buildEngine Optionsconst engine = await buildEngine<TContext, TEntityInput>({
schema, // string — raw .graplix schema text (required)
resolvers, // Resolvers<TContext> — keyed by type name (required)
resolveType, // ResolveType<TContext> — entity type discriminator (required)
resolverTimeoutMs: 3000, // timeout (ms) for load and relation resolvers
maxCacheSize: 1000, // per-request LRU cache size (default: 500)
onError: (err) => { // called when entity resolution silently fails
console.error(err); // throw here to escalate to a hard failure
},
});
buildEngine is async. Schema validation happens at construction time — an invalid schema rejects immediately.
resolveTypetype ResolveType<TContext> = (value: unknown, context: TContext) => string | null;
null if unknownquery.user and query.object — must return the correct typenull is acceptable (engine uses schema hints)// ✅ Structural field discrimination
const resolveType: ResolveType<MyContext> = (value) => {
if (typeof value !== "object" || value === null) return null;
const v = value as Record<string, unknown>;
if ("adminIds" in v) return "organization";
if ("ownerIds" in v && "organizationId" in v) return "repository";
if ("ownerIds" in v) return "team";
return "user";
};
// ✅ instanceof checks (class-based models)
const resolveType: ResolveType<MyContext> = (value) => {
if (value instanceof Organization) return "organization";
if (value instanceof Repository) return ;
(value ) ;
;
};
Resolver Interfaceinterface Resolver<TEntity, TContext> {
id(entity: TEntity): string;
load(
id: string,
context: TContext,
info: ResolverInfo, // info.signal for timeout cancellation
): Promise<TEntity | null>;
relations?: {
[relation: string]: (
entity: TEntity,
context: TContext,
info: ResolverInfo,
) => TEntity | TEntity[] | null | Promise<TEntity | TEntity[] | null>;
};
}
contextPassed to every check/explain call and forwarded to all resolver functions. Use for request-scoped data: database connections, auth info, tenant IDs, etc.
type MyContext = { db: DB; userId: string };
const engine = await buildEngine<MyContext, User | Repo>({ ... });
await engine.check({
user: currentUser,
object: targetRepo,
relation: "owner",
context: { db, userId: "user-1" }, // required on every call
});
If resolvers need no context, use object and pass {}.
@graplix/codegen generates a fully-typed buildEngine wrapper from your schema:
npx @graplix/codegen ./schema.graplix
The generated file provides typed GraplixResolvers<TContext> and GraplixEntityInput so TypeScript enforces exhaustiveness:
import { buildEngine } from "./schema.generated";
const engine = await buildEngine({
resolvers: { ... }, // typed per schema + mappers
resolveType: (value) => { ... },
});
EntityRef directly to check/explain. query.user and query.object accept TEntityInput (your domain types) only.EntityRef as a type only when working with CheckEdge.from/to in explain results.null) — not IDs, not EntityRef instances.resolveType (or uses schema hints) to determine the returned entity's type.resolver.load() is never called inside toEntityRef — it is only called when an entity needs to be loaded by ID.resolveType PrecedenceresolveType(value) is always tried first.null, schema type hints (from relation definitions) are used as fallback.query.user and query.object, resolveType must return the correct type — no fallback.maxCacheSize (default: 500).// Runtime engine
import { buildEngine } from "@graplix/engine";
import type {
BuildEngineOptions,
GraplixEngine,
Query,
Resolver,
Resolvers,
ResolverInfo,
ResolveType,
CheckEdge,
CheckExplainResult,
EntityRef, // for CheckEdge.from/to type annotations
} from "@graplix/engine";
// Schema parsing (lower-level)
import { parse } from "@graplix/language";
// Codegen (CLI or programmatic)
import { generateTypeScript } from "@graplix/codegen";
Type errors often signal outdated knowledge. Common indicators: "Property X does not exist," module not found, incorrect generic parameters.
Response approach:
references/common-errors.mdnode_modules/@graplix/engine/dist/docs/)Source: daangn/graplix — distributed by TomeVault.