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.
- package/CHANGELOG.md +12 -0
- package/dist/index.d.ts +618 -563
- package/dist/index.esm.js +1011 -402
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1014 -401
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/columnSchema/array.test.ts +67 -0
- package/src/columnSchema/array.ts +39 -13
- package/src/columnSchema/boolean.test.ts +17 -0
- package/src/columnSchema/boolean.ts +5 -1
- package/src/columnSchema/columnType.test.ts +230 -107
- package/src/columnSchema/columnType.ts +198 -28
- package/src/columnSchema/columnTypes.ts +28 -15
- package/src/columnSchema/columnsSchema.ts +6 -4
- package/src/columnSchema/commonMethods.ts +11 -4
- package/src/columnSchema/dateTime.test.ts +298 -0
- package/src/columnSchema/dateTime.ts +59 -2
- package/src/columnSchema/enum.test.ts +33 -0
- package/src/columnSchema/enum.ts +11 -1
- package/src/columnSchema/json/array.test.ts +21 -0
- package/src/columnSchema/json/array.ts +27 -13
- package/src/columnSchema/json/discriminatedUnion.test.ts +32 -0
- package/src/columnSchema/json/discriminatedUnion.ts +17 -2
- package/src/columnSchema/json/enum.test.ts +9 -0
- package/src/columnSchema/json/enum.ts +9 -1
- package/src/columnSchema/json/index.ts +19 -19
- package/src/columnSchema/json/instanceOf.test.ts +8 -0
- package/src/columnSchema/json/instanceOf.ts +4 -1
- package/src/columnSchema/json/intersection.test.ts +19 -0
- package/src/columnSchema/json/intersection.ts +9 -1
- package/src/columnSchema/json/lazy.test.ts +22 -0
- package/src/columnSchema/json/lazy.ts +22 -1
- package/src/columnSchema/json/literal.test.ts +7 -0
- package/src/columnSchema/json/literal.ts +12 -1
- package/src/columnSchema/json/map.test.ts +10 -0
- package/src/columnSchema/json/map.ts +21 -1
- package/src/columnSchema/json/nativeEnum.test.ts +10 -0
- package/src/columnSchema/json/nativeEnum.ts +4 -1
- package/src/columnSchema/json/nullable.test.ts +18 -0
- package/src/columnSchema/json/nullish.test.ts +18 -0
- package/src/columnSchema/json/object.test.ts +77 -0
- package/src/columnSchema/json/object.ts +31 -3
- package/src/columnSchema/json/optional.test.ts +18 -0
- package/src/columnSchema/json/record.test.ts +14 -0
- package/src/columnSchema/json/record.ts +12 -1
- package/src/columnSchema/json/scalarTypes.test.ts +133 -0
- package/src/columnSchema/json/scalarTypes.ts +90 -1
- package/src/columnSchema/json/set.test.ts +29 -0
- package/src/columnSchema/json/set.ts +26 -7
- package/src/columnSchema/json/tuple.test.ts +17 -0
- package/src/columnSchema/json/tuple.ts +16 -1
- package/src/columnSchema/json/typeBase.test.ts +123 -0
- package/src/columnSchema/json/typeBase.ts +52 -13
- package/src/columnSchema/json/union.test.ts +10 -0
- package/src/columnSchema/json/union.ts +18 -1
- package/src/columnSchema/json.test.ts +17 -0
- package/src/columnSchema/json.ts +10 -2
- package/src/columnSchema/number.test.ts +176 -0
- package/src/columnSchema/number.ts +48 -1
- package/src/columnSchema/string.test.ts +412 -0
- package/src/columnSchema/string.ts +126 -15
- package/src/columnSchema/timestamps.test.ts +6 -6
- package/src/columnSchema/virtual.ts +4 -0
- package/src/db.ts +1 -1
- package/src/query.ts +1 -1
- package/src/queryMethods/create.ts +6 -6
- package/src/queryMethods/for.ts +3 -3
- package/src/queryMethods/having.ts +1 -1
- package/src/queryMethods/join.ts +4 -4
- package/src/queryMethods/json.ts +1 -1
- package/src/queryMethods/queryMethods.ts +2 -2
- package/src/queryMethods/select.ts +3 -3
- package/src/queryMethods/update.ts +17 -17
- package/src/queryMethods/where.test.ts +1 -1
- package/src/queryMethods/where.ts +4 -4
- package/src/relations.ts +1 -1
- package/src/sql/aggregate.ts +2 -2
- package/src/sql/copy.ts +3 -3
- package/src/sql/delete.ts +5 -5
- package/src/sql/fromAndAs.ts +4 -4
- package/src/sql/having.ts +7 -7
- package/src/sql/insert.ts +5 -5
- package/src/sql/join.ts +16 -16
- package/src/sql/select.ts +6 -6
- package/src/sql/toSql.ts +24 -24
- package/src/sql/update.ts +4 -4
- package/src/sql/where.ts +18 -18
- package/src/utils.test.ts +9 -0
- package/src/utils.ts +3 -0
- 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:
|
|
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
|
+
});
|
package/src/columnSchema/enum.ts
CHANGED
|
@@ -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
|
|
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<
|
|
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
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
//
|
|
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,
|
|
@@ -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
|
};
|