Primitive
Parses raw command-line strings into typed values.
A Primitive<A> receives one string and returns an Effect that either
produces an A or fails with a parser message. Argument and Flag build
on these primitives to add names, aliases, defaults, prompts, configuration
fallbacks, repetition, and help metadata. Primitive parsers cover common
scalar values, paths, files, structured config files, schema-decoded input,
redacted values, and key-value pairs.
Constructors
Creates a primitive that parses boolean values from string input.
Details
Recognizes various forms of true/false values:
- True values: "true", "1", "y", "yes", "on"
- False values: "false", "0", "n", "no", "off"
Signature
declare const boolean: Primitive<boolean>Example
(Parsing boolean values)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Primitive } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const parseBoolean = Effect.all([ Primitive.boolean.parse("true"), Primitive.boolean.parse("yes"), Primitive.boolean.parse("false"), Primitive.boolean.parse("0")])
await Effect.runPromise(parseBoolean.pipe(Effect.provide(CliTestLayer))) // => [true, true, false, false]Creates a primitive that accepts only specific choice values mapped to custom types.
Signature
declare function choice<A>(choices: readonly Array<readonly [string, A]>): Primitive<A>Example
(Parsing choices)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Primitive } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
type LogLevel = "debug" | "info" | "warn" | "error"
const logLevelPrimitive = Primitive.choice<LogLevel>([ ["debug", "debug"], ["info", "info"], ["warn", "warn"], ["error", "error"]])
const parseLogLevel = Effect.all([ logLevelPrimitive.parse("info"), logLevelPrimitive.parse("debug")])
await Effect.runPromise(parseLogLevel.pipe(Effect.provide(CliTestLayer))) // => ["info", "debug"]Creates a primitive that parses Date objects from string input.
Signature
declare const date: Primitive<Date>Example
(Parsing date values)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Primitive } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const parseDate = Effect.gen(function*() { const result = yield* Primitive.date.parse("2023-12-25") return result.toISOString()})
await Effect.runPromise(parseDate.pipe(Effect.provide(CliTestLayer))) // => "2023-12-25T00:00:00.000Z"Creates a primitive that reads a file and parses its content as structured data.
Details
The parser is selected from options.format when provided, otherwise from
the file extension. Supported formats include INI, JSON, TOML, YAML, and YML.
Signature
declare function fileParse(options?: FileParseOptions): Primitive<unknown>Example
(Parsing file content)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Primitive } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const services = Layer.mergeAll( Path.layer, FileSystem.layerNoop({ exists: () => Effect.succeed(true), stat: () => Effect.succeed({ type: "File" } as FileSystem.File.Info), readFileString: () => Effect.succeed('{"private":true}') }), Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const jsonFilePrimitive = Primitive.fileParse({ format: "json" })
const loadConfig = Effect.gen(function*() { const config = yield* jsonFilePrimitive.parse("./package.json") return config as { private: boolean }}).pipe(Effect.provide(services))
await Effect.runPromise(loadConfig) // => { private: true }fileSchema
Reads and parses file content using the specified schema.
Signature
declare function fileSchema<A>(schema: ConstraintDecoder<A, Environment>, options?: { readonly errorFormatter?: Formatter<string>; readonly format?: "json" | "ini" | "toml" | "yaml";}): Primitive<A>Example
(Parsing file content with a schema)
import { Effect, FileSystem, Layer, Path, Schema, Stdio, Terminal } from "effect"import { Primitive } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const services = Layer.mergeAll( Path.layer, FileSystem.layerNoop({ exists: () => Effect.succeed(true), stat: () => Effect.succeed({ type: "File" } as FileSystem.File.Info), readFileString: () => Effect.succeed('{"private":true}') }), Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const ConfigSchema = Schema.Struct({ private: Schema.Boolean})
const jsonConfigPrimitive = Primitive.fileSchema(ConfigSchema, { format: "json"})
const loadConfig = Effect.gen(function*() { return yield* jsonConfigPrimitive.parse("./package.json")}).pipe(Effect.provide(services))
await Effect.runPromise(loadConfig) // => { private: true }Creates a primitive that reads and returns the contents of a file as a string.
Signature
declare const fileText: Primitive<string>Example
(Reading file text)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Primitive } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const services = Layer.mergeAll( Path.layer, FileSystem.layerNoop({ exists: () => Effect.succeed(true), stat: () => Effect.succeed({ type: "File" } as FileSystem.File.Info), readFileString: () => Effect.succeed('{"private":true}') }), Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const readConfigFile = Effect.gen(function*() { const content = yield* Primitive.fileText.parse("./package.json") return JSON.parse(content) as { private: boolean }}).pipe(Effect.provide(services))
await Effect.runPromise(readConfigFile) // => { private: true }Creates a primitive that parses floating-point numbers from string input.
Signature
declare const float: Primitive<number>Example
(Parsing floating-point numbers)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Primitive } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const parseFloat = Effect.all([ Primitive.float.parse("3.14"), Primitive.float.parse("-42.5"), Primitive.float.parse("0")])
await Effect.runPromise(parseFloat.pipe(Effect.provide(CliTestLayer))) // => [3.14, -42.5, 0]Creates a primitive that parses integer numbers from string input.
Signature
declare const integer: Primitive<number>Example
(Parsing integer values)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Primitive } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const parseInteger = Effect.all([ Primitive.integer.parse("42"), Primitive.integer.parse("-123"), Primitive.integer.parse("0")])
await Effect.runPromise(parseInteger.pipe(Effect.provide(CliTestLayer))) // => [42, -123, 0]keyValuePair
Parses a single key=value pair into a record object.
Signature
declare const keyValuePair: Primitive<Record<string, string>>Example
(Parsing key-value pairs)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Primitive } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const parseKeyValue = Effect.all([ Primitive.keyValuePair.parse("name=john"), Primitive.keyValuePair.parse("port=3000"), Primitive.keyValuePair.parse("debug=true")])
const result = await Effect.runPromise(parseKeyValue.pipe(Effect.provide(CliTestLayer)))result // => [{ name: "john" }, { port: "3000" }, { debug: "true" }]Creates a sentinel primitive that always fails to parse a value.
When to use
Use when you need a CLI primitive for flags that do not accept values.
Signature
declare const none: Primitive<never>Example
(Rejecting option values)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Primitive } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const program = Effect.gen(function*() { // This will always fail - useful for boolean flags return yield* Primitive.none.parse("any-value")})
await Effect.runPromise(Effect.flip(program).pipe(Effect.provide(CliTestLayer))) // => "This option does not accept values"Creates a primitive that validates and resolves file system paths.
Signature
declare function path(pathType: PathType, mustExist?: boolean): Primitive<string>Example
(Parsing file system paths)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Primitive } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const services = Layer.mergeAll( Path.layer, FileSystem.layerNoop({ exists: () => Effect.succeed(true), stat: () => Effect.succeed({ type: "File" } as FileSystem.File.Info) }), Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const program = Effect.gen(function*() { const filePrimitive = Primitive.path("file", true) const filePath = yield* filePrimitive.parse("./package.json") return filePath.endsWith("/package.json")}).pipe(Effect.provide(services))
await Effect.runPromise(program) // => trueCreates a primitive that wraps string input in Redacted.
Details
The wrapped value is hidden when formatted or inspected, while the original
string remains available through the Redacted API when explicitly needed.
Signature
declare const redacted: Primitive<Redacted.Redacted<string>>Example
(Parsing redacted values)
import { Effect, FileSystem, Layer, Path, Redacted, Stdio, Terminal } from "effect"import { Primitive } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const parseRedacted = Effect.gen(function*() { const result = yield* Primitive.redacted.parse("secret-password") return [Redacted.value(result), String(result)] as const})
await Effect.runPromise(parseRedacted.pipe(Effect.provide(CliTestLayer))) // => ["secret-password", "<redacted>"]Creates a primitive that accepts any string value without validation.
Signature
declare const string: Primitive<string>Example
(Parsing string values)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Primitive } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const parseString = Effect.all([ Primitive.string.parse("hello world"), Primitive.string.parse(""), Primitive.string.parse("123")])
await Effect.runPromise(parseString.pipe(Effect.provide(CliTestLayer))) // => ["hello world", "", "123"]Getters
getTypeName
Gets a human-readable type name for a primitive.
When to use
Use when you need the display type name for a Primitive, such as when
generating CLI help documentation.
Signature
declare function getTypeName<A>(primitive: Primitive<A>): stringExample
(Getting primitive type names)
import { Primitive } from "effect/unstable/cli"
Primitive.getTypeName(Primitive.string) // => "string"Primitive.getTypeName(Primitive.integer) // => "integer"Primitive.getTypeName(Primitive.boolean) // => "boolean"Primitive.getTypeName(Primitive.date) // => "date"Primitive.getTypeName(Primitive.keyValuePair) // => "key=value"
const logLevelChoice = Primitive.choice([ ["debug", "debug"], ["info", "info"]])Primitive.getTypeName(logLevelChoice) // => "choice"Models
Specifies the type of path validation to perform.
Signature
type PathType = "file" | "directory" | "either"Example
(Choosing path validation)
import { Primitive } from "effect/unstable/cli"
// Only accept filesconst filePath = Primitive.path("file", true)
// Only accept directoriesconst dirPath = Primitive.path("directory", true)
// Accept either files or directoriesconst anyPath = Primitive.path("either", false)
const tags = [filePath._tag, dirPath._tag, anyPath._tag] // => ["Path", "Path", "Path"]Represents a primitive type that can parse string input into a typed value.
Signature
interface Primitive<out A> extends Variance<A> { readonly _tag: string; readonly parse: (value: string) => Effect<A, string, Environment>;}Example
(Parsing values with primitives)
import { Effect, FileSystem, Layer, Path, Stdio, Terminal } from "effect"import { Primitive } from "effect/unstable/cli"import { ChildProcessSpawner } from "effect/unstable/process"
const CliTestLayer = Layer.mergeAll( FileSystem.layerNoop({}), Path.layer, Stdio.layerTest({}), Layer.succeed(Terminal.Terminal, Terminal.make({ columns: Effect.succeed(80), rows: Effect.succeed(24), readInput: Effect.die("unused"), readLine: Effect.die("unused"), display: () => Effect.void })), Layer.succeed( ChildProcessSpawner.ChildProcessSpawner, ChildProcessSpawner.make(() => Effect.die("unused")) ))
const program = Effect.gen(function*() { const stringResult = yield* Primitive.string.parse("hello") const numberResult = yield* Primitive.integer.parse("42") const boolResult = yield* Primitive.boolean.parse("true") return [stringResult, numberResult, boolResult] as const})
await Effect.runPromise(program.pipe(Effect.provide(CliTestLayer))) // => ["hello", 42, true]Options
FileParseOptions type
Represents options which can be provided to methods that deal with parsing file content.
Signature
type FileParseOptions = { readonly format?: "ini" | "json" | "toml" | "yaml";}FileSchemaOptions type
Represents options which can be provided to methods that deal with parsing
file content and decoding the file content with a Schema.
Signature
type FileSchemaOptions = Struct.Simplify<FileParseOptions & { readonly errorFormatter?: Formatter<string>;}>