@optique/run 1.0.0-dev.908 → 1.0.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.
package/README.md CHANGED
@@ -23,6 +23,12 @@ Use *@optique/core* instead when:
23
23
  - Working in environments without `process` (browsers, web workers)
24
24
  - Building reusable parser components
25
25
 
26
+ Built-in help, version, and completion are parser-aware. Optique only
27
+ intercepts `help`, `version`, `completion`, `--help`, `--version`,
28
+ `--completion`, and configured aliases when the user parser leaves them
29
+ unconsumed. If your parser uses the same token sequence as ordinary
30
+ data, the parse result wins.
31
+
26
32
 
27
33
  Installation
28
34
  ------------
package/dist/run.cjs CHANGED
@@ -32,17 +32,17 @@ function run(parserOrProgram, options = {}) {
32
32
  return runImpl(parserOrProgram, options);
33
33
  }
34
34
  function runSync(parserOrProgram, options = {}) {
35
+ const parserToCheck = "parser" in parserOrProgram && "metadata" in parserOrProgram ? parserOrProgram.parser : parserOrProgram;
36
+ if (parserToCheck.mode !== "sync") throw new TypeError("Cannot use an async parser with runSync(). Use run() or runAsync() instead.");
35
37
  const contexts = options.contexts;
36
38
  if (contexts && contexts.length > 0) {
37
39
  const resolved = resolveProgramInput(parserOrProgram, options);
38
40
  const { parser, programMetadata } = resolved;
39
41
  options = resolved.options;
40
42
  const { programName, args, coreOptions } = buildCoreOptions(options, programMetadata);
41
- const contextOptions = {};
42
- for (const key of Object.keys(options)) if (!knownRunOptionsKeys.has(key)) contextOptions[key] = options[key];
43
43
  const runWithOptions = {
44
44
  ...coreOptions,
45
- ...contextOptions,
45
+ contextOptions: options.contextOptions,
46
46
  args
47
47
  };
48
48
  return (0, __optique_core_facade.runWithSync)(parser, programName, contexts, runWithOptions);
@@ -173,35 +173,6 @@ function buildCoreOptions(options, programMetadata) {
173
173
  coreOptions
174
174
  };
175
175
  }
176
- /**
177
- * Set of known RunOptions field names. Used by `runImpl()` to separate
178
- * RunOptions fields from context-required options via rest-spread.
179
- */
180
- const knownRunOptionsKeyList = [
181
- "programName",
182
- "args",
183
- "stdout",
184
- "stderr",
185
- "onExit",
186
- "colors",
187
- "maxWidth",
188
- "showDefault",
189
- "showChoices",
190
- "sectionOrder",
191
- "help",
192
- "version",
193
- "completion",
194
- "aboveError",
195
- "errorExitCode",
196
- "brief",
197
- "description",
198
- "examples",
199
- "author",
200
- "bugs",
201
- "footer",
202
- "contexts"
203
- ];
204
- const knownRunOptionsKeys = new Set(knownRunOptionsKeyList);
205
176
  function runImpl(parserOrProgram, options = {}) {
206
177
  const resolved = resolveProgramInput(parserOrProgram, options);
207
178
  const { parser, programMetadata } = resolved;
@@ -209,11 +180,9 @@ function runImpl(parserOrProgram, options = {}) {
209
180
  const contexts = options.contexts;
210
181
  if (contexts && contexts.length > 0) {
211
182
  const { programName: programName$1, args: args$1, coreOptions: coreOptions$1 } = buildCoreOptions(options, programMetadata);
212
- const contextOptions = {};
213
- for (const key of Object.keys(options)) if (!knownRunOptionsKeys.has(key)) contextOptions[key] = options[key];
214
183
  const runWithOptions = {
215
184
  ...coreOptions$1,
216
- ...contextOptions,
185
+ contextOptions: options.contextOptions,
217
186
  args: args$1
218
187
  };
219
188
  return (0, __optique_core_facade.runWith)(parser, programName$1, contexts, runWithOptions);
package/dist/run.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ShellCompletion } from "@optique/core/completion";
2
2
  import { SourceContext } from "@optique/core/context";
3
- import { CommandSubConfig, ExtractRequiredOptions, OptionSubConfig } from "@optique/core/facade";
3
+ import { CommandSubConfig, ContextOptionsParam, OptionSubConfig } from "@optique/core/facade";
4
4
  import { InferMode, InferValue, Mode, ModeValue, Parser } from "@optique/core/parser";
5
5
  import { Program } from "@optique/core/program";
6
6
  import { DocSection, ShowChoicesOptions, ShowDefaultOptions } from "@optique/core/doc";
@@ -225,6 +225,15 @@ interface RunOptions {
225
225
  * @since 1.0.0
226
226
  */
227
227
  readonly contexts?: readonly SourceContext<unknown>[];
228
+ /**
229
+ * Options to forward to source contexts. When contexts declare
230
+ * required options (via `$requiredOptions`), pass them here to
231
+ * avoid name collisions with runner-level options such as `help`,
232
+ * `programName`, or `version`.
233
+ *
234
+ * @since 1.0.0
235
+ */
236
+ readonly contextOptions?: Record<string, unknown>;
228
237
  }
229
238
  /**
230
239
  * Rejects context tuples that are statically known to be empty so those calls
@@ -323,23 +332,25 @@ type AcceptExactOptionalRunOptions<TOptions> = [TOptions] extends [RunOptions |
323
332
  * // myapp --format=<TAB> # Use completion
324
333
  * ```
325
334
  *
326
- * @since 0.11.0 Added support for {@link Program} objects.
335
+ * Supports {@link Program} objects.
336
+ *
337
+ * @since 1.0.0
327
338
  */
328
339
  declare function run<T extends Parser<Mode, unknown, unknown>, const TContexts extends NonEmptySourceContexts>(parser: T, options: RunOptions & {
329
340
  readonly contexts: TContexts;
330
- } & ExtractRequiredOptions<TContexts, InferValue<T>>): Promise<InferValue<T>>;
341
+ } & ContextOptionsParam<TContexts, InferValue<T>>): Promise<InferValue<T>>;
331
342
  declare function run<T extends Parser<Mode, unknown, unknown>, const TContexts extends readonly SourceContext<unknown>[]>(parser: T, options: RunOptions & {
332
343
  readonly contexts: TContexts;
333
- } & RejectEmptyContexts<TContexts> & ExtractRequiredOptions<TContexts, InferValue<T>>): ModeValue<InferMode<T>, InferValue<T>> | Promise<InferValue<T>>;
344
+ } & RejectEmptyContexts<TContexts> & ContextOptionsParam<TContexts, InferValue<T>>): ModeValue<InferMode<T>, InferValue<T>> | Promise<InferValue<T>>;
334
345
  declare function run<M extends Mode, T, const TContexts extends NonEmptySourceContexts>(program: Program<M, T>, options: RunOptions & {
335
346
  readonly contexts: TContexts;
336
- } & ExtractRequiredOptions<TContexts, T>): Promise<T>;
347
+ } & ContextOptionsParam<TContexts, T>): Promise<T>;
337
348
  declare function run<T, const TContexts extends readonly SourceContext<unknown>[]>(program: Program<"sync", T>, options: RunOptions & {
338
349
  readonly contexts: TContexts;
339
- } & RejectEmptyContexts<TContexts> & ExtractRequiredOptions<TContexts, T>): T | Promise<T>;
350
+ } & RejectEmptyContexts<TContexts> & ContextOptionsParam<TContexts, T>): T | Promise<T>;
340
351
  declare function run<T, const TContexts extends readonly SourceContext<unknown>[]>(program: Program<"async", T>, options: RunOptions & {
341
352
  readonly contexts: TContexts;
342
- } & RejectEmptyContexts<TContexts> & ExtractRequiredOptions<TContexts, T>): Promise<T>;
353
+ } & RejectEmptyContexts<TContexts> & ContextOptionsParam<TContexts, T>): Promise<T>;
343
354
  declare function run<T, const TOptions extends RunOptions | undefined>(program: Program<"sync", T>, options?: TOptions & RejectContextfulOptions<TOptions> & RejectUnknownRunOptionKeys<TOptions>): T;
344
355
  declare function run<T, TOptions extends RunOptions>(program: Program<"sync", T>, options: TOptions & AcceptExactRunOptions<TOptions>): T | Promise<T>;
345
356
  declare function run<T, const TOptions extends RunOptions | undefined>(program: Program<"async", T>, options?: TOptions & RejectContextfulOptions<TOptions> & RejectUnknownRunOptionKeys<TOptions>): Promise<T>;
@@ -358,14 +369,16 @@ declare function run<T extends Parser<Mode, unknown, unknown>>(parser: T, option
358
369
  * @param parser The synchronous command-line parser to execute.
359
370
  * @param options Configuration options for customizing behavior.
360
371
  * @returns The parsed result if successful.
372
+ * @throws {TypeError} If an async parser (or a {@link Program} wrapping one)
373
+ * is passed at runtime. Use {@link run} or {@link runAsync} instead.
361
374
  * @since 0.9.0
362
375
  */
363
376
  declare function runSync<T extends Parser<"sync", unknown, unknown>, TContexts extends readonly SourceContext<unknown>[]>(parser: T, options: RunOptions & {
364
377
  readonly contexts: TContexts;
365
- } & ExtractRequiredOptions<TContexts, InferValue<T>>): InferValue<T>;
378
+ } & ContextOptionsParam<TContexts, InferValue<T>>): InferValue<T>;
366
379
  declare function runSync<T, const TContexts extends readonly SourceContext<unknown>[]>(program: Program<"sync", T>, options: RunOptions & {
367
380
  readonly contexts: TContexts;
368
- } & RejectEmptyContexts<TContexts> & ExtractRequiredOptions<TContexts, T>): T;
381
+ } & RejectEmptyContexts<TContexts> & ContextOptionsParam<TContexts, T>): T;
369
382
  declare function runSync<T, const TOptions extends RunOptions | undefined>(program: Program<"sync", T>, options?: TOptions & RejectContextfulOptions<TOptions> & RejectUnknownRunOptionKeys<TOptions>): T;
370
383
  declare function runSync<T, TOptions extends RunOptions>(program: Program<"sync", T>, options: TOptions & AcceptExactRunOptions<TOptions>): T;
371
384
  declare function runSync<T, TOptions extends RunOptions | undefined>(program: Program<"sync", T>, options: TOptions & AcceptExactOptionalRunOptions<TOptions>): T;
@@ -385,10 +398,10 @@ declare function runSync<T extends Parser<"sync", unknown, unknown>>(parser: T,
385
398
  */
386
399
  declare function runAsync<T extends Parser<Mode, unknown, unknown>, TContexts extends readonly SourceContext<unknown>[]>(parser: T, options: RunOptions & {
387
400
  readonly contexts: TContexts;
388
- } & ExtractRequiredOptions<TContexts, InferValue<T>>): Promise<InferValue<T>>;
401
+ } & ContextOptionsParam<TContexts, InferValue<T>>): Promise<InferValue<T>>;
389
402
  declare function runAsync<M extends Mode, T, const TContexts extends readonly SourceContext<unknown>[]>(program: Program<M, T>, options: RunOptions & {
390
403
  readonly contexts: TContexts;
391
- } & RejectEmptyContexts<TContexts> & ExtractRequiredOptions<TContexts, T>): Promise<T>;
404
+ } & RejectEmptyContexts<TContexts> & ContextOptionsParam<TContexts, T>): Promise<T>;
392
405
  declare function runAsync<T, const TOptions extends RunOptions | undefined>(program: Program<"sync", T>, options?: TOptions & RejectContextfulOptions<TOptions> & RejectUnknownRunOptionKeys<TOptions>): Promise<T>;
393
406
  declare function runAsync<T, const TOptions extends RunOptions | undefined>(program: Program<"async", T>, options?: TOptions & RejectContextfulOptions<TOptions> & RejectUnknownRunOptionKeys<TOptions>): Promise<T>;
394
407
  declare function runAsync<T, TOptions extends RunOptions>(program: Program<Mode, T>, options: TOptions & AcceptExactRunOptions<TOptions>): Promise<T>;
package/dist/run.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CommandSubConfig, ExtractRequiredOptions, OptionSubConfig } from "@optique/core/facade";
1
+ import { CommandSubConfig, ContextOptionsParam, OptionSubConfig } from "@optique/core/facade";
2
2
  import { Message } from "@optique/core/message";
3
3
  import { ShellCompletion } from "@optique/core/completion";
4
4
  import { SourceContext } from "@optique/core/context";
@@ -225,6 +225,15 @@ interface RunOptions {
225
225
  * @since 1.0.0
226
226
  */
227
227
  readonly contexts?: readonly SourceContext<unknown>[];
228
+ /**
229
+ * Options to forward to source contexts. When contexts declare
230
+ * required options (via `$requiredOptions`), pass them here to
231
+ * avoid name collisions with runner-level options such as `help`,
232
+ * `programName`, or `version`.
233
+ *
234
+ * @since 1.0.0
235
+ */
236
+ readonly contextOptions?: Record<string, unknown>;
228
237
  }
229
238
  /**
230
239
  * Rejects context tuples that are statically known to be empty so those calls
@@ -323,23 +332,25 @@ type AcceptExactOptionalRunOptions<TOptions> = [TOptions] extends [RunOptions |
323
332
  * // myapp --format=<TAB> # Use completion
324
333
  * ```
325
334
  *
326
- * @since 0.11.0 Added support for {@link Program} objects.
335
+ * Supports {@link Program} objects.
336
+ *
337
+ * @since 1.0.0
327
338
  */
328
339
  declare function run<T extends Parser<Mode, unknown, unknown>, const TContexts extends NonEmptySourceContexts>(parser: T, options: RunOptions & {
329
340
  readonly contexts: TContexts;
330
- } & ExtractRequiredOptions<TContexts, InferValue<T>>): Promise<InferValue<T>>;
341
+ } & ContextOptionsParam<TContexts, InferValue<T>>): Promise<InferValue<T>>;
331
342
  declare function run<T extends Parser<Mode, unknown, unknown>, const TContexts extends readonly SourceContext<unknown>[]>(parser: T, options: RunOptions & {
332
343
  readonly contexts: TContexts;
333
- } & RejectEmptyContexts<TContexts> & ExtractRequiredOptions<TContexts, InferValue<T>>): ModeValue<InferMode<T>, InferValue<T>> | Promise<InferValue<T>>;
344
+ } & RejectEmptyContexts<TContexts> & ContextOptionsParam<TContexts, InferValue<T>>): ModeValue<InferMode<T>, InferValue<T>> | Promise<InferValue<T>>;
334
345
  declare function run<M extends Mode, T, const TContexts extends NonEmptySourceContexts>(program: Program<M, T>, options: RunOptions & {
335
346
  readonly contexts: TContexts;
336
- } & ExtractRequiredOptions<TContexts, T>): Promise<T>;
347
+ } & ContextOptionsParam<TContexts, T>): Promise<T>;
337
348
  declare function run<T, const TContexts extends readonly SourceContext<unknown>[]>(program: Program<"sync", T>, options: RunOptions & {
338
349
  readonly contexts: TContexts;
339
- } & RejectEmptyContexts<TContexts> & ExtractRequiredOptions<TContexts, T>): T | Promise<T>;
350
+ } & RejectEmptyContexts<TContexts> & ContextOptionsParam<TContexts, T>): T | Promise<T>;
340
351
  declare function run<T, const TContexts extends readonly SourceContext<unknown>[]>(program: Program<"async", T>, options: RunOptions & {
341
352
  readonly contexts: TContexts;
342
- } & RejectEmptyContexts<TContexts> & ExtractRequiredOptions<TContexts, T>): Promise<T>;
353
+ } & RejectEmptyContexts<TContexts> & ContextOptionsParam<TContexts, T>): Promise<T>;
343
354
  declare function run<T, const TOptions extends RunOptions | undefined>(program: Program<"sync", T>, options?: TOptions & RejectContextfulOptions<TOptions> & RejectUnknownRunOptionKeys<TOptions>): T;
344
355
  declare function run<T, TOptions extends RunOptions>(program: Program<"sync", T>, options: TOptions & AcceptExactRunOptions<TOptions>): T | Promise<T>;
345
356
  declare function run<T, const TOptions extends RunOptions | undefined>(program: Program<"async", T>, options?: TOptions & RejectContextfulOptions<TOptions> & RejectUnknownRunOptionKeys<TOptions>): Promise<T>;
@@ -358,14 +369,16 @@ declare function run<T extends Parser<Mode, unknown, unknown>>(parser: T, option
358
369
  * @param parser The synchronous command-line parser to execute.
359
370
  * @param options Configuration options for customizing behavior.
360
371
  * @returns The parsed result if successful.
372
+ * @throws {TypeError} If an async parser (or a {@link Program} wrapping one)
373
+ * is passed at runtime. Use {@link run} or {@link runAsync} instead.
361
374
  * @since 0.9.0
362
375
  */
363
376
  declare function runSync<T extends Parser<"sync", unknown, unknown>, TContexts extends readonly SourceContext<unknown>[]>(parser: T, options: RunOptions & {
364
377
  readonly contexts: TContexts;
365
- } & ExtractRequiredOptions<TContexts, InferValue<T>>): InferValue<T>;
378
+ } & ContextOptionsParam<TContexts, InferValue<T>>): InferValue<T>;
366
379
  declare function runSync<T, const TContexts extends readonly SourceContext<unknown>[]>(program: Program<"sync", T>, options: RunOptions & {
367
380
  readonly contexts: TContexts;
368
- } & RejectEmptyContexts<TContexts> & ExtractRequiredOptions<TContexts, T>): T;
381
+ } & RejectEmptyContexts<TContexts> & ContextOptionsParam<TContexts, T>): T;
369
382
  declare function runSync<T, const TOptions extends RunOptions | undefined>(program: Program<"sync", T>, options?: TOptions & RejectContextfulOptions<TOptions> & RejectUnknownRunOptionKeys<TOptions>): T;
370
383
  declare function runSync<T, TOptions extends RunOptions>(program: Program<"sync", T>, options: TOptions & AcceptExactRunOptions<TOptions>): T;
371
384
  declare function runSync<T, TOptions extends RunOptions | undefined>(program: Program<"sync", T>, options: TOptions & AcceptExactOptionalRunOptions<TOptions>): T;
@@ -385,10 +398,10 @@ declare function runSync<T extends Parser<"sync", unknown, unknown>>(parser: T,
385
398
  */
386
399
  declare function runAsync<T extends Parser<Mode, unknown, unknown>, TContexts extends readonly SourceContext<unknown>[]>(parser: T, options: RunOptions & {
387
400
  readonly contexts: TContexts;
388
- } & ExtractRequiredOptions<TContexts, InferValue<T>>): Promise<InferValue<T>>;
401
+ } & ContextOptionsParam<TContexts, InferValue<T>>): Promise<InferValue<T>>;
389
402
  declare function runAsync<M extends Mode, T, const TContexts extends readonly SourceContext<unknown>[]>(program: Program<M, T>, options: RunOptions & {
390
403
  readonly contexts: TContexts;
391
- } & RejectEmptyContexts<TContexts> & ExtractRequiredOptions<TContexts, T>): Promise<T>;
404
+ } & RejectEmptyContexts<TContexts> & ContextOptionsParam<TContexts, T>): Promise<T>;
392
405
  declare function runAsync<T, const TOptions extends RunOptions | undefined>(program: Program<"sync", T>, options?: TOptions & RejectContextfulOptions<TOptions> & RejectUnknownRunOptionKeys<TOptions>): Promise<T>;
393
406
  declare function runAsync<T, const TOptions extends RunOptions | undefined>(program: Program<"async", T>, options?: TOptions & RejectContextfulOptions<TOptions> & RejectUnknownRunOptionKeys<TOptions>): Promise<T>;
394
407
  declare function runAsync<T, TOptions extends RunOptions>(program: Program<Mode, T>, options: TOptions & AcceptExactRunOptions<TOptions>): Promise<T>;
package/dist/run.js CHANGED
@@ -31,17 +31,17 @@ function run(parserOrProgram, options = {}) {
31
31
  return runImpl(parserOrProgram, options);
32
32
  }
33
33
  function runSync(parserOrProgram, options = {}) {
34
+ const parserToCheck = "parser" in parserOrProgram && "metadata" in parserOrProgram ? parserOrProgram.parser : parserOrProgram;
35
+ if (parserToCheck.mode !== "sync") throw new TypeError("Cannot use an async parser with runSync(). Use run() or runAsync() instead.");
34
36
  const contexts = options.contexts;
35
37
  if (contexts && contexts.length > 0) {
36
38
  const resolved = resolveProgramInput(parserOrProgram, options);
37
39
  const { parser, programMetadata } = resolved;
38
40
  options = resolved.options;
39
41
  const { programName, args, coreOptions } = buildCoreOptions(options, programMetadata);
40
- const contextOptions = {};
41
- for (const key of Object.keys(options)) if (!knownRunOptionsKeys.has(key)) contextOptions[key] = options[key];
42
42
  const runWithOptions = {
43
43
  ...coreOptions,
44
- ...contextOptions,
44
+ contextOptions: options.contextOptions,
45
45
  args
46
46
  };
47
47
  return runWithSync(parser, programName, contexts, runWithOptions);
@@ -172,35 +172,6 @@ function buildCoreOptions(options, programMetadata) {
172
172
  coreOptions
173
173
  };
174
174
  }
175
- /**
176
- * Set of known RunOptions field names. Used by `runImpl()` to separate
177
- * RunOptions fields from context-required options via rest-spread.
178
- */
179
- const knownRunOptionsKeyList = [
180
- "programName",
181
- "args",
182
- "stdout",
183
- "stderr",
184
- "onExit",
185
- "colors",
186
- "maxWidth",
187
- "showDefault",
188
- "showChoices",
189
- "sectionOrder",
190
- "help",
191
- "version",
192
- "completion",
193
- "aboveError",
194
- "errorExitCode",
195
- "brief",
196
- "description",
197
- "examples",
198
- "author",
199
- "bugs",
200
- "footer",
201
- "contexts"
202
- ];
203
- const knownRunOptionsKeys = new Set(knownRunOptionsKeyList);
204
175
  function runImpl(parserOrProgram, options = {}) {
205
176
  const resolved = resolveProgramInput(parserOrProgram, options);
206
177
  const { parser, programMetadata } = resolved;
@@ -208,11 +179,9 @@ function runImpl(parserOrProgram, options = {}) {
208
179
  const contexts = options.contexts;
209
180
  if (contexts && contexts.length > 0) {
210
181
  const { programName: programName$1, args: args$1, coreOptions: coreOptions$1 } = buildCoreOptions(options, programMetadata);
211
- const contextOptions = {};
212
- for (const key of Object.keys(options)) if (!knownRunOptionsKeys.has(key)) contextOptions[key] = options[key];
213
182
  const runWithOptions = {
214
183
  ...coreOptions$1,
215
- ...contextOptions,
184
+ contextOptions: options.contextOptions,
216
185
  args: args$1
217
186
  };
218
187
  return runWith(parser, programName$1, contexts, runWithOptions);
@@ -18,8 +18,14 @@ const node_fs = require_rolldown_runtime.__toESM(require("node:fs"));
18
18
  * `"file"`, `"directory"`, or `"either"`.
19
19
  * @throws {TypeError} If any entry in {@link PathOptionsBase.extensions} does
20
20
  * not start with a dot (e.g., `"json"` instead of `".json"`).
21
+ * @throws {TypeError} If {@link PathOptionsMustExist.mustExist} is not a
22
+ * boolean.
23
+ * @throws {TypeError} If {@link PathOptionsMustNotExist.mustNotExist} is not a
24
+ * boolean.
25
+ * @throws {TypeError} If {@link PathOptionsBase.allowCreate} is not a boolean.
21
26
  * @throws {TypeError} If both {@link PathOptionsMustExist.mustExist} and
22
27
  * {@link PathOptionsMustNotExist.mustNotExist} are `true`.
28
+ * @throws {TypeError} If `placeholder` is not a string.
23
29
  *
24
30
  * @example
25
31
  * ```typescript
@@ -51,14 +57,20 @@ function path(options = {}) {
51
57
  (0, __optique_core_nonempty.ensureNonEmptyString)(metavar);
52
58
  if (type !== "file" && type !== "directory" && type !== "either") throw new TypeError(`Unsupported path type: ${JSON.stringify(type)}. Expected "file", "directory", or "either".`);
53
59
  if (extensions) {
54
- for (const ext of extensions) if (!ext.startsWith(".")) throw new TypeError(`Each extension must start with a dot, got: ${JSON.stringify(ext)}`);
60
+ for (const ext of extensions) if (typeof ext !== "string" || !ext.startsWith(".")) throw new TypeError(`Each extension must start with a dot, got: ${JSON.stringify(ext)}`);
55
61
  }
62
+ if (options.allowCreate !== void 0 && typeof options.allowCreate !== "boolean") throw new TypeError(`Expected allowCreate to be a boolean, but got ${typeof options.allowCreate}: ${String(options.allowCreate)}.`);
63
+ const rawOptions = options;
64
+ if ("mustExist" in options && rawOptions.mustExist !== void 0 && typeof rawOptions.mustExist !== "boolean") throw new TypeError(`Expected mustExist to be a boolean, but got ${typeof rawOptions.mustExist}: ${String(rawOptions.mustExist)}.`);
65
+ if ("mustNotExist" in options && rawOptions.mustNotExist !== void 0 && typeof rawOptions.mustNotExist !== "boolean") throw new TypeError(`Expected mustNotExist to be a boolean, but got ${typeof rawOptions.mustNotExist}: ${String(rawOptions.mustNotExist)}.`);
66
+ if (options.placeholder !== void 0 && typeof options.placeholder !== "string") throw new TypeError(`Expected placeholder to be a string, but got ${typeof options.placeholder}: ${String(options.placeholder)}.`);
56
67
  const mustExist = "mustExist" in options ? options.mustExist : false;
57
68
  const mustNotExist = "mustNotExist" in options ? options.mustNotExist : false;
58
69
  if (mustExist && mustNotExist) throw new TypeError("Options mustExist and mustNotExist are mutually exclusive.");
59
70
  return {
60
- $mode: "sync",
71
+ mode: "sync",
61
72
  metavar,
73
+ placeholder: options.placeholder ?? ".",
62
74
  parse(input) {
63
75
  if (input.trim() === "") return {
64
76
  success: false,
@@ -128,7 +140,7 @@ function path(options = {}) {
128
140
  pattern: prefix,
129
141
  type: type === "either" ? "any" : type,
130
142
  extensions,
131
- includeHidden: (0, node_path.basename)(prefix).startsWith("."),
143
+ includeHidden: (0, node_path.basename)(prefix).startsWith(".") && (0, node_path.basename)(prefix) !== "..",
132
144
  description: type === "directory" ? __optique_core_message.message`Directory` : type === "file" ? __optique_core_message.message`File` : __optique_core_message.message`File or directory`
133
145
  };
134
146
  }
@@ -82,6 +82,14 @@ interface PathOptionsBase {
82
82
  * start with a dot (e.g. `".json"`, `".yaml"`).
83
83
  */
84
84
  readonly extensions?: readonly string[];
85
+ /**
86
+ * A custom placeholder value used during deferred prompt resolution.
87
+ * Override the default `"."` when downstream `map()` transforms or
88
+ * path constraints require a specific path shape.
89
+ *
90
+ * @since 1.0.0
91
+ */
92
+ readonly placeholder?: string;
85
93
  /**
86
94
  * Custom error messages for path validation failures.
87
95
  * @since 0.5.0
@@ -153,8 +161,14 @@ type PathOptions = PathOptionsMustExist | PathOptionsMustNotExist | PathOptionsN
153
161
  * `"file"`, `"directory"`, or `"either"`.
154
162
  * @throws {TypeError} If any entry in {@link PathOptionsBase.extensions} does
155
163
  * not start with a dot (e.g., `"json"` instead of `".json"`).
164
+ * @throws {TypeError} If {@link PathOptionsMustExist.mustExist} is not a
165
+ * boolean.
166
+ * @throws {TypeError} If {@link PathOptionsMustNotExist.mustNotExist} is not a
167
+ * boolean.
168
+ * @throws {TypeError} If {@link PathOptionsBase.allowCreate} is not a boolean.
156
169
  * @throws {TypeError} If both {@link PathOptionsMustExist.mustExist} and
157
170
  * {@link PathOptionsMustNotExist.mustNotExist} are `true`.
171
+ * @throws {TypeError} If `placeholder` is not a string.
158
172
  *
159
173
  * @example
160
174
  * ```typescript
@@ -82,6 +82,14 @@ interface PathOptionsBase {
82
82
  * start with a dot (e.g. `".json"`, `".yaml"`).
83
83
  */
84
84
  readonly extensions?: readonly string[];
85
+ /**
86
+ * A custom placeholder value used during deferred prompt resolution.
87
+ * Override the default `"."` when downstream `map()` transforms or
88
+ * path constraints require a specific path shape.
89
+ *
90
+ * @since 1.0.0
91
+ */
92
+ readonly placeholder?: string;
85
93
  /**
86
94
  * Custom error messages for path validation failures.
87
95
  * @since 0.5.0
@@ -153,8 +161,14 @@ type PathOptions = PathOptionsMustExist | PathOptionsMustNotExist | PathOptionsN
153
161
  * `"file"`, `"directory"`, or `"either"`.
154
162
  * @throws {TypeError} If any entry in {@link PathOptionsBase.extensions} does
155
163
  * not start with a dot (e.g., `"json"` instead of `".json"`).
164
+ * @throws {TypeError} If {@link PathOptionsMustExist.mustExist} is not a
165
+ * boolean.
166
+ * @throws {TypeError} If {@link PathOptionsMustNotExist.mustNotExist} is not a
167
+ * boolean.
168
+ * @throws {TypeError} If {@link PathOptionsBase.allowCreate} is not a boolean.
156
169
  * @throws {TypeError} If both {@link PathOptionsMustExist.mustExist} and
157
170
  * {@link PathOptionsMustNotExist.mustNotExist} are `true`.
171
+ * @throws {TypeError} If `placeholder` is not a string.
158
172
  *
159
173
  * @example
160
174
  * ```typescript
@@ -17,8 +17,14 @@ import { existsSync, statSync } from "node:fs";
17
17
  * `"file"`, `"directory"`, or `"either"`.
18
18
  * @throws {TypeError} If any entry in {@link PathOptionsBase.extensions} does
19
19
  * not start with a dot (e.g., `"json"` instead of `".json"`).
20
+ * @throws {TypeError} If {@link PathOptionsMustExist.mustExist} is not a
21
+ * boolean.
22
+ * @throws {TypeError} If {@link PathOptionsMustNotExist.mustNotExist} is not a
23
+ * boolean.
24
+ * @throws {TypeError} If {@link PathOptionsBase.allowCreate} is not a boolean.
20
25
  * @throws {TypeError} If both {@link PathOptionsMustExist.mustExist} and
21
26
  * {@link PathOptionsMustNotExist.mustNotExist} are `true`.
27
+ * @throws {TypeError} If `placeholder` is not a string.
22
28
  *
23
29
  * @example
24
30
  * ```typescript
@@ -50,14 +56,20 @@ function path(options = {}) {
50
56
  ensureNonEmptyString(metavar);
51
57
  if (type !== "file" && type !== "directory" && type !== "either") throw new TypeError(`Unsupported path type: ${JSON.stringify(type)}. Expected "file", "directory", or "either".`);
52
58
  if (extensions) {
53
- for (const ext of extensions) if (!ext.startsWith(".")) throw new TypeError(`Each extension must start with a dot, got: ${JSON.stringify(ext)}`);
59
+ for (const ext of extensions) if (typeof ext !== "string" || !ext.startsWith(".")) throw new TypeError(`Each extension must start with a dot, got: ${JSON.stringify(ext)}`);
54
60
  }
61
+ if (options.allowCreate !== void 0 && typeof options.allowCreate !== "boolean") throw new TypeError(`Expected allowCreate to be a boolean, but got ${typeof options.allowCreate}: ${String(options.allowCreate)}.`);
62
+ const rawOptions = options;
63
+ if ("mustExist" in options && rawOptions.mustExist !== void 0 && typeof rawOptions.mustExist !== "boolean") throw new TypeError(`Expected mustExist to be a boolean, but got ${typeof rawOptions.mustExist}: ${String(rawOptions.mustExist)}.`);
64
+ if ("mustNotExist" in options && rawOptions.mustNotExist !== void 0 && typeof rawOptions.mustNotExist !== "boolean") throw new TypeError(`Expected mustNotExist to be a boolean, but got ${typeof rawOptions.mustNotExist}: ${String(rawOptions.mustNotExist)}.`);
65
+ if (options.placeholder !== void 0 && typeof options.placeholder !== "string") throw new TypeError(`Expected placeholder to be a string, but got ${typeof options.placeholder}: ${String(options.placeholder)}.`);
55
66
  const mustExist = "mustExist" in options ? options.mustExist : false;
56
67
  const mustNotExist = "mustNotExist" in options ? options.mustNotExist : false;
57
68
  if (mustExist && mustNotExist) throw new TypeError("Options mustExist and mustNotExist are mutually exclusive.");
58
69
  return {
59
- $mode: "sync",
70
+ mode: "sync",
60
71
  metavar,
72
+ placeholder: options.placeholder ?? ".",
61
73
  parse(input) {
62
74
  if (input.trim() === "") return {
63
75
  success: false,
@@ -127,7 +139,7 @@ function path(options = {}) {
127
139
  pattern: prefix,
128
140
  type: type === "either" ? "any" : type,
129
141
  extensions,
130
- includeHidden: basename(prefix).startsWith("."),
142
+ includeHidden: basename(prefix).startsWith(".") && basename(prefix) !== "..",
131
143
  description: type === "directory" ? message`Directory` : type === "file" ? message`File` : message`File or directory`
132
144
  };
133
145
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/run",
3
- "version": "1.0.0-dev.908+f60f037d",
3
+ "version": "1.0.0",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -70,7 +70,7 @@
70
70
  },
71
71
  "sideEffects": false,
72
72
  "dependencies": {
73
- "@optique/core": "1.0.0-dev.908+f60f037d"
73
+ "@optique/core": "1.0.0"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@types/node": "^20.19.9",