You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
createHostImporter's undeclared fallback resolves from @objectstack/types, not from the calling package — its documented contract says otherwise #10943
Filed unassigned from the domain:cli lane while implementing #10908. Recording only — out of scope there, and it is a contract defect in packages/types, not in the CLI.
The claim
packages/types/src/node.ts, in createHostImporter's docblock (step 3):
Undeclared falls back to the importing package's own resolution, which is what keeps every framework-owned load working (serve's plugin-auth / service-i18n path, bootStack's service plugins).
What it actually does
The fallback is a bare import() written inside packages/types/src/node.ts. ESM resolves a bare specifier by walking up node_modules from the module that contains the call, so the fallback resolves from @objectstack/types, not from whichever package called the importer. Under a pnpm-isolated layout @objectstack/types can see only its own dependencies (@objectstack/spec).
Measured on main (worktree at eee2b65018), from a temp app root whose package.json declares nothing:
via host importer bare import() from packages/cli
@objectstack/plugin-auth MODULE_NOT_FOUND OK
@objectstack/plugin-audit MODULE_NOT_FOUND OK
chalk MODULE_NOT_FOUND OK
@objectstack/spec OK —
@objectstack/spec is the one that resolves, and it is the only dependency packages/types declares. That is the whole pattern.
Why it matters
The docblock is load-bearing and wrong. It is what a reader consults before routing a load through the importer, and it says the undeclared case keeps resolving as it did. It does not; it re-bases to @objectstack/types.
It is a live trap for the remaining cards on this file.Two serve.ts comments claim the capability loop loads "host copy first", but it bare-imports — the CLI's copy always wins #10909 proposes host-anchoring the capability loop, whose providers are all CLI-declared (that is the standing justification in UNRESOLVABLE_BARE_IMPORTS). Swapping import(spec.pkg) for importFromHost(spec.pkg) there would make every one of them undeclared-by-the-app and hand them to this fallback. On a pnpm-isolated install that turns a working capability into a missing one.
#10908 sidestepped it rather than fixing it: Serve.importConfigPlugin consults the declaration itself and keeps the CLI's own import() for the undeclared case, so no app loses a plugin it does not declare but the CLI ships. That is a local workaround for one call site; every other caller of createHostImporter still inherits the claim above.
B. Fix the mechanism. Have createHostImporter take the caller's resolution base (e.g. an import.meta.url / a parentURL) and use it for the fallback, so the docblock becomes true for everyone. Larger blast radius — serve, bootStack, @objectstack/verify, cloud's loader — but it closes the class rather than documenting it.
Recommend B with A as the immediate stopgap, because the current text will keep producing exactly the reasoning error that #10908 caught only by measuring. Wants a maintainer decision either way.
Filed unassigned from the
domain:clilane while implementing #10908. Recording only — out of scope there, and it is a contract defect inpackages/types, not in the CLI.The claim
packages/types/src/node.ts, increateHostImporter's docblock (step 3):What it actually does
The fallback is a bare
import()written insidepackages/types/src/node.ts. ESM resolves a bare specifier by walking upnode_modulesfrom the module that contains the call, so the fallback resolves from@objectstack/types, not from whichever package called the importer. Under a pnpm-isolated layout@objectstack/typescan see only its own dependencies (@objectstack/spec).Measured on
main(worktree ateee2b65018), from a temp app root whosepackage.jsondeclares nothing:@objectstack/specis the one that resolves, and it is the only dependencypackages/typesdeclares. That is the whole pattern.Why it matters
@objectstack/types.node_modules. Under pnpm isolation it does not. So "undeclared falls back" is green in some installs and absent in others — the same shape as cloud#1013 and cli:serve's dynamic cluster-driver import cannot resolve app-declared@objectstack/service-cluster*— EE multi-nodeOS_CLUSTER_DRIVER=redisdies at boot #10645, one level up.serve.tscomments claim the capability loop loads "host copy first", but it bare-imports — the CLI's copy always wins #10909 proposes host-anchoring the capability loop, whose providers are all CLI-declared (that is the standing justification inUNRESOLVABLE_BARE_IMPORTS). Swappingimport(spec.pkg)forimportFromHost(spec.pkg)there would make every one of them undeclared-by-the-app and hand them to this fallback. On a pnpm-isolated install that turns a working capability into a missing one.#10908 sidestepped it rather than fixing it:
Serve.importConfigPluginconsults the declaration itself and keeps the CLI's ownimport()for the undeclared case, so no app loses a plugin it does not declare but the CLI ships. That is a local workaround for one call site; every other caller ofcreateHostImporterstill inherits the claim above.Options
@objectstack/typesand that callers needing their own resolution must checkisDeclaredByHostand import themselves. Cheapest; leaves each caller to re-derive the workaroundserve.tsloads an app's ownplugins: [...]config entries with a bareimport(), so a plugin package the APP declares resolves from the CLI #10908 wrote.createHostImportertake the caller's resolution base (e.g. animport.meta.url/ aparentURL) and use it for the fallback, so the docblock becomes true for everyone. Larger blast radius —serve,bootStack,@objectstack/verify, cloud's loader — but it closes the class rather than documenting it.Recommend B with A as the immediate stopgap, because the current text will keep producing exactly the reasoning error that #10908 caught only by measuring. Wants a maintainer decision either way.
Not a duplicate
createHostRequire认 NODE_PATH,于是 #4699 立下的「host app 必须自己声明」在 pnpm 工作区里根本没被强制 #4719 is whether a package may load (the declaration gate). This is where the fallback resolves from once the gate has said "undeclared".serve.ts's host-anchored importer is a mid-functionconst, so any app-declared optional package loaded ABOVE it silently falls back to CLI-anchored resolution — this has now shipped twice #10769 /serve.tsloads an app's ownplugins: [...]config entries with a bareimport(), so a plugin package the APP declares resolves from the CLI #10908 host-anchor specific call sites inserve.ts. This is the shared helper's own contract.