or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

codec.mdcombinators.mdcore-types.mddecoder.mdencoder.mdindex.mdinfrastructure.mdprimitives.mdrefinement.mdreporters.mdschema.mdtask-decoder.mdvalidation.md
tile.json

infrastructure.mddocs/

0

# Infrastructure Modules (Experimental)

1

2

**⚠️ EXPERIMENTAL:** These modules are experimental infrastructure for error handling and composition in the modern io-ts API.

3

4

Core infrastructure modules that provide error handling and functional composition utilities for the experimental decoder system.

5

6

## DecodeError Module

7

8

Structured error system for representing validation failures with detailed context information.

9

10

### Error Types

11

12

Algebraic data type representing different kinds of decode errors with structured information.

13

14

```typescript { .api }

15

/**

16

* Union type representing all possible decode error variants

17

*/

18

type DecodeError<E> = Leaf<E> | Key<E> | Index<E> | Member<E> | Lazy<E> | Wrap<E>;

19

20

/**

21

* Leaf error - basic validation failure

22

*/

23

interface Leaf<E> {

24

readonly _tag: 'Leaf';

25

readonly actual: unknown;

26

readonly error: E;

27

}

28

29

/**

30

* Key error - object property validation failure

31

*/

32

interface Key<E> {

33

readonly _tag: 'Key';

34

readonly key: string;

35

readonly kind: Kind;

36

readonly errors: FreeSemigroup<DecodeError<E>>;

37

}

38

39

/**

40

* Index error - array element validation failure

41

*/

42

interface Index<E> {

43

readonly _tag: 'Index';

44

readonly index: number;

45

readonly kind: Kind;

46

readonly errors: FreeSemigroup<DecodeError<E>>;

47

}

48

49

/**

50

* Member error - union member validation failure

51

*/

52

interface Member<E> {

53

readonly _tag: 'Member';

54

readonly index: number;

55

readonly errors: FreeSemigroup<DecodeError<E>>;

56

}

57

58

/**

59

* Lazy error - recursive decoder validation failure

60

*/

61

interface Lazy<E> {

62

readonly _tag: 'Lazy';

63

readonly id: string;

64

readonly errors: FreeSemigroup<DecodeError<E>>;

65

}

66

67

/**

68

* Wrap error - custom message wrapper for errors

69

*/

70

interface Wrap<E> {

71

readonly _tag: 'Wrap';

72

readonly error: E;

73

readonly errors: FreeSemigroup<DecodeError<E>>;

74

}

75

76

/**

77

* Kind of property/element validation

78

*/

79

type Kind = 'required' | 'optional';

80

81

/**

82

* Required property/element constant

83

*/

84

const required: 'required';

85

86

/**

87

* Optional property/element constant

88

*/

89

const optional: 'optional';

90

```

91

92

### Error Constructors

93

94

Functions to create different types of decode errors.

95

96

```typescript { .api }

97

/**

98

* Create a leaf error for basic validation failure

99

* @param actual - The actual value that failed validation

100

* @param error - The error message or object

101

*/

102

function leaf<E>(actual: unknown, error: E): DecodeError<E>;

103

104

/**

105

* Create a key error for object property validation failure

106

* @param key - The property key that failed

107

* @param kind - Whether the property is required or optional

108

* @param errors - Nested errors from property validation

109

*/

110

function key<E>(key: string, kind: Kind, errors: FreeSemigroup<DecodeError<E>>): DecodeError<E>;

111

112

/**

113

* Create an index error for array element validation failure

114

* @param index - The array index that failed

115

* @param kind - Whether the element is required or optional

116

* @param errors - Nested errors from element validation

117

*/

118

function index<E>(index: number, kind: Kind, errors: FreeSemigroup<DecodeError<E>>): DecodeError<E>;

119

120

/**

121

* Create a member error for union validation failure

122

* @param index - The union member index that failed

123

* @param errors - Nested errors from member validation

124

*/

125

function member<E>(index: number, errors: FreeSemigroup<DecodeError<E>>): DecodeError<E>;

126

127

/**

128

* Create a lazy error for recursive decoder failure

129

* @param id - Identifier for the recursive decoder

130

* @param errors - Nested errors from recursive validation

131

*/

132

function lazy<E>(id: string, errors: FreeSemigroup<DecodeError<E>>): DecodeError<E>;

133

134

/**

135

* Create a wrap error to add custom message to existing errors

136

* @param error - Custom error message or object

137

* @param errors - The wrapped errors

138

*/

139

function wrap<E>(error: E, errors: FreeSemigroup<DecodeError<E>>): DecodeError<E>;

140

```

141

142

### Error Processing

143

144

Functions for pattern matching and processing decode errors.

145

146

```typescript { .api }

147

/**

148

* Pattern match on DecodeError types

149

* @param patterns - Object with handlers for each error variant

150

*/

151

function fold<E, R>(patterns: {

152

Leaf: (input: unknown, error: E) => R;

153

Key: (key: string, kind: Kind, errors: FreeSemigroup<DecodeError<E>>) => R;

154

Index: (index: number, kind: Kind, errors: FreeSemigroup<DecodeError<E>>) => R;

155

Member: (index: number, errors: FreeSemigroup<DecodeError<E>>) => R;

156

Lazy: (id: string, errors: FreeSemigroup<DecodeError<E>>) => R;

157

Wrap: (error: E, errors: FreeSemigroup<DecodeError<E>>) => R;

158

}): (error: DecodeError<E>) => R;

159

160

/**

161

* Get Semigroup instance for DecodeError accumulation

162

*/

163

function getSemigroup<E = never>(): Semigroup<FreeSemigroup<DecodeError<E>>>;

164

```

165

166

**Usage Examples:**

167

168

```typescript

169

import * as DE from "io-ts/DecodeError";

170

import * as FS from "io-ts/FreeSemigroup";

171

172

// Create basic leaf error

173

const leafError = DE.leaf(42, "Expected string");

174

175

// Create nested object error

176

const keyError = DE.key(

177

"username",

178

DE.required,

179

FS.of(DE.leaf("", "Username cannot be empty"))

180

);

181

182

// Create array validation error

183

const indexError = DE.index(

184

2,

185

DE.required,

186

FS.of(DE.leaf("invalid", "Expected number"))

187

);

188

189

// Pattern match on error types

190

const formatError = DE.fold<string, string>({

191

Leaf: (actual, error) => `Invalid value ${JSON.stringify(actual)}: ${error}`,

192

Key: (key, kind, errors) => `Property '${key}' (${kind}): ${/* format nested errors */}`,

193

Index: (index, kind, errors) => `Element [${index}] (${kind}): ${/* format nested errors */}`,

194

Member: (index, errors) => `Union member ${index}: ${/* format nested errors */}`,

195

Lazy: (id, errors) => `Recursive type '${id}': ${/* format nested errors */}`,

196

Wrap: (error, errors) => `${error}: ${/* format nested errors */}`

197

});

198

199

console.log(formatError(leafError));

200

// Output: "Invalid value 42: Expected string"

201

```

202

203

## FreeSemigroup Module

204

205

Free semigroup implementation for accumulating validation errors in a structured way.

206

207

### FreeSemigroup Type

208

209

Algebraic data type representing a free semigroup structure.

210

211

```typescript { .api }

212

/**

213

* Free semigroup type - either a single value or concatenation of two free semigroups

214

*/

215

type FreeSemigroup<A> = Of<A> | Concat<A>;

216

217

/**

218

* Single value in free semigroup

219

*/

220

interface Of<A> {

221

readonly _tag: 'Of';

222

readonly value: A;

223

}

224

225

/**

226

* Concatenation of two free semigroups

227

*/

228

interface Concat<A> {

229

readonly _tag: 'Concat';

230

readonly left: FreeSemigroup<A>;

231

readonly right: FreeSemigroup<A>;

232

}

233

```

234

235

### Constructors

236

237

Functions to create FreeSemigroup values.

238

239

```typescript { .api }

240

/**

241

* Create FreeSemigroup from single value

242

* @param value - The value to wrap

243

*/

244

function of<A>(value: A): FreeSemigroup<A>;

245

246

/**

247

* Concatenate two FreeSemigroup values

248

* @param left - Left FreeSemigroup

249

* @param right - Right FreeSemigroup

250

*/

251

function concat<A>(left: FreeSemigroup<A>, right: FreeSemigroup<A>): FreeSemigroup<A>;

252

```

253

254

### Processing

255

256

Functions for processing FreeSemigroup structures.

257

258

```typescript { .api }

259

/**

260

* Fold over FreeSemigroup structure

261

* @param onOf - Handler for single values

262

* @param onConcat - Handler for concatenations

263

*/

264

function fold<A, R>(

265

onOf: (value: A) => R,

266

onConcat: (left: FreeSemigroup<A>, right: FreeSemigroup<A>) => R

267

): (fs: FreeSemigroup<A>) => R;

268

269

/**

270

* Get Semigroup instance for FreeSemigroup

271

*/

272

function getSemigroup<A = never>(): Semigroup<FreeSemigroup<A>>;

273

```

274

275

**Usage Examples:**

276

277

```typescript

278

import * as FS from "io-ts/FreeSemigroup";

279

import { pipe } from "fp-ts/function";

280

281

// Create single value

282

const single = FS.of("First error");

283

284

// Create another value

285

const another = FS.of("Second error");

286

287

// Concatenate errors

288

const combined = FS.concat(single, another);

289

290

// Process the structure

291

const collectErrors = FS.fold<string, string[]>(

292

(value) => [value], // Single error becomes array with one element

293

(left, right) => [

294

...collectErrors(left),

295

...collectErrors(right)

296

] // Concatenation becomes merged arrays

297

);

298

299

console.log(collectErrors(combined));

300

// Output: ["First error", "Second error"]

301

302

// Use with Semigroup instance

303

const semigroup = FS.getSemigroup<string>();

304

const result = semigroup.concat(

305

FS.of("Error 1"),

306

semigroup.concat(

307

FS.of("Error 2"),

308

FS.of("Error 3")

309

)

310

);

311

312

console.log(collectErrors(result));

313

// Output: ["Error 1", "Error 2", "Error 3"]

314

```

315

316

## Integration Example

317

318

How DecodeError and FreeSemigroup work together in validation:

319

320

```typescript

321

import * as DE from "io-ts/DecodeError";

322

import * as FS from "io-ts/FreeSemigroup";

323

import * as D from "io-ts/Decoder";

324

325

// Simulate validation of a user object

326

const validateUser = (input: unknown): string[] => {

327

// This would typically be done by a decoder, but shown here for illustration

328

if (typeof input !== 'object' || input === null) {

329

const error = DE.leaf(input, "Expected object");

330

return [formatDecodeError(error)];

331

}

332

333

const obj = input as Record<string, unknown>;

334

const errors: FS.FreeSemigroup<DE.DecodeError<string>>[] = [];

335

336

// Validate name property

337

if (typeof obj.name !== 'string') {

338

errors.push(FS.of(DE.key("name", DE.required, FS.of(DE.leaf(obj.name, "Expected string")))));

339

}

340

341

// Validate age property

342

if (typeof obj.age !== 'number') {

343

errors.push(FS.of(DE.key("age", DE.required, FS.of(DE.leaf(obj.age, "Expected number")))));

344

}

345

346

// Combine all errors

347

if (errors.length === 0) return [];

348

349

const combined = errors.reduce((acc, err) =>

350

acc ? FS.concat(acc, err) : err

351

);

352

353

return combined ? flattenErrors(combined) : [];

354

};

355

356

// Helper to format individual decode errors

357

const formatDecodeError = DE.fold<string, string>({

358

Leaf: (actual, error) => `${error} (got ${JSON.stringify(actual)})`,

359

Key: (key, kind, errors) => `${key}: ${flattenErrors(errors).join(', ')}`,

360

Index: (index, kind, errors) => `[${index}]: ${flattenErrors(errors).join(', ')}`,

361

Member: (index, errors) => `member ${index}: ${flattenErrors(errors).join(', ')}`,

362

Lazy: (id, errors) => `${id}: ${flattenErrors(errors).join(', ')}`,

363

Wrap: (error, errors) => `${error} (${flattenErrors(errors).join(', ')})`

364

});

365

366

// Helper to flatten FreeSemigroup of errors into array

367

const flattenErrors = (fs: FS.FreeSemigroup<DE.DecodeError<string>>): string[] => {

368

return FS.fold<DE.DecodeError<string>, string[]>(

369

(error) => [formatDecodeError(error)],

370

(left, right) => [...flattenErrors(left), ...flattenErrors(right)]

371

)(fs);

372

};

373

374

// Usage

375

console.log(validateUser({ name: 123, age: "thirty" }));

376

// Output: [

377

// "name: Expected string (got 123)",

378

// "age: Expected number (got \"thirty\")"

379

// ]

380

```

381

382

## Advanced Error Tree Processing

383

384

For complex error processing, you can build tree structures and traverse them:

385

386

```typescript

387

import * as DE from "io-ts/DecodeError";

388

import * as FS from "io-ts/FreeSemigroup";

389

390

interface ErrorTree {

391

message: string;

392

path: string[];

393

children: ErrorTree[];

394

}

395

396

const buildErrorTree = (

397

fs: FS.FreeSemigroup<DE.DecodeError<string>>,

398

path: string[] = []

399

): ErrorTree[] => {

400

return FS.fold<DE.DecodeError<string>, ErrorTree[]>(

401

(error) => [

402

DE.fold<string, ErrorTree>({

403

Leaf: (actual, msg) => ({

404

message: msg,

405

path,

406

children: []

407

}),

408

Key: (key, kind, errors) => ({

409

message: `Property '${key}' ${kind === DE.required ? '(required)' : '(optional)'}`,

410

path: [...path, key],

411

children: buildErrorTree(errors, [...path, key])

412

}),

413

Index: (index, kind, errors) => ({

414

message: `Element [${index}] ${kind === DE.required ? '(required)' : '(optional)'}`,

415

path: [...path, `[${index}]`],

416

children: buildErrorTree(errors, [...path, `[${index}]`])

417

}),

418

Member: (index, errors) => ({

419

message: `Union member ${index}`,

420

path: [...path, `<member-${index}>`],

421

children: buildErrorTree(errors, [...path, `<member-${index}>`])

422

}),

423

Lazy: (id, errors) => ({

424

message: `Recursive type '${id}'`,

425

path: [...path, `<${id}>`],

426

children: buildErrorTree(errors, [...path, `<${id}>`])

427

}),

428

Wrap: (msg, errors) => ({

429

message: msg,

430

path,

431

children: buildErrorTree(errors, path)

432

})

433

})(error)

434

],

435

(left, right) => [...buildErrorTree(left, path), ...buildErrorTree(right, path)]

436

)(fs);

437

};

438

439

// This enables rich error reporting with full context paths

440

```