@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 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 | Import | Purpose |
10
- | --- | --- | --- |
11
- | Shared contracts | `@crustjs/validate` | Provider-agnostic types (`ValidatedContext`, `ValidationIssue`) |
12
- | Effect provider | `@crustjs/validate/effect` | Schema-first command API (`defineEffectCommand`, `arg`, `flag`) |
13
- | Zod provider | `@crustjs/validate/zod` | Schema-first command API (`defineZodCommand`, `arg`, `flag`) |
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
- ## Effect schema-first mode (`defineEffectCommand`)
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
- arg,
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 = defineEffectCommand({
37
+ const serve = defineCommand({
39
38
  meta: { name: "serve", description: "Start dev server" },
40
39
  args: [
41
- arg("port", Schema.Number.annotations({ description: "Port to listen on" })),
42
- arg("host", Schema.UndefinedOr(
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
- Schema.Boolean.annotations({ description: "Enable verbose logging" }),
45
+ z.boolean().default(false).describe("Enable verbose logging"),
49
46
  { alias: "v" },
50
47
  ),
51
48
  format: flag(
52
- Schema.Literal("json", "text").annotations({ description: "Output format" }),
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 | undefined }
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
- ### Effect schema support
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
- ## Zod schema-first mode (`defineZodCommand`)
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
- Define schemas once and let Crust `args`/`flags` definitions be generated automatically.
72
+ ## Effect provider
80
73
 
81
74
  ```ts
82
- import { runMain } from "@crustjs/core";
83
- import { arg, defineZodCommand, flag } from "@crustjs/validate/zod";
84
- import { z } from "zod";
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 = defineZodCommand({
79
+ const serve = defineCommand({
87
80
  meta: { name: "serve", description: "Start dev server" },
88
81
  args: [
89
- arg("port", z.number().int().min(1).max(65535).describe("Port to listen on")),
90
- arg("host", z.string().default("localhost").describe("Host to bind")),
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
- z.boolean().default(false).describe("Enable verbose logging"),
89
+ Schema.Boolean.annotations({ description: "Enable verbose logging" }),
95
90
  { alias: "v" },
96
91
  ),
97
92
  format: flag(
98
- z.enum(["json", "text"]).default("text").describe("Output format"),
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
- ### Zod schema support
108
+ ### Effect schema support
114
109
 
115
- - Primitive schemas: `z.string()`, `z.number()`, `z.boolean()`
116
- - Enums/literals: `z.enum(...)`, `z.literal(...)`
117
- - Arrays: `z.array(...)` for flags with `multiple: true`
118
- - Wrappers: `.optional()`, `.default()`, `.nullable()`, `.transform()`, `.pipe()`
119
- - Descriptions: `.describe("...")` — auto-extracted through wrappers
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
- ### Positional args
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
- ### Flags
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("...")` on the schema for help text.
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
- ### Help plugin compatibility
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 currently supports context-free schemas only (`R = never`).
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.
@@ -1,5 +1,4 @@
1
- import { AnyCommand as AnyCommand2, ValidateFlagAliases, ValidateVariadicArgs } from "@crustjs/core";
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
- * An Effect schema used by the Effect entrypoint.
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
- /** Infer output type from an Effect schema. */
42
- type InferSchemaOutput<S> = S extends schema.Schema<infer A, infer _I, infer _R> ? A : never;
43
- /** Optional metadata for a positional argument declared with `arg()`. */
44
- interface ArgOptions {
45
- /** Collect remaining positionals into this arg as an array. */
46
- readonly variadic?: true;
47
- }
48
- /** A single positional argument spec produced by `arg()`. */
49
- interface ArgSpec<
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 schema: SchemaType;
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
- /** Ordered positional argument specs. */
60
- type ArgSpecs = readonly ArgSpec[];
61
- /** Output type for one ArgSpec in the validated handler context. */
62
- type InferArgValue<S extends ArgSpec> = S["variadic"] extends true ? InferSchemaOutput<S["schema"]>[] : InferSchemaOutput<S["schema"]>;
63
- /** Flattens an intersection of objects for readable inferred types. */
64
- type Simplify<T> = { [K in keyof T] : T[K] };
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 kind: "flag";
80
- readonly schema: SchemaType;
81
- readonly alias: Alias;
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
- /** Allowed value shape for `flags` in `defineEffectCommand()`. */
84
- type FlagShape = Record<string, EffectSchemaLike | FlagSpec>;
85
- /** Extract the schema from a flag shape value (plain schema or `flag()` wrapper). */
86
- type ExtractFlagSchema<V> = V extends FlagSpec<infer S> ? S : V extends EffectSchemaLike ? V : never;
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
- * Define a Crust command where Effect schemas are the source of truth.
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
- declare function defineEffectCommand<
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
- * Define a named positional argument schema for `defineEffectCommand()`.
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
- }): ArgSpec<Name, SchemaType, Variadic>;
180
+ }): EffectArgDef<Name, SchemaType, Variadic>;
139
181
  /**
140
- * Define a flag schema for `defineEffectCommand()` with optional alias metadata.
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
- }): FlagSpec<SchemaType, Alias>;
148
- export { flag, defineEffectCommand, arg, InferSchemaOutput, InferFlagsFromShape, InferFlagsFromConfig, InferArgsFromSpecs, InferArgsFromConfig, FlagSpec, FlagShape, FlagOptions, EffectSchemaLike, EffectCommandRunHandler, EffectCommandDef, ArgSpecs, ArgSpec, ArgOptions };
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 };