Civitai 单仓库改造全指南:基于 pnpm Workspaces 的基础设施包抽取实践
发布时间:2026/9/18 4:51:23来源:尧图网络
Civitai 单仓库改造全指南基于 pnpm Workspaces 的基础设施包抽取实践【免费下载链接】civitaiA repository of models, textual inversions, and more项目地址: https://gitcode.com/GitHub_Trending/ci/civitai本文以仓库中的 monorepo-conversion-plan.md 为核心骨架结合当前仓库实际落地状态pnpm-workspace.yaml、turbo.json、next.config.mjs、各packages/*/package.json与 monorepo-directory-snapshot.md编写。它记录了一次真实的、“先计划、后落地、再修正”的 monorepo 迁移全流程如何在不搬动 2,500 文件的前提下把全局基础设施Prisma、Postgres 连接池、Redis、ClickHouse、Axiom、OTEL抽成一组可被多个应用复用的 pnpm workspace 包。读完本文你将掌握主应用留在仓库根目录的增量式迁移策略、基础包互不依赖的规则、以工厂函数替代模块级单例的改造模式以及如何在迁移中保住 Git 历史与 Docker/CI 缓存。一、这是一份“历史计划”先读懂它再看它如何被修正文档开篇的说明非常关键——它是一份已被后续实施修正过的历史计划。有两处与原计划不同Prisma 契约被单独拆分原计划把 Prisma schema、生成的 client、enums.ts/models.ts全部放进civitai/db实际落地时它们落入了独立的civitai/db-schema包并引入了prisma-kysely生成器而civitai/db只保留运行时客户端工厂并依赖 db-schema。这一“契约与运行时分离”的设计在 monorepo-directory-snapshot.md 中被明确为类型消费者只依赖civitai/db-schema需要活连接的消费者才依赖civitai/db。Turborepo 后来被采用计划里写了“不用 Turborepo”但当前仓库根目录已存在 turbo.json定义了build/typecheck/lint/test/dev任务图。这与package.json中的turbo: ^2.9.17devDependency 相互印证。阅读本文时请始终带着“计划 vs 现实”的双重视角原计划的价值在于决策逻辑为什么、怎么做、避开什么坑当前仓库则是验证这些决策的活样本。二、改造目标把“全局基础设施”抽成共享包本仓库原本是一个单体 Next.js 应用主应用位于仓库根目录源码在src/下通过~/server/...这类路径别名互相引用。随着未来卫星应用如 moderator 应用的出现需要一种方式让多个应用共享底层基础设施而不是各自复制一份。改造目标非常聚焦把全局使用的基础设施——Prisma schema/client、Postgres 连接池、Redis、ClickHouse、Axiom 日志——抽取为共享 workspace 包未来的新应用moderator 应用见 moderator-app-shared-modules.md改为消费这些包不再从~/server/...导入该方案取代了早先的 git submodule 提案原本打算做成 submodule 的东西现在变成 workspace 包且包集合收窄到纯基础设施db、redis、clickhouse、axiom、telemetry。关键点是“只抽基础设施不抽领域代码”。Civitai 特有的领域常量——如browsingLevel.constants.tsDB 编码的 NSFW 位标志解释、air.tsAIR 标识符格式、flags.ts位运算辅助——留在主应用。它们是领域约定不是基础设施抽取它们要推迟到卫星应用真正需要时再说。三、为什么选 pnpm Workspaces而不是 Turborepo、Nx原计划的工具选型理由在当时的约束下是完全成立的包管理器已是 pnpm根 package.json 声明packageManager: pnpm10.28.1且有preinstall: npx only-allow pnpm强制全团队统一使用 pnpm工作区是一行配置在根package.json或pnpm-workspace.yaml声明即可无需引入新的编排层初期无需构建编排Next.js 通过transpilePackages透明地编译 workspace 包不需要每个包先tsc --build产出.jsTurborepo 可以后置如果 CI 构建时间成为问题再叠加 Turborepo 做缓存也不迟——它不需要在第一天就上。从当前仓库看第 4 点的“后置叠加”确实发生了turbo.json 已存在且package.json中出现了大量pnpm --filter civitai/xxx dev脚本如dev:auth、dev:moderator、dev:creator-studio、dev:training-studio、dev:storage说明 workspace 已从“主应用 若干包”扩展为“多应用 多包”的真实格局此时任务编排工具的价值才显现出来。四、基础包规则Base-package Rule互不依赖、纯基础设施这是整个架构最重要的两条纪律原文档明确写出落地后依然有效规则一基础包之间不允许互相导入。每个基础包db、redis、clickhouse、axiom、telemetry都是自包含的——只允许外部依赖不允许依赖某个civitai/*兄弟基础包。更高层包如未来的civitai-moderator-common可以组合多个基础包但基础包本身保持独立。如果两个基础包需要共享常量各自维护一份拷贝或通过子路径导出如civitai/redis/keys。这条规则的收益每个基础包可以独立使用一个纯文本工具应用可以只依赖civitai/db不必拉进 ClickHouse 或 Axiom避免低层包之间形成脆弱的依赖图。规则二基础包只装基础设施。Civitai 特有的领域常量留在主应用。当前仓库已用 ESLint 规则强制这条边界——monorepo-directory-snapshot.md 明确提到“package dependency-boundary enforcement一条 ESLint 规则阻止基础包互相导入或导入主应用”。五、布局决策主应用留在根目录不对称是特性原计划给出了目标布局/ ├── package.json # root: pnpm workspace config main app deps ├── pnpm-workspace.yaml # new ├── next.config.mjs # main app config (unchanged) ├── src/ # main app source (unchanged) ├── prisma/ # MOVES to packages/civitai-db ├── packages/ │ ├── civitai-db/ │ ├── civitai-redis/ │ ├── civitai-clickhouse/ │ ├── civitai-axiom/ │ └── civitai-telemetry/ # OTEL helpers (withSpan, etc.) — auto-instrumentation stays per-app └── apps/ └── moderator/ # added later为什么主应用留在根目录而不是搬进apps/main/把 2,500 个文件挪进apps/main/会触发 monorepo-split-overview.md 早就否掉的“灾难”。主应用留在根目录意味着~/路径导入永不改变新应用进apps/共享代码进packages/这种不对称本身就是特性——它让改造可以增量进行。如果未来对称性变得重要比如第三个应用出现让“根目录即应用”显得奇怪git mv src/ apps/main/src/可以变成工作区就绪后的一次性清理即下文 Phase 6。当前仓库正是这一决策的活证据pnpm-workspace.yaml 将根目录本身列为 workspace 成员packages: [., packages/*, apps/*]主应用源码仍在根src/而apps/下已有auth、moderator、creator-studio、training-studio、notifications、orchestrator-gateway、storage、event-engine等多个卫星应用。六、工作区引导Phase 0让 workspace 先“活”起来Phase 0 的目标只有一个workspace 建立起来但还没有任何包被引用。四个步骤1. 新增pnpm-workspace.yamlpackages: - . - packages/* - apps/*注意第一行.——根目录本身是 workspace 成员这正是“主应用留在根目录”的技术前提。pnpm 官方文档支持根成员且没有任何功能代价。2. 根package.json增加workspaces: [packages/*, apps/*]仅作信息提示pnpm 实际以 yaml 为准。3.next.config.mjs增加transpilePackages: [civitai/*]让 Next.js 按需编译 workspace 包——这样不需要为每个包单独配置构建步骤。当前仓库的 next.config.mjs 已把transpilePackages扩展为一个长列表civitai/db-schema、civitai/db、civitai/db-queries、civitai/shared、civitai/buzz、civitai/redis、civitai/clickhouse、civitai/axiom、civitai/flipt、civitai/telemetry、civitai/auth、civitai/notifications、civitai/moderation等与“包集合已扩大”的现状一致。4. 创建空包目录与最小package.jsonname: civitai/db、version: 0.0.0、main: src/index.ts。5. 验证pnpm install成功、pnpm run typecheck通过。Checkpointworkspace 存活但还没有任何代码导入它。七、包抽取顺序按依赖序逐步推进包必须按依赖顺序移动每个阶段结束时pnpm install、pnpm run typecheck、pnpm run build三项全部绿灯。Phase 1civitai/dbPostgres —— schema、client、连接池把 Postgres 相关的一切收进一个包Prisma schema、生成的 client、migrations、programmability 脚本、pg 连接池与辅助函数。未来若有第二个数据库 schema如 analytics应新建独立包如myapp-db——这个包是 Civitai 的权威 schema。迁移清单prisma/schema.full.prisma→packages/civitai-db/prisma/schema.full.prismaprisma/migrations/→packages/civitai-db/prisma/migrations/prisma/programmability/→packages/civitai-db/prisma/programmability/prisma/seed.ts→packages/civitai-db/prisma/seed.tssrc/server/db/client.ts→packages/civitai-db/src/client.tsPrisma client 包装src/server/db/db-helpers.ts→packages/civitai-db/src/db-helpers.ts553 行——pg 连接池、cancellableQuery、prom-client histogram 注册src/server/db/pgDb.ts、notifDb.ts、datapacketDb.ts、db-lag-helpers.ts→packages/civitai-db/src/src/shared/utils/prisma/enums.ts→packages/civitai-db/src/enums.tsPrisma 生成src/shared/utils/prisma/models.ts→packages/civitai-db/src/models.tsPrisma 生成Prisma client 输出目录在 schema 中添加generator client { provider prisma-client-js output ../generated/client }client 现在活在包内部不再位于根node_modules/.prisma/client。所有应用都从civitai/db/client导入。包导出package.json的exports字段{ exports: { .: ./src/index.ts, ./client: ./generated/client/index.js, ./enums: ./src/enums.ts } }对照当前仓库packages/civitai-db/package.json 的实际 exports 是.: ./src/index.ts与./kysely: ./src/kysely.ts——./client、./enums被收敛进 db-schema 的导出面packages/civitai-db-schema/package.json 的 exports 含./enums、./models、./kysely这正是文档开头“实际落地不同”的第二处体现。更新根脚本db:generate改为在包内运行db:migrate更新迁移脚本中的路径。当前仓库的 package.json 中db:generate是node scripts/generate-slim-schema.js prisma generate --no-hintsprisma字段的 schema 路径已指向packages/civitai-db-schema/prisma/schema.prisma印证了落地后的路径变更。为 monorepo 重构工厂函数替代模块级单例。现有代码用模块级单例直接读env// today import { env } from ~/env/server; const instanceUrlMap { primary: env.DATABASE_URL, ... }; export const dbWrite createClient(primary);对要被 N 个应用消费的包需要暴露工厂// packages/civitai-db/src/index.ts export function createDbClients(config: { databaseUrl: string; databaseReplicaUrl?: string; notificationDbUrl: string; notificationDbReplicaUrl?: string; datapacketReplicaUrl?: string; serviceLabel: string; // for prom-client labels — distinguishes apps }): { dbWrite: PrismaClient; dbRead: PrismaClient; pgDb: AugmentedPool; notifDb: AugmentedPool; // ... }主应用保留一个薄包装在src/server/db/client.tsimport { createDbClients } from civitai/db; import { env } from ~/env/server; const clients createDbClients({ databaseUrl: env.DATABASE_URL, // ... serviceLabel: civitai-app, }); export const { dbWrite, dbRead, pgDb, notifDb } clients;主应用的调用点不用改——它们仍然import { dbWrite } from ~/server/db/client只是实现搬走了。这就是“shim 薄包装”策略迁移对外部代码是零 diff的。三个已知坑Gotchas包内循环依赖db-helpers.ts:7从~/server/db/client导入dbWrite——文件一搬进包这就变成包内循环依赖。解法把dbWrite以参数传给需要的 helper或重构client.ts与db-helpers.ts使二者互不导入prom-client Histogram 注册的 HMR 兜底db-helpers.ts:30-35的 try/catch 回退模式要保留——它同时处理了测试期间多个应用同进程共存的情况Prisma.Sql类型来源来自prisma/client而它生活在这个包里。运行时与类型相同只是模块路径变了。Phase 2civitai/redis迁移清单src/server/redis/client.ts1,105 行——clients helperssrc/server/redis/caches.ts1,571 行——缓存 key 常量、TTL、createCachedObject基础设施src/server/redis/queues.ts、entity-metric.redis.ts、entity-metric-populate.ts、resource-data.redis.ts、fail-open-log.tssrc/utils/cache-helpers.ts若确为纯 helpers需先验证工厂模式export function createRedisClients(config: { redisUrl: string; sysRedisUrl: string; failOpenLogger?: (event: object) void; }): { redis: RedisClient; sysRedis: RedisClient; // ... }一个重要的解耦细节fail-open-log.ts原本直接用logToAxiom现在改为通过 config 注入 logger 函数——这样 redis 包就不会硬依赖civitai/axiom符合“基础包互不依赖”的规则。缓存 key 常量走子路径导出key 字符串与 TTL 从caches.ts抽出到keys.ts并通过子路径导出civitai/redis/keys。只想拿 key 的消费者比如一个只想失效缓存、不想实例化 redis client 的脚本直接import civitai/redis/keystree-shaking 会把 redis client 从它的 bundle 中剔除。当前仓库中 packages/civitai-redis/package.json 的依赖只有redis、lodash-es、msgpackr、slugify、zod、lru-cache没有兄弟civitai/*包——规则一在落地中得到了遵守。Phase 3civitai/clickhouse迁移src/server/clickhouse/client.ts733 行。与civitai/db相同的工厂模式。ClickHouse client 更简单——单个 URL无副本路由export function createClickhouseClient(config: { url: string; database: string; username: string; password: string; }): ClickHouseClient;如果 ClickHouse 查询 helper 存在于 services 层如事件追踪 helper留在应用内——只迁移连接层。packages/civitai-clickhouse/package.json 证实依赖仅为clickhouse/client、dayjs、zod。Phase 4civitai/axiom迁移src/server/logging/client.ts58 行——axiom、safeError、logToAxiom。最小、最简单的包几乎可以做成单文件包但它作为清晰的依赖边界仍有价值export function createAxiomLogger(config: { token?: string; orgId?: string; datastream?: string; podName?: string; echoToStderr?: boolean; }): { logToAxiom, safeError };Phase 5civitai/telemetryOTEL 是必须被拆开的两个东西自动插桩注册——src/instrumentation.node.ts调用sdk.start()在进程加载时 patch Prisma/Redis/HTTP。这部分必须留在每个应用内每个应用有自己的instrumentation.node.ts和 service name。搬进包里会丢失自动加载行为Helper 函数——withSpan、span attribute helpers、src/utils/otel-helpers.ts的工具。这些搬进civitai/telemetry。包还可以导出一个bootstrapOtel(config)函数做今天instrumentation.node.ts做的事让每个应用的该文件变成三行// apps/moderator/src/instrumentation.node.ts import { bootstrapOtel } from civitai/telemetry/node; bootstrapOtel({ serviceName: civitai-moderator });prom-client指标注册src/server/prom/client.ts遵循同样模式——helper 进包注册调用留在应用。当前仓库 packages/civitai-telemetry/src 下有index.ts、client.ts、otel-helpers.ts、otel-logs.ts及测试目录依赖为opentelemetry/*与prom-clientmonorepo-directory-snapshot.md 也确认了index.ts导出withSpan helpers浏览器安全子集、node.ts导出bootstrapOtel(config)、prom.ts的结构。主应用侧src/instrumentation.node.ts收缩为约 3 行调用bootstrapOtel({ serviceName: civitai-app })。八、横切关注点Cross-cutting Concerns1. env 校验当前在src/env/server.tsT3 风格 zod 校验。两个选项每应用自校验 env每个应用校验自己的 env包通过工厂接收解析好的配置——推荐且与工厂模式天然对齐共享 env 包civitai/env导出 zod schema——应用需要不同子集时会很脆。结论采用 per-app env。包绝不直接 importenv。2. 迁移工具链prisma/migrations/移到packages/civitai-db/prisma/migrations/。scripts/下的迁移脚本如 prisma-migrate-with-views-workaround.mjs更新路径。按 CLAUDE.md 约定的手动应用迁移惯例不变——这解释了根package.json中db:migrate使用该 workaround 脚本、db:deploy追加-p标志的写法。3. Prisma 客户端版本钉扎prisma/client与prismaCLI移入packages/civitai-db/package.json。主应用不再直接声明它们而是依赖civitai/db由它依赖prisma/client——整个 workspace 只有一个 Prisma 版本。4. CI工作区根一次pnpm install安装全部pnpm -r run typecheck类型检查所有包与应用pnpm run build主应用内仍然产出 Next standalone bundle现有 CI workflow 大体存活——主入口点未变为pr-check.yml增加paths:过滤器apps/moderator/内的变更不应触发主应用构建反之亦然否则每个 PR 都会全量重建。5. Docker对现有Dockerfile有两处具体改动1工作区感知的安装层。在pnpm install之前除了根package.json还要复制pnpm-workspace.yaml与 workspace 内每一个package.json根 packages/*apps/*。这保住了安装层缓存的技巧——lockfile package.json 很少变化安装层保持温热COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./ COPY packages/*/package.json ./packages/ COPY apps/*/package.json ./apps/ # only after satellites exist RUN pnpm install --frozen-lockfile2更新 Prisma schema 路径。当前 Dockerfile 有两行 COPY 分别钉住prisma/schema.full.prisma和prisma/schema.prisma以做层缓存——都更新为packages/civitai-db/prisma/...。对于卫星应用未来的apps/moderator/Dockerfile使用pnpm deploy --filter app --prod /tmp/out产出一个只含该应用传递依赖的精简部署包该 bundle 成为 runner 阶段的 COPY 源。这是 pnpm 官方认可的 monorepo Docker 镜像模式。6. Next.jsoutput: standalone与 workspace 包主应用的生产运行时依赖.next/standaloneDockerfile 第 61 行next.config.mjs 中output: standalone仍然存在。standalone 模式用nftNode File Trace决定打包内容。面对 workspace 包二选一transpilePackages: [civitai/*]——Next 把包源码内联进构建输出。最简单已在 Phase 0 计划中outputFileTracingRoot: path.join(__dirname, ../..)——告诉nft跟随 symlink 到 workspace 根。仅当包自己有tsc --build产出预编译.js时才需要。选择方案 1。方案 2 只在将来希望每个包有构建产物时才相关发布、用缓存加速冷构建。7. 明确不需要的工具Turborepo / Nx在“很多包 × 很多应用 × 慢 CI”时才值得。对 6 个包 2 个应用裸 pnpm 更简单。CI 构建时间成为真问题时再加。注如前所述当前仓库后来确实加了 Turborepo——计划的判断是“可以后置”而不是“永远不要”。Changesets / lerna只服务于对外发布的包。workspace:*已处理内部版本管理。TypeScript project referencestsconfig 的referencesNext transpilePackages透明处理跨包编译。独立 publish/registry 配置包保持内部使用。8. 现有分支的迁移最好在**冻结周freeze week**做此时活跃分支最少。每个未合分支都需要一次 rebase 以吸收包边界。Phase 1 的 shim 再导出技巧能显著缓解冲击没被改动过的分支依然能编译因为旧导入路径继续有效。9. Git 历史保留每个文件三提交模式Git隐式跟踪重命名——读取时通过逐提交比较“删除 vs 新增”的文件内容推断默认 50% 相似度阈值。为了让git log --follow和git blame在移动后继续工作每个文件遵循以下模式纯移动git mv src/server/db/db-helpers.ts packages/civitai-db/src/db-helpers.ts——不做任何内容改动Git 无歧义识别重命名重构此时再改 import、把env换成工厂配置、解开循环依赖。diff 小blame 归属正确加 shim如适用在旧路径创建一行再导出。它是真正的新文件——不需要历史因为原内容的历史在包路径那边。团队应掌握的工具git log --follow path——跨重命名显示完整历史裸git log path不会git blame——自动跟随简单重命名git blame -C还能检测从其他文件复制的内容对文件拆分有用GitHub Web UI——View blame 与文件历史无需 flag 即可跟随重命名。要避免的坑移动 大幅编辑合并进同一提交内容相似度跌破 50% 时Git 会漏掉重命名历史变成“可找到但不可跟随”squash-merge 迁移 PR把三提交模式压成一个大 diff提高重命名检测失败的概率。迁移 PR 用rebase-merge 或 merge commit文件拆分如果db-helpers.ts在移动中被拆成多个文件Git 只会对内容重叠最高的文件自动检测重命名。优先先移动后拆分先 move再在后续提交 split而不是先拆分后移动。九、决策记录Decisions原文档记录了五条已拍板的决策值得完整保留包命名civitai/scope。所有包使用civitai/前缀。保留 npm org 名即使永不公开发布也能让未来发布变得平凡并避免命名冲突。当前仓库所有packages/*/package.json的 name 均为civitai/*此决策已执行单一civitai/datavs 四个窄包db/redis/clickhouse/axiom拍板四个窄包。未来的纯文本工具应用可以只依赖civitai/db不必拉进 ClickHouse 或 Axiomschema-common 范围。是否包含 moderator 页面使用的~/server/schema/*.schema.tszod 文件拍板推迟。Phase 1–5 的基础设施包不导入任何 zod schema——它们只需要 Prisma 类型。任何~/server/schema/*.schema.ts抽取推迟到 moderator 应用阶段届时才知道卫星到底需要哪些 schema从 moderator 依赖分析看候选是report.schema、strike.schema、image.schema、scanner-review.schema见 moderator-app-shared-modules.md再导出 shimPhase 1 过渡永久保留drift-tolerant。意思是旧文件如src/server/db/client.ts保留为一行再导出文件export * from civitai/db或用主应用 env 调用createDbClients的薄包装。现有~/server/db/client导入继续编译。Drift-tolerant 永久保留 shim绝不批量重写主应用约 2,500 个调用点。代价是同一代码有两个合法导入路径评审时有些约定噪音收益是零强制 churn。当前仓库中src/server/db/client.ts、db-helpers.ts、pgDb.ts、notifDb.ts、datapacketDb.ts、db-lag-helpers.ts及 redis/clickhouse/logging 对应文件都保留了 shimmonorepo-directory-snapshot.md 的结构树明确标注了这些 SHIM主应用最终是否移入apps/main/拍板无限期跳过避免冻结周成本。新应用进apps/主应用永久留在根目录。仅当第三个应用或强理由出现时再重议。packages: [., packages/*, apps/*]完全支持根成员不对称布局无功能代价唯一摩擦是将来第三个应用加入时的轻微审美不一致。十、Phase 6可选不计划主应用移入apps/main/当前无限期跳过理由同上冻结周成本。不对称布局主应用在根 卫星在apps/是规划的永久状态。如果未来触发点出现第三个应用、强烈的一致性偏好、会因不对称而崩溃的工具链迁移步骤为git mv src/ apps/main/src/git mv next.config.mjs apps/main/git mvprisma 相关根脚本 →apps/main/只搬应用专属的不搬 workspace 级的根package.json改为纯 workspace不再含应用依赖pnpm-workspace.yaml从 packages 列表中去掉根更新以根为目标的 CI workflow需要冻结周无活跃功能分支monorepo-directory-snapshot.md 将其称为“Phase 7可选最后一步”并补充了纯 workspace shell 的未来形态根目录仅保留package.json、pnpm-workspace.yaml、pnpm-lock.yaml、tsconfig.base.json同时确认该步骤继续推迟直到卫星应用存在并证明 workspace 布局稳定。十一、阶段总结表PhasePackageEffortRisk0Workspace bootstrapLowLow — just config1civitai/dbHighMedium — Prisma client path change is the trickiest single step; circular dep indb-helpers.tsto untangle2civitai/redisMediumLow — lots of files, mostly mechanical3civitai/clickhouseLowLow4civitai/axiomLowLow5civitai/telemetryMediumMedium — splitting auto-instrumentation from helpers requires care6Move main app toapps/main/—Not planned— kept at root indefinitely to avoid freeze-week costPhase 5 完成后apps/moderator作为 workspace 成员消费这些包的基础就绪了——那是另一份独立计划见 moderator-app-shared-modules.md。十二、落地验证计划 vs 当前仓库对照把计划与仓库现状逐条核对可以确认这套方法论的可执行性workspace 配置pnpm-workspace.yaml 内容与 Phase 0 完全一致.packages/*apps/*包命名与数量packages/下确有civitai-db-schema、civitai-db、civitai-redis、civitai-clickhouse、civitai-axiom、civitai-telemetry等窄包且基础包之间无civitai/*互依赖对照各自 package.json 可验证契约/运行时分离civitai/db-schema持有 Prisma schema、migrations、生成的 enums/models/kysely 类型civitai/db只提供运行时工厂并依赖civitai/db-schema: workspace:*transpilePackagesnext.config.mjs 的transpilePackages列表覆盖全部 workspace 包且serverExternalPackages中保留了prisma/client、prisma/instrumentation、redis、OTEL 等原生/插桩依赖避免运行时出现“双份 Prisma runtime”导致的instanceof Sql失败配置注释里记录了operator does not exist: integer jsonb这个真实事故Turborepo 后置turbo.json 已按“计划允许后置”的方式加入package.json提供pnpm --filter civitai/xxx系列脚本编排多应用主应用留在根根src/、根next.config.mjs、根package.json含全部主应用依赖原样保留apps/已容纳多个卫星应用。结语这份 monorepo 改造计划的真正价值不在于“要不要用 Turborepo”这类单一答案而在于它提供了一套可以增量执行、且每一步都可验证的迁移方法论主应用留在根目录保护 2,500 文件的导入路径不动工厂函数替代模块级单例让包可被任意数量的应用消费shim 再导出实现零强制 churn 的过渡三提交模式保住 Git 历史Docker/CI 的缓存技巧让成本可控。当前仓库 monorepo-directory-snapshot.md 证明这套计划已经走完核心阶段并落地成型——对任何打算把大型单体 Next.js 应用改造成 pnpm workspace monorepo 的团队这是一份罕见的、有完整“事后对照”的实战蓝本。【免费下载链接】civitaiA repository of models, textual inversions, and more项目地址: https://gitcode.com/GitHub_Trending/ci/civitai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网