@optique/discover 1.1.0 → 1.2.0-dev.2167

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.
package/README.md CHANGED
@@ -117,6 +117,10 @@ By default, Deno and Bun discover `.ts`, `.mts`, `.js`, and `.mjs` files.
117
117
  Node.js discovers `.js`, `.mjs`, and `.cjs` files, plus `.ts`, `.mts`, and
118
118
  `.cts` when it reports native TypeScript support or runs with a recognized
119
119
  TypeScript loader. TypeScript declaration files such as `.d.ts` are ignored.
120
+ Entry files named `index` map to their containing command path, so
121
+ `commands/index.ts` defines the root command and `commands/user/index.ts`
122
+ defines `user`. Use `entryFileName` to choose another entry name or disable
123
+ this rule.
120
124
 
121
125
  For more resources, see the [docs] and the [*examples/*](/examples/)
122
126
  directory.
@@ -21,10 +21,10 @@ function isCommand(value) {
21
21
  return value != null && typeof value === "object" && value[commandBrand] === true && (value.path == null || isCommandPath(value.path)) && isParser(value.parser) && typeof value.handler === "function";
22
22
  }
23
23
  function validateCommandPath(path) {
24
- if (!isCommandPath(path)) throw new TypeError("Command path must be a non-empty array of non-empty strings.");
24
+ if (!isCommandPath(path)) throw new TypeError("Command path must be an array of non-empty strings.");
25
25
  }
26
26
  function isCommandPath(path) {
27
- return Array.isArray(path) && path.length > 0 && path.every((segment) => typeof segment === "string" && segment.length > 0);
27
+ return Array.isArray(path) && path.every((segment) => typeof segment === "string" && segment.length > 0);
28
28
  }
29
29
  function isParser(value) {
30
30
  return value != null && typeof value === "object" && typeof value.parse === "function" && typeof value.complete === "function" && typeof value.suggest === "function" && typeof value.getDocFragments === "function" && (value.mode === "sync" || value.mode === "async");
@@ -20,10 +20,10 @@ function isCommand(value) {
20
20
  return value != null && typeof value === "object" && value[commandBrand] === true && (value.path == null || isCommandPath(value.path)) && isParser(value.parser) && typeof value.handler === "function";
21
21
  }
22
22
  function validateCommandPath(path) {
23
- if (!isCommandPath(path)) throw new TypeError("Command path must be a non-empty array of non-empty strings.");
23
+ if (!isCommandPath(path)) throw new TypeError("Command path must be an array of non-empty strings.");
24
24
  }
25
25
  function isCommandPath(path) {
26
- return Array.isArray(path) && path.length > 0 && path.every((segment) => typeof segment === "string" && segment.length > 0);
26
+ return Array.isArray(path) && path.every((segment) => typeof segment === "string" && segment.length > 0);
27
27
  }
28
28
  function isParser(value) {
29
29
  return value != null && typeof value === "object" && typeof value.parse === "function" && typeof value.complete === "function" && typeof value.suggest === "function" && typeof value.getDocFragments === "function" && (value.mode === "sync" || value.mode === "async");
@@ -14,11 +14,13 @@ declare const commandBrand: unique symbol;
14
14
  */
15
15
  type CommandMetadata = CommandOptions;
16
16
  /**
17
- * Non-empty command path used by static command registration.
17
+ * Command path used by static command registration.
18
+ *
19
+ * An empty path represents the root command.
18
20
  *
19
21
  * @since 1.1.0
20
22
  */
21
- type CommandPath = readonly [string, ...string[]];
23
+ type CommandPath = readonly string[];
22
24
  /**
23
25
  * Input accepted by {@link defineCommand}.
24
26
  *
@@ -29,6 +31,7 @@ type CommandPath = readonly [string, ...string[]];
29
31
  interface CommandDefinition<M extends Mode, T> {
30
32
  /**
31
33
  * Command path used when commands are passed directly to `runProgram()`.
34
+ * Use an empty path (`[]`) to register the root command.
32
35
  *
33
36
  * File-based discovery derives the command path from the file name and uses
34
37
  * this field only to validate that the declared path matches.
@@ -14,11 +14,13 @@ declare const commandBrand: unique symbol;
14
14
  */
15
15
  type CommandMetadata = CommandOptions;
16
16
  /**
17
- * Non-empty command path used by static command registration.
17
+ * Command path used by static command registration.
18
+ *
19
+ * An empty path represents the root command.
18
20
  *
19
21
  * @since 1.1.0
20
22
  */
21
- type CommandPath = readonly [string, ...string[]];
23
+ type CommandPath = readonly string[];
22
24
  /**
23
25
  * Input accepted by {@link defineCommand}.
24
26
  *
@@ -29,6 +31,7 @@ type CommandPath = readonly [string, ...string[]];
29
31
  interface CommandDefinition<M extends Mode, T> {
30
32
  /**
31
33
  * Command path used when commands are passed directly to `runProgram()`.
34
+ * Use an empty path (`[]`) to register the root command.
32
35
  *
33
36
  * File-based discovery derives the command path from the file name and uses
34
37
  * this field only to validate that the declared path matches.
package/dist/command.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_command = require('./command-DZA06E08.cjs');
1
+ const require_command = require('./command-B0lV6NBO.cjs');
2
2
 
3
3
  exports.defineCommand = require_command.defineCommand;
4
4
  exports.isCommand = require_command.isCommand;
@@ -1,2 +1,2 @@
1
- import { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand } from "./command-2HtR3-TV.cjs";
1
+ import { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand } from "./command-DSHBTa5c.cjs";
2
2
  export { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand };
package/dist/command.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand } from "./command-BAVhIzfI.js";
1
+ import { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand } from "./command-DyiVIMUh.js";
2
2
  export { AnyCommand, AnyStaticCommand, Command, CommandDefinition, CommandMetadata, CommandPath, StaticCommand, defineCommand, isCommand };
package/dist/command.js CHANGED
@@ -1,3 +1,3 @@
1
- import { defineCommand, isCommand } from "./command-y_A3hG0g.js";
1
+ import { defineCommand, isCommand } from "./command-DO5zgkvS.js";
2
2
 
3
3
  export { defineCommand, isCommand };
package/dist/index.cjs CHANGED
@@ -21,10 +21,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  }) : target, mod));
22
22
 
23
23
  //#endregion
24
- const require_command = require('./command-DZA06E08.cjs');
24
+ const require_command = require('./command-B0lV6NBO.cjs');
25
25
  const __optique_core_constructs = __toESM(require("@optique/core/constructs"));
26
+ const __optique_core_extension = __toESM(require("@optique/core/extension"));
26
27
  const __optique_core_modifiers = __toESM(require("@optique/core/modifiers"));
27
28
  const __optique_core_primitives = __toESM(require("@optique/core/primitives"));
29
+ const __optique_core_usage = __toESM(require("@optique/core/usage"));
28
30
  const __optique_run = __toESM(require("@optique/run"));
29
31
  const node_fs_promises = __toESM(require("node:fs/promises"));
30
32
  const node_path = __toESM(require("node:path"));
@@ -61,37 +63,37 @@ function getDefaultExtensions(options = {}) {
61
63
  * @param options Discovery options.
62
64
  * @returns Discovered commands sorted by command path.
63
65
  * @throws {TypeError} If options are invalid, discovery finds no commands,
64
- * command paths conflict, or a module does not default-export a
65
- * command created with `defineCommand()`.
66
+ * command paths are duplicated, or a module does not default-export
67
+ * a command created with `defineCommand()`.
66
68
  * @since 1.1.0
67
69
  */
68
70
  async function discoverCommands(options) {
69
71
  const dir = pathFromDir(options.dir);
70
72
  const extensions = normalizeExtensions(options.extensions ?? getDefaultExtensions());
73
+ const entryFileName = normalizeEntryFileName(options.entryFileName);
71
74
  const files = await collectCommandFiles(dir, extensions);
72
75
  if (files.length < 1) throw new TypeError(`No command modules found in ${dir}.`);
73
76
  const seen = /* @__PURE__ */ new Map();
74
77
  const discovered = [];
75
78
  for (const filePath of files) {
76
- const path = commandPathFromFile(dir, filePath, extensions);
79
+ const path = commandPathFromFile(dir, filePath, extensions, entryFileName);
77
80
  const key = commandPathKey(path);
78
81
  const previous = seen.get(key);
79
82
  if (previous != null) {
80
- const displayPath = path.join(" ");
83
+ const displayPath = displayCommandPath(path);
81
84
  throw new TypeError(`Duplicate command path "${displayPath}" from ${previous} and ${filePath}.`);
82
85
  }
83
86
  seen.set(key, filePath);
84
87
  const mod = await import((0, node_url.pathToFileURL)(filePath).href);
85
88
  const commandDefinition = unwrapCommandExport(mod.default);
86
89
  if (commandDefinition == null) throw new TypeError(`Module ${filePath} default export must be created with defineCommand().`);
87
- if (commandDefinition.path != null && commandPathKey(commandDefinition.path) !== commandPathKey(path)) throw new TypeError(`Module ${filePath} declares command path "${commandDefinition.path.join(" ")}" but file path defines "${path.join(" ")}".`);
90
+ if (commandDefinition.path != null && commandPathKey(commandDefinition.path) !== commandPathKey(path)) throw new TypeError(`Module ${filePath} declares command path "${displayCommandPath(commandDefinition.path)}" but file path defines "${displayCommandPath(path)}".`);
88
91
  discovered.push({
89
92
  path,
90
93
  filePath,
91
94
  command: commandDefinition
92
95
  });
93
96
  }
94
- rejectPathConflicts(discovered);
95
97
  return sortCommands(discovered);
96
98
  }
97
99
  /**
@@ -100,7 +102,8 @@ async function discoverCommands(options) {
100
102
  * @param commands Commands to compose.
101
103
  * @param metadata Optional root documentation metadata.
102
104
  * @returns A parser that resolves to an internal command invocation.
103
- * @throws {TypeError} If no commands are provided or command paths conflict.
105
+ * @throws {TypeError} If no commands are provided or command paths are
106
+ * duplicated.
104
107
  * @since 1.1.0
105
108
  */
106
109
  function createProgramParser(commands, metadata = {}) {
@@ -108,11 +111,7 @@ function createProgramParser(commands, metadata = {}) {
108
111
  const sortedCommands = sortCommands(commands);
109
112
  rejectDuplicatePaths(sortedCommands.map((entry) => ({
110
113
  path: entry.path,
111
- filePath: entry.path.join("/")
112
- })));
113
- rejectPathConflicts(sortedCommands.map((entry) => ({
114
- path: entry.path,
115
- filePath: entry.path.join("/")
114
+ filePath: displayCommandPath(entry.path)
116
115
  })));
117
116
  const rootNode = buildCommandTree(sortedCommands);
118
117
  const parser = buildNodeParser(rootNode);
@@ -132,7 +131,8 @@ async function runProgram(options) {
132
131
  if (isStaticRunProgramOptions(options)) commands = staticCommandsToEntries(options.commands);
133
132
  else commands = await discoverCommands({
134
133
  dir: options.dir,
135
- extensions: options.extensions
134
+ extensions: options.extensions,
135
+ entryFileName: options.entryFileName
136
136
  });
137
137
  const parser = createProgramParser(commands, options.metadata);
138
138
  const invocation = await (0, __optique_run.runAsync)(parser, buildRunOptions(options));
@@ -163,6 +163,14 @@ function normalizeExtensions(extensions) {
163
163
  }
164
164
  return normalized.toSorted((a, b) => b.length - a.length || a.localeCompare(b));
165
165
  }
166
+ function normalizeEntryFileName(entryFileName) {
167
+ if (entryFileName === void 0) return "index";
168
+ if (entryFileName === false) return false;
169
+ if (typeof entryFileName !== "string") throw new TypeError(`Command entry file name must be a non-empty file name: ${entryFileName}`);
170
+ const normalized = entryFileName;
171
+ if (normalized.length < 1 || normalized.includes("/") || normalized.includes("\\")) throw new TypeError(`Command entry file name must be a non-empty file name: ${normalized}`);
172
+ return normalized;
173
+ }
166
174
  async function collectCommandFiles(dir, extensions, activeDirs = /* @__PURE__ */ new Set()) {
167
175
  const canonicalDir = await (0, node_fs_promises.realpath)(dir);
168
176
  if (activeDirs.has(canonicalDir)) return [];
@@ -194,29 +202,24 @@ async function getCommandFileEntryType(path, entry) {
194
202
  function isDeclarationFile(fileName) {
195
203
  return /\.d\.[cm]?ts$/.test(fileName);
196
204
  }
197
- function commandPathFromFile(rootDir, filePath, extensions) {
205
+ function commandPathFromFile(rootDir, filePath, extensions, entryFileName) {
198
206
  const matchedExtension = extensions.find((ext) => filePath.endsWith(ext));
199
207
  if (matchedExtension == null) throw new TypeError(`No configured extension matches ${filePath}.`);
200
208
  const withoutExtension = filePath.slice(0, -matchedExtension.length);
201
209
  const relativePath = (0, node_path.relative)(rootDir, withoutExtension);
202
210
  const path = relativePath.split(node_path.sep).filter((segment) => segment.length > 0);
203
- const [first, ...rest] = path;
204
- if (first == null) throw new TypeError(`Command file ${filePath} does not define a path.`);
205
- return [first, ...rest];
211
+ if (path.length < 1) throw new TypeError(`Command file ${filePath} does not define a path.`);
212
+ if (entryFileName !== false && path[path.length - 1] === entryFileName) return path.slice(0, -1);
213
+ return path;
206
214
  }
207
215
  function commandPathKey(path) {
208
216
  return path.join("\0");
209
217
  }
218
+ function displayCommandPath(path) {
219
+ return path.length < 1 ? "<root>" : path.join(" ");
220
+ }
210
221
  function isCommandPath(path) {
211
- return Array.isArray(path) && path.length > 0 && path.every((segment) => typeof segment === "string" && segment.length > 0);
212
- }
213
- function rejectPathConflicts(commands) {
214
- const paths = new Map(commands.map((entry) => [commandPathKey(entry.path), entry]));
215
- for (const entry of commands) for (let i = 1; i < entry.path.length; i++) {
216
- const parent = entry.path.slice(0, i);
217
- const parentEntry = paths.get(commandPathKey(parent));
218
- if (parentEntry != null) throw new TypeError(`Command path "${parent.join(" ")}" conflicts with nested command "${entry.path.join(" ")}".`);
219
- }
222
+ return Array.isArray(path) && path.every((segment) => typeof segment === "string" && segment.length > 0);
220
223
  }
221
224
  function rejectDuplicatePaths(commands) {
222
225
  const seen = /* @__PURE__ */ new Map();
@@ -224,7 +227,7 @@ function rejectDuplicatePaths(commands) {
224
227
  const key = commandPathKey(entry.path);
225
228
  const previous = seen.get(key);
226
229
  if (previous != null) {
227
- const displayPath = entry.path.join(" ");
230
+ const displayPath = displayCommandPath(entry.path);
228
231
  throw new TypeError(`Duplicate command path "${displayPath}" from ${previous} and ${entry.filePath}.`);
229
232
  }
230
233
  seen.set(key, entry.filePath);
@@ -242,7 +245,7 @@ function isStaticRunProgramOptions(options) {
242
245
  function staticCommandsToEntries(commands) {
243
246
  return commands.map((command$1) => {
244
247
  if (!require_command.isCommand(command$1)) throw new TypeError("Static command entries must be created with defineCommand().");
245
- if (!isCommandPath(command$1.path)) throw new TypeError("Static command entries must declare a non-empty path.");
248
+ if (!isCommandPath(command$1.path)) throw new TypeError("Static command entries must declare a path.");
246
249
  return {
247
250
  path: command$1.path,
248
251
  command: command$1
@@ -273,41 +276,354 @@ function buildCommandTree(commands) {
273
276
  }
274
277
  return root;
275
278
  }
276
- function buildNodeParser(node) {
279
+ function buildNodeParser(node, inheritedHidden) {
280
+ const childParser = buildChildrenParser(node, inheritedHidden);
281
+ if (childParser != null && node.command != null) return createExecutableNodeParser(childParser, node.command);
282
+ if (childParser != null) return childParser;
283
+ if (node.command != null) return createLeafParser(node.command);
284
+ throw new TypeError("Command tree node must contain a command.");
285
+ }
286
+ function createExecutableNodeParser(childParser, commandDefinition) {
287
+ const leafParser = createLeafParser(commandDefinition, true);
288
+ const branchParsers = [childParser, leafParser];
289
+ const parser = (0, __optique_core_constructs.longestMatch)(childParser, leafParser);
290
+ const phase2SeedHook = findPhase2SeedHook(parser);
291
+ const executableParser = {
292
+ ...parser,
293
+ $valueType: [],
294
+ $stateType: [],
295
+ initialState: void 0,
296
+ parse(context) {
297
+ const activeState = normalizeExecutableNodeState(context.state);
298
+ if (activeState?.committed === true && activeState.result.success) {
299
+ const branchParser = branchParsers[activeState.branch];
300
+ const result$1 = branchParser.parse(withExecutableNodeChildContext(context, activeState.branch, (0, __optique_core_extension.inheritAnnotations)(context.state, activeState.result.next.state), branchParser));
301
+ return (0, __optique_core_extension.mapModeValue)(parser.mode, (0, __optique_core_extension.wrapForMode)(parser.mode, result$1), (resolved) => wrapBranchParseResult(context, activeState, resolved));
302
+ }
303
+ const result = parser.parse({
304
+ ...context,
305
+ state: toExclusiveState(activeState, context.state)
306
+ });
307
+ return (0, __optique_core_extension.mapModeValue)(parser.mode, (0, __optique_core_extension.wrapForMode)(parser.mode, result), (resolved) => wrapInitialParseResult(context, resolved));
308
+ },
309
+ complete(state, exec) {
310
+ const activeState = normalizeExecutableNodeState(state);
311
+ if (activeState?.result.success === true) return (0, __optique_core_extension.wrapForMode)(parser.mode, branchParsers[activeState.branch].complete((0, __optique_core_extension.inheritAnnotations)(state, activeState.result.next.state), withExecutableNodeChildExecPath(exec, activeState.branch)));
312
+ if (activeState == null) return (0, __optique_core_extension.wrapForMode)(parser.mode, completeExecutableNodeLeaf(state, exec, leafParser));
313
+ return (0, __optique_core_extension.wrapForMode)(parser.mode, parser.complete(toExclusiveState(activeState, state), exec));
314
+ },
315
+ suggest(context, prefix) {
316
+ const activeState = normalizeExecutableNodeState(context.state);
317
+ if (activeState?.committed === true && activeState.result.success) {
318
+ const branchParser = branchParsers[activeState.branch];
319
+ return branchParser.suggest(withExecutableNodeChildContext(context, activeState.branch, (0, __optique_core_extension.inheritAnnotations)(context.state, activeState.result.next.state), branchParser), prefix);
320
+ }
321
+ return parser.suggest({
322
+ ...context,
323
+ state: toExclusiveState(activeState, context.state)
324
+ }, prefix);
325
+ },
326
+ getSuggestRuntimeNodes(state, path) {
327
+ const activeState = normalizeExecutableNodeState(state);
328
+ if (activeState == null) {
329
+ const branchPath$1 = [...path, 1];
330
+ const branchState$1 = (0, __optique_core_extension.inheritAnnotations)(state, leafParser.initialState);
331
+ return getExecutableNodeBranchSuggestRuntimeNodes(leafParser, branchState$1, branchPath$1);
332
+ }
333
+ if (activeState?.result.success !== true) return parser.getSuggestRuntimeNodes?.(toExclusiveState(activeState, state), path) ?? [];
334
+ const branchParser = branchParsers[activeState.branch];
335
+ const branchPath = [...path, activeState.branch];
336
+ const branchState = (0, __optique_core_extension.inheritAnnotations)(state, activeState.result.next.state);
337
+ return getExecutableNodeBranchSuggestRuntimeNodes(branchParser, branchState, branchPath);
338
+ },
339
+ getDocFragments(state, defaultValue) {
340
+ const activeState = state.kind === "available" ? normalizeExecutableNodeState(state.state) : void 0;
341
+ const fragments = parser.getDocFragments(state.kind === "available" ? {
342
+ kind: "available",
343
+ state: toExclusiveState(activeState, state.state)
344
+ } : state, defaultValue);
345
+ if (activeState == null) return withCommandDocMetadata(fragments, commandDefinition.metadata);
346
+ return fragments;
347
+ }
348
+ };
349
+ if (phase2SeedHook != null) Object.defineProperty(executableParser, phase2SeedHook.key, {
350
+ value(state, exec) {
351
+ return extractExecutableNodePhase2Seed(state, exec, leafParser, phase2SeedHook);
352
+ },
353
+ configurable: true,
354
+ enumerable: true
355
+ });
356
+ return executableParser;
357
+ }
358
+ function getExecutableNodeBranchSuggestRuntimeNodes(parser, state, path) {
359
+ return parser.getSuggestRuntimeNodes?.(state, path) ?? (parser.dependencyMetadata?.source != null ? [{
360
+ path,
361
+ parser,
362
+ state
363
+ }] : []);
364
+ }
365
+ const phase2SeedSymbolDescription = "@optique/core/extractPhase2Seed";
366
+ function findPhase2SeedHook(parser) {
367
+ for (const key of Object.getOwnPropertySymbols(parser)) {
368
+ if (key.description !== phase2SeedSymbolDescription) continue;
369
+ const value = Reflect.get(parser, key);
370
+ if (typeof value !== "function") continue;
371
+ return {
372
+ key,
373
+ extract(state, exec) {
374
+ const seed = Reflect.apply(value, parser, [state, exec]);
375
+ return seed;
376
+ }
377
+ };
378
+ }
379
+ return void 0;
380
+ }
381
+ function completeExecutableNodeLeaf(state, exec, leafParser) {
382
+ const result = parseExecutableNodeLeaf(state, exec, leafParser);
383
+ return (0, __optique_core_extension.dispatchByMode)(leafParser.mode, () => (0, __optique_core_extension.wrapForMode)("sync", completeParsedExecutableNodeLeaf(state, exec, leafParser, (0, __optique_core_extension.wrapForMode)("sync", result))), () => Promise.resolve((0, __optique_core_extension.wrapForMode)("async", result)).then((resolved) => (0, __optique_core_extension.wrapForMode)("async", completeParsedExecutableNodeLeaf(state, exec, leafParser, resolved))));
384
+ }
385
+ function completeParsedExecutableNodeLeaf(state, exec, leafParser, result) {
386
+ const childExec = withExecutableNodeChildExecPath(exec, 1);
387
+ const nextExec = result.success ? mergeExecutableNodeChildExec(childExec, result.next.exec) : childExec;
388
+ const nextState = result.success ? (0, __optique_core_extension.inheritAnnotations)(state, result.next.state) : (0, __optique_core_extension.inheritAnnotations)(state, leafParser.initialState);
389
+ return leafParser.complete(nextState, nextExec);
390
+ }
391
+ function extractExecutableNodePhase2Seed(state, exec, leafParser, phase2SeedHook) {
392
+ const activeState = normalizeExecutableNodeState(state);
393
+ if (activeState != null) return phase2SeedHook.extract(toExclusiveState(activeState, state), exec);
394
+ const result = parseExecutableNodeLeaf(state, exec, leafParser);
395
+ return (0, __optique_core_extension.dispatchByMode)(leafParser.mode, () => extractParsedExecutableNodePhase2Seed(state, exec, (0, __optique_core_extension.wrapForMode)("sync", result), phase2SeedHook), () => Promise.resolve((0, __optique_core_extension.wrapForMode)("async", result)).then((resolved) => extractParsedExecutableNodePhase2Seed(state, exec, resolved, phase2SeedHook)));
396
+ }
397
+ function extractParsedExecutableNodePhase2Seed(state, exec, result, phase2SeedHook) {
398
+ if (!result.success) return phase2SeedHook.extract(toExclusiveState(void 0, state), exec);
399
+ const executableState = (0, __optique_core_extension.inheritAnnotations)(state, {
400
+ branch: 1,
401
+ result,
402
+ committed: false
403
+ });
404
+ return phase2SeedHook.extract(toExclusiveState(executableState, state), exec);
405
+ }
406
+ function parseExecutableNodeLeaf(state, exec, leafParser) {
407
+ const childExec = withExecutableNodeChildExecPath(exec, 1);
408
+ const childContext = {
409
+ buffer: [],
410
+ optionsTerminated: false,
411
+ usage: leafParser.usage,
412
+ state: (0, __optique_core_extension.inheritAnnotations)(state, leafParser.initialState),
413
+ ...childExec != null ? {
414
+ exec: childExec,
415
+ dependencyRegistry: childExec.dependencyRegistry
416
+ } : {}
417
+ };
418
+ return leafParser.parse(childContext);
419
+ }
420
+ function normalizeExecutableNodeState(state) {
421
+ if (state == null || typeof state !== "object" || !("branch" in state) || !("result" in state)) return void 0;
422
+ const branch = state.branch;
423
+ if (branch !== 0 && branch !== 1) return void 0;
424
+ const result = state.result;
425
+ if (result == null || typeof result !== "object" || typeof result.success !== "boolean") return void 0;
426
+ return (0, __optique_core_extension.inheritAnnotations)(state, {
427
+ branch,
428
+ result,
429
+ committed: state.committed === true
430
+ });
431
+ }
432
+ function toExclusiveState(state, sourceState = state) {
433
+ const exclusiveState = state == null ? void 0 : [state.branch, state.result];
434
+ return (0, __optique_core_extension.inheritAnnotations)(sourceState, exclusiveState);
435
+ }
436
+ function fromExclusiveState(state) {
437
+ if (!Array.isArray(state) || state.length !== 2 || state[0] !== 0 && state[0] !== 1) return void 0;
438
+ return (0, __optique_core_extension.inheritAnnotations)(state, {
439
+ branch: state[0],
440
+ result: state[1],
441
+ committed: isCommittedResult(state[1])
442
+ });
443
+ }
444
+ function isCommittedResult(result) {
445
+ return result != null && typeof result === "object" && result.success === true && Array.isArray(result.consumed) && result.consumed.length > 0;
446
+ }
447
+ function wrapInitialParseResult(_context, result) {
448
+ if (!result.success) return result;
449
+ return {
450
+ success: true,
451
+ consumed: result.consumed,
452
+ provisional: result.provisional,
453
+ next: {
454
+ ...result.next,
455
+ state: fromExclusiveState(result.next.state)
456
+ }
457
+ };
458
+ }
459
+ function wrapBranchParseResult(context, activeState, result) {
460
+ if (!result.success) return result;
461
+ const mergedExec = mergeExecutableNodeChildExec(context.exec, result.next.exec);
462
+ const dependencyRegistry = mergedExec?.dependencyRegistry ?? result.next.dependencyRegistry ?? context.dependencyRegistry;
463
+ const nextState = (0, __optique_core_extension.inheritAnnotations)(result.next.state, {
464
+ branch: activeState.branch,
465
+ result,
466
+ committed: activeState.committed || result.consumed.length > 0
467
+ });
468
+ return {
469
+ success: true,
470
+ consumed: result.consumed,
471
+ provisional: result.provisional,
472
+ next: {
473
+ ...context,
474
+ buffer: result.next.buffer,
475
+ optionsTerminated: result.next.optionsTerminated,
476
+ state: (0, __optique_core_extension.inheritAnnotations)(context.state, nextState),
477
+ ...mergedExec != null ? {
478
+ exec: mergedExec,
479
+ trace: mergedExec.trace
480
+ } : {},
481
+ ...dependencyRegistry != null ? { dependencyRegistry } : {}
482
+ }
483
+ };
484
+ }
485
+ function withExecutableNodeChildContext(context, branch, state, parser) {
486
+ const exec = withExecutableNodeChildExecPath(context.exec, branch);
487
+ const dependencyRegistry = context.dependencyRegistry ?? exec?.dependencyRegistry;
488
+ return {
489
+ ...context,
490
+ state,
491
+ usage: parser.usage,
492
+ ...exec != null ? {
493
+ exec: dependencyRegistry === exec.dependencyRegistry ? exec : {
494
+ ...exec,
495
+ dependencyRegistry
496
+ },
497
+ dependencyRegistry
498
+ } : {}
499
+ };
500
+ }
501
+ function withExecutableNodeChildExecPath(exec, branch) {
502
+ if (exec == null) return void 0;
503
+ return {
504
+ ...exec,
505
+ path: [...exec.path ?? [], branch]
506
+ };
507
+ }
508
+ function mergeExecutableNodeChildExec(parent, child) {
509
+ if (parent == null) return child;
510
+ if (child == null) return parent;
511
+ return {
512
+ ...parent,
513
+ trace: child.trace ?? parent.trace,
514
+ dependencyRuntime: child.dependencyRuntime ?? parent.dependencyRuntime,
515
+ dependencyRegistry: child.dependencyRegistry ?? parent.dependencyRegistry,
516
+ commandPath: child.commandPath ?? parent.commandPath,
517
+ preCompletedByParser: child.preCompletedByParser ?? parent.preCompletedByParser,
518
+ excludedSourceFields: child.excludedSourceFields ?? parent.excludedSourceFields
519
+ };
520
+ }
521
+ function withCommandDocMetadata(fragments, metadata) {
522
+ if (metadata == null) return fragments;
523
+ return {
524
+ ...fragments,
525
+ brief: fragments.brief ?? metadata.brief,
526
+ description: fragments.description ?? metadata.description,
527
+ footer: fragments.footer ?? metadata.footer
528
+ };
529
+ }
530
+ function buildChildrenParser(node, inheritedHidden) {
277
531
  const parsers = [];
278
532
  for (const [name, child] of node.children) {
279
- const childParser = child.command != null ? createLeafParser(child.command) : buildNodeParser(child);
280
- const metadata = child.command?.metadata;
281
- parsers.push((0, __optique_core_primitives.command)(name, childParser, metadata));
533
+ const childHidden = (0, __optique_core_usage.mergeHidden)(inheritedHidden, child.command?.metadata?.hidden);
534
+ const childParser = buildNodeParser(child, childHidden);
535
+ if (child.children.size > 0) parsers.push(createNamespaceCommandParser(name, childParser, child.command, inheritedHidden));
536
+ else parsers.push((0, __optique_core_primitives.command)(name, childParser, commandMetadataWithInheritedHidden(child.command?.metadata, inheritedHidden)));
282
537
  }
538
+ if (parsers.length < 1) return void 0;
283
539
  if (parsers.length === 1) return parsers[0];
284
540
  return (0, __optique_core_constructs.or)(...parsers);
285
541
  }
286
- function createLeafParser(commandDefinition) {
287
- return (0, __optique_core_modifiers.map)(commandDefinition.parser, (value) => ({
542
+ function createNamespaceCommandParser(name, childParser, commandDefinition, inheritedHidden) {
543
+ const metadata = commandDefinition?.metadata;
544
+ const parser = (0, __optique_core_primitives.command)(name, childParser, namespaceCommandMetadata(metadata, inheritedHidden));
545
+ const description = metadata?.brief ?? metadata?.description;
546
+ if (description == null) return parser;
547
+ return {
548
+ ...parser,
549
+ getDocFragments(state, defaultValue) {
550
+ const fragments = parser.getDocFragments(state, defaultValue);
551
+ if (state.kind !== "unavailable" && (state.kind !== "available" || !Object.is(state.state, parser.initialState))) return fragments;
552
+ return withNamespaceListDocDescription(fragments, name, description);
553
+ }
554
+ };
555
+ }
556
+ function withNamespaceListDocDescription(fragments, name, description) {
557
+ return {
558
+ ...fragments,
559
+ fragments: fragments.fragments.map((fragment) => {
560
+ if (fragment.type !== "entry" || fragment.term.type !== "command" || fragment.term.name !== name) return fragment;
561
+ return {
562
+ ...fragment,
563
+ description: fragment.description ?? description
564
+ };
565
+ })
566
+ };
567
+ }
568
+ function namespaceCommandMetadata(metadata, inheritedHidden) {
569
+ const hidden = (0, __optique_core_usage.mergeHidden)(inheritedHidden, metadata?.hidden);
570
+ if (metadata?.aliases == null && metadata?.errors == null && hidden == null && metadata?.usageLine == null) return void 0;
571
+ return {
572
+ ...metadata?.aliases != null && { aliases: metadata.aliases },
573
+ ...metadata?.errors != null && { errors: metadata.errors },
574
+ ...hidden != null && { hidden },
575
+ ...metadata?.usageLine != null && { usageLine: metadata.usageLine }
576
+ };
577
+ }
578
+ function commandMetadataWithInheritedHidden(metadata, inheritedHidden) {
579
+ const hidden = (0, __optique_core_usage.mergeHidden)(inheritedHidden, metadata?.hidden);
580
+ if (metadata == null) return hidden == null ? void 0 : { hidden };
581
+ if (hidden === metadata.hidden) return metadata;
582
+ return {
583
+ ...metadata,
584
+ ...hidden != null && { hidden }
585
+ };
586
+ }
587
+ function createLeafParser(commandDefinition, includeMetadata = false) {
588
+ const parser = (0, __optique_core_modifiers.map)(commandDefinition.parser, (value) => ({
288
589
  command: commandDefinition,
289
590
  value,
290
591
  handler: commandDefinition.handler
291
592
  }));
593
+ if (!includeMetadata) return parser;
594
+ return {
595
+ ...parser,
596
+ getDocFragments(state, defaultValue) {
597
+ const fragments = parser.getDocFragments(state, defaultValue);
598
+ return withCommandDocMetadata(fragments, commandDefinition.metadata);
599
+ }
600
+ };
292
601
  }
293
602
  function withRootDocs(parser, commands, metadata) {
294
603
  const rootState = parser.initialState;
295
- const rootDocs = () => ({
296
- brief: metadata.brief,
297
- description: metadata.description,
298
- footer: metadata.footer,
299
- fragments: [{
604
+ const rootCommand = commands.find((entry) => entry.path.length < 1);
605
+ const listedCommands = commands.filter((entry) => entry.path.length > 0);
606
+ const commandsByPath = new Map(commands.map((entry) => [commandPathKey(entry.path), entry.command]));
607
+ const rootDocs = () => {
608
+ const fragments = [...rootCommand?.command.parser.getDocFragments({ kind: "unavailable" }).fragments ?? []];
609
+ if (listedCommands.length > 0) fragments.push({
300
610
  type: "section",
301
- entries: commands.map((entry) => ({
611
+ entries: listedCommands.map((entry) => ({
302
612
  term: {
303
613
  type: "command",
304
614
  name: entry.path.join(" "),
305
- hidden: entry.command.metadata?.hidden
615
+ hidden: commandPathHidden(entry.path, commandsByPath)
306
616
  },
307
617
  description: entry.command.metadata?.brief ?? entry.command.metadata?.description
308
618
  }))
309
- }]
310
- });
619
+ });
620
+ return {
621
+ brief: metadata.brief,
622
+ description: metadata.description,
623
+ footer: metadata.footer,
624
+ fragments
625
+ };
626
+ };
311
627
  return {
312
628
  ...parser,
313
629
  getDocFragments(state, defaultValue) {
@@ -316,9 +632,14 @@ function withRootDocs(parser, commands, metadata) {
316
632
  }
317
633
  };
318
634
  }
635
+ function commandPathHidden(path, commandsByPath) {
636
+ let hidden;
637
+ for (let length = 1; length <= path.length; length++) hidden = (0, __optique_core_usage.mergeHidden)(hidden, commandsByPath.get(commandPathKey(path.slice(0, length)))?.metadata?.hidden);
638
+ return hidden;
639
+ }
319
640
  function buildRunOptions(options) {
320
641
  const metadata = options.metadata;
321
- const { dir: _dir, commands: _commands, extensions: _extensions, metadata: _metadata, help, version, completion,...rest } = options;
642
+ const { dir: _dir, commands: _commands, extensions: _extensions, entryFileName: _entryFileName, metadata: _metadata, help, version, completion,...rest } = options;
322
643
  const runOptions = {
323
644
  ...rest,
324
645
  contexts: unwrapProgramContexts(rest.contexts),