burgee 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,284 @@
1
+ import { readdirSync } from 'node:fs';
2
+ import { readFile, writeFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { ambientRuntime, run } from 'bellpull';
5
+ import { GRADED } from './compat.js';
6
+ import { ExitCode } from './exit-code.js';
7
+ export const MAPPING = {
8
+ commander: 'burgee/commander',
9
+ yargs: 'burgee/yargs',
10
+ 'yargs/yargs': 'burgee/yargs',
11
+ 'yargs/helpers': 'burgee/yargs/helpers',
12
+ };
13
+ export const HOSTS = ['commander', 'yargs'];
14
+ const WORD = /[A-Za-z0-9_$]/;
15
+ const DIVIDES = new Set([')', ']', '}']);
16
+ function startsRegex(previous) {
17
+ if (previous === '')
18
+ return true;
19
+ if (DIVIDES.has(previous))
20
+ return false;
21
+ return !WORD.test(previous.charAt(previous.length - 1));
22
+ }
23
+ function endOfString(source, open) {
24
+ const quote = source.charAt(open);
25
+ for (let i = open + 1; i < source.length; i += 1) {
26
+ const c = source.charAt(i);
27
+ if (c === '\\') {
28
+ i += 1;
29
+ continue;
30
+ }
31
+ if (c === quote || c === '\n')
32
+ return i + 1;
33
+ }
34
+ return source.length;
35
+ }
36
+ function endOfTemplate(source, open) {
37
+ let depth = 0;
38
+ for (let i = open + 1; i < source.length; i += 1) {
39
+ const c = source.charAt(i);
40
+ if (c === '\\') {
41
+ i += 1;
42
+ continue;
43
+ }
44
+ if (c === '$' && source.charAt(i + 1) === '{') {
45
+ depth += 1;
46
+ i += 1;
47
+ continue;
48
+ }
49
+ if (c === '}' && depth > 0) {
50
+ depth -= 1;
51
+ continue;
52
+ }
53
+ if (c === '`' && depth === 0)
54
+ return i + 1;
55
+ }
56
+ return source.length;
57
+ }
58
+ function endOfRegex(source, open) {
59
+ let inClass = false;
60
+ for (let i = open + 1; i < source.length; i += 1) {
61
+ const c = source.charAt(i);
62
+ if (c === '\\') {
63
+ i += 1;
64
+ continue;
65
+ }
66
+ if (c === '[' || c === ']') {
67
+ inClass = c === '[';
68
+ continue;
69
+ }
70
+ if (c === '\n')
71
+ return i;
72
+ if (c === '/' && !inClass)
73
+ return i + 1;
74
+ }
75
+ return source.length;
76
+ }
77
+ function newlines(source, from, to) {
78
+ let count = 0;
79
+ for (let i = from; i < to; i += 1)
80
+ if (source.charAt(i) === '\n')
81
+ count += 1;
82
+ return count;
83
+ }
84
+ function isSpecifier(previous, before) {
85
+ if (previous === 'from' || previous === 'import')
86
+ return true;
87
+ return previous === '(' && (before === 'import' || before === 'require');
88
+ }
89
+ function endOfTrivia(source, at) {
90
+ const c = source.charAt(at);
91
+ if (c === ' ' || c === '\t' || c === '\r' || c === '\n' || c === ';')
92
+ return at + 1;
93
+ if (c !== '/')
94
+ return at;
95
+ const next = source.charAt(at + 1);
96
+ if (next === '/') {
97
+ const end = source.indexOf('\n', at);
98
+ return end === -1 ? source.length : end;
99
+ }
100
+ if (next !== '*')
101
+ return at;
102
+ const end = source.indexOf('*/', at + 2);
103
+ return end === -1 ? source.length : end + 2;
104
+ }
105
+ function endOfLiteral(source, at, previous) {
106
+ const c = source.charAt(at);
107
+ if (c === "'" || c === '"')
108
+ return endOfString(source, at);
109
+ if (c === '`')
110
+ return endOfTemplate(source, at);
111
+ if (c === '/' && startsRegex(previous))
112
+ return endOfRegex(source, at);
113
+ return -1;
114
+ }
115
+ function endOfWord(source, at) {
116
+ let i = at;
117
+ while (i < source.length && WORD.test(source.charAt(i)))
118
+ i += 1;
119
+ return i;
120
+ }
121
+ const QUOTED = 'str';
122
+ function push(tokens, text, line) {
123
+ if (tokens.pending && text !== QUOTED && text !== ')')
124
+ tokens.nonLiteral.push(line);
125
+ tokens.pending = text === '(' && (tokens.previous === 'import' || tokens.previous === 'require');
126
+ tokens.before = tokens.previous;
127
+ tokens.previous = text;
128
+ }
129
+ export function scan(source) {
130
+ const sites = [];
131
+ const tokens = { previous: '', before: '', pending: false, nonLiteral: [] };
132
+ let line = 1;
133
+ let i = 0;
134
+ while (i < source.length) {
135
+ const trivia = endOfTrivia(source, i);
136
+ if (trivia !== i) {
137
+ line += newlines(source, i, trivia);
138
+ i = trivia;
139
+ continue;
140
+ }
141
+ const literal = endOfLiteral(source, i, tokens.previous);
142
+ if (literal !== -1) {
143
+ const c = source.charAt(i);
144
+ const quoted = c === "'" || c === '"';
145
+ if (quoted && isSpecifier(tokens.previous, tokens.before))
146
+ sites.push({ specifier: source.slice(i + 1, literal - 1), start: i + 1, end: literal - 1, line });
147
+ push(tokens, quoted ? QUOTED : 'lit', line);
148
+ line += newlines(source, i, literal);
149
+ i = literal;
150
+ continue;
151
+ }
152
+ const word = endOfWord(source, i);
153
+ if (word !== i) {
154
+ push(tokens, source.slice(i, word), line);
155
+ i = word;
156
+ continue;
157
+ }
158
+ push(tokens, source.charAt(i), line);
159
+ i += 1;
160
+ }
161
+ return { sites, nonLiteral: tokens.nonLiteral };
162
+ }
163
+ const NEEDLE_BYTES = HOSTS.map((host) => Buffer.from(host));
164
+ export function mentionsAHost(source) {
165
+ return typeof source === 'string' ? HOSTS.some((host) => source.includes(host)) : NEEDLE_BYTES.some((bytes) => source.includes(bytes));
166
+ }
167
+ function isDeep(specifier) {
168
+ if (MAPPING[specifier] !== undefined)
169
+ return false;
170
+ return HOSTS.some((host) => specifier.startsWith(`${host}/`));
171
+ }
172
+ export function rewriteSource(source) {
173
+ if (!mentionsAHost(source))
174
+ return { source, mapped: [], refused: [], relevant: false };
175
+ const { sites, nonLiteral } = scan(source);
176
+ const hits = sites.filter((s) => MAPPING[s.specifier] !== undefined || isDeep(s.specifier));
177
+ if (hits.length === 0)
178
+ return { source, mapped: [], refused: [], relevant: false };
179
+ const refused = [
180
+ ...hits.filter((s) => isDeep(s.specifier)).map((s) => ({ line: s.line, specifier: s.specifier, reason: 'deep-import' })),
181
+ ...nonLiteral.map((line) => ({ line, specifier: '', reason: 'non-literal-specifier' })),
182
+ ].sort((a, b) => a.line - b.line);
183
+ if (refused.length > 0)
184
+ return { source, mapped: [], refused, relevant: true };
185
+ let out = source;
186
+ const mapped = [];
187
+ for (const site of [...hits].sort((a, b) => b.start - a.start)) {
188
+ const to = MAPPING[site.specifier];
189
+ out = `${out.slice(0, site.start)}${to}${out.slice(site.end)}`;
190
+ mapped.push({ from: site.specifier, to });
191
+ }
192
+ return { source: out, mapped: mapped.reverse(), refused: [], relevant: true };
193
+ }
194
+ const SOURCE_EXTENSIONS = new Set(['.js', '.jsx', '.mjs', '.cjs', '.ts', '.tsx', '.mts', '.cts']);
195
+ const SKIP = new Set(['node_modules', '.git', 'dist', 'build', 'coverage', '.turbo', '.next', '.output', '.cache', '.vercel']);
196
+ export function sourceFiles(dir, at = '', found = []) {
197
+ for (const entry of readdirSync(join(dir, at), { withFileTypes: true })) {
198
+ const path = at === '' ? entry.name : `${at}/${entry.name}`;
199
+ if (entry.isDirectory()) {
200
+ if (!SKIP.has(entry.name))
201
+ sourceFiles(dir, path, found);
202
+ }
203
+ else if (SOURCE_EXTENSIONS.has(entry.name.slice(entry.name.lastIndexOf('.'))))
204
+ found.push(path);
205
+ }
206
+ return found;
207
+ }
208
+ async function declaredHosts(dir) {
209
+ try {
210
+ const raw = JSON.parse(await readFile(join(dir, 'package.json'), 'utf8'));
211
+ const declared = { ...raw.dependencies, ...raw.devDependencies };
212
+ return HOSTS.filter((host) => declared[host] !== undefined);
213
+ }
214
+ catch {
215
+ return [];
216
+ }
217
+ }
218
+ export async function workingTree(dir) {
219
+ const result = await run('git', ['-C', dir, 'status', '--porcelain'], { runtime: ambientRuntime(), stdio: 'pipe' });
220
+ if (!result.ok)
221
+ return undefined;
222
+ return result.stdout.split('\n').filter((line) => line !== '');
223
+ }
224
+ export class DirtyTreeError extends Error {
225
+ entries;
226
+ fix = 'commit or stash your changes, or pass --force';
227
+ constructor(entries) {
228
+ super(`the git tree has ${entries.length} uncommitted change${entries.length === 1 ? '' : 's'}`);
229
+ this.entries = entries;
230
+ this.name = 'DirtyTreeError';
231
+ }
232
+ }
233
+ const EMPTY = { source: '', mapped: [], refused: [], relevant: false };
234
+ async function migrateBatch(dir, batch, write) {
235
+ const sources = await Promise.all(batch.map(async (file) => await readFile(join(dir, file))));
236
+ const results = sources.map((bytes) => (mentionsAHost(bytes) ? rewriteSource(bytes.toString('utf8')) : EMPTY));
237
+ if (write)
238
+ await Promise.all(results.map(async (r, i) => (r.mapped.length === 0 ? undefined : await writeFile(join(dir, batch[i]), r.source))));
239
+ return results;
240
+ }
241
+ function rollup(all) {
242
+ return Object.entries(MAPPING)
243
+ .map(([from, to]) => {
244
+ const hits = all.filter((m) => m.from === from);
245
+ return { from, to, imports: hits.length, files: new Set(hits.map((m) => m.file)).size };
246
+ })
247
+ .filter((row) => row.imports > 0);
248
+ }
249
+ function gradedFor(hosts) {
250
+ return hosts.filter((host) => GRADED[host] !== undefined).map((host) => ({ host, ...GRADED[host] }));
251
+ }
252
+ const BATCH = 256;
253
+ export async function migrate(options) {
254
+ const { dir, dryRun = false, force = false } = options;
255
+ const entries = await (options.status ?? workingTree)(dir);
256
+ if (!dryRun && !force && entries !== undefined && entries.length > 0)
257
+ throw new DirtyTreeError(entries);
258
+ const files = sourceFiles(dir);
259
+ const results = [];
260
+ for (let i = 0; i < files.length; i += BATCH) {
261
+ const batch = files.slice(i, i + BATCH);
262
+ const done = await migrateBatch(dir, batch, !dryRun);
263
+ results.push(...done.map((result, k) => ({ file: batch[k], result })));
264
+ }
265
+ const all = results.flatMap(({ file, result }) => result.mapped.map((m) => ({ ...m, file })));
266
+ const refused = results.flatMap(({ file, result }) => result.refused.map((r) => ({ file, ...r })));
267
+ const imported = [...new Set(results.flatMap(({ result }) => (result.relevant ? result.mapped.map((m) => m.from) : [])))];
268
+ const declared = await declaredHosts(dir);
269
+ const stillUsed = new Set(refused.map((r) => r.specifier.split('/')[0] ?? ''));
270
+ const removable = declared.filter((host) => !stillUsed.has(host));
271
+ const touched = [...new Set(all.map((m) => m.file))];
272
+ return {
273
+ files: touched.length,
274
+ imports: all.length,
275
+ mapped: rollup(all),
276
+ refused,
277
+ detected: { declared, imported: [...new Set(imported.map((s) => s.split('/')[0] ?? s))].sort() },
278
+ dependencies: { before: declared, removable, after: declared.length - removable.length },
279
+ graded: gradedFor([...new Set([...declared, ...imported.map((s) => s.split('/')[0] ?? s)])].sort()),
280
+ dryRun,
281
+ changed: !dryRun && touched.length > 0,
282
+ exitCode: refused.length > 0 ? ExitCode.RUNTIME : ExitCode.OK,
283
+ };
284
+ }
@@ -2,7 +2,7 @@
2
2
  * E5 and O5 — every door out of a burgee program, bound by the package whose job that is.
3
3
  *
4
4
  * `exit-code.ts` has declared `SIGINT: 130` and *"SIGINT after the terminal was restored"*
5
- * since the contract was written, and `.sdlc/intents/burgee/design.md` marks both E5 and O5
5
+ * since the contract was written, and `.sdlc/intents/burgee/spec.md` marks both E5 and O5
6
6
  * `R`. Neither was implemented. The engine's only exit was `host.exit(code)` — `process.exit`
7
7
  * — which restores nothing, runs nothing, and truncates a pipe by definition (yargs #1519,
8
8
  * #2118: *"No truncated JSON"*). A constant is not an implementation.
@@ -1,10 +1,3 @@
1
- /**
2
- * burgee's additions on yargs syntax — the pure half. `yargs-factory.ts` snapshots what
3
- * a program registered (options, descriptions, commands and their builders' results) and
4
- * this module projects that snapshot into the manifest every surface reads (J7, J8).
5
- * Nothing here runs at parse time unless a burgee surface was asked for.
6
- */
7
- import {} from '../manifest.js';
8
1
  import { camelCase } from '../yargs-parser.js';
9
2
  const DEFER_PREFIX = '__yargsString__:';
10
3
  function describe(descriptions, key) {
@@ -21,7 +14,6 @@ function typeOf(s, key) {
21
14
  return 'number';
22
15
  return 'string';
23
16
  }
24
- /** Options as the manifest describes them, on a null-prototype record keyed by the canonical camelCase name. */
25
17
  export function optionSpecs(s) {
26
18
  const specs = Object.create(null);
27
19
  const aliasOf = new Set();
@@ -76,10 +68,6 @@ function argumentsOf(s) {
76
68
  push(p, false);
77
69
  return out;
78
70
  }
79
- /**
80
- * Project a snapshot into the manifest: the root, then every command as a child path.
81
- * Plugin-contributed nodes survive re-projection, exactly as on the commander façade.
82
- */
83
71
  export function projectManifest(manifest, root) {
84
72
  const contributed = manifest.commands.filter((c) => c.plugin !== undefined);
85
73
  manifest.commands.splice(0, manifest.commands.length, ...contributed);
@@ -102,7 +90,6 @@ export function projectManifest(manifest, root) {
102
90
  };
103
91
  visit(root, [root.name], root.description, undefined);
104
92
  }
105
- /** What a run prints for a handler's return value when the streams are injected. */
106
93
  export function render(value) {
107
94
  if (value === undefined || value === null)
108
95
  return '';
@@ -115,4 +102,3 @@ export function render(value) {
115
102
  }
116
103
  return `${JSON.stringify(value)}\n`;
117
104
  }
118
- //# sourceMappingURL=burgee.js.map
@@ -1,35 +1,7 @@
1
1
  import { strip, width, wrap } from "linegauge";
2
2
  import { host } from "../runtime.js";
3
- /**
4
- * cliui 9 — the column layout yargs' usage renders through — with the wrap-ansi it depends
5
- * on, ported for `burgee/yargs`. Width and escape-stripping come from linegauge. The wrapping
6
- * arithmetic is byte-for-byte the upstream's: yargs' usage tests compare whole help
7
- * screens.
8
- */
9
- /*
10
- * `width` and `strip` are linegauge's — measuring text and removing escapes is the layer
11
- * below this one, and this file had its own copies of both. They were not merely duplicated,
12
- * they were wrong: the ITU T.416 sub-parameter form `ESC[38:2::255:0:0m`, which chalk emits
13
- * for truecolor, left `:2::255:0:0m` behind and measured a 13-column string as 25. linegauge
14
- * fixed that in its own `strip`; burgee inherited nothing because it was not asking.
15
- *
16
- * The names stay, because `usage.ts` and `shim.ts` import them and the upstream port reads
17
- * the way cliui reads.
18
- */
19
3
  export const stripAnsi = (str) => typeof str === "string" ? strip(str) : str;
20
4
  export const stringWidth = (input) => typeof input === "string" ? width(input) : 0;
21
- /*
22
- * The wrap is linegauge's too. This file carried a wrap-ansi port — ansi-styles' open→close
23
- * map, an escape-state machine over every character, hyperlink re-opening — to wrap a line
24
- * without breaking the styling across it. That is the same job `linegauge/wrap` exists for
25
- * and grades against wrap-ansi, so burgee kept a second implementation of a thing the layer
26
- * below already owned.
27
- *
28
- * Checked before swapping, because yargs' usage tests compare whole help screens: the two
29
- * agreed on all ten shapes probed — plain prose at several widths, an unbreakable word,
30
- * SGR-coloured text, single characters, CJK, an embedded newline, leading and trailing
31
- * spaces, empty, and a string exactly the column width.
32
- */
33
5
  export function wrapAnsi(string, columns, options) {
34
6
  return wrap(String(string).normalize().replace(/\r\n/g, "\n"), columns, options);
35
7
  }
@@ -90,13 +62,6 @@ export class UI {
90
62
  return { text, padding: this.measurePadding(text) };
91
63
  }
92
64
  measurePadding(str) {
93
- // An unanchored `\s*$` was the upstream spelling, and it is quadratic: the engine
94
- // retries at every position, so a cell of 50,000 spaces then an `x` costs 1,346 ms
95
- // here — CodeQL alert 35, "polynomial regular expression used on uncontrolled data",
96
- // raised once `stripAnsi` became library input. Spaces, not the tabs the alert names:
97
- // a tab routes into `applyLayoutDSL`, which splits on it long before this runs.
98
- // `trim{Start,End}` remove exactly the set `\s` matches (WhiteSpace + LineTerminator)
99
- // and are linear, so this is the same measurement without the backtracking.
100
65
  const noAnsi = stripAnsi(str);
101
66
  return [
102
67
  0,
@@ -222,21 +187,6 @@ export class UI {
222
187
  return widths.map((w, i) => w === undefined ? Math.max(unsetWidth, minWidth(row[i])) : w);
223
188
  }
224
189
  }
225
- /*
226
- * `str.replace(/ +$/, "")` was the upstream spelling, and it is the same quadratic shape
227
- * `measurePadding` had: the start is unanchored, so the engine retries the match at every
228
- * position in the run of spaces and each attempt walks to the end before failing on the
229
- * character that is not the end. A row built from a cell of 50,000 spaces then an `x` is
230
- * 100,001 characters of which the first 50,000 are the left padding, and trimming it cost
231
- * 1,049 ms of `toString()`'s 1,223 ms. Doubling the cell quadrupled it.
232
- *
233
- * `trimEnd()` is linear but not the same function — it also removes tabs, newlines and the
234
- * rest of `\s`. A trailing tab does reach here: with `wrap: false`, `rasterize` only splits
235
- * the cell on newlines, so nothing expands the tab and nothing routes the string through
236
- * `applyLayoutDSL` (that check is behind `this.wrap`). `cliui({ wrap: false }).div('a\t')`
237
- * renders `'a\t'` today and would render `'a'` under `trimEnd`, and the yargs usage tests
238
- * compare whole help screens. This removes exactly U+0020, exactly as the regex did.
239
- */
240
190
  function trimTrailingSpaces(str) {
241
191
  let end = str.length;
242
192
  while (end > 0 && str[end - 1] === " ")
@@ -261,8 +211,6 @@ function minWidth(col) {
261
211
  return min;
262
212
  }
263
213
  function getWindowWidth() {
264
- // `host.columns` carries the upstream's guard on the process global itself, so a bundle
265
- // that has no process at all still falls back to 80 rather than throwing.
266
214
  return host.columns || 80;
267
215
  }
268
216
  function alignRight(str, width) {
@@ -282,4 +230,3 @@ function alignCenter(str, width) {
282
230
  export function cliui(opts) {
283
231
  return new UI({ width: opts?.width || getWindowWidth(), wrap: opts?.wrap });
284
232
  }
285
- //# sourceMappingURL=cliui.js.map
@@ -1,8 +1,3 @@
1
- /**
2
- * yargs' command instance — `.command()` in its five shapes, `.commandDir()`, the
3
- * builder/handler pipeline, positionals and the default command — ported for
4
- * `burgee/yargs`.
5
- */
6
1
  import { applyMiddleware, commandMiddlewareFactory } from './middleware.js';
7
2
  import { isPromise, maybeAsyncResult, parseCommand } from './utils.js';
8
3
  const DEFAULT_MARKER = /(^\*)|(^\$0)/;
@@ -227,7 +222,6 @@ export class CommandInstance {
227
222
  yargs.getInternalMethods().getUsageInstance().fail(null, error);
228
223
  }
229
224
  catch {
230
- // the failure was reported; yargs swallows the rethrow here
231
225
  }
232
226
  });
233
227
  }
@@ -418,4 +412,3 @@ function isCommandBuilderOptionDefinitions(builder) {
418
412
  export function isCommandHandlerDefinition(cmd) {
419
413
  return typeof cmd === 'object' && !Array.isArray(cmd);
420
414
  }
421
- //# sourceMappingURL=command.js.map
@@ -1,7 +1,3 @@
1
- /**
2
- * yargs' completion — `--get-yargs-completions`, the custom completion function in its
3
- * three arities, and the bash/zsh script templates — ported for `burgee/yargs`.
4
- */
5
1
  import { isCommandBuilderCallback } from './command.js';
6
2
  import { isPromise, parseCommand } from './utils.js';
7
3
  export const completionShTemplate = `###-begin-{{app_name}}-completions-###
@@ -273,4 +269,3 @@ function isSyncCompletionFunction(fn) {
273
269
  function isFallbackCompletionFunction(fn) {
274
270
  return fn.length > 3;
275
271
  }
276
- //# sourceMappingURL=completion.js.map
@@ -1,10 +1,3 @@
1
- /**
2
- * yargs' `YargsInstance`, ported method for method from yargs 18 and graded by yargs'
3
- * own suite through `compat-oracle`. Every public method, its argsert contract, the
4
- * parse pipeline, the freeze/unfreeze bookkeeping around `.parse()` and the
5
- * `getInternalMethods()` seam are the upstream's — that is what makes a user's
6
- * existing program run unchanged (J2).
7
- */
8
1
  var _a;
9
2
  import { ExitCode } from '../exit-code.js';
10
3
  import { Manifest } from '../manifest.js';
@@ -71,7 +64,6 @@ export class YargsInstance {
71
64
  #usageConfig = {};
72
65
  #versionOpt = null;
73
66
  #validation;
74
- // ───── burgee: the manifest projection, plugins, --json, the surfaces and the seam ─────
75
67
  #burgee = undefined;
76
68
  #effects = undefined;
77
69
  #manifest = undefined;
@@ -82,7 +74,6 @@ export class YargsInstance {
82
74
  this.#parentRequire = parentRequire;
83
75
  this.#globalMiddleware = new GlobalMiddleware(this);
84
76
  this.$0 = this.#getDollarZero();
85
- // kReset builds the four collaborators; the definite assignments below are its result.
86
77
  this.#options = undefined;
87
78
  this.#usage = undefined;
88
79
  this.#validation = undefined;
@@ -602,8 +593,6 @@ export class YargsInstance {
602
593
  argsert('[string|array] [function|boolean|object] [function]', [args, shortCircuit, _parseFn], arguments.length);
603
594
  if (shortCircuit === true)
604
595
  return this.#parse(args, shortCircuit, _parseFn);
605
- // burgee: --json and the seam are per parse. What this parse turns on (json, an exit
606
- // already reported) is put back afterwards, so the next parse starts as the program left it.
607
596
  const before = this.#burgee === undefined ? undefined : { ...this.#burgee };
608
597
  const restore = () => {
609
598
  this.#burgee = before;
@@ -622,8 +611,6 @@ export class YargsInstance {
622
611
  throw err;
623
612
  }
624
613
  }
625
- // The seam: the whole run settles to one E1 exit. yargs reports its own failures
626
- // through exit(); a handler that throws synchronously is the one thing that escapes it.
627
614
  seam.exited = false;
628
615
  const finish = (argv) => {
629
616
  if (!this.#burgee?.exited)
@@ -675,8 +662,6 @@ export class YargsInstance {
675
662
  if (!shortCircuit) {
676
663
  const served = this.#burgeeSurface(args);
677
664
  if (served !== false) {
678
- // A surface was (or is being) served: the argv handed back is the short-circuit parse,
679
- // as after --help. Completions and --mcp load lazily, so those two return a promise.
680
665
  const settle = () => {
681
666
  const argv = this.#runYargsParserAndExecuteCommands(args, true);
682
667
  this.#unfreeze();
@@ -920,7 +905,6 @@ export class YargsInstance {
920
905
  delete argv['--'];
921
906
  }
922
907
  catch {
923
- // a frozen argv keeps its `--`; yargs ignores the failure
924
908
  }
925
909
  return argv;
926
910
  }
@@ -941,8 +925,6 @@ export class YargsInstance {
941
925
  const burgee = this.#burgee;
942
926
  const line = args.join(' ');
943
927
  if (burgee?.json) {
944
- // Under --json the failure is one envelope on stdout; the help screen and the
945
- // message yargs prints on the way are kept only as the envelope's message.
946
928
  if (line.trim() !== '')
947
929
  burgee.lastError = line;
948
930
  }
@@ -1060,7 +1042,6 @@ export class YargsInstance {
1060
1042
  obj = JSON.parse(this.#shim.readFileSync(pkgJsonPath, 'utf8'));
1061
1043
  }
1062
1044
  catch {
1063
- // no package.json above: version reads 'unknown', as upstream
1064
1045
  }
1065
1046
  this.#pkgs[npath] = obj || {};
1066
1047
  return this.#pkgs[npath];
@@ -1154,32 +1135,19 @@ export class YargsInstance {
1154
1135
  runHandler: this.#runHandler.bind(this),
1155
1136
  };
1156
1137
  }
1157
- // ───── burgee: additive, and guarded so a program that asks for none of it runs as on yargs ─────
1158
- /**
1159
- * The manifest every surface reads (J7, J8). Projected on each access from what the
1160
- * program registered; a command's builder is run on a scratch instance to learn its
1161
- * options, exactly as yargs' own completion does. Plugin-contributed nodes are kept.
1162
- */
1163
1138
  get manifest() {
1164
1139
  this.#manifest ??= new Manifest();
1165
1140
  projectManifest(this.#manifest, this.#snapshot());
1166
1141
  return this.#manifest;
1167
1142
  }
1168
- /** burgee: declare what the command does to the world (N6); what exposes it as an MCP tool (N2). */
1169
1143
  effects(value) {
1170
1144
  this.#effects = value;
1171
1145
  return this;
1172
1146
  }
1173
- /** Additive: plugins yargs never had. `preRun`/`postRun` fire around every handler. */
1174
1147
  use(plugin) {
1175
1148
  this.manifest.use(plugin);
1176
1149
  return this;
1177
1150
  }
1178
- /**
1179
- * Inject the streams and the exit for this instance (T1). Output goes to `stdout`/`stderr`
1180
- * instead of the console, and `exit` receives an E1 code: OK for help and version, USAGE
1181
- * for a validation failure, RUNTIME for a handler that threw.
1182
- */
1183
1151
  burgee(seam) {
1184
1152
  this.#burgee = { ...(this.#burgee ?? { json: false, lastError: '' }), ...seam };
1185
1153
  return this;
@@ -1192,7 +1160,6 @@ export class YargsInstance {
1192
1160
  let positionals = { demanded: [], optional: [] };
1193
1161
  let hasHandler = false;
1194
1162
  let description;
1195
- // The default command (`$0`, `*`) is the root's own handler, not a child path.
1196
1163
  const isDefault = (h) => /^\$0( |$)/.test(h.original);
1197
1164
  for (const [name, handler] of Object.entries(handlers)) {
1198
1165
  if (isDefault(handler))
@@ -1234,7 +1201,6 @@ export class YargsInstance {
1234
1201
  commands,
1235
1202
  };
1236
1203
  }
1237
- /** A command's snapshot: what its builder registered on the scratch, plus the command string's positionals. */
1238
1204
  #snapshotOf(handler, name) {
1239
1205
  const snap = this.#snapshot();
1240
1206
  snap.name = name;
@@ -1244,7 +1210,6 @@ export class YargsInstance {
1244
1210
  snap.version = undefined;
1245
1211
  return snap;
1246
1212
  }
1247
- /** Run a command's builder on a fresh instance, as yargs' completion does, and hand that instance back. */
1248
1213
  #childOf(handler) {
1249
1214
  const child = new _a([], this.#cwd, this.#parentRequire, this.#shim);
1250
1215
  child.#context.fullCommands.push(handler.original);
@@ -1261,11 +1226,9 @@ export class YargsInstance {
1261
1226
  }
1262
1227
  }
1263
1228
  catch {
1264
- // A builder that cannot run outside a parse projects only the command string.
1265
1229
  }
1266
1230
  return child;
1267
1231
  }
1268
- /** `--json` that the program did not declare is burgee's envelope, not an unknown option. */
1269
1232
  #takeJson(args) {
1270
1233
  const list = typeof args === 'string' ? tokenizeArgString(args) : args;
1271
1234
  const terminator = list.indexOf('--');
@@ -1277,11 +1240,6 @@ export class YargsInstance {
1277
1240
  this.#burgee = { ...(this.#burgee ?? { lastError: '' }), json: true };
1278
1241
  return [...list.slice(0, index), ...list.slice(index + 1)];
1279
1242
  }
1280
- /**
1281
- * Whether the program declared an option or command by this name — at the root, or in
1282
- * any command's builder, which the manifest projection runs to find out. Any command in
1283
- * the tree that declares the flag keeps it: the surface is additive only.
1284
- */
1285
1243
  #declares(key) {
1286
1244
  if (this.#options.key[key] || Object.values(this.#options.alias).some((list) => list.includes(key)))
1287
1245
  return true;
@@ -1289,11 +1247,6 @@ export class YargsInstance {
1289
1247
  return true;
1290
1248
  return this.manifest.commands.some((node) => Object.prototype.hasOwnProperty.call(node.options, key));
1291
1249
  }
1292
- /**
1293
- * `--schema`, `--mcp` and `completion <shell>` on a yargs-syntax program, from its
1294
- * manifest (J2). Only when the program declares none of them itself; `--schema` is
1295
- * synchronous, the other two load lazily and return a promise.
1296
- */
1297
1250
  #burgeeSurface(args) {
1298
1251
  const list = typeof args === 'string' ? tokenizeArgString(args) : args;
1299
1252
  const terminator = list.indexOf('--');
@@ -1315,11 +1268,6 @@ export class YargsInstance {
1315
1268
  });
1316
1269
  }
1317
1270
  if (head.includes('--schema') && !this.#declares('schema')) {
1318
- // R1, through the same seam the engine and the commander façade use. Hand-rolling
1319
- // `JSON.stringify(…, null, 2)` here made this façade the one front-end that could not
1320
- // see `--format=json-pretty`, and emitted a different document from the other two for
1321
- // the same CLI. `--schema` is burgee's surface, not yargs', so it answers to burgee's
1322
- // byte discipline.
1323
1271
  this.#logger.log(machineJson(schemaOf(this.manifest), head));
1324
1272
  this.exit(0);
1325
1273
  return true;
@@ -1347,7 +1295,6 @@ export class YargsInstance {
1347
1295
  }
1348
1296
  return false;
1349
1297
  }
1350
- /** The handler, wrapped in the plugin hooks and followed by the envelope or the rendering. */
1351
1298
  #runHandler(handler, argv, original) {
1352
1299
  const manifest = this.#manifest;
1353
1300
  const settle = (value) => {
@@ -1388,7 +1335,6 @@ export class YargsInstance {
1388
1335
  const code = err instanceof YError || err === undefined || typeof err === 'string' ? 'usage' : 'runtime';
1389
1336
  this.#logger.log(JSON.stringify({ ok: false, error: { code, message } }));
1390
1337
  }
1391
- /** burgee: where every option value came from (V3), from yargs-parser's own bookkeeping where it keeps any. */
1392
1338
  #provenance(argv) {
1393
1339
  const out = {};
1394
1340
  const defaulted = this.parsed?.defaulted ?? {};
@@ -1658,4 +1604,3 @@ _a = YargsInstance;
1658
1604
  export function isYargsInstance(y) {
1659
1605
  return !!y && typeof y.getInternalMethods === 'function';
1660
1606
  }
1661
- //# sourceMappingURL=factory.js.map
@@ -1,7 +1,3 @@
1
- /**
2
- * yargs' middleware — global, per-command, and the coerce middleware `.coerce()`
3
- * registers — ported for `burgee/yargs`.
4
- */
5
1
  import { argsert, isPromise } from './utils.js';
6
2
  export class GlobalMiddleware {
7
3
  globalMiddleware = [];
@@ -83,4 +79,3 @@ export function applyMiddleware(argv, yargs, middlewares, beforeValidation) {
83
79
  return isPromise(result) ? result.then((middlewareObj) => Object.assign(acc, middlewareObj)) : Object.assign(acc, result);
84
80
  }, argv);
85
81
  }
86
- //# sourceMappingURL=middleware.js.map