Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .changeset/capability-loop-host-copy-first.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
---
"@objectstack/cli": patch
---

Stop claiming the `os serve` capability loop loads a "host copy first" (#10909).
Two module-header comments in `packages/cli/src/commands/serve.ts` — above the
`@objectstack/plugin-email` and `@objectstack/service-sms` imports — described
the capability loop (`Serve.CAPABILITY_PROVIDERS`, the `for (const cap of
requires)` block) as resolving `EmailServicePlugin`/`SmsServicePlugin` "host
copy first". Measured at head, the loop does a bare `await import(spec.pkg)` /
`await import(ex.pkg)` — no `importFromHost` in either path — which Node ESM
resolves against **this CLI's own** realpath, so the CLI's bundled copy always
wins; the host app's copy is never consulted. The comments described a
behaviour the code does not have.

The corrected comments also name the contrast the file now actually contains:
`Serve.importConfigPlugin` (the served app's own `plugins: [...]` entries) IS
host-anchored — an app-declared package wins there — while the capability
loop is not. Making that split legible is the point of the fix, so the next
reader does not assume one resolution rule governs the whole file.

Comment-only: no runtime path, resolution order, or accepted specifier changes.
All 21 `CAPABILITY_PROVIDERS` packages remain CLI-declared, so bare resolution
still finds every one of them today — this only corrects what the comment
claims about *how* that resolution happens.
10 changes: 7 additions & 3 deletions packages/cli/src/commands/serve.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,15 +33,19 @@ import { missingProviderMessage } from '../utils/capability-preflight.js';
// transports rather than restated here (#5132) — `resolveEmailCapabilityArg`
// has to refuse exactly the configurations `makeTransport` cannot build, and
// two literal lists for one vocabulary is the drift #5094 was filed for. Values
// only (no plugin class): `os serve` loads `EmailServicePlugin` itself through
// the capability loop's dynamic import, host copy first.
// only (no plugin class): the capability loop loads `EmailServicePlugin` itself
// with a bare `import()`, resolved against THIS CLI's own realpath — its
// bundled copy always wins, never the host app's. Contrast `importConfigPlugin`
// below, which IS host-anchored: an app-declared package wins there (#10909).
import { isEmailTransportProvider, emailProviderRequiresApiKey, unsupportedProviderFix } from '@objectstack/plugin-email';
// The SMS provider vocabulary, read from the package that materialises the
// transports, for the same reason and by the same rule as the mail one above
// (#5713). `resolveSmsCapabilityArg` has to refuse exactly the tags
// `makeSmsTransport` cannot build — restating `log`/`aliyun`/`twilio` here would
// be the second literal #5094 was filed for. Values only (no plugin class): the
// capability loop dynamic-imports `SmsServicePlugin` itself, host copy first.
// capability loop dynamic-imports `SmsServicePlugin` itself the same way — a
// bare `import()` resolved against this CLI's own realpath, so its bundled
// copy wins, never the host's (#10909).
import { isSmsTransportProvider, SMS_TRANSPORT_PROVIDERS } from '@objectstack/service-sms';
import { resolveObjectStackHome } from '@objectstack/runtime';
import { LOG_LEVELS, resolveLogLevel, readLogLevelEnv } from '../utils/log-level.js';
Expand Down
Loading