@optique/valibot 0.7.0-dev.144

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright 2025 Hong Minhee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,180 @@
1
+ @optique/valibot
2
+ ================
3
+
4
+ > [!WARNING]
5
+ > The API is stabilizing, but may change before the 1.0 release.
6
+
7
+ Valibot value parsers for Optique. This package provides seamless integration
8
+ between [Valibot] schemas and *@optique/core*, enabling powerful validation and
9
+ type-safe parsing of command-line arguments with minimal bundle size.
10
+
11
+ [Valibot]: https://valibot.dev/
12
+
13
+
14
+ Installation
15
+ ------------
16
+
17
+ ~~~~ bash
18
+ deno add jsr:@optique/valibot jsr:@optique/run jsr:@optique/core valibot
19
+ npm add @optique/valibot @optique/run @optique/core valibot
20
+ pnpm add @optique/valibot @optique/run @optique/core valibot
21
+ yarn add @optique/valibot @optique/run @optique/core valibot
22
+ bun add @optique/valibot @optique/run @optique/core valibot
23
+ ~~~~
24
+
25
+ This package supports Valibot versions 0.42.0 and above.
26
+
27
+
28
+ Quick example
29
+ -------------
30
+
31
+ The following example uses the `valibot()` value parser to validate an email
32
+ address.
33
+
34
+ ~~~~ typescript
35
+ import { run } from "@optique/run";
36
+ import { option } from "@optique/core/parser";
37
+ import { valibot } from "@optique/valibot";
38
+ import * as v from "valibot";
39
+
40
+ const cli = run({
41
+ email: option("--email", valibot(v.pipe(v.string(), v.email()))),
42
+ });
43
+
44
+ console.log(`Welcome, ${cli.email}!`);
45
+ ~~~~
46
+
47
+ Run it:
48
+
49
+ ~~~~ bash
50
+ $ node cli.js --email user@example.com
51
+ Welcome, user@example.com!
52
+
53
+ $ node cli.js --email invalid-email
54
+ Error: Invalid email
55
+ ~~~~
56
+
57
+
58
+ Common use cases
59
+ ----------------
60
+
61
+ ### Email validation
62
+
63
+ ~~~~ typescript
64
+ import { valibot } from "@optique/valibot";
65
+ import * as v from "valibot";
66
+
67
+ const email = option("--email", valibot(v.pipe(v.string(), v.email())));
68
+ ~~~~
69
+
70
+ ### URL validation
71
+
72
+ ~~~~ typescript
73
+ import { valibot } from "@optique/valibot";
74
+ import * as v from "valibot";
75
+
76
+ const url = option("--url", valibot(v.pipe(v.string(), v.url())));
77
+ ~~~~
78
+
79
+ ### Port numbers with range validation
80
+
81
+ > [!IMPORTANT]
82
+ > Always use explicit transformations with `v.pipe()` and `v.transform()` for
83
+ > non-string types, since CLI arguments are always strings.
84
+
85
+ ~~~~ typescript
86
+ import { valibot } from "@optique/valibot";
87
+ import * as v from "valibot";
88
+
89
+ const port = option("-p", "--port",
90
+ valibot(v.pipe(
91
+ v.string(),
92
+ v.transform(Number),
93
+ v.number(),
94
+ v.integer(),
95
+ v.minValue(1024),
96
+ v.maxValue(65535)
97
+ ))
98
+ );
99
+ ~~~~
100
+
101
+ ### Enum choices
102
+
103
+ ~~~~ typescript
104
+ import { valibot } from "@optique/valibot";
105
+ import * as v from "valibot";
106
+
107
+ const logLevel = option("--log-level",
108
+ valibot(v.picklist(["debug", "info", "warn", "error"]))
109
+ );
110
+ ~~~~
111
+
112
+ ### Date transformations
113
+
114
+ ~~~~ typescript
115
+ import { valibot } from "@optique/valibot";
116
+ import * as v from "valibot";
117
+
118
+ const startDate = argument(
119
+ valibot(v.pipe(v.string(), v.transform((s) => new Date(s))))
120
+ );
121
+ ~~~~
122
+
123
+
124
+ Custom error messages
125
+ ---------------------
126
+
127
+ You can customize error messages using the `errors` option:
128
+
129
+ ~~~~ typescript
130
+ import { valibot } from "@optique/valibot";
131
+ import { message } from "@optique/core/message";
132
+ import * as v from "valibot";
133
+
134
+ const email = option("--email", valibot(v.pipe(v.string(), v.email()), {
135
+ metavar: "EMAIL",
136
+ errors: {
137
+ valibotError: (issues, input) =>
138
+ message`Please provide a valid email address, got ${input}.`
139
+ }
140
+ }));
141
+ ~~~~
142
+
143
+
144
+ Important notes
145
+ ---------------
146
+
147
+ ### Always use explicit transformations for non-string types
148
+
149
+ CLI arguments are always strings. If you want to parse numbers, booleans,
150
+ or other types, you must use explicit `v.transform()`:
151
+
152
+ ~~~~ typescript
153
+ // ✅ Correct
154
+ const port = option("-p", valibot(v.pipe(v.string(), v.transform(Number))));
155
+
156
+ // ❌ Won't work (CLI arguments are always strings)
157
+ const port = option("-p", valibot(v.number()));
158
+ ~~~~
159
+
160
+ ### Async validations are not supported
161
+
162
+ Optique's `ValueParser.parse()` is synchronous, so async Valibot features like
163
+ async validations cannot be supported:
164
+
165
+ ~~~~ typescript
166
+ // ❌ Not supported
167
+ const email = option("--email",
168
+ valibot(v.pipeAsync(v.string(), v.checkAsync(async (val) => await checkDB(val))))
169
+ );
170
+ ~~~~
171
+
172
+ If you need async validation, perform it after parsing the CLI arguments.
173
+
174
+
175
+ For more resources
176
+ ------------------
177
+
178
+ - [Optique documentation](https://optique.dev/)
179
+ - [Valibot documentation](https://valibot.dev/)
180
+ - [Examples directory](/examples/)
package/dist/index.cjs ADDED
@@ -0,0 +1,198 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+
23
+ //#endregion
24
+ const __optique_core_message = __toESM(require("@optique/core/message"));
25
+ const valibot = __toESM(require("valibot"));
26
+
27
+ //#region src/index.ts
28
+ /**
29
+ * Infers an appropriate metavar string from a Valibot schema.
30
+ *
31
+ * This function analyzes the Valibot schema's internal structure to determine
32
+ * the most appropriate metavar string for help text generation.
33
+ *
34
+ * @param schema A Valibot schema to analyze.
35
+ * @returns The inferred metavar string.
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * inferMetavar(v.string()) // "STRING"
40
+ * inferMetavar(v.email()) // "EMAIL"
41
+ * inferMetavar(v.number()) // "NUMBER"
42
+ * inferMetavar(v.integer()) // "INTEGER"
43
+ * inferMetavar(v.picklist(["a"])) // "CHOICE"
44
+ * ```
45
+ *
46
+ * @since 0.7.0
47
+ */
48
+ function inferMetavar(schema) {
49
+ const schemaType = schema.type;
50
+ if (!schemaType) return "VALUE";
51
+ if (schemaType === "string") {
52
+ const pipeline = schema.pipe;
53
+ if (Array.isArray(pipeline)) for (const action of pipeline) {
54
+ const actionType = action.type;
55
+ if (actionType === "transform") return "VALUE";
56
+ if (actionType === "email") return "EMAIL";
57
+ if (actionType === "url") return "URL";
58
+ if (actionType === "uuid") return "UUID";
59
+ if (actionType === "ulid") return "ULID";
60
+ if (actionType === "cuid2") return "CUID2";
61
+ if (actionType === "iso_date") return "DATE";
62
+ if (actionType === "iso_date_time") return "DATETIME";
63
+ if (actionType === "iso_time") return "TIME";
64
+ if (actionType === "iso_timestamp") return "TIMESTAMP";
65
+ if (actionType === "ipv4") return "IPV4";
66
+ if (actionType === "ipv6") return "IPV6";
67
+ if (actionType === "ip") return "IP";
68
+ if (actionType === "emoji") return "EMOJI";
69
+ if (actionType === "base64") return "BASE64";
70
+ }
71
+ return "STRING";
72
+ }
73
+ if (schemaType === "number") {
74
+ const pipeline = schema.pipe;
75
+ if (Array.isArray(pipeline)) for (const action of pipeline) {
76
+ const actionType = action.type;
77
+ if (actionType === "transform") return "VALUE";
78
+ if (actionType === "integer") return "INTEGER";
79
+ }
80
+ return "NUMBER";
81
+ }
82
+ if (schemaType === "boolean") return "BOOLEAN";
83
+ if (schemaType === "date") return "DATE";
84
+ if (schemaType === "picklist") return "CHOICE";
85
+ if (schemaType === "literal") return "VALUE";
86
+ if (schemaType === "union" || schemaType === "variant") return "VALUE";
87
+ if (schemaType === "optional" || schemaType === "nullable" || schemaType === "nullish") {
88
+ const wrapped = schema.wrapped;
89
+ if (wrapped) return inferMetavar(wrapped);
90
+ }
91
+ return "VALUE";
92
+ }
93
+ /**
94
+ * Creates a value parser from a Valibot schema.
95
+ *
96
+ * This parser validates CLI argument strings using Valibot schemas, enabling
97
+ * powerful validation and transformation capabilities with minimal bundle size
98
+ * for command-line interfaces.
99
+ *
100
+ * The metavar is automatically inferred from the schema type unless explicitly
101
+ * provided in options. For example:
102
+ * - `v.string()` → `"STRING"`
103
+ * - `v.pipe(v.string(), v.email())` → `"EMAIL"`
104
+ * - `v.number()` → `"NUMBER"`
105
+ * - `v.pipe(v.number(), v.integer())` → `"INTEGER"`
106
+ * - `v.picklist([...])` → `"CHOICE"`
107
+ *
108
+ * @template T The output type of the Valibot schema.
109
+ * @param schema A Valibot schema to validate input against.
110
+ * @param options Optional configuration for the parser.
111
+ * @returns A value parser that validates inputs using the provided schema.
112
+ *
113
+ * @example Basic string validation
114
+ * ```typescript
115
+ * import * as v from "valibot";
116
+ * import { valibot } from "@optique/valibot";
117
+ * import { option } from "@optique/core/primitives";
118
+ *
119
+ * const email = option("--email", valibot(v.pipe(v.string(), v.email())));
120
+ * ```
121
+ *
122
+ * @example Number validation with pipeline
123
+ * ```typescript
124
+ * import * as v from "valibot";
125
+ * import { valibot } from "@optique/valibot";
126
+ * import { option } from "@optique/core/primitives";
127
+ *
128
+ * // Use v.pipe with v.transform for non-string types since CLI args are always strings
129
+ * const port = option("-p", "--port",
130
+ * valibot(v.pipe(
131
+ * v.string(),
132
+ * v.transform(Number),
133
+ * v.number(),
134
+ * v.integer(),
135
+ * v.minValue(1024),
136
+ * v.maxValue(65535)
137
+ * ))
138
+ * );
139
+ * ```
140
+ *
141
+ * @example Picklist validation
142
+ * ```typescript
143
+ * import * as v from "valibot";
144
+ * import { valibot } from "@optique/valibot";
145
+ * import { option } from "@optique/core/primitives";
146
+ *
147
+ * const logLevel = option("--log-level",
148
+ * valibot(v.picklist(["debug", "info", "warn", "error"]))
149
+ * );
150
+ * ```
151
+ *
152
+ * @example Custom error messages
153
+ * ```typescript
154
+ * import * as v from "valibot";
155
+ * import { valibot } from "@optique/valibot";
156
+ * import { message } from "@optique/core/message";
157
+ * import { option } from "@optique/core/primitives";
158
+ *
159
+ * const email = option("--email",
160
+ * valibot(v.pipe(v.string(), v.email()), {
161
+ * metavar: "EMAIL",
162
+ * errors: {
163
+ * valibotError: (issues, input) =>
164
+ * message`Please provide a valid email address, got ${input}.`
165
+ * }
166
+ * })
167
+ * );
168
+ * ```
169
+ *
170
+ * @since 0.7.0
171
+ */
172
+ function valibot$1(schema, options = {}) {
173
+ return {
174
+ metavar: options.metavar ?? inferMetavar(schema),
175
+ parse(input) {
176
+ const result = (0, valibot.safeParse)(schema, input);
177
+ if (result.success) return {
178
+ success: true,
179
+ value: result.output
180
+ };
181
+ if (options.errors?.valibotError) return {
182
+ success: false,
183
+ error: typeof options.errors.valibotError === "function" ? options.errors.valibotError(result.issues, input) : options.errors.valibotError
184
+ };
185
+ const firstIssue = result.issues[0];
186
+ return {
187
+ success: false,
188
+ error: __optique_core_message.message`${firstIssue?.message ?? "Validation failed"}`
189
+ };
190
+ },
191
+ format(value) {
192
+ return String(value);
193
+ }
194
+ };
195
+ }
196
+
197
+ //#endregion
198
+ exports.valibot = valibot$1;
@@ -0,0 +1,113 @@
1
+ import { ValueParser } from "@optique/core/valueparser";
2
+ import { Message } from "@optique/core/message";
3
+ import * as v from "valibot";
4
+
5
+ //#region src/index.d.ts
6
+
7
+ /**
8
+ * Options for creating a Valibot value parser.
9
+ * @since 0.7.0
10
+ */
11
+ interface ValibotParserOptions {
12
+ /**
13
+ * The metavariable name for this parser. This is used in help messages to
14
+ * indicate what kind of value this parser expects. Usually a single
15
+ * word in uppercase, like `VALUE` or `SCHEMA`.
16
+ * @default `"VALUE"`
17
+ */
18
+ readonly metavar?: string;
19
+ /**
20
+ * Custom error messages for Valibot validation failures.
21
+ */
22
+ readonly errors?: {
23
+ /**
24
+ * Custom error message when input fails Valibot validation.
25
+ * Can be a static message or a function that receives the Valibot issues
26
+ * and input string.
27
+ * @since 0.7.0
28
+ */
29
+ valibotError?: Message | ((issues: v.InferIssue<v.BaseSchema<unknown, unknown, v.BaseIssue<unknown>>>[], input: string) => Message);
30
+ };
31
+ }
32
+ /**
33
+ * Creates a value parser from a Valibot schema.
34
+ *
35
+ * This parser validates CLI argument strings using Valibot schemas, enabling
36
+ * powerful validation and transformation capabilities with minimal bundle size
37
+ * for command-line interfaces.
38
+ *
39
+ * The metavar is automatically inferred from the schema type unless explicitly
40
+ * provided in options. For example:
41
+ * - `v.string()` → `"STRING"`
42
+ * - `v.pipe(v.string(), v.email())` → `"EMAIL"`
43
+ * - `v.number()` → `"NUMBER"`
44
+ * - `v.pipe(v.number(), v.integer())` → `"INTEGER"`
45
+ * - `v.picklist([...])` → `"CHOICE"`
46
+ *
47
+ * @template T The output type of the Valibot schema.
48
+ * @param schema A Valibot schema to validate input against.
49
+ * @param options Optional configuration for the parser.
50
+ * @returns A value parser that validates inputs using the provided schema.
51
+ *
52
+ * @example Basic string validation
53
+ * ```typescript
54
+ * import * as v from "valibot";
55
+ * import { valibot } from "@optique/valibot";
56
+ * import { option } from "@optique/core/primitives";
57
+ *
58
+ * const email = option("--email", valibot(v.pipe(v.string(), v.email())));
59
+ * ```
60
+ *
61
+ * @example Number validation with pipeline
62
+ * ```typescript
63
+ * import * as v from "valibot";
64
+ * import { valibot } from "@optique/valibot";
65
+ * import { option } from "@optique/core/primitives";
66
+ *
67
+ * // Use v.pipe with v.transform for non-string types since CLI args are always strings
68
+ * const port = option("-p", "--port",
69
+ * valibot(v.pipe(
70
+ * v.string(),
71
+ * v.transform(Number),
72
+ * v.number(),
73
+ * v.integer(),
74
+ * v.minValue(1024),
75
+ * v.maxValue(65535)
76
+ * ))
77
+ * );
78
+ * ```
79
+ *
80
+ * @example Picklist validation
81
+ * ```typescript
82
+ * import * as v from "valibot";
83
+ * import { valibot } from "@optique/valibot";
84
+ * import { option } from "@optique/core/primitives";
85
+ *
86
+ * const logLevel = option("--log-level",
87
+ * valibot(v.picklist(["debug", "info", "warn", "error"]))
88
+ * );
89
+ * ```
90
+ *
91
+ * @example Custom error messages
92
+ * ```typescript
93
+ * import * as v from "valibot";
94
+ * import { valibot } from "@optique/valibot";
95
+ * import { message } from "@optique/core/message";
96
+ * import { option } from "@optique/core/primitives";
97
+ *
98
+ * const email = option("--email",
99
+ * valibot(v.pipe(v.string(), v.email()), {
100
+ * metavar: "EMAIL",
101
+ * errors: {
102
+ * valibotError: (issues, input) =>
103
+ * message`Please provide a valid email address, got ${input}.`
104
+ * }
105
+ * })
106
+ * );
107
+ * ```
108
+ *
109
+ * @since 0.7.0
110
+ */
111
+ declare function valibot<T>(schema: v.BaseSchema<unknown, T, v.BaseIssue<unknown>>, options?: ValibotParserOptions): ValueParser<T>;
112
+ //#endregion
113
+ export { ValibotParserOptions, valibot };
@@ -0,0 +1,113 @@
1
+ import { Message } from "@optique/core/message";
2
+ import * as v from "valibot";
3
+ import { ValueParser } from "@optique/core/valueparser";
4
+
5
+ //#region src/index.d.ts
6
+
7
+ /**
8
+ * Options for creating a Valibot value parser.
9
+ * @since 0.7.0
10
+ */
11
+ interface ValibotParserOptions {
12
+ /**
13
+ * The metavariable name for this parser. This is used in help messages to
14
+ * indicate what kind of value this parser expects. Usually a single
15
+ * word in uppercase, like `VALUE` or `SCHEMA`.
16
+ * @default `"VALUE"`
17
+ */
18
+ readonly metavar?: string;
19
+ /**
20
+ * Custom error messages for Valibot validation failures.
21
+ */
22
+ readonly errors?: {
23
+ /**
24
+ * Custom error message when input fails Valibot validation.
25
+ * Can be a static message or a function that receives the Valibot issues
26
+ * and input string.
27
+ * @since 0.7.0
28
+ */
29
+ valibotError?: Message | ((issues: v.InferIssue<v.BaseSchema<unknown, unknown, v.BaseIssue<unknown>>>[], input: string) => Message);
30
+ };
31
+ }
32
+ /**
33
+ * Creates a value parser from a Valibot schema.
34
+ *
35
+ * This parser validates CLI argument strings using Valibot schemas, enabling
36
+ * powerful validation and transformation capabilities with minimal bundle size
37
+ * for command-line interfaces.
38
+ *
39
+ * The metavar is automatically inferred from the schema type unless explicitly
40
+ * provided in options. For example:
41
+ * - `v.string()` → `"STRING"`
42
+ * - `v.pipe(v.string(), v.email())` → `"EMAIL"`
43
+ * - `v.number()` → `"NUMBER"`
44
+ * - `v.pipe(v.number(), v.integer())` → `"INTEGER"`
45
+ * - `v.picklist([...])` → `"CHOICE"`
46
+ *
47
+ * @template T The output type of the Valibot schema.
48
+ * @param schema A Valibot schema to validate input against.
49
+ * @param options Optional configuration for the parser.
50
+ * @returns A value parser that validates inputs using the provided schema.
51
+ *
52
+ * @example Basic string validation
53
+ * ```typescript
54
+ * import * as v from "valibot";
55
+ * import { valibot } from "@optique/valibot";
56
+ * import { option } from "@optique/core/primitives";
57
+ *
58
+ * const email = option("--email", valibot(v.pipe(v.string(), v.email())));
59
+ * ```
60
+ *
61
+ * @example Number validation with pipeline
62
+ * ```typescript
63
+ * import * as v from "valibot";
64
+ * import { valibot } from "@optique/valibot";
65
+ * import { option } from "@optique/core/primitives";
66
+ *
67
+ * // Use v.pipe with v.transform for non-string types since CLI args are always strings
68
+ * const port = option("-p", "--port",
69
+ * valibot(v.pipe(
70
+ * v.string(),
71
+ * v.transform(Number),
72
+ * v.number(),
73
+ * v.integer(),
74
+ * v.minValue(1024),
75
+ * v.maxValue(65535)
76
+ * ))
77
+ * );
78
+ * ```
79
+ *
80
+ * @example Picklist validation
81
+ * ```typescript
82
+ * import * as v from "valibot";
83
+ * import { valibot } from "@optique/valibot";
84
+ * import { option } from "@optique/core/primitives";
85
+ *
86
+ * const logLevel = option("--log-level",
87
+ * valibot(v.picklist(["debug", "info", "warn", "error"]))
88
+ * );
89
+ * ```
90
+ *
91
+ * @example Custom error messages
92
+ * ```typescript
93
+ * import * as v from "valibot";
94
+ * import { valibot } from "@optique/valibot";
95
+ * import { message } from "@optique/core/message";
96
+ * import { option } from "@optique/core/primitives";
97
+ *
98
+ * const email = option("--email",
99
+ * valibot(v.pipe(v.string(), v.email()), {
100
+ * metavar: "EMAIL",
101
+ * errors: {
102
+ * valibotError: (issues, input) =>
103
+ * message`Please provide a valid email address, got ${input}.`
104
+ * }
105
+ * })
106
+ * );
107
+ * ```
108
+ *
109
+ * @since 0.7.0
110
+ */
111
+ declare function valibot<T>(schema: v.BaseSchema<unknown, T, v.BaseIssue<unknown>>, options?: ValibotParserOptions): ValueParser<T>;
112
+ //#endregion
113
+ export { ValibotParserOptions, valibot };
package/dist/index.js ADDED
@@ -0,0 +1,175 @@
1
+ import { message } from "@optique/core/message";
2
+ import { safeParse } from "valibot";
3
+
4
+ //#region src/index.ts
5
+ /**
6
+ * Infers an appropriate metavar string from a Valibot schema.
7
+ *
8
+ * This function analyzes the Valibot schema's internal structure to determine
9
+ * the most appropriate metavar string for help text generation.
10
+ *
11
+ * @param schema A Valibot schema to analyze.
12
+ * @returns The inferred metavar string.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * inferMetavar(v.string()) // "STRING"
17
+ * inferMetavar(v.email()) // "EMAIL"
18
+ * inferMetavar(v.number()) // "NUMBER"
19
+ * inferMetavar(v.integer()) // "INTEGER"
20
+ * inferMetavar(v.picklist(["a"])) // "CHOICE"
21
+ * ```
22
+ *
23
+ * @since 0.7.0
24
+ */
25
+ function inferMetavar(schema) {
26
+ const schemaType = schema.type;
27
+ if (!schemaType) return "VALUE";
28
+ if (schemaType === "string") {
29
+ const pipeline = schema.pipe;
30
+ if (Array.isArray(pipeline)) for (const action of pipeline) {
31
+ const actionType = action.type;
32
+ if (actionType === "transform") return "VALUE";
33
+ if (actionType === "email") return "EMAIL";
34
+ if (actionType === "url") return "URL";
35
+ if (actionType === "uuid") return "UUID";
36
+ if (actionType === "ulid") return "ULID";
37
+ if (actionType === "cuid2") return "CUID2";
38
+ if (actionType === "iso_date") return "DATE";
39
+ if (actionType === "iso_date_time") return "DATETIME";
40
+ if (actionType === "iso_time") return "TIME";
41
+ if (actionType === "iso_timestamp") return "TIMESTAMP";
42
+ if (actionType === "ipv4") return "IPV4";
43
+ if (actionType === "ipv6") return "IPV6";
44
+ if (actionType === "ip") return "IP";
45
+ if (actionType === "emoji") return "EMOJI";
46
+ if (actionType === "base64") return "BASE64";
47
+ }
48
+ return "STRING";
49
+ }
50
+ if (schemaType === "number") {
51
+ const pipeline = schema.pipe;
52
+ if (Array.isArray(pipeline)) for (const action of pipeline) {
53
+ const actionType = action.type;
54
+ if (actionType === "transform") return "VALUE";
55
+ if (actionType === "integer") return "INTEGER";
56
+ }
57
+ return "NUMBER";
58
+ }
59
+ if (schemaType === "boolean") return "BOOLEAN";
60
+ if (schemaType === "date") return "DATE";
61
+ if (schemaType === "picklist") return "CHOICE";
62
+ if (schemaType === "literal") return "VALUE";
63
+ if (schemaType === "union" || schemaType === "variant") return "VALUE";
64
+ if (schemaType === "optional" || schemaType === "nullable" || schemaType === "nullish") {
65
+ const wrapped = schema.wrapped;
66
+ if (wrapped) return inferMetavar(wrapped);
67
+ }
68
+ return "VALUE";
69
+ }
70
+ /**
71
+ * Creates a value parser from a Valibot schema.
72
+ *
73
+ * This parser validates CLI argument strings using Valibot schemas, enabling
74
+ * powerful validation and transformation capabilities with minimal bundle size
75
+ * for command-line interfaces.
76
+ *
77
+ * The metavar is automatically inferred from the schema type unless explicitly
78
+ * provided in options. For example:
79
+ * - `v.string()` → `"STRING"`
80
+ * - `v.pipe(v.string(), v.email())` → `"EMAIL"`
81
+ * - `v.number()` → `"NUMBER"`
82
+ * - `v.pipe(v.number(), v.integer())` → `"INTEGER"`
83
+ * - `v.picklist([...])` → `"CHOICE"`
84
+ *
85
+ * @template T The output type of the Valibot schema.
86
+ * @param schema A Valibot schema to validate input against.
87
+ * @param options Optional configuration for the parser.
88
+ * @returns A value parser that validates inputs using the provided schema.
89
+ *
90
+ * @example Basic string validation
91
+ * ```typescript
92
+ * import * as v from "valibot";
93
+ * import { valibot } from "@optique/valibot";
94
+ * import { option } from "@optique/core/primitives";
95
+ *
96
+ * const email = option("--email", valibot(v.pipe(v.string(), v.email())));
97
+ * ```
98
+ *
99
+ * @example Number validation with pipeline
100
+ * ```typescript
101
+ * import * as v from "valibot";
102
+ * import { valibot } from "@optique/valibot";
103
+ * import { option } from "@optique/core/primitives";
104
+ *
105
+ * // Use v.pipe with v.transform for non-string types since CLI args are always strings
106
+ * const port = option("-p", "--port",
107
+ * valibot(v.pipe(
108
+ * v.string(),
109
+ * v.transform(Number),
110
+ * v.number(),
111
+ * v.integer(),
112
+ * v.minValue(1024),
113
+ * v.maxValue(65535)
114
+ * ))
115
+ * );
116
+ * ```
117
+ *
118
+ * @example Picklist validation
119
+ * ```typescript
120
+ * import * as v from "valibot";
121
+ * import { valibot } from "@optique/valibot";
122
+ * import { option } from "@optique/core/primitives";
123
+ *
124
+ * const logLevel = option("--log-level",
125
+ * valibot(v.picklist(["debug", "info", "warn", "error"]))
126
+ * );
127
+ * ```
128
+ *
129
+ * @example Custom error messages
130
+ * ```typescript
131
+ * import * as v from "valibot";
132
+ * import { valibot } from "@optique/valibot";
133
+ * import { message } from "@optique/core/message";
134
+ * import { option } from "@optique/core/primitives";
135
+ *
136
+ * const email = option("--email",
137
+ * valibot(v.pipe(v.string(), v.email()), {
138
+ * metavar: "EMAIL",
139
+ * errors: {
140
+ * valibotError: (issues, input) =>
141
+ * message`Please provide a valid email address, got ${input}.`
142
+ * }
143
+ * })
144
+ * );
145
+ * ```
146
+ *
147
+ * @since 0.7.0
148
+ */
149
+ function valibot(schema, options = {}) {
150
+ return {
151
+ metavar: options.metavar ?? inferMetavar(schema),
152
+ parse(input) {
153
+ const result = safeParse(schema, input);
154
+ if (result.success) return {
155
+ success: true,
156
+ value: result.output
157
+ };
158
+ if (options.errors?.valibotError) return {
159
+ success: false,
160
+ error: typeof options.errors.valibotError === "function" ? options.errors.valibotError(result.issues, input) : options.errors.valibotError
161
+ };
162
+ const firstIssue = result.issues[0];
163
+ return {
164
+ success: false,
165
+ error: message`${firstIssue?.message ?? "Validation failed"}`
166
+ };
167
+ },
168
+ format(value) {
169
+ return String(value);
170
+ }
171
+ };
172
+ }
173
+
174
+ //#endregion
175
+ export { valibot };
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@optique/valibot",
3
+ "version": "0.7.0-dev.144+da70df70",
4
+ "description": "Valibot value parsers for Optique",
5
+ "keywords": [
6
+ "CLI",
7
+ "command-line",
8
+ "commandline",
9
+ "parser",
10
+ "valibot",
11
+ "validation"
12
+ ],
13
+ "license": "MIT",
14
+ "author": {
15
+ "name": "Hong Minhee",
16
+ "email": "hong@minhee.org",
17
+ "url": "https://hongminhee.org/"
18
+ },
19
+ "homepage": "https://optique.dev/",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/dahlia/optique.git",
23
+ "directory": "packages/valibot/"
24
+ },
25
+ "bugs": {
26
+ "url": "https://github.com/dahlia/optique/issues"
27
+ },
28
+ "funding": [
29
+ "https://github.com/sponsors/dahlia"
30
+ ],
31
+ "engines": {
32
+ "node": ">=20.0.0",
33
+ "bun": ">=1.2.0",
34
+ "deno": ">=2.3.0"
35
+ },
36
+ "files": [
37
+ "dist/",
38
+ "package.json",
39
+ "README.md"
40
+ ],
41
+ "type": "module",
42
+ "module": "./dist/index.js",
43
+ "main": "./dist/index.cjs",
44
+ "types": "./dist/index.d.ts",
45
+ "exports": {
46
+ ".": {
47
+ "types": {
48
+ "import": "./dist/index.d.ts",
49
+ "require": "./dist/index.d.cts"
50
+ },
51
+ "import": "./dist/index.js",
52
+ "require": "./dist/index.cjs"
53
+ }
54
+ },
55
+ "sideEffects": false,
56
+ "peerDependencies": {
57
+ "valibot": "^0.42.0"
58
+ },
59
+ "dependencies": {
60
+ "@optique/core": ""
61
+ },
62
+ "devDependencies": {
63
+ "@types/node": "^20.19.9",
64
+ "tsdown": "^0.13.0",
65
+ "typescript": "^5.8.3",
66
+ "valibot": "^0.42.0"
67
+ },
68
+ "scripts": {
69
+ "build": "tsdown",
70
+ "prepublish": "tsdown",
71
+ "test": "tsdown && node --experimental-transform-types --test",
72
+ "test:bun": "tsdown && bun test",
73
+ "test:deno": "deno test",
74
+ "test-all": "tsdown && node --experimental-transform-types --test && bun test && deno test"
75
+ }
76
+ }