@optique/run 1.0.0-dev.921 → 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,6 +32,8 @@ 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);
package/dist/run.d.cts CHANGED
@@ -332,7 +332,9 @@ type AcceptExactOptionalRunOptions<TOptions> = [TOptions] extends [RunOptions |
332
332
  * // myapp --format=<TAB> # Use completion
333
333
  * ```
334
334
  *
335
- * @since 0.11.0 Added support for {@link Program} objects.
335
+ * Supports {@link Program} objects.
336
+ *
337
+ * @since 1.0.0
336
338
  */
337
339
  declare function run<T extends Parser<Mode, unknown, unknown>, const TContexts extends NonEmptySourceContexts>(parser: T, options: RunOptions & {
338
340
  readonly contexts: TContexts;
@@ -367,6 +369,8 @@ declare function run<T extends Parser<Mode, unknown, unknown>>(parser: T, option
367
369
  * @param parser The synchronous command-line parser to execute.
368
370
  * @param options Configuration options for customizing behavior.
369
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.
370
374
  * @since 0.9.0
371
375
  */
372
376
  declare function runSync<T extends Parser<"sync", unknown, unknown>, TContexts extends readonly SourceContext<unknown>[]>(parser: T, options: RunOptions & {
package/dist/run.d.ts CHANGED
@@ -332,7 +332,9 @@ type AcceptExactOptionalRunOptions<TOptions> = [TOptions] extends [RunOptions |
332
332
  * // myapp --format=<TAB> # Use completion
333
333
  * ```
334
334
  *
335
- * @since 0.11.0 Added support for {@link Program} objects.
335
+ * Supports {@link Program} objects.
336
+ *
337
+ * @since 1.0.0
336
338
  */
337
339
  declare function run<T extends Parser<Mode, unknown, unknown>, const TContexts extends NonEmptySourceContexts>(parser: T, options: RunOptions & {
338
340
  readonly contexts: TContexts;
@@ -367,6 +369,8 @@ declare function run<T extends Parser<Mode, unknown, unknown>>(parser: T, option
367
369
  * @param parser The synchronous command-line parser to execute.
368
370
  * @param options Configuration options for customizing behavior.
369
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.
370
374
  * @since 0.9.0
371
375
  */
372
376
  declare function runSync<T extends Parser<"sync", unknown, unknown>, TContexts extends readonly SourceContext<unknown>[]>(parser: T, options: RunOptions & {
package/dist/run.js CHANGED
@@ -31,6 +31,8 @@ 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);
@@ -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.921+754748bd",
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.921+754748bd"
73
+ "@optique/core": "1.0.0"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@types/node": "^20.19.9",