fix(cli): resolve @objectstack/organizations from the host app (cloud#1013) - #4699
Merged
Merged
Conversation
…#1013)
`objectstack serve` loaded the enterprise multi-org runtime with a bare
`import('@objectstack/organizations')`. Node ESM resolves a bare specifier
against the importer's own realpath — the CLI's, inside the framework
workspace it is linked out of — while that package is cloud-private and
lives in the served app's node_modules. The import could therefore never
succeed: every self-hosted deployment requesting a walled tenancy posture
(`group` / `isolated`) hit the ADR-0093 D5 fail-fast and exited 1, and the
only way past it was OS_ALLOW_DEGRADED_TENANCY=1 — the unwalled state D5
exists to prevent.
The file already had the right resolver (`importFromHost`), but it was
declared AFTER the auth block that contains this load, so the organizations
site could not use it. Extracted to `src/utils/import-from-host.ts`
(`createHostRequire` / `createHostImporter`), hoisted above the auth block,
and used at the organizations site.
Two adjacent corrections:
- the fallback now applies only when the host cannot RESOLVE the package. A
host-resolved package that throws while it evaluates propagates its real
error instead of being re-imported bare and reported as MODULE_NOT_FOUND,
which every caller classifies as "not installed".
- the D5 fatal names the app as where the package must be installed.
Regression: `test/serve-organizations-host-resolution.e2e.test.ts` spawns the
REAL `os serve` against a temp app that carries the package in its own
node_modules. Every existing multi-org test bypasses this path (cloud's
dogfood suites pass `extraPlugins: [new OrganizationsPlugin()]`; the verify
harness posture test mocks the module), which is why the defect survived. The
new e2e fails against the bare import with the exact issue message and passes
with the fix; a second case pins that the D5 fail-fast still fires when the
app genuinely lacks the package.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019TYoxKa8yFLiDDh7tkBqtu
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 20 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…st be installed (cloud#1013) The D5 boot-guard remediation list said "install @objectstack/organizations", which was un-actionable while the CLI resolved the package against its own realpath — installing it ANYWHERE did not lift the guard. Now that `serve` resolves from the host app, the location is exact and load-bearing, so the bullet says it: declare it in the served app's package.json. Same wording as the fatal message the CLI prints, so the doc and the terminal agree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019TYoxKa8yFLiDDh7tkBqtu
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes objectstack-ai/cloud#1013
问题
objectstack serve用裸 import 加载企业版多组织运行时:Node ESM 对裸 specifier 按导入方自己的 realpath 解析 —— CLI 是通过 workspace/
link:接入的,realpath 落在 framework 工作区里;而@objectstack/organizations是 cloud 私有包,只存在于被服务的 app 的node_modules。这个 import 因此永远不可能成功:后果是任何自托管的 walled posture(
OS_TENANCY_POSTURE=group/isolated)都直接撞上 ADR-0093 D5 的 fail-fast 并exit(1),唯一绕过方式是OS_ALLOW_DEGRADED_TENANCY=1—— 正是 D5 要防的「墙不生效还照常服务」状态。在 app 的package.json里声明依赖没有任何用,因为解析基点根本不是 host app。修复
同一个文件里本来就有正确的解析器
importFromHost(先用 host app 的createRequire解析,再 import 解析出的绝对路径),但它定义在 auth 区块之后,而 organizations 的加载在该区块之内,所以用不上。packages/cli/src/utils/import-from-host.ts(createHostRequire/createHostImporter),上提到 auth 区块之前,organizations 处改用它。AI 服务那条路径行为不变(仍是同一个 helper)。package.json里声明@objectstack/organizations即可 —— 两个 showcase app 已经声明了。顺带两处修正:
try { host } catch { bare }会把已解析成功但求值时抛错的包重新裸 import 一遍,真实错误被替换成MODULE_NOT_FOUND—— 而所有调用方都把它归类为「没装」(Serve.isModuleNotFoundError)。于是一个坏掉的包会被静默跳过(可选服务),或者让 D5 打印「请安装」去指责一个已经装好的包。现在求值期的崩溃原样抛出。回归 —— 这是本 PR 的重点
这个缺陷能活这么久,是因为所有多组织测试都绕开了 CLI 自己的解析路径:cloud 的 dogfood 套件显式传
extraPlugins: [new OrganizationsPlugin()],packages/verify的 posture 测试直接vi.mock掉这个模块。两者都跳过了唯一坏掉的那一环。新增
packages/cli/test/serve-organizations-host-resolution.e2e.test.ts:用现成的test/helpers/serve-process.tsspawn 真实的os serve进程,跑在一个临时 app 目录上 —— 真的package.json、真的node_modules/@objectstack/organizations(企业包在本工作区装不了,这正是问题本身,所以用一个注册同样org-scoping服务 + posture entitlement 的替身),不 mock 任何东西。OS_TENANCY_POSTURE=isolated⇒ 必须启动到 banner,且插件列表里出现Organizations。对旧代码验证过会红:把那一行改回裸 import 后重跑,case 1 失败并打印出 issue 里那条一模一样的信息:
另加
packages/cli/src/utils/import-from-host.test.ts—— 用真实 fixture 目录钉住解析语义:issue 的两半 repro(CLI 侧解析不到 / host 侧解析得到)、回退分支、以及「求值崩溃不得被伪装成 MODULE_NOT_FOUND」。文档
content/docs/deployment/tenancy-modes.mdx的 D5 boot-guard 补救清单原文是「install@objectstack/organizations」—— 在修复前这句根本无法执行(装在哪都没用,因为解析基点是 CLI 的 realpath);修复后位置变成精确且是关键信息,所以那一条改为「装进被服务的 app,在该 app 的package.json里声明」,措辞与 CLI 打印的 D5 致命信息一致。只改这一条,不做文档普扫。验证
顺带扫描到的同类缺陷(不在本 PR 范围,已另开 issue #4700)
packages/verify/src/harness.ts:270的bootStack({ multiTenant: true })是同一形状的裸 import,同样解析不到;packages/qa/dogfood/test/attachments-permission-matrix.dogfood.test.ts:528用裸 import 探测该包是否可用,因而永远判定「不可用」。修它需要先决定这个 node-only 解析器的共享位置(CLI 的 utils 用不了,@objectstack/types会把node:module带进可能跑在 edge/浏览器的包里),属于跨包架构决定 —— 详见 #4700。另注(未改、未开 issue,留给维护者判断):
tenancy-modes.mdx里那段引用的 FATAL 代码块仍是OS_MULTI_ORG_ENABLED=true but …的旧措辞,而 CLI 现在打印的是 ADR-0105 D1 的 posture 措辞(tenancy posture 'isolated' was requested but …)。这是本次改动之前就存在的漂移,按 PM 指示本 PR 只动那一条 bullet,未一并修改。Refs: ADR-0093 D5、ADR-0081 D2、ADR-0105 D12、cloud#1012
🤖 Generated with Claude Code
https://claude.ai/code/session_019TYoxKa8yFLiDDh7tkBqtu