1949007c99
- Add coding convention: no '?:', use explicit 'T | null' - ReflexConfig → discriminated union (SenseReflexConfig | WorkflowReflexConfig) - All optional fields → explicit null (throttle, timeout, interval, on, maxQueue, workflows) - Add exactOptionalPropertyTypes to tsconfig - Add lib: ES2022 to tsconfig - Refactor validateReflexConfig to reduce cognitive complexity 小橘 <xiaoju@shazhou.work>
189 lines
4.6 KiB
Markdown
189 lines
4.6 KiB
Markdown
# Nerve Coding Conventions
|
|
|
|
## 语言与范式
|
|
|
|
### 函数式优先
|
|
|
|
用 `function` + `type`,不用 `class` + `interface`。
|
|
|
|
```typescript
|
|
// ✅ Good
|
|
type Signal = {
|
|
senseId: string
|
|
value: unknown
|
|
ts: number
|
|
}
|
|
|
|
function createSignal(senseId: string, value: unknown): Signal {
|
|
return { senseId, value, ts: Date.now() }
|
|
}
|
|
|
|
// ❌ Bad
|
|
interface ISignal {
|
|
senseId: string
|
|
value: unknown
|
|
ts: number
|
|
}
|
|
|
|
class Signal implements ISignal {
|
|
constructor(public senseId: string, public value: unknown, public ts: number) {}
|
|
}
|
|
```
|
|
|
|
### 具体规则
|
|
|
|
| 规则 | 说明 |
|
|
|------|------|
|
|
| `type` over `interface` | 所有类型定义用 `type`,不用 `interface` |
|
|
| `function` over `class` | 用纯函数 + 闭包,不用 class |
|
|
| 无 `this` | 函数不依赖 `this` 上下文 |
|
|
| 无继承 | 不用 `extends`、`implements`、`abstract` |
|
|
| 组合优先 | 用函数组合代替继承层次 |
|
|
| 不可变优先 | 用 `Readonly<T>`、`as const`,避免 mutation |
|
|
| 禁用 optional properties | 不用 `?:`,用 `T \| null` 显式标记可空;多个互斥字段用 discriminated union |
|
|
|
|
### 禁用 Optional Properties
|
|
|
|
不使用 `?:`,所有可空字段显式标注 `T | null`。
|
|
|
|
```typescript
|
|
// ✅ Good — 明确表达"可以为空"
|
|
type SenseConfig = {
|
|
group: string;
|
|
throttle: string | null;
|
|
timeout: string | null;
|
|
}
|
|
|
|
// ❌ Bad — optional 隐藏了"缺失"和"空值"的区别
|
|
type SenseConfig = {
|
|
group: string;
|
|
throttle?: string;
|
|
timeout?: string;
|
|
}
|
|
```
|
|
|
|
当多个字段互斥时,用 discriminated union 代替一堆 optional:
|
|
|
|
```typescript
|
|
// ✅ Good — 编译器保证 sense 和 workflow 不会同时出现
|
|
type ReflexConfig =
|
|
| { kind: "sense"; sense: string; interval: string | null; on: string[] | null }
|
|
| { kind: "workflow"; workflow: string; on: string[] | null }
|
|
|
|
// ❌ Bad — sense 和 workflow 都 optional,运行时才知道到底填了哪个
|
|
type ReflexConfig = {
|
|
sense?: string;
|
|
workflow?: string;
|
|
interval?: string;
|
|
on?: string[];
|
|
}
|
|
```
|
|
|
|
### 例外
|
|
|
|
以下场景允许 class:
|
|
- 第三方库要求(如 Drizzle 的 `sqliteTable` 返回值)
|
|
- Error 子类(`class NerveError extends Error`)
|
|
|
|
## 模块与导出
|
|
|
|
```typescript
|
|
// ✅ Named exports only
|
|
export function startEngine(config: EngineConfig): Engine { ... }
|
|
export type EngineConfig = { ... }
|
|
|
|
// ❌ No default exports
|
|
export default function startEngine() { ... }
|
|
```
|
|
|
|
- 一律 named export,不用 default export
|
|
- 一个模块做一件事,文件名即职责
|
|
|
|
## 命名
|
|
|
|
| 类型 | 风格 | 示例 |
|
|
|------|------|------|
|
|
| 文件 | kebab-case | `signal-bus.ts` |
|
|
| 类型 | PascalCase | `SignalBus` |
|
|
| 函数/变量 | camelCase | `createSignalBus` |
|
|
| 常量 | UPPER_SNAKE | `MAX_RETRY_COUNT` |
|
|
| 泛型参数 | 单字母或描述性 | `T`, `TValue` |
|
|
|
|
## 错误处理
|
|
|
|
```typescript
|
|
// ✅ 用 Result 类型表达可预期的失败
|
|
type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E }
|
|
|
|
function parseSenseConfig(raw: unknown): Result<SenseConfig> {
|
|
// ...
|
|
}
|
|
|
|
// ✅ throw 只用于不可恢复的 bug
|
|
function unreachable(x: never): never {
|
|
throw new Error(`Unreachable: ${x}`)
|
|
}
|
|
```
|
|
|
|
- 可预期的失败用 `Result` 类型返回
|
|
- `throw` 只用于 bug(程序员错误),不用于业务逻辑
|
|
- 不用 try-catch 做流程控制
|
|
|
|
## 异步
|
|
|
|
```typescript
|
|
// ✅ async/await,不用 .then() 链
|
|
async function runCompute(sense: SenseModule): Promise<Signal | null> {
|
|
const value = await sense.compute(db, peers)
|
|
if (value == null) return null
|
|
return createSignal(sense.id, value)
|
|
}
|
|
```
|
|
|
|
## 工具链
|
|
|
|
| 工具 | 用途 |
|
|
|------|------|
|
|
| **pnpm** | 包管理 |
|
|
| **TypeScript** | 类型检查(strict mode) |
|
|
| **Biome** | lint + format(替代 ESLint + Prettier) |
|
|
| **tsup** | 打包 |
|
|
|
|
### 常用命令
|
|
|
|
```bash
|
|
pnpm run check # biome check(lint + format 检查)
|
|
pnpm run format # biome format --write(自动修复格式)
|
|
pnpm run build # 全量构建
|
|
```
|
|
|
|
## Monorepo 结构
|
|
|
|
```
|
|
nerve/
|
|
packages/
|
|
core/ # @nerve/core — 共享类型和工具函数
|
|
cli/ # @nerve/cli — CLI 入口
|
|
daemon/ # @nerve/daemon — 引擎运行时
|
|
docs/ # RFC、convention 等文档
|
|
biome.json # 根级 Biome 配置
|
|
tsconfig.json # 根级 TypeScript 配置(composite project references)
|
|
```
|
|
|
|
- `core` 是共享层,`cli` 和 `daemon` 都依赖它
|
|
- `cli` 和 `daemon` 之间不互相依赖
|
|
- 未来云端版本作为新 package 加入
|
|
|
|
## Commit Convention
|
|
|
|
```
|
|
<type>(<scope>): <description>
|
|
|
|
type: feat | fix | refactor | docs | chore | test
|
|
scope: core | cli | daemon | rfc-001 | ...
|
|
```
|
|
|
|
---
|
|
|
|
*小橘 🍊(NEKO Team)*
|