@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 +20 -0
- package/README.md +180 -0
- package/dist/index.cjs +198 -0
- package/dist/index.d.cts +113 -0
- package/dist/index.d.ts +113 -0
- package/dist/index.js +175 -0
- package/package.json +76 -0
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;
|
package/dist/index.d.cts
ADDED
|
@@ -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 };
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|