|
| 1 | +--- |
| 2 | +"@objectstack/cli": patch |
| 3 | +--- |
| 4 | + |
| 5 | +fix(cli): `resolveConfigPath` throws its two refusals so the ten `--json` faces emit their envelopes, and `os verify` gains the catch-all it never had (#15547) |
| 6 | + |
| 7 | +Every `--json` face in this CLI declares that it answers an error path with a |
| 8 | +payload. `resolveConfigPath()` was the one path that bypassed that declaration: |
| 9 | +it wrote its refusal and then called `process.exit(1)` **directly**, so nothing |
| 10 | +was thrown and the catch-all each command already carries — all of which sit |
| 11 | +downstream of a throw — never ran. Ten published faces answered a missing config |
| 12 | +file with an empty stdout. |
| 13 | + |
| 14 | +Measured before this change on the published entry `packages/cli/bin/run.js`, |
| 15 | +`NO_COLOR=1`, streams captured separately, exit read before any pipe — ten faces |
| 16 | +(`build` · `compile` · `diff` · `i18n check` · `i18n extract` · `info` · `lint` · |
| 17 | +`migrate meta` · `validate` · `verify`) across both branches of the helper, 19 |
| 18 | +runs: **exit 1, stdout 0 bytes, stderr 296 B (explicit path) / 123 B |
| 19 | +(auto-detect)** — and `JSON.parse` on that stdout throws in all 19. After: the |
| 20 | +same 19 runs answer **exit 1 with a parseable document on stdout**, stderr |
| 21 | +unchanged byte for byte. |
| 22 | + |
| 23 | +The refusals now throw `ConfigRefusalError`. That is not a new contract — it is |
| 24 | +this path being pulled back onto the one its callers had already published, so |
| 25 | +it adds **zero** accept-set members and **zero** error codes. |
| 26 | + |
| 27 | +Three properties hold it in place: |
| 28 | + |
| 29 | +- **No face becomes a crash dump.** `os verify` had no `try` at all — measured, |
| 30 | + a throw through it produced an oclif error line and no payload where every |
| 31 | + sibling emitted an envelope — so it gains the catch-all its nine siblings |
| 32 | + already had, in this same change rather than after it. |
| 33 | +- **The text face does not narrow.** The refusal and both hint lines are still |
| 34 | + written by the helper, to stderr, byte-identical: all 19 non-`--json` runs |
| 35 | + compare equal before and after on stdout, on stderr and on exit status. The |
| 36 | + catch-alls skip re-rendering the sentence a second time on stdout. |
| 37 | +- **No error code is minted.** The thrown error carries neither `code` nor |
| 38 | + `httpStatus`, so `errorCodeFields()` contributes nothing and each face emits |
| 39 | + its own bare `{ error }`. Whether that shape is right is **#15549**'s open |
| 40 | + question, and this change deliberately does not answer it. |
| 41 | + |
| 42 | +The `--json` stdout-purity instrument is widened with the fix rather than after |
| 43 | +it: the pre-boot family's discovery moves into a shared module, the pin that |
| 44 | +drives it now demands a document (empty stdout no longer passes) and compares |
| 45 | +the text face's stderr as a whole string, and `json-stdout-purity.e2e.test.ts` |
| 46 | +— whose own discovery is `bootSchemaStack`-based and cannot see a command that |
| 47 | +fails above the kernel — reconciles against that population so neither half can |
| 48 | +be lost silently. |
0 commit comments