@gunshi/combinators 0.28.2

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/lib/index.js ADDED
@@ -0,0 +1,503 @@
1
+ //#region ../../node_modules/.pnpm/args-tokens@0.24.1/node_modules/args-tokens/lib/combinators.js
2
+ /**
3
+ * Create a string argument schema with optional validation.
4
+ *
5
+ * @param opts - Validation options.
6
+ * @returns A combinator schema that resolves to string.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const args = {
11
+ * name: string({ minLength: 1, maxLength: 50 })
12
+ * }
13
+ * ```
14
+ *
15
+ * @experimental
16
+ */
17
+ function string(opts) {
18
+ return {
19
+ type: "string",
20
+ metavar: "string",
21
+ ...opts?.description != null ? { description: opts.description } : {},
22
+ ...opts?.short != null ? { short: opts.short } : {},
23
+ ...opts?.required != null ? { required: opts.required } : {},
24
+ parse(value) {
25
+ if (opts?.minLength != null && value.length < opts.minLength) throw new RangeError(`String must be at least ${opts.minLength} characters`);
26
+ if (opts?.maxLength != null && value.length > opts.maxLength) throw new RangeError(`String must be at most ${opts.maxLength} characters`);
27
+ if (opts?.pattern != null && !opts.pattern.test(value)) throw new Error(`String must match pattern ${opts.pattern}`);
28
+ return value;
29
+ }
30
+ };
31
+ }
32
+ /**
33
+ * Create a number argument schema with optional range validation.
34
+ *
35
+ * Accepts any numeric value (integer or float).
36
+ *
37
+ * @param opts - Range options.
38
+ * @returns A combinator schema that resolves to number.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const args = {
43
+ * timeout: number({ min: 0, max: 30000 })
44
+ * }
45
+ * ```
46
+ *
47
+ * @experimental
48
+ */
49
+ function number(opts) {
50
+ return {
51
+ type: "number",
52
+ metavar: "number",
53
+ ...opts?.description != null ? { description: opts.description } : {},
54
+ ...opts?.short != null ? { short: opts.short } : {},
55
+ ...opts?.required != null ? { required: opts.required } : {},
56
+ parse(value) {
57
+ const n = Number(value);
58
+ if (value.trim() === "" || isNaN(n)) throw new TypeError(`Expected a number, got '${value}'`);
59
+ if (opts?.min != null && n < opts.min) throw new RangeError(`Number must be >= ${opts.min}, got ${n}`);
60
+ if (opts?.max != null && n > opts.max) throw new RangeError(`Number must be <= ${opts.max}, got ${n}`);
61
+ return n;
62
+ }
63
+ };
64
+ }
65
+ /**
66
+ * Create an integer argument schema with optional range validation.
67
+ *
68
+ * Only accepts integer values (no decimals).
69
+ *
70
+ * @param opts - Range options.
71
+ * @returns A combinator schema that resolves to number (integer).
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * const args = {
76
+ * retries: integer({ min: 0, max: 10 })
77
+ * }
78
+ * ```
79
+ *
80
+ * @experimental
81
+ */
82
+ function integer(opts) {
83
+ return {
84
+ type: "custom",
85
+ metavar: "integer",
86
+ ...opts?.description != null ? { description: opts.description } : {},
87
+ ...opts?.short != null ? { short: opts.short } : {},
88
+ ...opts?.required != null ? { required: opts.required } : {},
89
+ parse(value) {
90
+ if (!/^-?\d+$/.test(value)) throw new TypeError(`Expected an integer, got '${value}'`);
91
+ const n = Number.parseInt(value, 10);
92
+ if (isNaN(n)) throw new TypeError(`Expected an integer, got '${value}'`);
93
+ if (opts?.min != null && n < opts.min) throw new RangeError(`Integer must be >= ${opts.min}, got ${n}`);
94
+ if (opts?.max != null && n > opts.max) throw new RangeError(`Integer must be <= ${opts.max}, got ${n}`);
95
+ return n;
96
+ }
97
+ };
98
+ }
99
+ /**
100
+ * Create a floating-point argument schema with optional range validation.
101
+ *
102
+ * Rejects `NaN` and `Infinity` values.
103
+ *
104
+ * @param opts - Range options.
105
+ * @returns A combinator schema that resolves to number (float).
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * const args = {
110
+ * ratio: float({ min: 0, max: 1 })
111
+ * }
112
+ * ```
113
+ *
114
+ * @experimental
115
+ */
116
+ function float(opts) {
117
+ return {
118
+ type: "custom",
119
+ metavar: "float",
120
+ ...opts?.description != null ? { description: opts.description } : {},
121
+ ...opts?.short != null ? { short: opts.short } : {},
122
+ ...opts?.required != null ? { required: opts.required } : {},
123
+ parse(value) {
124
+ const trimmed = value.trim();
125
+ if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i.test(trimmed)) throw new TypeError(`Expected a finite float, got '${value}'`);
126
+ const n = Number.parseFloat(trimmed);
127
+ if (isNaN(n) || !isFinite(n)) throw new TypeError(`Expected a finite float, got '${value}'`);
128
+ if (opts?.min != null && n < opts.min) throw new RangeError(`Float must be >= ${opts.min}, got ${n}`);
129
+ if (opts?.max != null && n > opts.max) throw new RangeError(`Float must be <= ${opts.max}, got ${n}`);
130
+ return n;
131
+ }
132
+ };
133
+ }
134
+ /**
135
+ * Create a boolean argument schema.
136
+ *
137
+ * Boolean arguments are existence-based. The resolver passes `"true"` or `"false"`
138
+ * to the parse function based on the presence or negation of the flag.
139
+ *
140
+ * @param opts - Boolean options.
141
+ * @returns A combinator schema for boolean flags.
142
+ *
143
+ * @example
144
+ * ```ts
145
+ * const args = {
146
+ * color: boolean({ negatable: true })
147
+ * }
148
+ * // Usage: --color (true), --no-color (false)
149
+ * ```
150
+ *
151
+ * @experimental
152
+ */
153
+ function boolean(opts) {
154
+ return {
155
+ type: "boolean",
156
+ ...opts?.negatable != null ? { negatable: opts.negatable } : {},
157
+ metavar: "boolean",
158
+ ...opts?.description != null ? { description: opts.description } : {},
159
+ ...opts?.short != null ? { short: opts.short } : {},
160
+ ...opts?.required != null ? { required: opts.required } : {},
161
+ parse(value) {
162
+ return value === "true";
163
+ }
164
+ };
165
+ }
166
+ function positional(parser) {
167
+ if (parser && "parse" in parser) return {
168
+ type: "positional",
169
+ parse: parser.parse,
170
+ metavar: parser.metavar,
171
+ ...parser.description != null ? { description: parser.description } : {},
172
+ ...parser.required != null ? { required: parser.required } : {}
173
+ };
174
+ const opts = parser;
175
+ return {
176
+ type: "positional",
177
+ ...opts?.description != null ? { description: opts.description } : {},
178
+ ...opts?.short != null ? { short: opts.short } : {},
179
+ ...opts?.required != null ? { required: opts.required } : {}
180
+ };
181
+ }
182
+ /**
183
+ * Create an enum-like argument schema with literal type inference.
184
+ *
185
+ * Uses `const T` generic to infer literal union types from the values array.
186
+ *
187
+ * @typeParam T - The readonly array of allowed string values.
188
+ *
189
+ * @param values - Allowed values.
190
+ * @param opts - Common options (description, short, required).
191
+ * @returns A combinator schema that resolves to a union of the allowed values.
192
+ *
193
+ * @example
194
+ * ```ts
195
+ * const args = {
196
+ * level: choice(['debug', 'info', 'warn', 'error'] as const)
197
+ * }
198
+ * // typeof values.level === 'debug' | 'info' | 'warn' | 'error'
199
+ * ```
200
+ *
201
+ * @experimental
202
+ */
203
+ function choice(values, opts) {
204
+ return {
205
+ type: "enum",
206
+ metavar: values.join("|"),
207
+ choices: values,
208
+ ...opts?.description != null ? { description: opts.description } : {},
209
+ ...opts?.short != null ? { short: opts.short } : {},
210
+ ...opts?.required != null ? { required: opts.required } : {},
211
+ parse(value) {
212
+ if (!values.includes(value)) throw new Error(`Value must be one of: ${values.join(", ")}`);
213
+ return value;
214
+ }
215
+ };
216
+ }
217
+ /**
218
+ * Create a custom argument schema with a user-defined parse function.
219
+ *
220
+ * This is the most general custom combinator. Use it when none of the built-in
221
+ * base combinators ({@link string}, {@link number}, {@link integer},
222
+ * {@link float}, {@link boolean}, {@link choice}) fit your needs.
223
+ *
224
+ * The returned schema has `type: 'custom'`.
225
+ *
226
+ * @typeParam T - The parsed value type.
227
+ *
228
+ * @param config - Configuration with a parse function and optional metavar.
229
+ * @returns A combinator schema that resolves to the parse function's return type.
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * const date = combinator({
234
+ * parse: (value) => {
235
+ * const d = new Date(value)
236
+ * if (isNaN(d.getTime())) {
237
+ * throw new Error('Invalid date format')
238
+ * }
239
+ * return d
240
+ * },
241
+ * metavar: 'date'
242
+ * })
243
+ * ```
244
+ *
245
+ * @experimental
246
+ */
247
+ function combinator(config) {
248
+ return {
249
+ type: "custom",
250
+ metavar: config.metavar ?? "custom",
251
+ ...config.description != null ? { description: config.description } : {},
252
+ ...config.short != null ? { short: config.short } : {},
253
+ ...config.required != null ? { required: config.required } : {},
254
+ parse: config.parse
255
+ };
256
+ }
257
+ /**
258
+ * Transform the output of a combinator schema.
259
+ *
260
+ * Creates a new schema that applies `transform` to the result of `schema.parse`.
261
+ * The original schema is not modified.
262
+ *
263
+ * @typeParam T - The input schema's parsed type.
264
+ * @typeParam U - The transformed type.
265
+ *
266
+ * @param schema - The base combinator schema.
267
+ * @param transform - The transformation function.
268
+ * @returns A new combinator schema that resolves to the transformed type.
269
+ *
270
+ * @example
271
+ * ```ts
272
+ * const args = {
273
+ * doubled: map(integer(), n => n * 2)
274
+ * }
275
+ * ```
276
+ *
277
+ * @experimental
278
+ */
279
+ function map(schema, transform) {
280
+ const baseParse = schema.parse;
281
+ return {
282
+ ...schema,
283
+ parse(value) {
284
+ return transform(baseParse(value));
285
+ }
286
+ };
287
+ }
288
+ /**
289
+ * Set a default value on a combinator schema.
290
+ *
291
+ * The original schema is not modified.
292
+ *
293
+ * @typeParam T - The schema's parsed type.
294
+ *
295
+ * @param schema - The base combinator schema.
296
+ * @param defaultValue - The default value.
297
+ * @returns A new schema with the default value set.
298
+ *
299
+ * @example
300
+ * ```ts
301
+ * const args = {
302
+ * port: withDefault(integer({ min: 1, max: 65535 }), 8080)
303
+ * }
304
+ * ```
305
+ *
306
+ * @experimental
307
+ */
308
+ function withDefault(schema, defaultValue) {
309
+ return {
310
+ ...schema,
311
+ default: defaultValue
312
+ };
313
+ }
314
+ /**
315
+ * Mark a combinator schema as accepting multiple values.
316
+ *
317
+ * The resolved value becomes an array. The original schema is not modified.
318
+ *
319
+ * @typeParam T - The schema's parsed type.
320
+ * @param schema - The base combinator schema.
321
+ * @returns A new schema with `multiple: true`.
322
+ *
323
+ * @example
324
+ * ```ts
325
+ * const args = {
326
+ * tags: multiple(string())
327
+ * }
328
+ * // typeof values.tags === string[]
329
+ * ```
330
+ *
331
+ * @experimental
332
+ */
333
+ function multiple(schema) {
334
+ return {
335
+ ...schema,
336
+ multiple: true
337
+ };
338
+ }
339
+ /**
340
+ * Mark a combinator schema as required.
341
+ *
342
+ * The original schema is not modified.
343
+ *
344
+ * @typeParam T - The schema's parsed type.
345
+ *
346
+ * @param schema - The base combinator schema.
347
+ * @returns A new schema with `required: true`.
348
+ *
349
+ * @example
350
+ * ```ts
351
+ * const args = {
352
+ * name: required(string())
353
+ * }
354
+ * ```
355
+ *
356
+ * @experimental
357
+ */
358
+ function required(schema) {
359
+ return {
360
+ ...schema,
361
+ required: true
362
+ };
363
+ }
364
+ /**
365
+ * Set a short alias on a combinator schema.
366
+ *
367
+ * The original schema is not modified.
368
+ *
369
+ * @typeParam T - The schema's parsed type.
370
+ * @typeParam S - The short alias string literal type.
371
+ *
372
+ * @param schema - The base combinator schema.
373
+ * @param alias - Single character short alias.
374
+ * @returns A new schema with the short alias set.
375
+ *
376
+ * @example
377
+ * ```ts
378
+ * const args = {
379
+ * verbose: short(boolean(), 'v')
380
+ * }
381
+ * // Usage: -v or --verbose
382
+ * ```
383
+ *
384
+ * @experimental
385
+ */
386
+ function short(schema, alias) {
387
+ return {
388
+ ...schema,
389
+ short: alias
390
+ };
391
+ }
392
+ /**
393
+ * Set a description on a combinator schema for help text generation.
394
+ *
395
+ * The original schema is not modified.
396
+ *
397
+ * @typeParam T - The schema's parsed type.
398
+ * @typeParam D - The description string literal type.
399
+ *
400
+ * @param schema - The base combinator schema.
401
+ * @param text - Human-readable description.
402
+ * @returns A new schema with the description set.
403
+ *
404
+ * @example
405
+ * ```ts
406
+ * const args = {
407
+ * port: describe(integer(), 'Port number to listen on')
408
+ * }
409
+ * ```
410
+ *
411
+ * @experimental
412
+ */
413
+ function describe(schema, text) {
414
+ return {
415
+ ...schema,
416
+ description: text
417
+ };
418
+ }
419
+ /**
420
+ * Mark a combinator schema as not required.
421
+ *
422
+ * Useful for overriding a base combinator that was created with `required: true`.
423
+ * The original schema is not modified.
424
+ *
425
+ * @typeParam T - The schema's parsed type.
426
+ *
427
+ * @param schema - The base combinator schema.
428
+ * @returns A new schema with `required: false`.
429
+ *
430
+ * @example
431
+ * ```ts
432
+ * const args = {
433
+ * name: unrequired(string({ required: true }))
434
+ * }
435
+ * ```
436
+ *
437
+ * @experimental
438
+ */
439
+ function unrequired(schema) {
440
+ return {
441
+ ...schema,
442
+ required: false
443
+ };
444
+ }
445
+ /**
446
+ * Type-safe schema factory.
447
+ *
448
+ * Returns the input unchanged at runtime, but provides type inference
449
+ * so that `satisfies Args` is not needed.
450
+ *
451
+ * @typeParam T - The exact schema type.
452
+ *
453
+ * @param fields - The argument schema object.
454
+ * @returns The same schema object with its type inferred.
455
+ *
456
+ * @example
457
+ * ```ts
458
+ * const common = args({
459
+ * verbose: boolean(),
460
+ * help: short(boolean(), 'h')
461
+ * })
462
+ * ```
463
+ *
464
+ * @experimental
465
+ */
466
+ function args(fields) {
467
+ return fields;
468
+ }
469
+ function merge(...schemas) {
470
+ const result = Object.create(null);
471
+ for (const schema of schemas) for (const key of Object.keys(schema)) result[key] = schema[key];
472
+ return result;
473
+ }
474
+ /**
475
+ * Extend a schema by overriding or adding fields.
476
+ *
477
+ * Equivalent to `merge(base, overrides)` but communicates the intent of
478
+ * intentional overrides rather than general composition.
479
+ *
480
+ * @typeParam T - Base schema type.
481
+ * @typeParam U - Overrides schema type.
482
+ *
483
+ * @param base - The base schema to extend.
484
+ * @param overrides - Fields to override or add.
485
+ * @returns A new schema with overrides applied.
486
+ *
487
+ * @example
488
+ * ```ts
489
+ * const base = args({ port: withDefault(integer(), 8080) })
490
+ * const strict = extend(base, { port: required(integer({ min: 1, max: 65535 })) })
491
+ * ```
492
+ *
493
+ * @experimental
494
+ */
495
+ function extend(base, overrides) {
496
+ const result = Object.create(null);
497
+ for (const key of Object.keys(base)) result[key] = base[key];
498
+ for (const key of Object.keys(overrides)) result[key] = overrides[key];
499
+ return result;
500
+ }
501
+
502
+ //#endregion
503
+ export { args, boolean, choice, combinator, describe, extend, float, integer, map, merge, multiple, number, positional, required, short, string, unrequired, withDefault };
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@gunshi/combinators",
3
+ "description": "parser combinators for gunshi argument schema",
4
+ "version": "0.28.2",
5
+ "author": {
6
+ "name": "kazuya kawaguchi",
7
+ "email": "kawakazu80@gmail.com"
8
+ },
9
+ "license": "MIT",
10
+ "funding": "https://github.com/sponsors/kazupon",
11
+ "bugs": {
12
+ "url": "https://github.com/kazupon/gunshi/issues"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/kazupon/gunshi.git",
17
+ "directory": "packages/combinators"
18
+ },
19
+ "keywords": [
20
+ "gunshi",
21
+ "combinators",
22
+ "parser",
23
+ "cli"
24
+ ],
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "engines": {
29
+ "node": ">= 20"
30
+ },
31
+ "type": "module",
32
+ "files": [
33
+ "lib"
34
+ ],
35
+ "module": "lib/index.js",
36
+ "exports": {
37
+ ".": {
38
+ "types": "./lib/index.d.ts",
39
+ "import": "./lib/index.js",
40
+ "require": "./lib/index.js",
41
+ "default": "./lib/index.js"
42
+ },
43
+ "./package.json": "./package.json"
44
+ },
45
+ "types": "lib/index.d.ts",
46
+ "typesVersions": {
47
+ "*": {
48
+ "*": [
49
+ "./lib/*",
50
+ "./*"
51
+ ]
52
+ }
53
+ },
54
+ "devDependencies": {
55
+ "deno": "^2.6.3",
56
+ "jsr": "^0.13.5",
57
+ "jsr-exports-lint": "^0.4.1",
58
+ "publint": "^0.3.16",
59
+ "tsdown": "0.15.12",
60
+ "gunshi": "0.28.2"
61
+ },
62
+ "scripts": {
63
+ "build": "tsdown",
64
+ "lint:jsr": "jsr publish --dry-run --allow-dirty",
65
+ "typecheck:deno": "deno check --import-map=../../importmap.json ./src"
66
+ }
67
+ }