mcpField
E
encore-code-review

encore-code-review

github.com/encoredev测试与质量30833 次安装2 天前更新
代码仓库

关于 encore-code-review

面向 Encore.ts 项目的代码审查技能,可检查常见的架构、安全和可靠性问题,并按严重程度与位置报告问题、警告及良好实践。

主要功能

  1. 01检查基础设施声明位置
  2. 02审查导入方式与服务调用模式
  3. 03标记错误处理和 SQL 风险
  4. 04检查端点暴露与订阅处理器
  5. 05按严重程度和位置报告结果

适用场景

  1. 01审查 Encore.ts 拉取请求
  2. 02查找不安全的 SQL 拼接
  3. 03检查服务边界和导入方式
  4. 04审计订阅幂等性与密钥访问

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/encoredev/skills --skill encore-code-review

安装后在 Claude Code 中输入/encore-code-review确认已加载。

详细介绍

Encore 代码审查

说明

审查 Encore.ts 代码时,请检查以下常见问题:

严重问题

1. 在函数内部定义基础设施
// WRONG: Infrastructure declared inside function
async function setup() {
  const db = new SQLDatabase("mydb", { migrations: "./migrations" });
  const topic = new Topic<Event>("events", { deliveryGuarantee: "at-least-once" });
}

// CORRECT: Package level declaration
const db = new SQLDatabase("mydb", { migrations: "./migrations" });
const topic = new Topic<Event>("events", { deliveryGuarantee: "at-least-once" });
2. 使用 require() 而非 import
// WRONG
const { api } = require("encore.dev/api");

// CORRECT
import { api } from "encore.dev/api";
3. 服务导入模式错误
// WRONG: Direct import from another service
import { getUser } from "../user/api";

// CORRECT: Use ~encore/clients
import { user } from "~encore/clients";
const result = await user.getUser({ id });
4. 缺少错误处理
// WRONG: Returning null for not found
const user = await db.queryRow`SELECT * FROM users WHERE id = ${id}`;
if (!user) return null;

// CORRECT: Throw APIError
import { APIError } from "encore.dev/api";

const user = await db.queryRow`SELECT * FROM users WHERE id = ${id}`;
if (!user) {
  throw APIError.notFound("user not found");
}
5. SQL 注入风险
// WRONG: String concatenation
await db.query(`SELECT * FROM users WHERE email = '${email}'`);

// CORRECT: Template literal with automatic escaping
await db.queryRow`SELECT * FROM users WHERE email = ${email}`;

警告问题

6. 缺少类型注解
// WEAK: No explicit types
export const getUser = api(
  { method: "GET", path: "/users/:id", expose: true },
  async ({ id }) => {
    return await findUser(id);
  }
);

// BETTER: Explicit request/response types
interface GetUserRequest { id: string; }
interface User { id: string; email: string; name: string; }

export const getUser = api(
  { method: "GET", path: "/users/:id", expose: true },
  async ({ id }: GetUserRequest): Promise<User> => {
    return await findUser(id);
  }
);
7. 暴露内部端点
// CHECK: Should this cron endpoint be exposed?
export const cleanupJob = api(
  { expose: true },  // Probably should be false
  async () => { /* ... */ }
);
8. 订阅处理程序不具备幂等性
// RISKY: Not idempotent (pubsub has at-least-once delivery)
const _ = new Subscription(orderCreated, "process-order", {
  handler: async (event) => {
    await chargeCustomer(event.orderId);  // Could charge twice!
  },
});

// SAFER: Check before processing
const _ = new Subscription(orderCreated, "process-order", {
  handler: async (event) => {
    const order = await getOrder(event.orderId);
    if (order.status !== "pending") return;  // Already processed
    await chargeCustomer(event.orderId);
  },
});
9. 在模块级别调用密钥
// WRONG: Secret accessed at startup
const stripeKey = secret("StripeKey");
const client = new Stripe(stripeKey());  // Called during import

// CORRECT: Access inside functions
const stripeKey = secret("StripeKey");

async function charge() {
  const client = new Stripe(stripeKey());  // Called at runtime
}

审查清单

  • 所有基础设施都定义在包级别
  • 使用 ES6 导入,而非 require()
  • 跨服务调用使用 ~encore/clients
  • 使用 APIError 进行恰当的错误处理
  • SQL 使用模板字符串
  • 已定义请求和响应类型
  • 内部端点设置了 expose: false
  • 订阅处理程序具备幂等性
  • 在函数内部访问密钥,而非在导入时访问
  • 迁移文件遵循命名约定(001_name.up.sql)

输出格式

审查时,请按以下格式报告问题:

[CRITICAL] [file:line] Description of issue
[WARNING] [file:line] Description of concern  
[GOOD] Notable good practice observed

评论

支持 Markdown