@optique/discover 1.1.0 → 1.2.0-dev.2169

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/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,81 @@ 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
- const commandDefinition = unwrapCommandExport(mod.default);
86
- 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(" ")}".`);
88
+ const commandDefinition = commandFromModuleExport(filePath, mod.default);
89
+ validateDeclaredCommandPath(commandDefinition, path, filePath, "file path");
88
90
  discovered.push({
89
91
  path,
90
92
  filePath,
91
93
  command: commandDefinition
92
94
  });
93
95
  }
94
- rejectPathConflicts(discovered);
96
+ return sortCommands(discovered);
97
+ }
98
+ /**
99
+ * Converts a static module map into command entries.
100
+ *
101
+ * This is useful for bundlers and single-file packagers that can statically
102
+ * see module maps, such as `import.meta.glob(..., { eager: true })`, while
103
+ * still deriving command paths from file-like module keys.
104
+ *
105
+ * @param modules Static module map keyed by module path.
106
+ * @param options Module path derivation options.
107
+ * @returns Command entries sorted by command path.
108
+ * @throws {TypeError} If options are invalid, no command modules are found,
109
+ * command paths are duplicated, a module does not default-export a
110
+ * command created with `defineCommand()`, or an explicit command
111
+ * `path` does not match the module-derived path.
112
+ * @since 1.2.0
113
+ */
114
+ function commandsFromModules(modules, options = {}) {
115
+ if (modules == null || typeof modules !== "object") throw new TypeError("commandsFromModules() requires a module map object.");
116
+ const base = normalizeModuleBase(options.base);
117
+ const extensions = normalizeExtensions(options.extensions ?? getDefaultExtensions());
118
+ const entryFileName = normalizeEntryFileName(options.entryFileName);
119
+ const modulePaths = Object.keys(modules).toSorted((a, b) => a.localeCompare(b));
120
+ const seen = /* @__PURE__ */ new Map();
121
+ const discovered = [];
122
+ for (const modulePath of modulePaths) {
123
+ if (isDeclarationFile(node_path.posix.basename(modulePath)) || !extensions.some((ext) => modulePath.endsWith(ext))) continue;
124
+ const path = commandPathFromModulePath(base, modulePath, extensions, entryFileName);
125
+ const key = commandPathKey(path);
126
+ const previous = seen.get(key);
127
+ if (previous != null) {
128
+ const displayPath = displayCommandPath(path);
129
+ throw new TypeError(`Duplicate command path "${displayPath}" from ${previous} and ${modulePath}.`);
130
+ }
131
+ seen.set(key, modulePath);
132
+ const commandDefinition = commandFromModuleExport(modulePath, modules[modulePath]);
133
+ validateDeclaredCommandPath(commandDefinition, path, modulePath, "module path");
134
+ discovered.push({
135
+ path,
136
+ modulePath,
137
+ command: commandDefinition
138
+ });
139
+ }
140
+ if (discovered.length < 1) throw new TypeError("No command modules found in module map.");
95
141
  return sortCommands(discovered);
96
142
  }
97
143
  /**
@@ -100,7 +146,8 @@ async function discoverCommands(options) {
100
146
  * @param commands Commands to compose.
101
147
  * @param metadata Optional root documentation metadata.
102
148
  * @returns A parser that resolves to an internal command invocation.
103
- * @throws {TypeError} If no commands are provided or command paths conflict.
149
+ * @throws {TypeError} If no commands are provided or command paths are
150
+ * duplicated.
104
151
  * @since 1.1.0
105
152
  */
106
153
  function createProgramParser(commands, metadata = {}) {
@@ -108,11 +155,7 @@ function createProgramParser(commands, metadata = {}) {
108
155
  const sortedCommands = sortCommands(commands);
109
156
  rejectDuplicatePaths(sortedCommands.map((entry) => ({
110
157
  path: entry.path,
111
- filePath: entry.path.join("/")
112
- })));
113
- rejectPathConflicts(sortedCommands.map((entry) => ({
114
- path: entry.path,
115
- filePath: entry.path.join("/")
158
+ filePath: displayCommandPath(entry.path)
116
159
  })));
117
160
  const rootNode = buildCommandTree(sortedCommands);
118
161
  const parser = buildNodeParser(rootNode);
@@ -132,7 +175,8 @@ async function runProgram(options) {
132
175
  if (isStaticRunProgramOptions(options)) commands = staticCommandsToEntries(options.commands);
133
176
  else commands = await discoverCommands({
134
177
  dir: options.dir,
135
- extensions: options.extensions
178
+ extensions: options.extensions,
179
+ entryFileName: options.entryFileName
136
180
  });
137
181
  const parser = createProgramParser(commands, options.metadata);
138
182
  const invocation = await (0, __optique_run.runAsync)(parser, buildRunOptions(options));
@@ -163,6 +207,19 @@ function normalizeExtensions(extensions) {
163
207
  }
164
208
  return normalized.toSorted((a, b) => b.length - a.length || a.localeCompare(b));
165
209
  }
210
+ function normalizeEntryFileName(entryFileName) {
211
+ if (entryFileName === void 0) return "index";
212
+ if (entryFileName === false) return false;
213
+ if (typeof entryFileName !== "string") throw new TypeError(`Command entry file name must be a non-empty file name: ${entryFileName}`);
214
+ const normalized = entryFileName;
215
+ if (normalized.length < 1 || normalized.includes("/") || normalized.includes("\\")) throw new TypeError(`Command entry file name must be a non-empty file name: ${normalized}`);
216
+ return normalized;
217
+ }
218
+ function normalizeModuleBase(base) {
219
+ if (base === void 0) return ".";
220
+ if (typeof base !== "string" || base.length < 1) throw new TypeError(`Module base path must be a non-empty string: ${base}`);
221
+ return normalizeModulePath(base);
222
+ }
166
223
  async function collectCommandFiles(dir, extensions, activeDirs = /* @__PURE__ */ new Set()) {
167
224
  const canonicalDir = await (0, node_fs_promises.realpath)(dir);
168
225
  if (activeDirs.has(canonicalDir)) return [];
@@ -194,29 +251,45 @@ async function getCommandFileEntryType(path, entry) {
194
251
  function isDeclarationFile(fileName) {
195
252
  return /\.d\.[cm]?ts$/.test(fileName);
196
253
  }
197
- function commandPathFromFile(rootDir, filePath, extensions) {
198
- const matchedExtension = extensions.find((ext) => filePath.endsWith(ext));
199
- if (matchedExtension == null) throw new TypeError(`No configured extension matches ${filePath}.`);
200
- const withoutExtension = filePath.slice(0, -matchedExtension.length);
254
+ function commandPathFromFile(rootDir, filePath, extensions, entryFileName) {
255
+ const withoutExtension = stripCommandExtension(filePath, extensions);
201
256
  const relativePath = (0, node_path.relative)(rootDir, withoutExtension);
202
257
  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];
258
+ return commandPathFromSegments(path, filePath, entryFileName);
259
+ }
260
+ function commandPathFromModulePath(base, modulePath, extensions, entryFileName) {
261
+ const withoutExtension = stripCommandExtension(modulePath, extensions);
262
+ const relativePath = relativeModulePath(base, withoutExtension, modulePath);
263
+ const path = relativePath.split("/").filter((segment) => segment.length > 0);
264
+ return commandPathFromSegments(path, modulePath, entryFileName);
265
+ }
266
+ function stripCommandExtension(path, extensions) {
267
+ const matchedExtension = extensions.find((ext) => path.endsWith(ext));
268
+ if (matchedExtension == null) throw new TypeError(`No configured extension matches ${path}.`);
269
+ return path.slice(0, -matchedExtension.length);
270
+ }
271
+ function relativeModulePath(base, modulePath, originalModulePath) {
272
+ const normalizedPath = normalizeModulePath(modulePath);
273
+ const relativePath = node_path.posix.relative(base, normalizedPath);
274
+ if (relativePath.length < 1 || relativePath === ".." || relativePath.startsWith("../") || node_path.posix.isAbsolute(relativePath)) throw new TypeError(`Module path ${originalModulePath} is not under base path ${base}.`);
275
+ return relativePath;
276
+ }
277
+ function normalizeModulePath(path) {
278
+ return node_path.posix.normalize(path.replaceAll("\\", "/"));
279
+ }
280
+ function commandPathFromSegments(path, source, entryFileName) {
281
+ if (path.length < 1) throw new TypeError(`Command module ${source} does not define a path.`);
282
+ if (entryFileName !== false && path[path.length - 1] === entryFileName) return path.slice(0, -1);
283
+ return path;
206
284
  }
207
285
  function commandPathKey(path) {
208
286
  return path.join("\0");
209
287
  }
288
+ function displayCommandPath(path) {
289
+ return path.length < 1 ? "<root>" : path.join(" ");
290
+ }
210
291
  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
- }
292
+ return Array.isArray(path) && path.every((segment) => typeof segment === "string" && segment.length > 0);
220
293
  }
221
294
  function rejectDuplicatePaths(commands) {
222
295
  const seen = /* @__PURE__ */ new Map();
@@ -224,7 +297,7 @@ function rejectDuplicatePaths(commands) {
224
297
  const key = commandPathKey(entry.path);
225
298
  const previous = seen.get(key);
226
299
  if (previous != null) {
227
- const displayPath = entry.path.join(" ");
300
+ const displayPath = displayCommandPath(entry.path);
228
301
  throw new TypeError(`Duplicate command path "${displayPath}" from ${previous} and ${entry.filePath}.`);
229
302
  }
230
303
  seen.set(key, entry.filePath);
@@ -240,23 +313,38 @@ function isStaticRunProgramOptions(options) {
240
313
  return hasCommands;
241
314
  }
242
315
  function staticCommandsToEntries(commands) {
243
- return commands.map((command$1) => {
244
- 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.");
316
+ return commands.map((entry) => {
317
+ if (isCommandEntry(entry)) return entry;
318
+ if (!require_command.isCommand(entry)) throw new TypeError("Static command entries must be created with defineCommand().");
319
+ if (!isCommandPath(entry.path)) throw new TypeError("Static command entries must declare a path.");
246
320
  return {
247
- path: command$1.path,
248
- command: command$1
321
+ path: entry.path,
322
+ command: entry
249
323
  };
250
324
  });
251
325
  }
326
+ function isCommandEntry(value) {
327
+ return value != null && typeof value === "object" && isCommandPath(value.path) && require_command.isCommand(value.command);
328
+ }
252
329
  function unwrapCommandExport(value) {
253
- if (require_command.isCommand(value)) return value;
254
- if (value != null && typeof value === "object") {
255
- const nestedDefault = value.default;
256
- if (require_command.isCommand(nestedDefault)) return nestedDefault;
330
+ let current = value;
331
+ for (let depth = 0; depth < 3; depth++) {
332
+ if (require_command.isCommand(current)) return current;
333
+ if (current == null || typeof current !== "object") return void 0;
334
+ const nestedDefault = current.default;
335
+ if (Object.is(nestedDefault, current)) return void 0;
336
+ current = nestedDefault;
257
337
  }
258
338
  return void 0;
259
339
  }
340
+ function commandFromModuleExport(source, value) {
341
+ const commandDefinition = unwrapCommandExport(value);
342
+ if (commandDefinition == null) throw new TypeError(`Module ${source} default export must be created with defineCommand().`);
343
+ return commandDefinition;
344
+ }
345
+ function validateDeclaredCommandPath(commandDefinition, path, source, sourcePathLabel) {
346
+ if (commandDefinition.path != null && commandPathKey(commandDefinition.path) !== commandPathKey(path)) throw new TypeError(`Module ${source} declares command path "${displayCommandPath(commandDefinition.path)}" but ${sourcePathLabel} defines "${displayCommandPath(path)}".`);
347
+ }
260
348
  function buildCommandTree(commands) {
261
349
  const root = { children: /* @__PURE__ */ new Map() };
262
350
  for (const entry of commands) {
@@ -273,41 +361,354 @@ function buildCommandTree(commands) {
273
361
  }
274
362
  return root;
275
363
  }
276
- function buildNodeParser(node) {
364
+ function buildNodeParser(node, inheritedHidden) {
365
+ const childParser = buildChildrenParser(node, inheritedHidden);
366
+ if (childParser != null && node.command != null) return createExecutableNodeParser(childParser, node.command);
367
+ if (childParser != null) return childParser;
368
+ if (node.command != null) return createLeafParser(node.command);
369
+ throw new TypeError("Command tree node must contain a command.");
370
+ }
371
+ function createExecutableNodeParser(childParser, commandDefinition) {
372
+ const leafParser = createLeafParser(commandDefinition, true);
373
+ const branchParsers = [childParser, leafParser];
374
+ const parser = (0, __optique_core_constructs.longestMatch)(childParser, leafParser);
375
+ const phase2SeedHook = findPhase2SeedHook(parser);
376
+ const executableParser = {
377
+ ...parser,
378
+ $valueType: [],
379
+ $stateType: [],
380
+ initialState: void 0,
381
+ parse(context) {
382
+ const activeState = normalizeExecutableNodeState(context.state);
383
+ if (activeState?.committed === true && activeState.result.success) {
384
+ const branchParser = branchParsers[activeState.branch];
385
+ const result$1 = branchParser.parse(withExecutableNodeChildContext(context, activeState.branch, (0, __optique_core_extension.inheritAnnotations)(context.state, activeState.result.next.state), branchParser));
386
+ return (0, __optique_core_extension.mapModeValue)(parser.mode, (0, __optique_core_extension.wrapForMode)(parser.mode, result$1), (resolved) => wrapBranchParseResult(context, activeState, resolved));
387
+ }
388
+ const result = parser.parse({
389
+ ...context,
390
+ state: toExclusiveState(activeState, context.state)
391
+ });
392
+ return (0, __optique_core_extension.mapModeValue)(parser.mode, (0, __optique_core_extension.wrapForMode)(parser.mode, result), (resolved) => wrapInitialParseResult(context, resolved));
393
+ },
394
+ complete(state, exec) {
395
+ const activeState = normalizeExecutableNodeState(state);
396
+ 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)));
397
+ if (activeState == null) return (0, __optique_core_extension.wrapForMode)(parser.mode, completeExecutableNodeLeaf(state, exec, leafParser));
398
+ return (0, __optique_core_extension.wrapForMode)(parser.mode, parser.complete(toExclusiveState(activeState, state), exec));
399
+ },
400
+ suggest(context, prefix) {
401
+ const activeState = normalizeExecutableNodeState(context.state);
402
+ if (activeState?.committed === true && activeState.result.success) {
403
+ const branchParser = branchParsers[activeState.branch];
404
+ return branchParser.suggest(withExecutableNodeChildContext(context, activeState.branch, (0, __optique_core_extension.inheritAnnotations)(context.state, activeState.result.next.state), branchParser), prefix);
405
+ }
406
+ return parser.suggest({
407
+ ...context,
408
+ state: toExclusiveState(activeState, context.state)
409
+ }, prefix);
410
+ },
411
+ getSuggestRuntimeNodes(state, path) {
412
+ const activeState = normalizeExecutableNodeState(state);
413
+ if (activeState == null) {
414
+ const branchPath$1 = [...path, 1];
415
+ const branchState$1 = (0, __optique_core_extension.inheritAnnotations)(state, leafParser.initialState);
416
+ return getExecutableNodeBranchSuggestRuntimeNodes(leafParser, branchState$1, branchPath$1);
417
+ }
418
+ if (activeState?.result.success !== true) return parser.getSuggestRuntimeNodes?.(toExclusiveState(activeState, state), path) ?? [];
419
+ const branchParser = branchParsers[activeState.branch];
420
+ const branchPath = [...path, activeState.branch];
421
+ const branchState = (0, __optique_core_extension.inheritAnnotations)(state, activeState.result.next.state);
422
+ return getExecutableNodeBranchSuggestRuntimeNodes(branchParser, branchState, branchPath);
423
+ },
424
+ getDocFragments(state, defaultValue) {
425
+ const activeState = state.kind === "available" ? normalizeExecutableNodeState(state.state) : void 0;
426
+ const fragments = parser.getDocFragments(state.kind === "available" ? {
427
+ kind: "available",
428
+ state: toExclusiveState(activeState, state.state)
429
+ } : state, defaultValue);
430
+ if (activeState == null) return withCommandDocMetadata(fragments, commandDefinition.metadata);
431
+ return fragments;
432
+ }
433
+ };
434
+ if (phase2SeedHook != null) Object.defineProperty(executableParser, phase2SeedHook.key, {
435
+ value(state, exec) {
436
+ return extractExecutableNodePhase2Seed(state, exec, leafParser, phase2SeedHook);
437
+ },
438
+ configurable: true,
439
+ enumerable: true
440
+ });
441
+ return executableParser;
442
+ }
443
+ function getExecutableNodeBranchSuggestRuntimeNodes(parser, state, path) {
444
+ return parser.getSuggestRuntimeNodes?.(state, path) ?? (parser.dependencyMetadata?.source != null ? [{
445
+ path,
446
+ parser,
447
+ state
448
+ }] : []);
449
+ }
450
+ const phase2SeedSymbolDescription = "@optique/core/extractPhase2Seed";
451
+ function findPhase2SeedHook(parser) {
452
+ for (const key of Object.getOwnPropertySymbols(parser)) {
453
+ if (key.description !== phase2SeedSymbolDescription) continue;
454
+ const value = Reflect.get(parser, key);
455
+ if (typeof value !== "function") continue;
456
+ return {
457
+ key,
458
+ extract(state, exec) {
459
+ const seed = Reflect.apply(value, parser, [state, exec]);
460
+ return seed;
461
+ }
462
+ };
463
+ }
464
+ return void 0;
465
+ }
466
+ function completeExecutableNodeLeaf(state, exec, leafParser) {
467
+ const result = parseExecutableNodeLeaf(state, exec, leafParser);
468
+ 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))));
469
+ }
470
+ function completeParsedExecutableNodeLeaf(state, exec, leafParser, result) {
471
+ const childExec = withExecutableNodeChildExecPath(exec, 1);
472
+ const nextExec = result.success ? mergeExecutableNodeChildExec(childExec, result.next.exec) : childExec;
473
+ const nextState = result.success ? (0, __optique_core_extension.inheritAnnotations)(state, result.next.state) : (0, __optique_core_extension.inheritAnnotations)(state, leafParser.initialState);
474
+ return leafParser.complete(nextState, nextExec);
475
+ }
476
+ function extractExecutableNodePhase2Seed(state, exec, leafParser, phase2SeedHook) {
477
+ const activeState = normalizeExecutableNodeState(state);
478
+ if (activeState != null) return phase2SeedHook.extract(toExclusiveState(activeState, state), exec);
479
+ const result = parseExecutableNodeLeaf(state, exec, leafParser);
480
+ 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)));
481
+ }
482
+ function extractParsedExecutableNodePhase2Seed(state, exec, result, phase2SeedHook) {
483
+ if (!result.success) return phase2SeedHook.extract(toExclusiveState(void 0, state), exec);
484
+ const executableState = (0, __optique_core_extension.inheritAnnotations)(state, {
485
+ branch: 1,
486
+ result,
487
+ committed: false
488
+ });
489
+ return phase2SeedHook.extract(toExclusiveState(executableState, state), exec);
490
+ }
491
+ function parseExecutableNodeLeaf(state, exec, leafParser) {
492
+ const childExec = withExecutableNodeChildExecPath(exec, 1);
493
+ const childContext = {
494
+ buffer: [],
495
+ optionsTerminated: false,
496
+ usage: leafParser.usage,
497
+ state: (0, __optique_core_extension.inheritAnnotations)(state, leafParser.initialState),
498
+ ...childExec != null ? {
499
+ exec: childExec,
500
+ dependencyRegistry: childExec.dependencyRegistry
501
+ } : {}
502
+ };
503
+ return leafParser.parse(childContext);
504
+ }
505
+ function normalizeExecutableNodeState(state) {
506
+ if (state == null || typeof state !== "object" || !("branch" in state) || !("result" in state)) return void 0;
507
+ const branch = state.branch;
508
+ if (branch !== 0 && branch !== 1) return void 0;
509
+ const result = state.result;
510
+ if (result == null || typeof result !== "object" || typeof result.success !== "boolean") return void 0;
511
+ return (0, __optique_core_extension.inheritAnnotations)(state, {
512
+ branch,
513
+ result,
514
+ committed: state.committed === true
515
+ });
516
+ }
517
+ function toExclusiveState(state, sourceState = state) {
518
+ const exclusiveState = state == null ? void 0 : [state.branch, state.result];
519
+ return (0, __optique_core_extension.inheritAnnotations)(sourceState, exclusiveState);
520
+ }
521
+ function fromExclusiveState(state) {
522
+ if (!Array.isArray(state) || state.length !== 2 || state[0] !== 0 && state[0] !== 1) return void 0;
523
+ return (0, __optique_core_extension.inheritAnnotations)(state, {
524
+ branch: state[0],
525
+ result: state[1],
526
+ committed: isCommittedResult(state[1])
527
+ });
528
+ }
529
+ function isCommittedResult(result) {
530
+ return result != null && typeof result === "object" && result.success === true && Array.isArray(result.consumed) && result.consumed.length > 0;
531
+ }
532
+ function wrapInitialParseResult(_context, result) {
533
+ if (!result.success) return result;
534
+ return {
535
+ success: true,
536
+ consumed: result.consumed,
537
+ provisional: result.provisional,
538
+ next: {
539
+ ...result.next,
540
+ state: fromExclusiveState(result.next.state)
541
+ }
542
+ };
543
+ }
544
+ function wrapBranchParseResult(context, activeState, result) {
545
+ if (!result.success) return result;
546
+ const mergedExec = mergeExecutableNodeChildExec(context.exec, result.next.exec);
547
+ const dependencyRegistry = mergedExec?.dependencyRegistry ?? result.next.dependencyRegistry ?? context.dependencyRegistry;
548
+ const nextState = (0, __optique_core_extension.inheritAnnotations)(result.next.state, {
549
+ branch: activeState.branch,
550
+ result,
551
+ committed: activeState.committed || result.consumed.length > 0
552
+ });
553
+ return {
554
+ success: true,
555
+ consumed: result.consumed,
556
+ provisional: result.provisional,
557
+ next: {
558
+ ...context,
559
+ buffer: result.next.buffer,
560
+ optionsTerminated: result.next.optionsTerminated,
561
+ state: (0, __optique_core_extension.inheritAnnotations)(context.state, nextState),
562
+ ...mergedExec != null ? {
563
+ exec: mergedExec,
564
+ trace: mergedExec.trace
565
+ } : {},
566
+ ...dependencyRegistry != null ? { dependencyRegistry } : {}
567
+ }
568
+ };
569
+ }
570
+ function withExecutableNodeChildContext(context, branch, state, parser) {
571
+ const exec = withExecutableNodeChildExecPath(context.exec, branch);
572
+ const dependencyRegistry = context.dependencyRegistry ?? exec?.dependencyRegistry;
573
+ return {
574
+ ...context,
575
+ state,
576
+ usage: parser.usage,
577
+ ...exec != null ? {
578
+ exec: dependencyRegistry === exec.dependencyRegistry ? exec : {
579
+ ...exec,
580
+ dependencyRegistry
581
+ },
582
+ dependencyRegistry
583
+ } : {}
584
+ };
585
+ }
586
+ function withExecutableNodeChildExecPath(exec, branch) {
587
+ if (exec == null) return void 0;
588
+ return {
589
+ ...exec,
590
+ path: [...exec.path ?? [], branch]
591
+ };
592
+ }
593
+ function mergeExecutableNodeChildExec(parent, child) {
594
+ if (parent == null) return child;
595
+ if (child == null) return parent;
596
+ return {
597
+ ...parent,
598
+ trace: child.trace ?? parent.trace,
599
+ dependencyRuntime: child.dependencyRuntime ?? parent.dependencyRuntime,
600
+ dependencyRegistry: child.dependencyRegistry ?? parent.dependencyRegistry,
601
+ commandPath: child.commandPath ?? parent.commandPath,
602
+ preCompletedByParser: child.preCompletedByParser ?? parent.preCompletedByParser,
603
+ excludedSourceFields: child.excludedSourceFields ?? parent.excludedSourceFields
604
+ };
605
+ }
606
+ function withCommandDocMetadata(fragments, metadata) {
607
+ if (metadata == null) return fragments;
608
+ return {
609
+ ...fragments,
610
+ brief: fragments.brief ?? metadata.brief,
611
+ description: fragments.description ?? metadata.description,
612
+ footer: fragments.footer ?? metadata.footer
613
+ };
614
+ }
615
+ function buildChildrenParser(node, inheritedHidden) {
277
616
  const parsers = [];
278
617
  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));
618
+ const childHidden = (0, __optique_core_usage.mergeHidden)(inheritedHidden, child.command?.metadata?.hidden);
619
+ const childParser = buildNodeParser(child, childHidden);
620
+ if (child.children.size > 0) parsers.push(createNamespaceCommandParser(name, childParser, child.command, inheritedHidden));
621
+ else parsers.push((0, __optique_core_primitives.command)(name, childParser, commandMetadataWithInheritedHidden(child.command?.metadata, inheritedHidden)));
282
622
  }
623
+ if (parsers.length < 1) return void 0;
283
624
  if (parsers.length === 1) return parsers[0];
284
625
  return (0, __optique_core_constructs.or)(...parsers);
285
626
  }
286
- function createLeafParser(commandDefinition) {
287
- return (0, __optique_core_modifiers.map)(commandDefinition.parser, (value) => ({
627
+ function createNamespaceCommandParser(name, childParser, commandDefinition, inheritedHidden) {
628
+ const metadata = commandDefinition?.metadata;
629
+ const parser = (0, __optique_core_primitives.command)(name, childParser, namespaceCommandMetadata(metadata, inheritedHidden));
630
+ const description = metadata?.brief ?? metadata?.description;
631
+ if (description == null) return parser;
632
+ return {
633
+ ...parser,
634
+ getDocFragments(state, defaultValue) {
635
+ const fragments = parser.getDocFragments(state, defaultValue);
636
+ if (state.kind !== "unavailable" && (state.kind !== "available" || !Object.is(state.state, parser.initialState))) return fragments;
637
+ return withNamespaceListDocDescription(fragments, name, description);
638
+ }
639
+ };
640
+ }
641
+ function withNamespaceListDocDescription(fragments, name, description) {
642
+ return {
643
+ ...fragments,
644
+ fragments: fragments.fragments.map((fragment) => {
645
+ if (fragment.type !== "entry" || fragment.term.type !== "command" || fragment.term.name !== name) return fragment;
646
+ return {
647
+ ...fragment,
648
+ description: fragment.description ?? description
649
+ };
650
+ })
651
+ };
652
+ }
653
+ function namespaceCommandMetadata(metadata, inheritedHidden) {
654
+ const hidden = (0, __optique_core_usage.mergeHidden)(inheritedHidden, metadata?.hidden);
655
+ if (metadata?.aliases == null && metadata?.errors == null && hidden == null && metadata?.usageLine == null) return void 0;
656
+ return {
657
+ ...metadata?.aliases != null && { aliases: metadata.aliases },
658
+ ...metadata?.errors != null && { errors: metadata.errors },
659
+ ...hidden != null && { hidden },
660
+ ...metadata?.usageLine != null && { usageLine: metadata.usageLine }
661
+ };
662
+ }
663
+ function commandMetadataWithInheritedHidden(metadata, inheritedHidden) {
664
+ const hidden = (0, __optique_core_usage.mergeHidden)(inheritedHidden, metadata?.hidden);
665
+ if (metadata == null) return hidden == null ? void 0 : { hidden };
666
+ if (hidden === metadata.hidden) return metadata;
667
+ return {
668
+ ...metadata,
669
+ ...hidden != null && { hidden }
670
+ };
671
+ }
672
+ function createLeafParser(commandDefinition, includeMetadata = false) {
673
+ const parser = (0, __optique_core_modifiers.map)(commandDefinition.parser, (value) => ({
288
674
  command: commandDefinition,
289
675
  value,
290
676
  handler: commandDefinition.handler
291
677
  }));
678
+ if (!includeMetadata) return parser;
679
+ return {
680
+ ...parser,
681
+ getDocFragments(state, defaultValue) {
682
+ const fragments = parser.getDocFragments(state, defaultValue);
683
+ return withCommandDocMetadata(fragments, commandDefinition.metadata);
684
+ }
685
+ };
292
686
  }
293
687
  function withRootDocs(parser, commands, metadata) {
294
688
  const rootState = parser.initialState;
295
- const rootDocs = () => ({
296
- brief: metadata.brief,
297
- description: metadata.description,
298
- footer: metadata.footer,
299
- fragments: [{
689
+ const rootCommand = commands.find((entry) => entry.path.length < 1);
690
+ const listedCommands = commands.filter((entry) => entry.path.length > 0);
691
+ const commandsByPath = new Map(commands.map((entry) => [commandPathKey(entry.path), entry.command]));
692
+ const rootDocs = () => {
693
+ const fragments = [...rootCommand?.command.parser.getDocFragments({ kind: "unavailable" }).fragments ?? []];
694
+ if (listedCommands.length > 0) fragments.push({
300
695
  type: "section",
301
- entries: commands.map((entry) => ({
696
+ entries: listedCommands.map((entry) => ({
302
697
  term: {
303
698
  type: "command",
304
699
  name: entry.path.join(" "),
305
- hidden: entry.command.metadata?.hidden
700
+ hidden: commandPathHidden(entry.path, commandsByPath)
306
701
  },
307
702
  description: entry.command.metadata?.brief ?? entry.command.metadata?.description
308
703
  }))
309
- }]
310
- });
704
+ });
705
+ return {
706
+ brief: metadata.brief,
707
+ description: metadata.description,
708
+ footer: metadata.footer,
709
+ fragments
710
+ };
711
+ };
311
712
  return {
312
713
  ...parser,
313
714
  getDocFragments(state, defaultValue) {
@@ -316,9 +717,14 @@ function withRootDocs(parser, commands, metadata) {
316
717
  }
317
718
  };
318
719
  }
720
+ function commandPathHidden(path, commandsByPath) {
721
+ let hidden;
722
+ for (let length = 1; length <= path.length; length++) hidden = (0, __optique_core_usage.mergeHidden)(hidden, commandsByPath.get(commandPathKey(path.slice(0, length)))?.metadata?.hidden);
723
+ return hidden;
724
+ }
319
725
  function buildRunOptions(options) {
320
726
  const metadata = options.metadata;
321
- const { dir: _dir, commands: _commands, extensions: _extensions, metadata: _metadata, help, version, completion,...rest } = options;
727
+ const { dir: _dir, commands: _commands, extensions: _extensions, entryFileName: _entryFileName, metadata: _metadata, help, version, completion,...rest } = options;
322
728
  const runOptions = {
323
729
  ...rest,
324
730
  contexts: unwrapProgramContexts(rest.contexts),
@@ -368,6 +774,7 @@ function isProgramInvocation(value) {
368
774
  }
369
775
 
370
776
  //#endregion
777
+ exports.commandsFromModules = commandsFromModules;
371
778
  exports.createProgramParser = createProgramParser;
372
779
  exports.defineCommand = require_command.defineCommand;
373
780
  exports.discoverCommands = discoverCommands;