Skip to content

🐛 [Bug] pnpm dev 启动失败:plugin.app.dev-workspace failed to start — 三重问题合并修复 #904

Closed

Description

@hotlong

概述

运行 pnpm dev 时,服务器启动失败并退出:

  Loading objectstack.config.ts...
(node:73691) ExperimentalWarning: CommonJS module .../plugin-auth/dist/index.js is loading ES Module .../better-auth/dist/index.mjs using require().

  ✗ Plugin plugin.app.dev-workspace failed to start - rollback complete
 ELIFECYCLE  Command failed with exit code 1.

经过对启动流程的完整追踪分析,发现这是 三个相互关联的问题 共同导致的,需要整体修复。


问题 1:CLI serve 命令对根聚合配置的误判(核心触发点)

分析

serve.ts 第 192-201 行存在一个有缺陷的自动注册逻辑:

// packages/cli/src/commands/serve.ts L192-201
if (config.objects || config.manifest || config.apps) {
  try {
     const { AppPlugin } = await import('@objectstack/runtime');
     await kernel.use(new AppPlugin(config));  // ← 问题所在
     trackPlugin('App');
  } catch (e: any) {
     // silent
  }
}

根配置 objectstack.config.ts 含有 manifest 字段,因此该条件为 true。CLI 将**整个 root config(包含 plugins 数组)**包装为一个 AppPlugin(config),生成名为 plugin.app.dev-workspace 的插件。

但是,root config 是一个聚合器/宿主配置(Host Config),它的 plugins 数组中已经包含了实例化的插件对象(ObjectQLPlugin、DriverPlugin、AuthPlugin、AppPlugin(CrmApp) 等)。它不是一个普通的 App Bundle,不应被 AppPlugin 包装。

后果

在 kernel.bootstrap() 的 Phase 2 (Start) 中,plugin.app.dev-workspace 的 start() 方法尝试:

  1. 获取 ObjectQL 引擎 → 可能失败(因为重复注册冲突)
  2. 调用 runtime.onEnable() → root config 没有 onEnable
  3. 处理 seed data → root config 没有 data
  4. 但更严重的是,init 阶段的 registerService('app.dev-workspace', ...) 传入了整个 config 对象(含 plugins 数组实例),可能导致序列化或处理错误

同时,同一批 plugins 被注册了两次:一次通过 CLI 的 plugins.forEach(kernel.use) 循环,一次通过 AppPlugin(config) 的服务注册。

修复方案

在 serve.ts 中增加检测逻辑:当 config 已包含 plugins 数组(且数组中存在已实例化的 Plugin 对象),则跳过 auto-AppPlugin 包装。

// 检测是否为聚合器配置(已有 plugins 数组含实例化插件)
const isHostConfig = Array.isArray(config.plugins) && 
  config.plugins.some((p: any) => typeof p?.init === 'function');

if (!isHostConfig && (config.objects || config.manifest || config.apps)) {
  // 只为纯 App Bundle 创建 AppPlugin 包装
  const { AppPlugin } = await import('@objectstack/runtime');
  await kernel.use(new AppPlugin(config));
  trackPlugin('App');
}

问题 2:plugin-auth CJS→ESM 互操作问题(连锁放大器)

分析

ExperimentalWarning: CommonJS module .../plugin-auth/dist/index.js is loading 
ES Module .../better-auth/dist/index.mjs using require()

根因链条:

层级 配置 结果
Root package.json 无 "type": "module" 默认 CJS
plugin-auth/package.json "main": "dist/index.js", 无 "type" 继承 CJS
共享 tsup.config.ts format: ['esm', 'cjs'] 输出 .js(CJS) + .mjs(ESM)
plugin-auth/package.json "main": "dist/index.js" Node.js 加载 CJS 版本
better-auth 只导出 ESM (.mjs) CJS require() 加载 ESM → 实验性警告

plugin-auth 通过 tsup 构建输出了 CJS 和 ESM 双格式,但 package.json 中 "main" 指向 .js(CJS),没有 "module" 或 "exports" 字段。Node.js 默认加载 CJS 版本,当它 require('better-auth') 时触发 CJS→ESM 互操作,导致实验性警告,在某些 Node.js 版本下可能直接抛出异常。

修复方案

更新 plugin-auth/package.json 添加 exports 字段,优先使用 ESM:

{
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
  }
}

这样 serve.ts 通过 await import('@objectstack/plugin-auth') 加载时,Node.js 会选择 .mjs(ESM)版本,避免 CJS→ESM 互操作问题。


问题 3:与 #893 的关联(better-auth 字段名映射)

Issue #893 报告了 better-auth 的 camelCase↔snake_case 字段映射问题导致登录失败。这个问题与本 Issue 形成了阻塞链:

  1. 即使修复了问题 1 和 2,AuthPlugin 成功启动后
  2. 用户注册/登录时仍会遇到 [Bug] ObjectQL Adapter field name mapping error: camelCase vs snake_case causes 'Credential account not found' on sign-in #893 的字段映射错误
  3. objectql-adapter.ts 需要实现字段名自动转换

本 Issue 修复后,#893 的修复才能被验证,因为服务器必须先能成功启动。


影响范围

文件 修改类型
packages/cli/src/commands/serve.ts 修复自动 AppPlugin 包装逻辑
packages/plugins/plugin-auth/package.json 添加 ESM exports 字段
packages/cli/src/commands/serve.ts (测试) 添加聚合器配置测试
CHANGELOG.md 记录修复

验收标准

  • pnpm dev 能够成功启动,不再出现 plugin.app.dev-workspace failed to start 错误
  • ExperimentalWarning: CommonJS module ... is loading ES Module 警告消除
  • Root config 中的 plugins 不会被重复注册
  • examples/app-host/objectstack.config.ts 等聚合器配置也能正常工作
  • 纯 App Bundle 配置(无 plugins 数组)的 auto-AppPlugin 包装功能不受影响
  • 所��现有测试通过:pnpm test
  • 更新 CHANGELOG.md

相关

Labels

bug, P0, cli, plugin-auth, kernel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions