pqb 0.0.1 → 0.0.3

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.
Files changed (55) hide show
  1. package/README.md +5 -0
  2. package/package.json +5 -2
  3. package/rollup.config.js +2 -34
  4. package/src/adapter.ts +11 -9
  5. package/src/columnSchema/array.ts +115 -3
  6. package/src/columnSchema/boolean.ts +4 -1
  7. package/src/columnSchema/columnType.test.ts +1 -1
  8. package/src/columnSchema/columnType.ts +227 -5
  9. package/src/columnSchema/columnTypes.test.ts +568 -0
  10. package/src/columnSchema/columnTypes.ts +136 -24
  11. package/src/columnSchema/columnsSchema.ts +1 -1
  12. package/src/columnSchema/commonMethods.ts +162 -80
  13. package/src/columnSchema/dateTime.ts +41 -10
  14. package/src/columnSchema/enum.ts +11 -6
  15. package/src/columnSchema/json/discriminatedUnion.ts +51 -42
  16. package/src/columnSchema/json/enum.ts +9 -9
  17. package/src/columnSchema/json/lazy.ts +5 -3
  18. package/src/columnSchema/json/nativeEnum.ts +1 -1
  19. package/src/columnSchema/json/nullish.ts +1 -1
  20. package/src/columnSchema/json/record.ts +14 -11
  21. package/src/columnSchema/json/scalarTypes.ts +17 -8
  22. package/src/columnSchema/json/set.ts +4 -4
  23. package/src/columnSchema/json/tuple.ts +17 -2
  24. package/src/columnSchema/json/typeBase.ts +11 -16
  25. package/src/columnSchema/json/union.ts +1 -1
  26. package/src/columnSchema/json.ts +4 -4
  27. package/src/columnSchema/number.ts +41 -30
  28. package/src/columnSchema/string.ts +28 -19
  29. package/src/columnSchema/utils.ts +0 -2
  30. package/src/{operators.test.ts → columnsOperators.test.ts} +0 -0
  31. package/src/{operators.ts → columnsOperators.ts} +0 -0
  32. package/src/common.ts +18 -16
  33. package/src/db.ts +10 -8
  34. package/src/index.ts +2 -7
  35. package/src/query.ts +2 -2
  36. package/src/queryMethods/aggregate.ts +6 -3
  37. package/src/queryMethods/get.ts +6 -3
  38. package/src/queryMethods/index.ts +22 -0
  39. package/src/queryMethods/join.ts +1 -1
  40. package/src/queryMethods/log.ts +5 -5
  41. package/src/queryMethods/select.test.ts +2 -2
  42. package/src/queryMethods/select.ts +10 -7
  43. package/src/queryMethods/then.ts +8 -19
  44. package/src/queryMethods/transaction.test.ts +2 -2
  45. package/src/queryMethods/transaction.ts +5 -5
  46. package/src/queryMethods/update.ts +11 -14
  47. package/src/sql/having.ts +1 -1
  48. package/src/test-utils.ts +3 -3
  49. package/src/utils.ts +3 -0
  50. package/dist/index.d.ts +0 -3630
  51. package/dist/index.esm.js +0 -4587
  52. package/dist/index.esm.js.map +0 -1
  53. package/dist/index.js +0 -4691
  54. package/dist/index.js.map +0 -1
  55. package/tsconfig.build.json +0 -6
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # pqb
2
+
3
+ A query builder for Postgres database focused on type safety with TypeScript.
4
+
5
+ [Read docs here](https://porm.netlify.app/guide/query-builder.html).
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "pqb",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Postgres query builder",
5
+ "homepage": "https://porm.netlify.app/guide/query-builder.html",
5
6
  "repository": {
6
7
  "type": "git",
7
- "url": "git+https://github.com/romeerez/porm"
8
+ "url": "git+https://github.com/romeerez/porm/tree/main/packages/pqb"
8
9
  },
9
10
  "main": "dist/index.js",
10
11
  "module": "dist/index.esm.js",
@@ -29,6 +30,7 @@
29
30
  }
30
31
  },
31
32
  "keywords": [
33
+ "pg",
32
34
  "postgres",
33
35
  "query-builder"
34
36
  ],
@@ -39,6 +41,7 @@
39
41
  "rollup": "^2.79.0",
40
42
  "rollup-plugin-dts": "^4.2.2",
41
43
  "rollup-plugin-esbuild": "^4.10.1",
44
+ "esbuild": "^0.15.10",
42
45
  "@swc/core": "^1.2.210",
43
46
  "@swc/jest": "^0.2.21",
44
47
  "@types/jest": "^28.1.2",
package/rollup.config.js CHANGED
@@ -1,35 +1,3 @@
1
- import dts from 'rollup-plugin-dts';
2
- import esbuild from 'rollup-plugin-esbuild';
1
+ import config from '../../rollup.config';
3
2
 
4
- export default [
5
- {
6
- input: 'src/index.ts',
7
- plugins: [esbuild()],
8
- output: [
9
- {
10
- file: 'dist/index.js',
11
- format: 'cjs',
12
- sourcemap: true,
13
- },
14
- ],
15
- },
16
- {
17
- input: 'src/index.ts',
18
- plugins: [esbuild()],
19
- output: [
20
- {
21
- file: 'dist/index.esm.js',
22
- format: 'es',
23
- sourcemap: true,
24
- },
25
- ],
26
- },
27
- {
28
- input: 'src/index.ts',
29
- plugins: [dts()],
30
- output: {
31
- file: `dist/index.d.ts`,
32
- format: 'es',
33
- },
34
- },
35
- ];
3
+ export default config;
package/src/adapter.ts CHANGED
@@ -7,7 +7,7 @@ export interface QueryResultRow {
7
7
 
8
8
  export type TypeParsers = Record<number, (input: string) => unknown>;
9
9
 
10
- type Query = string | { text: string; values?: unknown[] };
10
+ export type QueryInput = string | { text: string; values?: unknown[] };
11
11
 
12
12
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
13
  export type QueryResult<T extends QueryResultRow = any> = {
@@ -34,6 +34,7 @@ for (const key in types.builtins) {
34
34
  types.builtins.TIMESTAMP,
35
35
  types.builtins.TIMESTAMPTZ,
36
36
  types.builtins.TIME,
37
+ types.builtins.CIRCLE,
37
38
  ].forEach((id) => {
38
39
  delete defaultTypeParsers[id];
39
40
  });
@@ -55,7 +56,7 @@ export class Adapter {
55
56
 
56
57
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
58
  async query<T extends QueryResultRow = any>(
58
- query: Query,
59
+ query: QueryInput,
59
60
  types: TypeParsers = this.types,
60
61
  ): Promise<QueryResult<T>> {
61
62
  const client = await this.pool.connect();
@@ -68,7 +69,7 @@ export class Adapter {
68
69
 
69
70
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
71
  async arrays<R extends any[] = any[]>(
71
- query: Query,
72
+ query: QueryInput,
72
73
  types: TypeParsers = this.types,
73
74
  ): Promise<QueryArraysResult<R>> {
74
75
  const client = await this.pool.connect();
@@ -80,7 +81,7 @@ export class Adapter {
80
81
  }
81
82
 
82
83
  async transaction<Result>(
83
- cb: (adapter: Adapter) => Promise<Result>,
84
+ cb: (adapter: TransactionAdapter) => Promise<Result>,
84
85
  ): Promise<Result> {
85
86
  const client = await this.pool.connect();
86
87
  try {
@@ -97,6 +98,7 @@ export class Adapter {
97
98
  client.release();
98
99
  }
99
100
  }
101
+
100
102
  destroy(): Promise<void> {
101
103
  return this.pool.end();
102
104
  }
@@ -104,7 +106,7 @@ export class Adapter {
104
106
 
105
107
  const performQuery = <T extends QueryResultRow>(
106
108
  client: PoolClient,
107
- query: Query,
109
+ query: QueryInput,
108
110
  types: TypeParsers,
109
111
  ) => {
110
112
  return client.query<T>({
@@ -121,7 +123,7 @@ const performQuery = <T extends QueryResultRow>(
121
123
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
122
124
  const performQueryArrays = <T extends any[] = any[]>(
123
125
  client: PoolClient,
124
- query: Query,
126
+ query: QueryInput,
125
127
  types: TypeParsers,
126
128
  ) => {
127
129
  return client.query<T>({
@@ -145,7 +147,7 @@ export class TransactionAdapter implements Adapter {
145
147
 
146
148
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
147
149
  async query<T extends QueryResultRow = any>(
148
- query: Query,
150
+ query: QueryInput,
149
151
  types: TypeParsers = this.types,
150
152
  ): Promise<QueryResult<T>> {
151
153
  return await performQuery<T>(this.client, query, types);
@@ -153,14 +155,14 @@ export class TransactionAdapter implements Adapter {
153
155
 
154
156
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
155
157
  async arrays<R extends any[] = any[]>(
156
- query: Query,
158
+ query: QueryInput,
157
159
  types: TypeParsers = this.types,
158
160
  ): Promise<QueryArraysResult<R>> {
159
161
  return await performQueryArrays<R>(this.client, query, types);
160
162
  }
161
163
 
162
164
  async transaction<Result>(
163
- cb: (adapter: Adapter) => Promise<Result>,
165
+ cb: (adapter: TransactionAdapter) => Promise<Result>,
164
166
  ): Promise<Result> {
165
167
  return await cb(this);
166
168
  }
@@ -1,5 +1,20 @@
1
- import { ColumnType } from './columnType';
2
- import { Operators } from '../operators';
1
+ import { ColumnData, ColumnType } from './columnType';
2
+ import { Operators } from '../columnsOperators';
3
+ import { assignMethodsToClass } from './utils';
4
+ import { arrayMethods } from './commonMethods';
5
+
6
+ export type ArrayData<Item extends ColumnType> = ColumnData & {
7
+ item: Item;
8
+ min?: number;
9
+ max?: number;
10
+ length?: number;
11
+ };
12
+
13
+ type ArrayMethods = typeof arrayMethods;
14
+
15
+ export interface ArrayColumn<Item extends ColumnType>
16
+ extends ColumnType<Item['type'][], typeof Operators.array>,
17
+ ArrayMethods {}
3
18
 
4
19
  export class ArrayColumn<Item extends ColumnType> extends ColumnType<
5
20
  Item['type'][],
@@ -7,7 +22,7 @@ export class ArrayColumn<Item extends ColumnType> extends ColumnType<
7
22
  > {
8
23
  dataType = 'array' as const;
9
24
  operators = Operators.array;
10
- data: { item: Item };
25
+ data: ArrayData<Item>;
11
26
 
12
27
  constructor(item: Item) {
13
28
  super();
@@ -18,4 +33,101 @@ export class ArrayColumn<Item extends ColumnType> extends ColumnType<
18
33
  toSQL() {
19
34
  return `${this.data.item.toSQL()}[]`;
20
35
  }
36
+
37
+ parseFn = (input: unknown) => {
38
+ const entries: unknown[] = [];
39
+ parseArray(
40
+ input as string,
41
+ 0,
42
+ (input as string).length,
43
+ entries,
44
+ false,
45
+ this.data.item,
46
+ );
47
+ return entries;
48
+ };
21
49
  }
50
+
51
+ const parseArray = (
52
+ input: string,
53
+ pos: number,
54
+ len: number,
55
+ entries: unknown[],
56
+ nested: boolean,
57
+ item: ColumnType,
58
+ ): number => {
59
+ if (input[0] === '[') {
60
+ while (pos < len) {
61
+ let char = input[pos++];
62
+ if (char === '\\') {
63
+ char = input[pos++];
64
+ }
65
+ if (char === '=') break;
66
+ }
67
+ }
68
+
69
+ let quote = false;
70
+ let start = pos;
71
+ while (pos < len) {
72
+ let char = input[pos++];
73
+ const escaped = char === '\\';
74
+ if (escaped) {
75
+ char = input[pos++];
76
+ }
77
+
78
+ if (char === '"' && !escaped) {
79
+ if (quote) {
80
+ pushEntry(input, start, pos, entries, item);
81
+ } else {
82
+ start = pos;
83
+ }
84
+ quote = !quote;
85
+ } else if (char === ',' && !quote) {
86
+ if (start !== pos) {
87
+ pushEntry(input, start, pos, entries, item);
88
+ }
89
+ start = pos;
90
+ } else if (char === '{' && !quote) {
91
+ let array: unknown[];
92
+ let nestedItem = item;
93
+ if (nested) {
94
+ array = [];
95
+ entries.push(array);
96
+ if ('item' in item.data) {
97
+ nestedItem = (item as ArrayColumn<ColumnType>).data
98
+ .item as ColumnType;
99
+ }
100
+ } else {
101
+ array = entries;
102
+ }
103
+ pos = parseArray(input, pos, len, array, true, nestedItem);
104
+ start = pos + 1;
105
+ } else if (char === '}' && !quote) {
106
+ if (start !== pos) {
107
+ pushEntry(input, start, pos, entries, item);
108
+ }
109
+ start = pos + 1;
110
+ break;
111
+ }
112
+ }
113
+
114
+ return pos;
115
+ };
116
+
117
+ assignMethodsToClass(ArrayColumn, arrayMethods);
118
+
119
+ const pushEntry = (
120
+ input: string,
121
+ start: number,
122
+ pos: number,
123
+ entries: unknown[],
124
+ item: ColumnType,
125
+ ) => {
126
+ let entry: unknown = input.slice(start, pos - 1);
127
+ if (entry === 'NULL') {
128
+ entry = null;
129
+ } else if (item.parseItem) {
130
+ entry = item.parseItem(entry as string);
131
+ }
132
+ entries.push(entry);
133
+ };
@@ -1,10 +1,13 @@
1
1
  import { ColumnType } from './columnType';
2
- import { Operators } from '../operators';
2
+ import { Operators } from '../columnsOperators';
3
3
 
4
+ // 1 byte, true or false
4
5
  export class BooleanColumn extends ColumnType<
5
6
  boolean,
6
7
  typeof Operators.boolean
7
8
  > {
8
9
  dataType = 'boolean' as const;
9
10
  operators = Operators.boolean;
11
+
12
+ parseItem = (input: string) => input[0] === 't';
10
13
  }
@@ -1,5 +1,5 @@
1
1
  import { ColumnType } from './columnType';
2
- import { Operators } from '../operators';
2
+ import { Operators } from '../columnsOperators';
3
3
  import {
4
4
  adapter,
5
5
  AssertEqual,
@@ -1,5 +1,8 @@
1
- import { Operator, Operators } from '../operators';
2
- import { EmptyObject } from './utils';
1
+ import { Operator, Operators } from '../columnsOperators';
2
+ import { JSONTypeAny } from './json/typeBase';
3
+ import { ColumnsShape } from './columnsSchema';
4
+ import { RawExpression, StringKey } from '../common';
5
+ import { MaybeArray } from '../utils';
3
6
 
4
7
  export type ColumnOutput<T extends ColumnType> = T['type'];
5
8
 
@@ -7,9 +10,10 @@ export type ColumnInput<T extends ColumnType> = T['inputType'];
7
10
 
8
11
  export type NullableColumn<T extends ColumnType> = Omit<
9
12
  T,
10
- 'type' | 'operators'
13
+ 'type' | 'inputType' | 'operators'
11
14
  > & {
12
15
  type: T['type'] | null;
16
+ inputType: T['inputType'] | null;
13
17
  isNullable: true;
14
18
  operators: Omit<T['operators'], 'equals' | 'not'> & {
15
19
  equals: Operator<T['type'] | null>;
@@ -20,8 +24,90 @@ export type NullableColumn<T extends ColumnType> = Omit<
20
24
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
25
  export type AnyColumnType = ColumnType<any, Record<string, Operator<any>>>;
22
26
 
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
28
+ export type AnyColumnTypeCreator = (...args: any[]) => AnyColumnType | {};
29
+
30
+ export type ColumnTypesBase = Record<
31
+ string,
32
+ // eslint-disable-next-line @typescript-eslint/ban-types
33
+ AnyColumnTypeCreator
34
+ >;
35
+
23
36
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
- export type AnyColumnTypeCreator = (...args: any[]) => AnyColumnType;
37
+ export type ValidationContext = any;
38
+
39
+ export type ColumnData = {
40
+ default?: unknown;
41
+ validationDefault?: unknown;
42
+ index?: Omit<SingleColumnIndexOptions, 'column'>;
43
+ comment?: string;
44
+ collate?: string;
45
+ compression?: string;
46
+ foreignKey?: ForeignKey<string, string[]>;
47
+ };
48
+
49
+ type ForeignKeyMatch = 'FULL' | 'PARTIAL' | 'SIMPLE';
50
+
51
+ type ForeignKeyAction =
52
+ | 'NO ACTION'
53
+ | 'RESTRICT'
54
+ | 'CASCADE'
55
+ | 'SET NULL'
56
+ | 'SET DEFAULT';
57
+
58
+ export type ForeignKey<Table extends string, Columns extends string[]> = (
59
+ | {
60
+ fn(): new () => { table: Table };
61
+ }
62
+ | {
63
+ table: Table;
64
+ }
65
+ ) & {
66
+ columns: Columns;
67
+ } & ForeignKeyOptions;
68
+
69
+ export type DropMode = 'CASCADE' | 'RESTRICT';
70
+
71
+ export type ForeignKeyOptions = {
72
+ name?: string;
73
+ match?: ForeignKeyMatch;
74
+ onUpdate?: ForeignKeyAction;
75
+ onDelete?: ForeignKeyAction;
76
+ dropMode?: DropMode;
77
+ };
78
+
79
+ export type IndexColumnOptions = {
80
+ column: string;
81
+ expression?: number | string;
82
+ collate?: string;
83
+ operator?: string;
84
+ order?: string;
85
+ };
86
+
87
+ export type IndexOptions = {
88
+ name?: string;
89
+ unique?: boolean;
90
+ using?: string;
91
+ include?: MaybeArray<string>;
92
+ with?: string;
93
+ tablespace?: string;
94
+ where?: string;
95
+ dropMode?: 'CASCADE' | 'RESTRICT';
96
+ };
97
+
98
+ export type SingleColumnIndexOptions = IndexColumnOptions & IndexOptions;
99
+
100
+ export type ForeignKeyModel = new () => {
101
+ table: string;
102
+ };
103
+
104
+ export type ForeignKeyModelWithColumns = new () => {
105
+ table: string;
106
+ columns: { shape: ColumnsShape };
107
+ };
108
+
109
+ export type ColumnNameOfModel<Model extends ForeignKeyModelWithColumns> =
110
+ StringKey<keyof InstanceType<Model>['columns']['shape']>;
25
111
 
26
112
  export abstract class ColumnType<
27
113
  Type = unknown,
@@ -33,18 +119,68 @@ export abstract class ColumnType<
33
119
 
34
120
  type!: Type;
35
121
  inputType!: InputType;
36
- data = {} as EmptyObject;
122
+ data = {} as ColumnData;
37
123
  isPrimaryKey = false;
38
124
  isHidden = false;
39
125
  isNullable = false;
126
+
40
127
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
128
  encodeFn?: (input: any) => unknown;
42
129
  parseFn?: (input: unknown) => unknown;
130
+ // parse item in array:
131
+ parseItem?: (input: string) => unknown;
132
+
133
+ chain = [] as (
134
+ | ['transform', (input: unknown, ctx: ValidationContext) => unknown]
135
+ | ['to', (input: unknown) => JSONTypeAny | undefined, JSONTypeAny]
136
+ | ['refine', (input: unknown) => unknown]
137
+ | ['superRefine', (input: unknown, ctx: ValidationContext) => unknown]
138
+ )[];
43
139
 
44
140
  primaryKey<T extends ColumnType>(this: T): T & { isPrimaryKey: true } {
45
141
  return Object.assign(this, { isPrimaryKey: true as const });
46
142
  }
47
143
 
144
+ foreignKey<
145
+ T extends ColumnType,
146
+ Model extends ForeignKeyModelWithColumns,
147
+ Column extends ColumnNameOfModel<Model>,
148
+ >(
149
+ this: T,
150
+ fn: () => Model,
151
+ column: Column,
152
+ options?: ForeignKeyOptions,
153
+ ): Omit<T, 'foreignKeyData'> & {
154
+ foreignKeyData: ForeignKey<InstanceType<Model>['table'], [Column]>;
155
+ };
156
+ foreignKey<T extends ColumnType, Table extends string, Column extends string>(
157
+ this: T,
158
+ table: Table,
159
+ column: Column,
160
+ options?: ForeignKeyOptions,
161
+ ): Omit<T, 'foreignKeyData'> & {
162
+ foreignKeyData: ForeignKey<Table, [Column]>;
163
+ };
164
+ foreignKey(
165
+ fnOrTable: (() => ForeignKeyModel) | string,
166
+ column: string,
167
+ options: ForeignKeyOptions = {},
168
+ ) {
169
+ const cloned = Object.create(this);
170
+ if (typeof fnOrTable === 'string') {
171
+ cloned.data = {
172
+ ...this.data,
173
+ foreignKey: { table: fnOrTable, columns: [column], ...options },
174
+ };
175
+ } else {
176
+ cloned.data = {
177
+ ...this.data,
178
+ foreignKey: { fn: fnOrTable, columns: [column], ...options },
179
+ };
180
+ }
181
+ return cloned;
182
+ }
183
+
48
184
  hidden<T extends ColumnType>(this: T): T & { isHidden: true } {
49
185
  return Object.assign(this, { isHidden: true as const });
50
186
  }
@@ -68,10 +204,96 @@ export abstract class ColumnType<
68
204
  fn: (input: T['type']) => Output,
69
205
  ): Omit<T, 'type'> & { type: Output } {
70
206
  this.parseFn = fn;
207
+ this.parseItem = fn;
71
208
  return this as unknown as Omit<T, 'type'> & { type: Output };
72
209
  }
73
210
 
74
211
  toSQL() {
75
212
  return this.dataType;
76
213
  }
214
+
215
+ default<T extends ColumnType>(this: T, value: T['type'] | RawExpression): T {
216
+ const cloned = Object.create(this);
217
+ cloned.data = { ...cloned.data, default: value };
218
+ return cloned;
219
+ }
220
+
221
+ index<T extends ColumnType>(
222
+ this: T,
223
+ options: Omit<SingleColumnIndexOptions, 'column'> = {},
224
+ ) {
225
+ const cloned = Object.create(this);
226
+ cloned.data = { ...cloned.data, index: options };
227
+ return cloned;
228
+ }
229
+
230
+ unique<T extends ColumnType>(
231
+ this: T,
232
+ options: Omit<SingleColumnIndexOptions, 'column' | 'unique'> = {},
233
+ ): T {
234
+ const cloned = Object.create(this);
235
+ cloned.data = { ...cloned.data, index: { ...options, unique: true } };
236
+ return cloned;
237
+ }
238
+
239
+ comment<T extends ColumnType>(this: T, comment: string): T {
240
+ const cloned = Object.create(this);
241
+ cloned.data = { ...cloned.data, comment };
242
+ return cloned;
243
+ }
244
+
245
+ validationDefault<T extends ColumnType>(this: T, value: T['type']): T {
246
+ const cloned = Object.create(this);
247
+ cloned.data = { ...cloned.data, validationDefault: value };
248
+ return cloned;
249
+ }
250
+
251
+ compression<T extends ColumnType>(this: T, compression: string): T {
252
+ const cloned = Object.create(this);
253
+ cloned.data = { ...cloned.data, compression };
254
+ return cloned;
255
+ }
256
+
257
+ collate<T extends ColumnType>(this: T, collate: string): T {
258
+ const cloned = Object.create(this);
259
+ cloned.data = { ...cloned.data, collate };
260
+ return cloned;
261
+ }
262
+
263
+ transform<T extends ColumnType, Transformed>(
264
+ this: T,
265
+ fn: (input: T['type'], ctx: ValidationContext) => Transformed,
266
+ ): Omit<T, 'type'> & { type: Transformed } {
267
+ const cloned = Object.create(this);
268
+ cloned.chain = [...this.chain, ['transform', fn]];
269
+ return cloned as Omit<T, 'type'> & { type: Transformed };
270
+ }
271
+
272
+ to<T extends ColumnType, ToType extends ColumnType>(
273
+ this: T,
274
+ fn: (input: T['type']) => ToType['type'] | undefined,
275
+ type: ToType,
276
+ ): ToType {
277
+ const cloned = Object.create(type);
278
+ cloned.chain = [...this.chain, ['to', fn, type], ...cloned.chain];
279
+ return cloned as ToType;
280
+ }
281
+
282
+ refine<T extends ColumnType, RefinedOutput extends T['type']>(
283
+ this: T,
284
+ check: (arg: T['type']) => unknown,
285
+ ): T & { type: RefinedOutput } {
286
+ const cloned = Object.create(this);
287
+ cloned.chain = [...this.chain, ['refine', check]];
288
+ return cloned as T & { type: RefinedOutput };
289
+ }
290
+
291
+ superRefine<T extends ColumnType, RefinedOutput extends T['type']>(
292
+ this: T,
293
+ check: (arg: T['type'], ctx: ValidationContext) => unknown,
294
+ ): T & { type: RefinedOutput } {
295
+ const cloned = Object.create(this);
296
+ cloned.chain = [...this.chain, ['superRefine', check]];
297
+ return cloned as T & { type: RefinedOutput };
298
+ }
77
299
  }