@crustjs/validate 0.0.1 → 0.0.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/README.md +69 -69
- package/dist/effect/index.d.ts +183 -87
- package/dist/effect/index.js +105 -121
- package/dist/shared/chunk-p8pzhj6c.js +120 -0
- package/dist/zod/index.d.ts +177 -135
- package/dist/zod/index.js +105 -116
- package/package.json +1 -1
- package/dist/shared/chunk-zfhm7pmv.js +0 -196
package/README.md
CHANGED
|
@@ -4,13 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
Validation support for the [Crust](https://crustjs.com) CLI framework.
|
|
6
6
|
|
|
7
|
+
Define args and flags once using schema-first `arg()` / `flag()` helpers, then
|
|
8
|
+
use `withZod()` or `withEffect()` as `run` middleware for `defineCommand`. CLI
|
|
9
|
+
metadata (type, required, description, variadic) is derived from the schema
|
|
10
|
+
automatically — single source of truth.
|
|
11
|
+
|
|
7
12
|
## Entry points
|
|
8
13
|
|
|
9
|
-
| Entry
|
|
10
|
-
|
|
|
11
|
-
| Shared contracts | `@crustjs/validate`
|
|
12
|
-
| Effect provider
|
|
13
|
-
| Zod provider
|
|
14
|
+
| Entry | Import | Purpose |
|
|
15
|
+
| ---------------- | ---------------------------- | -------------------------------------------------------------- |
|
|
16
|
+
| Shared contracts | `@crustjs/validate` | Provider-agnostic types (`ValidatedContext`, `ValidationIssue`) |
|
|
17
|
+
| Effect provider | `@crustjs/validate/effect` | `arg`, `flag`, `withEffect` |
|
|
18
|
+
| Zod provider | `@crustjs/validate/zod` | `arg`, `flag`, `withZod` |
|
|
14
19
|
|
|
15
20
|
## Install
|
|
16
21
|
|
|
@@ -22,105 +27,98 @@ bun add zod
|
|
|
22
27
|
bun add effect
|
|
23
28
|
```
|
|
24
29
|
|
|
25
|
-
##
|
|
26
|
-
|
|
27
|
-
Define schemas once and let Crust `args`/`flags` definitions be generated automatically.
|
|
30
|
+
## Zod provider
|
|
28
31
|
|
|
29
32
|
```ts
|
|
30
|
-
import { runMain } from "@crustjs/core";
|
|
31
|
-
import {
|
|
32
|
-
|
|
33
|
-
defineEffectCommand,
|
|
34
|
-
flag,
|
|
35
|
-
} from "@crustjs/validate/effect";
|
|
36
|
-
import * as Schema from "effect/Schema";
|
|
33
|
+
import { defineCommand, runMain } from "@crustjs/core";
|
|
34
|
+
import { arg, flag, withZod } from "@crustjs/validate/zod";
|
|
35
|
+
import { z } from "zod";
|
|
37
36
|
|
|
38
|
-
const serve =
|
|
37
|
+
const serve = defineCommand({
|
|
39
38
|
meta: { name: "serve", description: "Start dev server" },
|
|
40
39
|
args: [
|
|
41
|
-
arg("port",
|
|
42
|
-
arg("host",
|
|
43
|
-
Schema.String.annotations({ description: "Host to bind" }),
|
|
44
|
-
)),
|
|
40
|
+
arg("port", z.number().int().min(1).max(65535).describe("Port to listen on")),
|
|
41
|
+
arg("host", z.string().default("localhost").describe("Host to bind")),
|
|
45
42
|
],
|
|
46
43
|
flags: {
|
|
47
44
|
verbose: flag(
|
|
48
|
-
|
|
45
|
+
z.boolean().default(false).describe("Enable verbose logging"),
|
|
49
46
|
{ alias: "v" },
|
|
50
47
|
),
|
|
51
48
|
format: flag(
|
|
52
|
-
|
|
49
|
+
z.enum(["json", "text"]).default("text").describe("Output format"),
|
|
53
50
|
{ alias: "f" },
|
|
54
51
|
),
|
|
55
52
|
},
|
|
56
|
-
run({ args, flags, input }) {
|
|
57
|
-
// args: { port: number; host: string
|
|
53
|
+
run: withZod(({ args, flags, input }) => {
|
|
54
|
+
// args: { port: number; host: string }
|
|
58
55
|
// flags: { verbose: boolean; format: "json" | "text" }
|
|
59
56
|
console.log(args.port, args.host, flags.verbose, flags.format);
|
|
60
57
|
console.log(input.args, input.flags); // original parser output
|
|
61
|
-
},
|
|
58
|
+
}),
|
|
62
59
|
});
|
|
63
60
|
|
|
64
61
|
runMain(serve);
|
|
65
62
|
```
|
|
66
63
|
|
|
67
|
-
###
|
|
68
|
-
|
|
69
|
-
- Primitive schemas: `Schema.String`, `Schema.Number`, `Schema.Boolean`
|
|
70
|
-
- Enums/literals: `Schema.Enums(...)`, `Schema.Literal(...)`
|
|
71
|
-
- Arrays: `Schema.Array(...)` and array-like tuple rest schemas
|
|
72
|
-
- Wrappers: refinement/transformation/suspend wrappers are unwrapped for parser-shape analysis
|
|
73
|
-
- Descriptions: `schema.annotations({ description: "..." })` — auto-extracted through wrappers
|
|
74
|
-
|
|
75
|
-
For optional args/flags, use schemas whose encoded input allows `undefined` (for example `Schema.UndefinedOr(Schema.String)`).
|
|
64
|
+
### Zod schema support
|
|
76
65
|
|
|
77
|
-
|
|
66
|
+
- Primitive schemas: `z.string()`, `z.number()`, `z.boolean()`
|
|
67
|
+
- Enums/literals: `z.enum(...)`, `z.literal(...)`
|
|
68
|
+
- Arrays: `z.array(...)` for variadic args or flags with `multiple: true`
|
|
69
|
+
- Wrappers: `.optional()`, `.default()`, `.nullable()`, `.transform()`, `.pipe()`
|
|
70
|
+
- Descriptions: `.describe("...")` — auto-extracted through wrappers
|
|
78
71
|
|
|
79
|
-
|
|
72
|
+
## Effect provider
|
|
80
73
|
|
|
81
74
|
```ts
|
|
82
|
-
import { runMain } from "@crustjs/core";
|
|
83
|
-
import { arg,
|
|
84
|
-
import
|
|
75
|
+
import { defineCommand, runMain } from "@crustjs/core";
|
|
76
|
+
import { arg, flag, withEffect } from "@crustjs/validate/effect";
|
|
77
|
+
import * as Schema from "effect/Schema";
|
|
85
78
|
|
|
86
|
-
const serve =
|
|
79
|
+
const serve = defineCommand({
|
|
87
80
|
meta: { name: "serve", description: "Start dev server" },
|
|
88
81
|
args: [
|
|
89
|
-
arg("port",
|
|
90
|
-
arg("host",
|
|
82
|
+
arg("port", Schema.Number.annotations({ description: "Port to listen on" })),
|
|
83
|
+
arg("host", Schema.UndefinedOr(
|
|
84
|
+
Schema.String.annotations({ description: "Host to bind" }),
|
|
85
|
+
)),
|
|
91
86
|
],
|
|
92
87
|
flags: {
|
|
93
88
|
verbose: flag(
|
|
94
|
-
|
|
89
|
+
Schema.Boolean.annotations({ description: "Enable verbose logging" }),
|
|
95
90
|
{ alias: "v" },
|
|
96
91
|
),
|
|
97
92
|
format: flag(
|
|
98
|
-
|
|
93
|
+
Schema.Literal("json", "text").annotations({ description: "Output format" }),
|
|
99
94
|
{ alias: "f" },
|
|
100
95
|
),
|
|
101
96
|
},
|
|
102
|
-
run({ args, flags, input }) {
|
|
103
|
-
// args: { port: number; host: string }
|
|
97
|
+
run: withEffect(({ args, flags, input }) => {
|
|
98
|
+
// args: { port: number; host: string | undefined }
|
|
104
99
|
// flags: { verbose: boolean; format: "json" | "text" }
|
|
105
100
|
console.log(args.port, args.host, flags.verbose, flags.format);
|
|
106
101
|
console.log(input.args, input.flags); // original parser output
|
|
107
|
-
},
|
|
102
|
+
}),
|
|
108
103
|
});
|
|
109
104
|
|
|
110
105
|
runMain(serve);
|
|
111
106
|
```
|
|
112
107
|
|
|
113
|
-
###
|
|
108
|
+
### Effect schema support
|
|
114
109
|
|
|
115
|
-
- Primitive schemas: `
|
|
116
|
-
- Enums/literals: `
|
|
117
|
-
- Arrays: `
|
|
118
|
-
- Wrappers:
|
|
119
|
-
- Descriptions:
|
|
110
|
+
- Primitive schemas: `Schema.String`, `Schema.Number`, `Schema.Boolean`
|
|
111
|
+
- Enums/literals: `Schema.Enums(...)`, `Schema.Literal(...)`
|
|
112
|
+
- Arrays: `Schema.Array(...)` and array-like tuple rest schemas
|
|
113
|
+
- Wrappers: refinement/transformation/suspend wrappers are unwrapped for parser-shape analysis
|
|
114
|
+
- Descriptions: `schema.annotations({ description: "..." })` — auto-extracted through wrappers
|
|
115
|
+
|
|
116
|
+
For optional args/flags, use schemas whose encoded input allows `undefined` (for example `Schema.UndefinedOr(Schema.String)`).
|
|
120
117
|
|
|
121
|
-
|
|
118
|
+
## Positional args
|
|
119
|
+
|
|
120
|
+
Use ordered `arg(name, schema, options?)` entries.
|
|
122
121
|
|
|
123
|
-
- Use ordered `arg(name, schema, options?)` entries.
|
|
124
122
|
- Optional/default schemas become optional CLI args (`[name]`).
|
|
125
123
|
- Variadic args use `{ variadic: true }` and must be last.
|
|
126
124
|
|
|
@@ -131,20 +129,30 @@ args: [
|
|
|
131
129
|
];
|
|
132
130
|
```
|
|
133
131
|
|
|
134
|
-
|
|
132
|
+
## Flags
|
|
133
|
+
|
|
134
|
+
Use `flag(schema, options?)` to define flags.
|
|
135
135
|
|
|
136
|
-
- Pass plain Zod schemas or `flag(schema, options?)` wrappers.
|
|
137
136
|
- Use `flag(..., { alias })` for short aliases.
|
|
138
|
-
- Use `.describe("...")`
|
|
137
|
+
- Use `.describe("...")` (Zod) or `.annotations({ description: "..." })` (Effect) for help text.
|
|
139
138
|
|
|
140
139
|
```ts
|
|
141
140
|
flags: {
|
|
142
|
-
debug: z.boolean().default(false).describe("Enable debug mode"),
|
|
141
|
+
debug: flag(z.boolean().default(false).describe("Enable debug mode")),
|
|
143
142
|
outDir: flag(z.string().default("dist").describe("Output directory"), { alias: "o" }),
|
|
144
143
|
};
|
|
145
144
|
```
|
|
146
145
|
|
|
147
|
-
|
|
146
|
+
## Strict mode
|
|
147
|
+
|
|
148
|
+
When using `withZod()` or `withEffect()`, **all** args and flags must be created
|
|
149
|
+
with the matching provider's `arg()` / `flag()` helpers. Mixing plain core
|
|
150
|
+
definitions causes a compile-time error (handler parameter becomes `never`).
|
|
151
|
+
|
|
152
|
+
Plugin-injected flags (e.g. `--help` from `helpPlugin`) are silently skipped at
|
|
153
|
+
runtime — they don't need schema metadata.
|
|
154
|
+
|
|
155
|
+
## Help plugin compatibility
|
|
148
156
|
|
|
149
157
|
Generated definitions are compatible with `helpPlugin`.
|
|
150
158
|
|
|
@@ -155,14 +163,6 @@ import { helpPlugin } from "@crustjs/plugins";
|
|
|
155
163
|
runMain(serve, { plugins: [helpPlugin()] });
|
|
156
164
|
```
|
|
157
165
|
|
|
158
|
-
### Lifecycle hooks
|
|
159
|
-
|
|
160
|
-
`defineZodCommand` supports `preRun` and `postRun` passthrough hooks.
|
|
161
|
-
|
|
162
|
-
- Both hooks receive the core `CommandContext` (raw parser output).
|
|
163
|
-
- Schema validation/transforms run inside `run`, so validated values are only
|
|
164
|
-
available in the schema-first `run` handler.
|
|
165
|
-
|
|
166
166
|
## Validation errors
|
|
167
167
|
|
|
168
168
|
Failures throw `CrustError("VALIDATION")`.
|
|
@@ -173,6 +173,6 @@ Failures throw `CrustError("VALIDATION")`.
|
|
|
173
173
|
## v1 constraints
|
|
174
174
|
|
|
175
175
|
- Args and flags only (no env/config validation).
|
|
176
|
-
- Effect mode
|
|
176
|
+
- Effect mode supports context-free, synchronous schemas only (`R = never`).
|
|
177
177
|
- Zod mode requires Zod 4+.
|
|
178
178
|
- No automatic schema inheritance across subcommands.
|
package/dist/effect/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { CommandContext, CommandDef } from "@crustjs/core";
|
|
1
|
+
import { ArgDef, ArgsDef, FlagDef, FlagsDef } from "@crustjs/core";
|
|
3
2
|
import * as schema from "effect/Schema";
|
|
4
3
|
import { AnyCommand } from "@crustjs/core";
|
|
5
4
|
/**
|
|
@@ -30,104 +29,147 @@ interface ValidatedContext<
|
|
|
30
29
|
};
|
|
31
30
|
}
|
|
32
31
|
/**
|
|
33
|
-
*
|
|
32
|
+
* Unique symbol used to attach an Effect schema to a core `ArgDef` or `FlagDef`.
|
|
33
|
+
*
|
|
34
|
+
* Survives `{ ...def }` spread in `defineCommand` and `Object.freeze`,
|
|
35
|
+
* making the schema available at runtime via `def[EFFECT_SCHEMA]`.
|
|
36
|
+
*/
|
|
37
|
+
declare const EFFECT_SCHEMA: unique symbol;
|
|
38
|
+
type EFFECT_SCHEMA = typeof EFFECT_SCHEMA;
|
|
39
|
+
/**
|
|
40
|
+
* An Effect schema used by the validate/effect entrypoint.
|
|
34
41
|
*
|
|
35
42
|
* v1 intentionally supports context-free schemas only (`R = never`).
|
|
36
|
-
* Schemas must also be **synchronous** — async combinators such as
|
|
37
|
-
* `Schema.filterEffect` or async `Schema.transformOrFail` will cause
|
|
38
|
-
* `Effect.runSync` to throw at runtime.
|
|
39
43
|
*/
|
|
40
44
|
type EffectSchemaLike = schema.Schema.AnyNoContext;
|
|
41
|
-
/**
|
|
42
|
-
type
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
/** CLI value type literals. */
|
|
46
|
+
type ValueType = "string" | "number" | "boolean";
|
|
47
|
+
/**
|
|
48
|
+
* Resolve CLI ValueType from an Effect schema's encoded (input) type.
|
|
49
|
+
*
|
|
50
|
+
* Uses `Schema.Encoded<S>` to determine the CLI input type. If the encoded
|
|
51
|
+
* type is a primitive (possibly `| undefined`), resolves to the matching
|
|
52
|
+
* ValueType literal. Falls back to `ValueType` (the union) for complex types.
|
|
53
|
+
*/
|
|
54
|
+
type StripUndefined<T> = Exclude<T, undefined>;
|
|
55
|
+
type PrimitiveToValueType<T> = [T] extends [string] ? "string" : [T] extends [number] ? "number" : [T] extends [boolean] ? "boolean" : ValueType;
|
|
56
|
+
type ResolveEffectValueType<S> = S extends schema.Schema<infer _A, infer I, infer _R> ? PrimitiveToValueType<StripUndefined<I>> : ValueType;
|
|
57
|
+
/**
|
|
58
|
+
* An `ArgDef` enriched with a hidden Effect schema.
|
|
59
|
+
*
|
|
60
|
+
* The `Type` parameter is resolved from the schema's encoded type.
|
|
61
|
+
*
|
|
62
|
+
* The `EFFECT_SCHEMA` symbol key carries the schema for runtime validation.
|
|
63
|
+
*/
|
|
64
|
+
interface EffectArgDef<
|
|
50
65
|
Name extends string = string,
|
|
51
66
|
SchemaType extends EffectSchemaLike = EffectSchemaLike,
|
|
52
|
-
Variadic extends true | undefined = true | undefined
|
|
67
|
+
Variadic extends true | undefined = true | undefined,
|
|
68
|
+
Type extends ValueType = ResolveEffectValueType<SchemaType>
|
|
53
69
|
> {
|
|
54
|
-
readonly kind: "arg";
|
|
55
70
|
readonly name: Name;
|
|
56
|
-
readonly
|
|
71
|
+
readonly type: Type;
|
|
72
|
+
readonly description?: string;
|
|
73
|
+
readonly required?: true;
|
|
74
|
+
/** Non-optional so `ValidateVariadicArgs` can match `{ variadic: true }`. */
|
|
57
75
|
readonly variadic: Variadic;
|
|
76
|
+
readonly [EFFECT_SCHEMA]: SchemaType;
|
|
58
77
|
}
|
|
59
|
-
/**
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
/** Recursively maps ordered ArgSpec entries to a named output object type. */
|
|
66
|
-
type InferArgsFromTuple<A extends readonly ArgSpec[]> = A extends readonly [infer Head extends ArgSpec, ...infer Tail extends readonly ArgSpec[]] ? { [K in Head["name"]] : InferArgValue<Head> } & InferArgsFromTuple<Tail> : {};
|
|
67
|
-
/** Infer validated args object type from ordered ArgSpec entries. */
|
|
68
|
-
type InferArgsFromSpecs<A extends ArgSpecs> = Simplify<InferArgsFromTuple<A>>;
|
|
69
|
-
/** Optional metadata for a flag declared with `flag()`. */
|
|
70
|
-
interface FlagOptions {
|
|
71
|
-
/** Short alias or array of aliases (e.g. `"v"` or `["v", "V"]`). */
|
|
72
|
-
readonly alias?: string | readonly string[];
|
|
73
|
-
}
|
|
74
|
-
/** A named flag schema wrapper produced by `flag()`. */
|
|
75
|
-
interface FlagSpec<
|
|
78
|
+
/**
|
|
79
|
+
* A `FlagDef` enriched with a hidden Effect schema.
|
|
80
|
+
*
|
|
81
|
+
* The `EFFECT_SCHEMA` symbol key carries the schema for runtime validation.
|
|
82
|
+
*/
|
|
83
|
+
interface EffectFlagDef<
|
|
76
84
|
SchemaType extends EffectSchemaLike = EffectSchemaLike,
|
|
77
|
-
Alias extends string | readonly string[] | undefined = string | readonly string[] | undefined
|
|
85
|
+
Alias extends string | readonly string[] | undefined = string | readonly string[] | undefined,
|
|
86
|
+
Type extends ValueType = ResolveEffectValueType<SchemaType>
|
|
78
87
|
> {
|
|
79
|
-
readonly
|
|
80
|
-
readonly
|
|
81
|
-
readonly
|
|
88
|
+
readonly type: Type;
|
|
89
|
+
readonly description?: string;
|
|
90
|
+
readonly required?: true;
|
|
91
|
+
/** Non-optional so `ValidateFlagAliases` can extract narrow alias literals. */
|
|
92
|
+
readonly alias: Alias extends string | readonly string[] ? Alias : undefined;
|
|
93
|
+
readonly [EFFECT_SCHEMA]: SchemaType;
|
|
82
94
|
}
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
/** Infer validated flags object type from the flags shape. */
|
|
88
|
-
type InferFlagsFromShape<F extends FlagShape> = { [K in keyof F] : InferSchemaOutput<ExtractFlagSchema<F[K]>> };
|
|
89
|
-
/** Handler type for `defineEffectCommand()` with validated/transformed context. */
|
|
90
|
-
type EffectCommandRunHandler<
|
|
91
|
-
ArgsOut,
|
|
92
|
-
FlagsOut
|
|
93
|
-
> = (context: ValidatedContext<ArgsOut, FlagsOut>) => void | Promise<void>;
|
|
94
|
-
/** Infer args output type from command config args. */
|
|
95
|
-
type InferArgsFromConfig<A> = A extends ArgSpecs ? InferArgsFromSpecs<A> : Record<string, never>;
|
|
96
|
-
/** Infer flags output type from command config flags. */
|
|
97
|
-
type InferFlagsFromConfig<F> = F extends FlagShape ? InferFlagsFromShape<F> : Record<string, never>;
|
|
98
|
-
type EffectOverriddenKeys = "args" | "flags" | "run" | "preRun" | "postRun";
|
|
99
|
-
/** Config for `defineEffectCommand()` using `arg()` + `flag()` schema-first DSL. */
|
|
100
|
-
interface EffectCommandDef<
|
|
101
|
-
A extends ArgSpecs | undefined = undefined,
|
|
102
|
-
F extends FlagShape | undefined = undefined
|
|
103
|
-
> extends Omit<CommandDef, EffectOverriddenKeys> {
|
|
104
|
-
/** Ordered positional args as `arg()` specs. */
|
|
105
|
-
readonly args?: A;
|
|
106
|
-
/** Named flags as plain schemas or `flag()` wrappers. */
|
|
107
|
-
readonly flags?: F;
|
|
108
|
-
/** Optional setup hook before schema validation runs. */
|
|
109
|
-
readonly preRun?: (context: CommandContext) => void | Promise<void>;
|
|
110
|
-
/** Main handler with validated/transformed args and flags. */
|
|
111
|
-
readonly run?: EffectCommandRunHandler<InferArgsFromConfig<A>, InferFlagsFromConfig<F>>;
|
|
112
|
-
/** Optional teardown hook after command execution. */
|
|
113
|
-
readonly postRun?: (context: CommandContext) => void | Promise<void>;
|
|
95
|
+
/** Options for `arg()`. */
|
|
96
|
+
interface ArgOptions {
|
|
97
|
+
/** Collect remaining positionals into this arg as an array. */
|
|
98
|
+
readonly variadic?: true;
|
|
114
99
|
}
|
|
100
|
+
/** Options for `flag()`. */
|
|
101
|
+
interface FlagOptions {
|
|
102
|
+
/** Short alias or array of aliases (e.g. `"v"` or `["v", "V"]`). */
|
|
103
|
+
readonly alias?: string | readonly string[];
|
|
104
|
+
}
|
|
105
|
+
/** Infer Effect output type from a schema. */
|
|
106
|
+
type InferSchemaOutput<S> = S extends schema.Schema<infer A, infer _I, infer _R> ? A : never;
|
|
107
|
+
/** Output type for a single arg: variadic → `output[]`, scalar → `output`. */
|
|
108
|
+
type InferValidatedArgValue<D> = D extends {
|
|
109
|
+
readonly [EFFECT_SCHEMA]: infer S;
|
|
110
|
+
readonly variadic: true;
|
|
111
|
+
} ? InferSchemaOutput<S>[] : D extends {
|
|
112
|
+
readonly [EFFECT_SCHEMA]: infer S;
|
|
113
|
+
} ? InferSchemaOutput<S> : never;
|
|
114
|
+
/** Flattens an intersection of objects for readable inferred types. */
|
|
115
|
+
type Simplify<T> = { [K in keyof T] : T[K] };
|
|
116
|
+
/** Recursively maps args tuple to a named output object. */
|
|
117
|
+
type InferValidatedArgsTuple<A extends readonly ArgDef[]> = A extends readonly [infer Head extends ArgDef, ...infer Tail extends readonly ArgDef[]] ? Head extends {
|
|
118
|
+
readonly name: infer N extends string;
|
|
119
|
+
} ? { [K in N] : InferValidatedArgValue<Head> } & InferValidatedArgsTuple<Tail> : InferValidatedArgsTuple<Tail> : {};
|
|
115
120
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* Only context-free (`R = never`), synchronous schemas are supported.
|
|
119
|
-
* Async combinators like `Schema.filterEffect` or async `Schema.transformOrFail`
|
|
120
|
-
* will throw at runtime.
|
|
121
|
+
* Infer the validated args output type from an `ArgsDef` tuple
|
|
122
|
+
* where each element carries an `[EFFECT_SCHEMA]` brand.
|
|
121
123
|
*/
|
|
122
|
-
|
|
123
|
-
const A extends readonly ArgSpec[] | undefined,
|
|
124
|
-
const F extends FlagShape | undefined
|
|
125
|
-
>(config: EffectCommandDef<A, F> & {
|
|
126
|
-
args?: A extends readonly object[] ? ValidateVariadicArgs<A> : A;
|
|
127
|
-
flags?: F extends Record<string, unknown> ? ValidateFlagAliases<F> : F;
|
|
128
|
-
}): AnyCommand2;
|
|
124
|
+
type InferValidatedArgs<A> = A extends readonly ArgDef[] ? Simplify<InferValidatedArgsTuple<A>> : Record<string, never>;
|
|
129
125
|
/**
|
|
130
|
-
*
|
|
126
|
+
* Infer the validated flags output type from a `FlagsDef` record
|
|
127
|
+
* where each value carries an `[EFFECT_SCHEMA]` brand.
|
|
128
|
+
*/
|
|
129
|
+
type InferValidatedFlags<F> = F extends Record<string, FlagDef> ? Simplify<{ [K in keyof F] : F[K] extends {
|
|
130
|
+
readonly [EFFECT_SCHEMA]: infer S;
|
|
131
|
+
} ? InferSchemaOutput<S> : never }> : Record<string, never>;
|
|
132
|
+
/** Check that every arg in a tuple carries the `[EFFECT_SCHEMA]` brand. */
|
|
133
|
+
type AllArgsHaveSchema<A extends ArgsDef> = A extends readonly [infer Head, ...infer Tail extends readonly ArgDef[]] ? Head extends {
|
|
134
|
+
readonly [EFFECT_SCHEMA]: unknown;
|
|
135
|
+
} ? AllArgsHaveSchema<Tail> : false : true;
|
|
136
|
+
/** Check that every flag in a record carries the `[EFFECT_SCHEMA]` brand. */
|
|
137
|
+
type AllFlagsHaveSchema<F extends FlagsDef> = string extends keyof F ? true : { [K in keyof F] : F[K] extends {
|
|
138
|
+
readonly [EFFECT_SCHEMA]: unknown;
|
|
139
|
+
} ? true : false }[keyof F] extends true ? true : false;
|
|
140
|
+
/**
|
|
141
|
+
* Resolves to `true` only when all args and flags carry schema metadata.
|
|
142
|
+
* Used by `withEffect` to enforce strict mode at compile time.
|
|
143
|
+
*/
|
|
144
|
+
type HasAllSchemas<
|
|
145
|
+
A extends ArgsDef,
|
|
146
|
+
F extends FlagsDef
|
|
147
|
+
> = AllArgsHaveSchema<A> extends true ? AllFlagsHaveSchema<F> extends true ? true : false : false;
|
|
148
|
+
/**
|
|
149
|
+
* The validated handler type for `withEffect()`.
|
|
150
|
+
*/
|
|
151
|
+
type WithEffectHandler<
|
|
152
|
+
A extends ArgsDef,
|
|
153
|
+
F extends FlagsDef
|
|
154
|
+
> = HasAllSchemas<A, F> extends true ? (context: ValidatedContext<InferValidatedArgs<A>, InferValidatedFlags<F>>) => void | Promise<void> : never;
|
|
155
|
+
/**
|
|
156
|
+
* Define a named positional argument from an Effect schema.
|
|
157
|
+
*
|
|
158
|
+
* Returns a core `ArgDef` (accepted by `defineCommand`) enriched with hidden
|
|
159
|
+
* schema metadata (via `[EFFECT_SCHEMA]` symbol) for runtime validation by `withEffect`.
|
|
160
|
+
*
|
|
161
|
+
* CLI metadata (`type`, `required`, `description`, `variadic`) is derived
|
|
162
|
+
* from the schema automatically — single source of truth.
|
|
163
|
+
*
|
|
164
|
+
* @param name - Positional arg name used in parser output and help text
|
|
165
|
+
* @param schema - Effect schema (source of truth for type/optionality/description)
|
|
166
|
+
* @param options - Optional CLI metadata (`variadic`)
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```ts
|
|
170
|
+
* arg("port", Schema.Number.annotations({ description: "Port to listen on" }))
|
|
171
|
+
* arg("files", Schema.String, { variadic: true })
|
|
172
|
+
* ```
|
|
131
173
|
*/
|
|
132
174
|
declare function arg<
|
|
133
175
|
Name extends string,
|
|
@@ -135,14 +177,68 @@ declare function arg<
|
|
|
135
177
|
const Variadic extends true | undefined = undefined
|
|
136
178
|
>(name: Name, schema: SchemaType, options?: ArgOptions & {
|
|
137
179
|
variadic?: Variadic;
|
|
138
|
-
}):
|
|
180
|
+
}): EffectArgDef<Name, SchemaType, Variadic>;
|
|
139
181
|
/**
|
|
140
|
-
* Define a flag
|
|
182
|
+
* Define a flag from an Effect schema with optional alias.
|
|
183
|
+
*
|
|
184
|
+
* Returns a core `FlagDef` (accepted by `defineCommand`) enriched with hidden
|
|
185
|
+
* schema metadata (via `[EFFECT_SCHEMA]` symbol) for runtime validation by `withEffect`.
|
|
186
|
+
*
|
|
187
|
+
* CLI metadata (`type`, `multiple`, `required`, `description`) is derived
|
|
188
|
+
* from the schema automatically — single source of truth.
|
|
189
|
+
*
|
|
190
|
+
* @param schema - Effect schema (source of truth for type/optionality/description)
|
|
191
|
+
* @param options - Optional flag metadata (`alias`)
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```ts
|
|
195
|
+
* flag(Schema.Boolean.annotations({ description: "Enable verbose logging" }), { alias: "v" })
|
|
196
|
+
* flag(Schema.UndefinedOr(Schema.Number))
|
|
197
|
+
* ```
|
|
141
198
|
*/
|
|
142
199
|
declare function flag<
|
|
143
200
|
SchemaType extends EffectSchemaLike,
|
|
144
201
|
const Alias extends string | readonly string[] | undefined = undefined
|
|
145
202
|
>(schema: SchemaType, options?: FlagOptions & {
|
|
146
203
|
alias?: Alias;
|
|
147
|
-
}):
|
|
148
|
-
|
|
204
|
+
}): EffectFlagDef<SchemaType, Alias>;
|
|
205
|
+
import { ArgsDef as ArgsDef2, CommandContext, FlagsDef as FlagsDef2 } from "@crustjs/core";
|
|
206
|
+
/**
|
|
207
|
+
* Create a validated `run` handler for `defineCommand`.
|
|
208
|
+
*
|
|
209
|
+
* Reads Effect schemas from the command's `arg()` / `flag()` definitions,
|
|
210
|
+
* validates parsed CLI input against them, and calls `handler` with
|
|
211
|
+
* the transformed, fully-typed result.
|
|
212
|
+
*
|
|
213
|
+
* **Strict mode**: all args and flags in the command must be created with
|
|
214
|
+
* `arg()` / `flag()` from `@crustjs/validate/effect`. Plain core defs cause
|
|
215
|
+
* a compile-time error (handler parameter becomes `never`).
|
|
216
|
+
*
|
|
217
|
+
* **Sync only**: only context-free (`R = never`), synchronous schemas are
|
|
218
|
+
* supported. Async combinators like `Schema.filterEffect` or async
|
|
219
|
+
* `Schema.transformOrFail` will throw at runtime.
|
|
220
|
+
*
|
|
221
|
+
* @param handler - Receives `ValidatedContext` with typed args/flags after validation
|
|
222
|
+
* @returns A `run` function compatible with `defineCommand`
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```ts
|
|
226
|
+
* import { defineCommand } from "@crustjs/core";
|
|
227
|
+
* import * as Schema from "effect/Schema";
|
|
228
|
+
* import { arg, flag, withEffect } from "@crustjs/validate/effect";
|
|
229
|
+
*
|
|
230
|
+
* const serve = defineCommand({
|
|
231
|
+
* meta: { name: "serve" },
|
|
232
|
+
* args: [arg("port", Schema.Number)],
|
|
233
|
+
* flags: { verbose: flag(Schema.Boolean, { alias: "v" }) },
|
|
234
|
+
* run: withEffect(({ args, flags }) => {
|
|
235
|
+
* // args.port: number, flags.verbose: boolean
|
|
236
|
+
* }),
|
|
237
|
+
* });
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
declare function withEffect<
|
|
241
|
+
A extends ArgsDef2 = ArgsDef2,
|
|
242
|
+
F extends FlagsDef2 = FlagsDef2
|
|
243
|
+
>(handler: WithEffectHandler<A, F>): (context: CommandContext<A, F>) => Promise<void>;
|
|
244
|
+
export { withEffect, flag, arg, WithEffectHandler, InferValidatedFlags, InferValidatedArgs, InferSchemaOutput, FlagOptions, EffectSchemaLike, EffectFlagDef, EffectArgDef, ArgOptions };
|