pqb 0.7.13 → 0.8.1

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 (91) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.d.ts +618 -563
  3. package/dist/index.esm.js +1011 -402
  4. package/dist/index.esm.js.map +1 -1
  5. package/dist/index.js +1014 -401
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/columnSchema/array.test.ts +67 -0
  9. package/src/columnSchema/array.ts +39 -13
  10. package/src/columnSchema/boolean.test.ts +17 -0
  11. package/src/columnSchema/boolean.ts +5 -1
  12. package/src/columnSchema/columnType.test.ts +230 -107
  13. package/src/columnSchema/columnType.ts +198 -28
  14. package/src/columnSchema/columnTypes.ts +28 -15
  15. package/src/columnSchema/columnsSchema.ts +6 -4
  16. package/src/columnSchema/commonMethods.ts +11 -4
  17. package/src/columnSchema/dateTime.test.ts +298 -0
  18. package/src/columnSchema/dateTime.ts +59 -2
  19. package/src/columnSchema/enum.test.ts +33 -0
  20. package/src/columnSchema/enum.ts +11 -1
  21. package/src/columnSchema/json/array.test.ts +21 -0
  22. package/src/columnSchema/json/array.ts +27 -13
  23. package/src/columnSchema/json/discriminatedUnion.test.ts +32 -0
  24. package/src/columnSchema/json/discriminatedUnion.ts +17 -2
  25. package/src/columnSchema/json/enum.test.ts +9 -0
  26. package/src/columnSchema/json/enum.ts +9 -1
  27. package/src/columnSchema/json/index.ts +19 -19
  28. package/src/columnSchema/json/instanceOf.test.ts +8 -0
  29. package/src/columnSchema/json/instanceOf.ts +4 -1
  30. package/src/columnSchema/json/intersection.test.ts +19 -0
  31. package/src/columnSchema/json/intersection.ts +9 -1
  32. package/src/columnSchema/json/lazy.test.ts +22 -0
  33. package/src/columnSchema/json/lazy.ts +22 -1
  34. package/src/columnSchema/json/literal.test.ts +7 -0
  35. package/src/columnSchema/json/literal.ts +12 -1
  36. package/src/columnSchema/json/map.test.ts +10 -0
  37. package/src/columnSchema/json/map.ts +21 -1
  38. package/src/columnSchema/json/nativeEnum.test.ts +10 -0
  39. package/src/columnSchema/json/nativeEnum.ts +4 -1
  40. package/src/columnSchema/json/nullable.test.ts +18 -0
  41. package/src/columnSchema/json/nullish.test.ts +18 -0
  42. package/src/columnSchema/json/object.test.ts +77 -0
  43. package/src/columnSchema/json/object.ts +31 -3
  44. package/src/columnSchema/json/optional.test.ts +18 -0
  45. package/src/columnSchema/json/record.test.ts +14 -0
  46. package/src/columnSchema/json/record.ts +12 -1
  47. package/src/columnSchema/json/scalarTypes.test.ts +133 -0
  48. package/src/columnSchema/json/scalarTypes.ts +90 -1
  49. package/src/columnSchema/json/set.test.ts +29 -0
  50. package/src/columnSchema/json/set.ts +26 -7
  51. package/src/columnSchema/json/tuple.test.ts +17 -0
  52. package/src/columnSchema/json/tuple.ts +16 -1
  53. package/src/columnSchema/json/typeBase.test.ts +123 -0
  54. package/src/columnSchema/json/typeBase.ts +52 -13
  55. package/src/columnSchema/json/union.test.ts +10 -0
  56. package/src/columnSchema/json/union.ts +18 -1
  57. package/src/columnSchema/json.test.ts +17 -0
  58. package/src/columnSchema/json.ts +10 -2
  59. package/src/columnSchema/number.test.ts +176 -0
  60. package/src/columnSchema/number.ts +48 -1
  61. package/src/columnSchema/string.test.ts +412 -0
  62. package/src/columnSchema/string.ts +126 -15
  63. package/src/columnSchema/timestamps.test.ts +6 -6
  64. package/src/columnSchema/virtual.ts +4 -0
  65. package/src/db.ts +1 -1
  66. package/src/query.ts +1 -1
  67. package/src/queryMethods/create.ts +6 -6
  68. package/src/queryMethods/for.ts +3 -3
  69. package/src/queryMethods/having.ts +1 -1
  70. package/src/queryMethods/join.ts +4 -4
  71. package/src/queryMethods/json.ts +1 -1
  72. package/src/queryMethods/queryMethods.ts +2 -2
  73. package/src/queryMethods/select.ts +3 -3
  74. package/src/queryMethods/update.ts +17 -17
  75. package/src/queryMethods/where.test.ts +1 -1
  76. package/src/queryMethods/where.ts +4 -4
  77. package/src/relations.ts +1 -1
  78. package/src/sql/aggregate.ts +2 -2
  79. package/src/sql/copy.ts +3 -3
  80. package/src/sql/delete.ts +5 -5
  81. package/src/sql/fromAndAs.ts +4 -4
  82. package/src/sql/having.ts +7 -7
  83. package/src/sql/insert.ts +5 -5
  84. package/src/sql/join.ts +16 -16
  85. package/src/sql/select.ts +6 -6
  86. package/src/sql/toSql.ts +24 -24
  87. package/src/sql/update.ts +4 -4
  88. package/src/sql/where.ts +18 -18
  89. package/src/utils.test.ts +9 -0
  90. package/src/utils.ts +3 -0
  91. package/src/columnSchema/columnTypes.test.ts +0 -527
@@ -0,0 +1,298 @@
1
+ import {
2
+ assertType,
3
+ db,
4
+ expectSql,
5
+ userData,
6
+ useTestDatabase,
7
+ } from '../test-utils/test-utils';
8
+ import {
9
+ DateColumn,
10
+ IntervalColumn,
11
+ TimeColumn,
12
+ TimeInterval,
13
+ TimestampColumn,
14
+ TimestampWithTimeZoneColumn,
15
+ TimeWithTimeZoneColumn,
16
+ } from './dateTime';
17
+ import { columnTypes } from './columnTypes';
18
+
19
+ describe('date time columns', () => {
20
+ useTestDatabase();
21
+
22
+ describe('date', () => {
23
+ it('should output string', async () => {
24
+ const result = await db.get(
25
+ db.raw(() => new DateColumn(), `'1999-01-08'::date`),
26
+ );
27
+ expect(result).toBe('1999-01-08');
28
+
29
+ assertType<typeof result, string>();
30
+ });
31
+
32
+ it('should have toCode', () => {
33
+ const column = new DateColumn();
34
+ expect(column.toCode('t')).toBe('t.date()');
35
+
36
+ const now = new Date();
37
+ const s = now.toISOString();
38
+ expect(column.min(now).max(now).toCode('t')).toBe(
39
+ `t.date().min(new Date('${s}')).max(new Date('${s}'))`,
40
+ );
41
+ });
42
+ });
43
+
44
+ describe('timestamp', () => {
45
+ it('should output string', async () => {
46
+ const result = await db.get(
47
+ db.raw(() => new TimestampColumn(), `'1999-01-08 04:05:06'::timestamp`),
48
+ );
49
+ expect(result).toBe('1999-01-08 04:05:06');
50
+
51
+ assertType<typeof result, string>();
52
+ });
53
+
54
+ it('should have toCode', () => {
55
+ expect(new TimestampColumn().toCode('t')).toBe('t.timestamp()');
56
+ expect(new TimestampColumn(10).toCode('t')).toBe('t.timestamp(10)');
57
+
58
+ const now = new Date();
59
+ const s = now.toISOString();
60
+ expect(new TimestampColumn().min(now).max(now).toCode('t')).toEqual(
61
+ `t.timestamp().min(new Date('${s}')).max(new Date('${s}'))`,
62
+ );
63
+ });
64
+ });
65
+
66
+ describe('timestamp with time zone', () => {
67
+ it('should output string', async () => {
68
+ const result = await db.get(
69
+ db.raw(
70
+ () => new TimestampWithTimeZoneColumn(),
71
+ `'1999-01-08 04:05:06 +0'::timestamptz AT TIME ZONE 'UTC'`,
72
+ ),
73
+ );
74
+ expect(result).toBe('1999-01-08 04:05:06');
75
+
76
+ assertType<typeof result, string>();
77
+ });
78
+
79
+ it('should have toCode', () => {
80
+ expect(new TimestampWithTimeZoneColumn().toCode('t')).toBe(
81
+ 't.timestampWithTimeZone()',
82
+ );
83
+ expect(new TimestampWithTimeZoneColumn(10).toCode('t')).toBe(
84
+ 't.timestampWithTimeZone(10)',
85
+ );
86
+
87
+ const now = new Date();
88
+ const s = now.toISOString();
89
+ expect(
90
+ new TimestampWithTimeZoneColumn().min(now).max(now).toCode('t'),
91
+ ).toEqual(
92
+ `t.timestampWithTimeZone().min(new Date('${s}')).max(new Date('${s}'))`,
93
+ );
94
+ });
95
+ });
96
+
97
+ describe('time', () => {
98
+ it('should output string', async () => {
99
+ const result = await db.get(
100
+ db.raw(() => new TimeColumn(), `'12:00'::time`),
101
+ );
102
+ expect(result).toBe('12:00:00');
103
+
104
+ assertType<typeof result, string>();
105
+ });
106
+
107
+ it('should have toCode', () => {
108
+ expect(new TimeColumn().toCode('t')).toBe('t.time()');
109
+ expect(new TimeColumn(10).toCode('t')).toBe('t.time(10)');
110
+
111
+ const now = new Date();
112
+ const s = now.toISOString();
113
+ expect(new TimeColumn().min(now).max(now).toCode('t')).toEqual(
114
+ `t.time().min(new Date('${s}')).max(new Date('${s}'))`,
115
+ );
116
+ });
117
+ });
118
+
119
+ describe('time with time zone', () => {
120
+ it('should output string', async () => {
121
+ const result = await db.get(
122
+ db.raw(
123
+ () => new TimeWithTimeZoneColumn(),
124
+ `'12:00 +0'::timetz AT TIME ZONE 'UTC'`,
125
+ ),
126
+ );
127
+ expect(result).toBe('12:00:00+00');
128
+
129
+ assertType<typeof result, string>();
130
+ });
131
+
132
+ it('should have toCode', () => {
133
+ expect(new TimeWithTimeZoneColumn().toCode('t')).toBe(
134
+ 't.timeWithTimeZone()',
135
+ );
136
+ expect(new TimeWithTimeZoneColumn(10).toCode('t')).toBe(
137
+ 't.timeWithTimeZone(10)',
138
+ );
139
+
140
+ const now = new Date();
141
+ const s = now.toISOString();
142
+ expect(
143
+ new TimeWithTimeZoneColumn().min(now).max(now).toCode('t'),
144
+ ).toEqual(
145
+ `t.timeWithTimeZone().min(new Date('${s}')).max(new Date('${s}'))`,
146
+ );
147
+ });
148
+ });
149
+
150
+ describe('interval', () => {
151
+ it('should output string', async () => {
152
+ const result = await db.get(
153
+ db.raw(
154
+ () => new IntervalColumn(),
155
+ `'1 year 2 months 3 days 4 hours 5 minutes 6 seconds'::interval`,
156
+ ),
157
+ );
158
+ expect(result).toEqual({
159
+ years: 1,
160
+ months: 2,
161
+ days: 3,
162
+ hours: 4,
163
+ minutes: 5,
164
+ seconds: 6,
165
+ });
166
+
167
+ assertType<typeof result, TimeInterval>();
168
+ });
169
+
170
+ it('should have toCode', () => {
171
+ expect(new IntervalColumn().toCode('t')).toBe('t.interval()');
172
+ expect(new IntervalColumn('fields').toCode('t')).toBe(
173
+ "t.interval('fields')",
174
+ );
175
+ expect(new IntervalColumn('fields', 10).toCode('t')).toBe(
176
+ "t.interval('fields', 10)",
177
+ );
178
+ });
179
+ });
180
+
181
+ describe('asNumber', () => {
182
+ it('should parse and encode timestamp as a number', async () => {
183
+ const UserWithNumberTimestamp = db('user', (t) => ({
184
+ id: t.serial().primaryKey(),
185
+ name: t.text(),
186
+ password: t.text(),
187
+ createdAt: t.timestamp().asNumber(),
188
+ updatedAt: t.timestamp().asNumber(),
189
+ }));
190
+
191
+ const now = Date.now();
192
+
193
+ const createQuery = UserWithNumberTimestamp.create({
194
+ ...userData,
195
+ createdAt: now,
196
+ updatedAt: now,
197
+ });
198
+
199
+ expectSql(
200
+ createQuery.toSql(),
201
+ `
202
+ INSERT INTO "user"("name", "password", "createdAt", "updatedAt")
203
+ VALUES ($1, $2, $3, $4)
204
+ RETURNING *
205
+ `,
206
+ [userData.name, userData.password, new Date(now), new Date(now)],
207
+ );
208
+
209
+ const { id } = await createQuery;
210
+ const user = await UserWithNumberTimestamp.select(
211
+ 'createdAt',
212
+ 'updatedAt',
213
+ ).find(id);
214
+
215
+ assertType<typeof user, { createdAt: number; updatedAt: number }>();
216
+
217
+ expect(typeof user.createdAt).toBe('number');
218
+ expect(typeof user.updatedAt).toBe('number');
219
+
220
+ const updateQuery = UserWithNumberTimestamp.find(id).update({
221
+ createdAt: now,
222
+ updatedAt: now,
223
+ });
224
+
225
+ expectSql(
226
+ updateQuery.toSql(),
227
+ `
228
+ UPDATE "user"
229
+ SET "createdAt" = $1, "updatedAt" = $2
230
+ WHERE "user"."id" = $3
231
+ `,
232
+ [new Date(now), new Date(now), id],
233
+ );
234
+ });
235
+ });
236
+
237
+ describe('asDate', () => {
238
+ it('should parse and encode timestamp as a number', async () => {
239
+ columnTypes
240
+ .text(0, 100)
241
+ .encode((input: number) => input)
242
+ .parse((text) => parseInt(text))
243
+ .as(columnTypes.integer());
244
+
245
+ const UserWithNumberTimestamp = db('user', (t) => ({
246
+ id: t.serial().primaryKey(),
247
+ name: t.text(),
248
+ password: t.text(),
249
+ createdAt: columnTypes.timestamp().asDate(),
250
+ updatedAt: columnTypes.timestamp().asDate(),
251
+ }));
252
+
253
+ const now = new Date();
254
+
255
+ const createQuery = UserWithNumberTimestamp.create({
256
+ ...userData,
257
+ createdAt: now,
258
+ updatedAt: now,
259
+ });
260
+
261
+ expectSql(
262
+ createQuery.toSql(),
263
+ `
264
+ INSERT INTO "user"("name", "password", "createdAt", "updatedAt")
265
+ VALUES ($1, $2, $3, $4)
266
+ RETURNING *
267
+ `,
268
+ [userData.name, userData.password, new Date(now), new Date(now)],
269
+ );
270
+
271
+ const { id } = await createQuery;
272
+ const user = await UserWithNumberTimestamp.select(
273
+ 'createdAt',
274
+ 'updatedAt',
275
+ ).find(id);
276
+
277
+ assertType<typeof user, { createdAt: Date; updatedAt: Date }>();
278
+
279
+ expect(user.createdAt).toBeInstanceOf(Date);
280
+ expect(user.updatedAt).toBeInstanceOf(Date);
281
+
282
+ const updateQuery = UserWithNumberTimestamp.find(id).update({
283
+ createdAt: now,
284
+ updatedAt: now,
285
+ });
286
+
287
+ expectSql(
288
+ updateQuery.toSql(),
289
+ `
290
+ UPDATE "user"
291
+ SET "createdAt" = $1, "updatedAt" = $2
292
+ WHERE "user"."id" = $3
293
+ `,
294
+ [now, now, id],
295
+ );
296
+ });
297
+ });
298
+ });
@@ -1,4 +1,4 @@
1
- import { ColumnData, ColumnType } from './columnType';
1
+ import { Code, columnCode, ColumnData, ColumnType } from './columnType';
2
2
  import { Operators } from '../columnsOperators';
3
3
  import { joinTruthy } from '../utils';
4
4
  import { dateTypeMethods } from './commonMethods';
@@ -37,9 +37,21 @@ export abstract class DateBaseColumn extends ColumnType<
37
37
 
38
38
  assignMethodsToClass(DateBaseColumn, dateTypeMethods);
39
39
 
40
+ const dateDataToCode = (data: DateColumnData) => {
41
+ let code = '';
42
+
43
+ if (data.min) code += `.min(new Date('${data.min.toISOString()}'))`;
44
+ if (data.max) code += `.max(new Date('${data.max.toISOString()}'))`;
45
+
46
+ return code;
47
+ };
48
+
40
49
  // date 4 bytes date (no time of day) 4713 BC 5874897 AD 1 day
41
50
  export class DateColumn extends DateBaseColumn {
42
51
  dataType = 'date' as const;
52
+ toCode(t: string): Code {
53
+ return columnCode(this, t, `${t}.date()${dateDataToCode(this.data)}`);
54
+ }
43
55
  }
44
56
 
45
57
  export type DateTimeColumnData = DateColumnData & {
@@ -84,6 +96,14 @@ export class TimestampColumn<
84
96
  Precision extends number | undefined = undefined,
85
97
  > extends DateTimeBaseClass<Precision> {
86
98
  dataType = 'timestamp' as const;
99
+ toCode(t: string): Code {
100
+ const { precision } = this.data;
101
+ return columnCode(
102
+ this,
103
+ t,
104
+ `${t}.timestamp(${precision || ''})${dateDataToCode(this.data)}`,
105
+ );
106
+ }
87
107
  }
88
108
 
89
109
  // timestamp [ (p) ] with time zone 8 bytes both date and time, with time zone 4713 BC 294276 AD 1 microsecond
@@ -92,6 +112,16 @@ export class TimestampWithTimeZoneColumn<
92
112
  > extends DateTimeWithTimeZoneBaseClass<Precision> {
93
113
  dataType = 'timestamp with time zone' as const;
94
114
  baseDataType = 'timestamp' as const;
115
+ toCode(t: string): Code {
116
+ const { precision } = this.data;
117
+ return columnCode(
118
+ this,
119
+ t,
120
+ `${t}.timestampWithTimeZone(${precision || ''})${dateDataToCode(
121
+ this.data,
122
+ )}`,
123
+ );
124
+ }
95
125
  }
96
126
 
97
127
  // time [ (p) ] [ without time zone ] 8 bytes time of day (no date) 00:00:00 24:00:00 1 microsecond
@@ -99,6 +129,14 @@ export class TimeColumn<
99
129
  Precision extends number | undefined = undefined,
100
130
  > extends DateTimeBaseClass<Precision> {
101
131
  dataType = 'time' as const;
132
+ toCode(t: string): Code {
133
+ const { precision } = this.data;
134
+ return columnCode(
135
+ this,
136
+ t,
137
+ `${t}.time(${precision || ''})${dateDataToCode(this.data)}`,
138
+ );
139
+ }
102
140
  }
103
141
 
104
142
  // time [ (p) ] with time zone 12 bytes time of day (no date), with time zone 00:00:00+1559 24:00:00-1559 1 microsecond
@@ -107,6 +145,14 @@ export class TimeWithTimeZoneColumn<
107
145
  > extends DateTimeWithTimeZoneBaseClass<Precision> {
108
146
  dataType = 'time with time zone' as const;
109
147
  baseDataType = 'time' as const;
148
+ toCode(t: string): Code {
149
+ const { precision } = this.data;
150
+ return columnCode(
151
+ this,
152
+ t,
153
+ `${t}.timeWithTimeZone(${precision || ''})${dateDataToCode(this.data)}`,
154
+ );
155
+ }
110
156
  }
111
157
 
112
158
  export type TimeInterval = {
@@ -124,7 +170,7 @@ export class IntervalColumn<
124
170
  Precision extends number | undefined = undefined,
125
171
  > extends ColumnType<TimeInterval, typeof Operators.date> {
126
172
  dataType = 'interval' as const;
127
- data: DateTimeColumnData & { fields: Fields; precision: Precision };
173
+ data: ColumnData & { fields: Fields; precision: Precision };
128
174
  operators = Operators.date;
129
175
 
130
176
  constructor(fields?: Fields, precision?: Precision) {
@@ -136,6 +182,17 @@ export class IntervalColumn<
136
182
  };
137
183
  }
138
184
 
185
+ toCode(t: string): Code {
186
+ const { fields, precision } = this.data;
187
+ return columnCode(
188
+ this,
189
+ t,
190
+ `${t}.interval(${[fields && `'${fields}'`, precision && String(precision)]
191
+ .filter((part) => part)
192
+ .join(', ')})`,
193
+ );
194
+ }
195
+
139
196
  toSQL() {
140
197
  return joinTruthy(
141
198
  this.dataType,
@@ -0,0 +1,33 @@
1
+ import { assertType, db } from '../test-utils/test-utils';
2
+ import { EnumColumn } from './enum';
3
+
4
+ describe('enum column', () => {
5
+ beforeAll(async () => {
6
+ await db.adapter.query(`
7
+ DROP TYPE IF EXISTS mood
8
+ `);
9
+ await db.adapter.query(`
10
+ CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
11
+ `);
12
+ });
13
+
14
+ type MoodUnion = 'sad' | 'ok' | 'happy';
15
+
16
+ it('should output proper union', async () => {
17
+ const result = await db.get(
18
+ db.raw(
19
+ () => new EnumColumn('mood', ['sad', 'ok', 'happy']),
20
+ `'happy'::mood`,
21
+ ),
22
+ );
23
+ expect(result).toBe('happy');
24
+
25
+ assertType<typeof result, MoodUnion>();
26
+ });
27
+
28
+ it('should have toCode', () => {
29
+ expect(new EnumColumn('mood', ['sad', 'ok', 'happy']).toCode('t')).toBe(
30
+ `t.enum('mood', ['sad', 'ok', 'happy'])`,
31
+ );
32
+ });
33
+ });
@@ -1,4 +1,4 @@
1
- import { ColumnType } from './columnType';
1
+ import { Code, columnCode, ColumnType } from './columnType';
2
2
  import { Operators } from '../columnsOperators';
3
3
 
4
4
  export class EnumColumn<
@@ -12,6 +12,16 @@ export class EnumColumn<
12
12
  super();
13
13
  }
14
14
 
15
+ toCode(t: string): Code {
16
+ return columnCode(
17
+ this,
18
+ t,
19
+ `${t}.enum('${this.enumName}', [${this.options
20
+ .map((option) => `'${option}'`)
21
+ .join(', ')}])`,
22
+ );
23
+ }
24
+
15
25
  toSql() {
16
26
  return this.enumName;
17
27
  }
@@ -0,0 +1,21 @@
1
+ import { array } from './array';
2
+ import { scalarTypes } from './scalarTypes';
3
+
4
+ const { string } = scalarTypes;
5
+
6
+ describe('array', () => {
7
+ it('should have toCode', () => {
8
+ expect(array(string()).toCode('t')).toEqual(['t.string()', '.array()']);
9
+
10
+ expect(array(string()).deepPartial().nonEmpty().toCode('t')).toEqual([
11
+ 't.string().optional()',
12
+ '.array()',
13
+ '.deepPartial().nonEmpty()',
14
+ ]);
15
+
16
+ expect(array(string()).min(1).max(10).length(15).toCode('t')).toEqual([
17
+ 't.string()',
18
+ '.array().min(1).max(10).length(15)',
19
+ ]);
20
+ });
21
+ });
@@ -4,12 +4,14 @@ import {
4
4
  JSONType,
5
5
  JSONTypeAny,
6
6
  JSONTypeData,
7
+ toCode,
7
8
  } from './typeBase';
8
9
  import { arrayMethods, ArrayMethods } from '../commonMethods';
10
+ import { toArray } from '../../utils';
9
11
 
10
12
  export type ArrayCardinality = 'many' | 'atLeastOne';
11
13
 
12
- type arrayOutputType<
14
+ type ArrayOutputType<
13
15
  T extends JSONTypeAny,
14
16
  Cardinality extends ArrayCardinality = 'many',
15
17
  > = Cardinality extends 'atLeastOne'
@@ -19,7 +21,7 @@ type arrayOutputType<
19
21
  export interface JSONArray<
20
22
  Type extends JSONTypeAny,
21
23
  Cardinality extends ArrayCardinality = 'many',
22
- > extends JSONType<arrayOutputType<Type, Cardinality>, 'array'>,
24
+ > extends JSONType<ArrayOutputType<Type, Cardinality>, 'array'>,
23
25
  ArrayMethods {
24
26
  data: JSONTypeData & {
25
27
  min?: number;
@@ -27,10 +29,9 @@ export interface JSONArray<
27
29
  length?: number;
28
30
  };
29
31
  element: Type;
30
- deepPartial(): JSONArray<DeepPartial<Type>, Cardinality>;
31
- nonEmpty(
32
- this: JSONArray<Type>,
33
- ): JSONArray<Type, 'atLeastOne'> & { data: { min: 1 } };
32
+ deepPartial<T extends JSONArray<Type>>(
33
+ this: T,
34
+ ): JSONArray<DeepPartial<Type>, Cardinality>;
34
35
  }
35
36
 
36
37
  export const array = <Type extends JSONTypeAny>(
@@ -39,16 +40,29 @@ export const array = <Type extends JSONTypeAny>(
39
40
  return constructType<JSONArray<Type>>({
40
41
  dataType: 'array' as const,
41
42
  element,
42
- deepPartial(this: JSONArray<Type>) {
43
+ toCode(this: JSONArray<Type>, t: string) {
44
+ let code = '.array()';
45
+
46
+ const { min, max, length, isNonEmpty } = this.data;
47
+
48
+ if (min !== undefined && (!isNonEmpty || (isNonEmpty && min !== 1)))
49
+ code += `.min(${min})`;
50
+
51
+ if (max !== undefined) code += `.max(${max})`;
52
+
53
+ if (length !== undefined) code += `.length(${length})`;
54
+
55
+ return toCode(this, t, [...toArray(this.element.toCode(t)), code]);
56
+ },
57
+ deepPartial<T extends JSONArray<Type>>(this: T) {
43
58
  return {
44
59
  ...this,
45
60
  element: this.element.deepPartial(),
46
- };
47
- },
48
- nonEmpty(this: JSONArray<Type>) {
49
- return this.min(1) as unknown as JSONArray<Type, 'atLeastOne'> & {
50
- data: { min: 1 };
51
- };
61
+ data: {
62
+ ...this.data,
63
+ isDeepPartial: true,
64
+ },
65
+ } as T;
52
66
  },
53
67
  ...arrayMethods,
54
68
  });
@@ -0,0 +1,32 @@
1
+ import { scalarTypes } from './scalarTypes';
2
+ import { discriminatedUnion } from './discriminatedUnion';
3
+ import { object } from './object';
4
+ import { literal } from './literal';
5
+
6
+ describe('discriminatedUnion', () => {
7
+ it('should have toCode', () => {
8
+ expect(
9
+ discriminatedUnion('type', [
10
+ object({
11
+ type: literal('one'),
12
+ a: scalarTypes.string(),
13
+ }),
14
+ object({
15
+ type: literal('two'),
16
+ b: scalarTypes.number(),
17
+ }),
18
+ ]).toCode('t'),
19
+ ).toEqual([
20
+ `t.discriminatedUnion('type', [`,
21
+ [
22
+ 't.object({',
23
+ [`type: t.literal('one'),`, `a: t.string(),`],
24
+ '})',
25
+ 't.object({',
26
+ [`type: t.literal('two'),`, `b: t.number(),`],
27
+ '})',
28
+ ],
29
+ '])',
30
+ ]);
31
+ });
32
+ });
@@ -1,6 +1,7 @@
1
- import { constructType, JSONType, Primitive } from './typeBase';
1
+ import { constructType, JSONType, Primitive, toCode } from './typeBase';
2
2
  import { JSONObject, JSONObjectShape } from './object';
3
3
  import { JSONLiteral } from './literal';
4
+ import { singleQuote } from '../../utils';
4
5
 
5
6
  export interface JSONDiscriminatedUnion<
6
7
  Discriminator extends string,
@@ -11,7 +12,7 @@ export interface JSONDiscriminatedUnion<
11
12
  discriminatorValue: DiscriminatorValue;
12
13
  options: Map<DiscriminatorValue, Option>;
13
14
  _option: Option;
14
- // TODO: gave up on deepPartial type
15
+ // WON'T DO: gave up on deepPartial type
15
16
  // deepPartial(): JSONDiscriminatedUnion<
16
17
  // Discriminator,
17
18
  // {
@@ -72,6 +73,20 @@ export const discriminatedUnion = <
72
73
  discriminatorValue: undefined as unknown as DiscriminatorValue,
73
74
  options: optionsMap,
74
75
  _option: undefined as unknown as Types[number],
76
+ toCode(
77
+ this: JSONDiscriminatedUnion<
78
+ string,
79
+ Primitive,
80
+ JSONDiscriminatedObject<Discriminator, DiscriminatorValue>
81
+ >,
82
+ t: string,
83
+ ) {
84
+ return toCode(this, t, [
85
+ `${t}.discriminatedUnion(${singleQuote(this.discriminator)}, [`,
86
+ options.flatMap((option) => option.toCode(t)),
87
+ '])',
88
+ ]);
89
+ },
75
90
  deepPartial(
76
91
  this: JSONDiscriminatedUnion<
77
92
  Discriminator,
@@ -0,0 +1,9 @@
1
+ import { enumType } from './enum';
2
+
3
+ describe('enum', () => {
4
+ it('should have toCode', () => {
5
+ expect(enumType(['a', 'b', 'c']).toCode('t')).toBe(
6
+ `t.enum(['a', 'b', 'c'])`,
7
+ );
8
+ });
9
+ });
@@ -1,4 +1,5 @@
1
- import { constructType, JSONType } from './typeBase';
1
+ import { constructType, JSONType, toCode } from './typeBase';
2
+ import { singleQuote } from '../../utils';
2
3
 
3
4
  export interface JSONEnum<
4
5
  U extends string = string,
@@ -25,5 +26,12 @@ export const enumType = <U extends string, T extends [U, ...U[]]>(
25
26
  dataType: 'enum',
26
27
  enum: arrayToEnum(options),
27
28
  options,
29
+ toCode(this: JSONEnum<U, T>, t: string) {
30
+ return toCode(
31
+ this,
32
+ t,
33
+ `${t}.enum([${options.map(singleQuote).join(', ')}])`,
34
+ );
35
+ },
28
36
  });
29
37
  };