diff --git a/docs/3.results/4.table_changes.md b/docs/3.results/4.table_changes.md index ca4f77b..5817dfd 100644 --- a/docs/3.results/4.table_changes.md +++ b/docs/3.results/4.table_changes.md @@ -17,9 +17,25 @@ Field types describe the structure of each field: ```typescript type FieldType = - | string // Primitive type name (e.g., "string", "number") - | Array // Array of a given type - | { [subfield: string]: FieldType }; // Nested object + | string // Primitive type name (e.g., "string", "number") + | Array // Array of a given type + | { [subfield: string]: StringFieldType } // Nested object + | import("io-ts").Mixed; // io-ts codec at the field root +``` + +A field can be described either with the string-token vocabulary (primitives, arrays, nested records) or with a single [io-ts](https://github.com/gcanti/io-ts) codec at the root of the field. Codecs are not mixed inside string-tree records — use io-ts combinators (`t.type`, `t.array`, …) to nest within a codec. + +```typescript +import * as t from "io-ts"; + +const Order = { + fields: { + id: "string", + status: t.union([t.literal("open"), t.literal("closed")]), + meta: t.type({ source: t.string, retries: t.number }), + }, + indexes: {}, +}; ``` ## Define Tables in a Schema diff --git a/package.json b/package.json index a25d153..5d3dcb7 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,8 @@ "@types/node": "^22.19.15", "@types/semver": "^7.7.1", "chai": "^4.5.0", + "fp-ts": "^2.16.0", + "io-ts": "^2.2.0", "mongodb-memory-server-core": "^11.0.1", "release-it": "^19.2.4", "release-it-changelogen": "^0.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7b3e80e..7d66232 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,6 +30,12 @@ importers: chai: specifier: ^4.5.0 version: 4.5.0 + fp-ts: + specifier: ^2.16.0 + version: 2.16.11 + io-ts: + specifier: ^2.2.0 + version: 2.2.22(fp-ts@2.16.11) mongodb-memory-server-core: specifier: ^11.0.1 version: 11.0.1(socks@2.8.7) @@ -669,6 +675,9 @@ packages: debug: optional: true + fp-ts@2.16.11: + resolution: {integrity: sha512-LaI+KaX2NFkfn1ZGHoKCmcfv7yrZsC3b8NtWsTVQeHkq4F27vI5igUuO53sxqDEa2gNQMHFPmpojDw/1zmUK7w==} + fs-minipass@2.1.0: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} @@ -740,6 +749,11 @@ packages: '@types/node': optional: true + io-ts@2.2.22: + resolution: {integrity: sha512-FHCCztTkHoV9mdBsHpocLpdTAfh956ZQcIkWQxxS0U5HT53vtrcuYdQneEJKH6xILaLNzXVl2Cvwtoy8XNN0AA==} + peerDependencies: + fp-ts: ^2.5.0 + ip-address@10.1.0: resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} engines: {node: '>= 12'} @@ -1940,6 +1954,8 @@ snapshots: optionalDependencies: debug: 4.4.3 + fp-ts@2.16.11: {} + fs-minipass@2.1.0: dependencies: minipass: 3.3.6 @@ -2031,6 +2047,10 @@ snapshots: optionalDependencies: '@types/node': 22.19.15 + io-ts@2.2.22(fp-ts@2.16.11): + dependencies: + fp-ts: 2.16.11 + ip-address@10.1.0: {} is-binary-path@2.1.0: diff --git a/src/schema.ts b/src/schema.ts index df00a51..3220747 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -18,10 +18,22 @@ export interface IndexDefinition { multi?: boolean; } -export type FieldType = +export type StringFieldType = | string - | Array - | { [subfield: string]: FieldType }; + | Array + | { [subfield: string]: StringFieldType }; + +interface IoTsCodec { + readonly _A: unknown; + readonly _O: unknown; + readonly _I: unknown; + readonly name: string; + is(u: unknown): boolean; + encode(a: never): unknown; + decode(i: unknown): unknown; +} + +export type FieldType = StringFieldType | IoTsCodec; /** * Schema table definition diff --git a/src/tests/datasets/codec_fields.ts b/src/tests/datasets/codec_fields.ts new file mode 100644 index 0000000..a1346e2 --- /dev/null +++ b/src/tests/datasets/codec_fields.ts @@ -0,0 +1,16 @@ +import * as t from "io-ts"; +import type { SchemaDefinition } from "../../schema"; + +export const definition: SchemaDefinition = { + orders: { + fields: { + id: "string", + status: t.union([t.literal("open"), t.literal("closed")]), + meta: t.type({ source: t.string, retries: t.number }), + tags: "string[]", + }, + indexes: { + status: {}, + }, + }, +};