chore(deno): migrate toolchain from Node 24 + pnpm to Deno 2.9.7 + TypeScript 7.0.2 (W1.5)

- root deno.json workspace (nodeModulesDir auto), npm versions registered once in imports, deno.lock;
  remove pnpm-lock/pnpm-workspace/root package.json; members keep minimal package.json (name/type/exports)
- tasks: dev, typecheck (tsc 7.0.2 per member + deno check), lint/fmt (biome, useImportExtensions),
  test (vitest via deno), check:tokens, build (web + deno compile), ci, db:migrate/reset, gen:types
- tsconfig base moved to config/ (no root tsconfig.json); deno types -> types/deno.d.ts; web drops baseUrl
- server: Deno.serve entry with graceful shutdown, --role migrate|preflight|probe, WEB_ROOT static SPA hosting;
  drop @hono/node-server, tsx, esbuild build.mjs
- logger: redact child() bindings and serialize err by message/stack; sync stdout destination; tests
- relative imports get explicit extensions (sites/mp-sync paths left for follow-up)
- crash-recovery tests spawn `deno run -A`; tasks tests use unique per-file schemas
- single-container Dockerfile + entrypoint (migrate -> preflight -> api+worker watchdog), docker-compose.yml
- docs: README, runbook, ADR-001 superseded, plan §1 validation row

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
rootandClaude Fable 5.1 committed 2026-09-27 18:53:06 +08:00
1 parent d9f2af2f3c
commit 304feea141
226 files changed
+6601 -8620

No files matched your search

+1 -1
View File
@@ -5,7 +5,7 @@ PostgreSQL 访问层:Kysely 客户端与连接池、表类型(`src/types.ts`)、
## 规则
- **迁移是唯一事实来源**。所有 schema 变更只能新增迁移,已发布迁移不得修改。迁移用原生 DDL 书写(`src/migration-helpers.ts`:`entityTable()` 统一加 uuid 主键 `gen_random_uuid()`、`created_at/updated_at` 与 updated_at 触发器;`oneOf()` 生成状态 CHECK)。`migrations/` 内相对导入必须带 `.ts` 扩展名(Kysely 用原生 `import()` 加载)。
- **`src/types.ts` 是生成物**:`pnpm db:migrate && pnpm --filter @umt/db gen:types`。CHECK 枚举自动变成字面量联合(如 `AcquisitionSlotStatus`)。`test/schema.test.ts` 会在隔离 schema 里重新反射并比对,漂移即失败;`packages/contracts/test/db-sync.test.ts` 再校验 contracts 的 Row schema 与枚举和这里一致。
- **`src/types.ts` 是生成物**:`deno task db:migrate && deno task gen:types`。CHECK 枚举自动变成字面量联合(如 `AcquisitionSlotStatus`)。`test/schema.test.ts` 会在隔离 schema 里重新反射并比对,漂移即失败;`packages/contracts/test/db-sync.test.ts` 再校验 contracts 的 Row schema 与枚举和这里一致。
- 状态列一律 `text + CHECK`,不用 PG enum(便于迁移加值)。JSONB 只放证据、规则参数、原文快照与演进字段;稳定查询字段用关系列。
- 凭证只存 `secret_ref`(→ `secret.id`),明文不入库、不进日志。
- `createDb()` 把 `int8` 解析为 `number`(超出 2^53 抛错)、`date` 保持 `YYYY-MM-DD` 字符串。写 jsonb 数组时须先 `JSON.stringify`(pg 会把 JS 数组当 PG 数组)。
+12
View File
@@ -0,0 +1,12 @@
{
"name": "@umt/db",
"version": "0.0.0",
"exports": { ".": "./src/index.ts" },
"tasks": {
"test": "deno run -A npm:vitest run",
"typecheck": "deno run -A npm:typescript/tsc --noEmit -p tsconfig.json",
"migrate": "deno run -A --env-file=../../.env scripts/migrate.ts latest",
"reset": "deno run -A --env-file=../../.env scripts/migrate.ts reset",
"gen:types": "deno run -A --env-file=../../.env scripts/gen-types.ts"
}
}
-15
View File
@@ -1,23 +1,8 @@
{
"name": "@umt/db",
"version": "0.0.0",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts"
},
"scripts": {
"typecheck": "tsc -p tsconfig.json",
"test": "vitest run",
"migrate": "tsx --env-file-if-exists=../../.env scripts/migrate.ts latest",
"reset": "tsx --env-file-if-exists=../../.env scripts/migrate.ts reset",
"gen:types": "tsx --env-file-if-exists=../../.env scripts/gen-types.ts"
},
"dependencies": {
"kysely": "^0.29.6",
"pg": "^8.23.0"
},
"devDependencies": {
"@types/pg": "^8.23.1"
}
}
+2 -2
View File
@@ -3,7 +3,7 @@ import { fileURLToPath } from "node:url";
import { type Kysely, sql } from "kysely";
// 从已迁移的数据库反射出 src/types.ts:列类型 + CHECK (col in (...)) 枚举字面量联合。
// 迁移是唯一事实来源;改表后运行 `pnpm --filter @umt/db gen:types`,测试会校验两者无漂移。
// 迁移是唯一事实来源;改表后运行 `deno task gen:types`,测试会校验两者无漂移。
interface ColumnInfo {
table_name: string;
@@ -73,7 +73,7 @@ export async function generateTypesSource(db: Kysely<any>): Promise<string> {
const out: string[] = [
"// 本文件由 scripts/gen-types.ts 从迁移后的数据库反射生成,请勿手改。",
"// 改表:新增迁移 → pnpm db:migrate → pnpm --filter @umt/db gen:types。",
"// 改表:新增迁移 → deno task db:migrate → deno task gen:types。",
"",
'import type { ColumnType, Generated, Insertable, Selectable, Updateable } from "kysely";',
"",
+3 -3
View File
@@ -1,7 +1,7 @@
// 用法:tsx scripts/gen-types.ts —— 对 DATABASE_URL 的当前 schema 反射并重写 src/types.ts
// 用法:deno task gen:types —— 对 DATABASE_URL 的当前 schema 反射并重写 src/types.ts
import { writeFileSync } from "node:fs";
import { createDb } from "../src";
import { formatWithBiome, generateTypesSource, TYPES_FILE } from "./gen-types-lib";
import { createDb } from "../src/index.ts";
import { formatWithBiome, generateTypesSource, TYPES_FILE } from "./gen-types-lib.ts";
const db = createDb({ max: 1 });
try {
+2 -2
View File
@@ -1,5 +1,5 @@
// 用法:tsx scripts/migrate.ts latest|reset
import { createDb, migrateToLatest, resetSchema } from "../src";
// 用法:deno task db:migrate / db:reset(= deno run -A --env-file=.env packages/db/scripts/migrate.ts latest|reset)
import { createDb, migrateToLatest, resetSchema } from "../src/index.ts";
const cmd = process.argv[2] ?? "latest";
const db = createDb({ max: 1 });
+1 -1
View File
@@ -1,6 +1,6 @@
import { Kysely, PostgresDialect } from "kysely";
import pg from "pg";
import type { DB } from "./types";
import type { DB } from "./types.ts";
// int8 → number(大小/序号都在 2^53 内;超出即报错而不是静默丢精度)
pg.types.setTypeParser(pg.types.builtins.INT8, (v: string) => {
+3 -3
View File
@@ -1,5 +1,5 @@
// 统一从本包取 Kysely 与 sql,避免各包各装一份 kysely
export { type Kysely, sql, type Transaction } from "kysely";
export { type CreateDbOptions, createDb } from "./client";
export { createMigrator, MIGRATIONS_DIR, type MigratorOptions, migrateToLatest, resetSchema } from "./migrator";
export type * from "./types";
export { type CreateDbOptions, createDb } from "./client.ts";
export { createMigrator, MIGRATIONS_DIR, type MigratorOptions, migrateToLatest, resetSchema } from "./migrator.ts";
export type * from "./types.ts";
+1 -1
View File
@@ -1,5 +1,5 @@
// 本文件由 scripts/gen-types.ts 从迁移后的数据库反射生成,请勿手改。
// 改表:新增迁移 → pnpm db:migrate → pnpm --filter @umt/db gen:types。
// 改表:新增迁移 → deno task db:migrate → deno task gen:types。
import type { ColumnType, Generated, Insertable, Selectable, Updateable } from "kysely";
+1 -1
View File
@@ -1,6 +1,6 @@
import { readdirSync } from "node:fs";
import { describe, expect, it } from "vitest";
import { MIGRATIONS_DIR } from "../src";
import { MIGRATIONS_DIR } from "../src/index.ts";
describe("migrations", () => {
it("迁移文件按 NNNN_name.ts 命名且导出 up/down", async () => {
+2 -2
View File
@@ -3,8 +3,8 @@ import { Kysely, PostgresDialect, sql } from "kysely";
import { NO_MIGRATIONS } from "kysely/migration";
import pg from "pg";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { formatWithBiome, generateTypesSource, TYPES_FILE } from "../scripts/gen-types-lib";
import { createMigrator, type DB, migrateToLatest } from "../src";
import { formatWithBiome, generateTypesSource, TYPES_FILE } from "../scripts/gen-types-lib.ts";
import { createMigrator, type DB, migrateToLatest } from "../src/index.ts";
// 对真实 PG(默认开发库 55433)跑迁移往返;每次测试用独立 schema,互不干扰且不碰 public。
// 无数据库环境可设 UMT_SKIP_DB_TESTS=1 跳过(CI 默认要求数据库可用)。
+1 -6
View File
@@ -1,9 +1,4 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
// 迁移文件由 Kysely FileMigrationProvider 用原生 import() 加载(Node 类型剥离,不做无扩展名解析),
// 因此 migrations/ 内相对导入必须写 .ts 扩展名
"allowImportingTsExtensions": true
},
"extends": "../../config/tsconfig.base.json",
"include": ["src", "test", "scripts", "migrations"]
}