pqb 0.1.0 → 0.1.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pqb",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Postgres query builder",
5
5
  "homepage": "https://porm.netlify.app/guide/query-builder.html",
6
6
  "repository": {
@@ -177,10 +177,14 @@ export const columnTypes = {
177
177
  jsonText: () => new JSONTextColumn(),
178
178
  array: <Item extends ColumnType>(item: Item) => new ArrayColumn(item),
179
179
 
180
- timestamps: () => ({
181
- createdAt: new TimestampColumn().default(raw('now()')),
182
- updatedAt: new TimestampColumn().default(raw('now()')),
183
- }),
180
+ timestamps<T extends ColumnType>(this: {
181
+ timestamp(): T;
182
+ }): { createdAt: T; updatedAt: T } {
183
+ return {
184
+ createdAt: this.timestamp().default(raw('now()')),
185
+ updatedAt: this.timestamp().default(raw('now()')),
186
+ };
187
+ },
184
188
 
185
189
  primaryKey(columns: string[], options?: { name?: string }) {
186
190
  tableData.primaryKey = { columns, options };
@@ -1,16 +1,19 @@
1
1
  import {
2
- AssertEqual,
2
+ assertType,
3
3
  Chat,
4
4
  chatData,
5
5
  expectQueryNotMutated,
6
6
  expectSql,
7
7
  Message,
8
8
  messageData,
9
+ MessageRecord,
9
10
  now,
10
11
  Profile,
11
12
  profileData,
13
+ ProfileRecord,
12
14
  User,
13
15
  userData,
16
+ UserRecord,
14
17
  useTestDatabase,
15
18
  } from '../test-utils';
16
19
  import { raw } from '../common';
@@ -29,25 +32,27 @@ describe('selectMethods', () => {
29
32
  describe('select', () => {
30
33
  it('should have no effect if no columns provided', () => {
31
34
  const q = User.all();
35
+ const query = q.select();
36
+
37
+ assertType<Awaited<typeof q>, UserRecord[]>();
38
+
32
39
  expectSql(
33
- q.select().toSql(),
40
+ query.toSql(),
34
41
  `
35
42
  SELECT * FROM "user"
36
43
  `,
37
44
  );
38
- expectSql(
39
- q.select('id').select().toSql(),
40
- `
41
- SELECT "user"."id" FROM "user"
42
- `,
43
- );
44
45
  expectQueryNotMutated(q);
45
46
  });
46
47
 
47
48
  it('should select provided columns', () => {
48
49
  const q = User.all();
50
+ const query = q.select('id', 'name');
51
+
52
+ assertType<Awaited<typeof query>, Pick<UserRecord, 'id' | 'name'>[]>();
53
+
49
54
  expectSql(
50
- q.select('id', 'name').toSql(),
55
+ query.toSql(),
51
56
  `
52
57
  SELECT "user"."id", "user"."name" FROM "user"
53
58
  `,
@@ -57,8 +62,12 @@ describe('selectMethods', () => {
57
62
 
58
63
  it('should select table.column', () => {
59
64
  const q = User.all();
65
+ const query = q.select('user.id', 'user.name');
66
+
67
+ assertType<Awaited<typeof query>, Pick<UserRecord, 'id' | 'name'>[]>();
68
+
60
69
  expectSql(
61
- q.select('user.id', 'user.name').toSql(),
70
+ query.toSql(),
62
71
  `
63
72
  SELECT "user"."id", "user"."name" FROM "user"
64
73
  `,
@@ -68,12 +77,14 @@ describe('selectMethods', () => {
68
77
 
69
78
  it('should select joined columns', () => {
70
79
  const q = User.all();
80
+ const query = q
81
+ .join(Profile, 'profile.userId', '=', 'user.id')
82
+ .select('user.id', 'profile.userId');
83
+
84
+ assertType<Awaited<typeof query>, { id: number; userId: number }[]>();
71
85
 
72
86
  expectSql(
73
- q
74
- .join(Profile, 'profile.userId', '=', 'user.id')
75
- .select('user.id', 'profile.userId')
76
- .toSql(),
87
+ query.toSql(),
77
88
  `
78
89
  SELECT "user"."id", "profile"."userId" FROM "user"
79
90
  JOIN "profile" ON "profile"."userId" = "user"."id"
@@ -84,11 +95,14 @@ describe('selectMethods', () => {
84
95
 
85
96
  it('should select joined columns with alias', () => {
86
97
  const q = User.all();
98
+ const query = q
99
+ .join(Profile.as('p'), 'p.userId', '=', 'user.id')
100
+ .select('user.id', 'p.userId');
101
+
102
+ assertType<Awaited<typeof query>, { id: number; userId: number }[]>();
103
+
87
104
  expectSql(
88
- q
89
- .join(Profile.as('p'), 'p.userId', '=', 'user.id')
90
- .select('user.id', 'p.userId')
91
- .toSql(),
105
+ query.toSql(),
92
106
  `
93
107
  SELECT "user"."id", "p"."userId" FROM "user"
94
108
  JOIN "profile" AS "p" ON "p"."userId" = "user"."id"
@@ -125,11 +139,10 @@ describe('selectMethods', () => {
125
139
  const q = User.all();
126
140
 
127
141
  const query = q.select('id', profileRelation.where({ bio: 'bio' }));
128
- const eq: AssertEqual<
142
+ assertType<
129
143
  Awaited<typeof query>,
130
144
  { id: number; profile: typeof Profile['type'] | null }[]
131
- > = true;
132
- expect(eq).toBe(true);
145
+ >();
133
146
 
134
147
  expectSql(
135
148
  query.toSql(),
@@ -167,11 +180,11 @@ describe('selectMethods', () => {
167
180
  true
168
181
  >,
169
182
  );
170
- const eq: AssertEqual<
183
+
184
+ assertType<
171
185
  Awaited<typeof query>,
172
186
  { id: number; profile: typeof Profile['type'] }[]
173
- > = true;
174
- expect(eq).toBe(true);
187
+ >();
175
188
  });
176
189
 
177
190
  it('should parse columns in single relation record result', async () => {
@@ -180,6 +193,12 @@ describe('selectMethods', () => {
180
193
  await Profile.insert({ userId, updatedAt: now, createdAt: now });
181
194
 
182
195
  const [record] = await User.select('id', profileRelation);
196
+
197
+ assertType<
198
+ typeof record,
199
+ { id: number; profile: ProfileRecord | null }
200
+ >();
201
+
183
202
  expect(record.profile).toMatchObject({
184
203
  updatedAt: now,
185
204
  createdAt: now,
@@ -213,11 +232,10 @@ describe('selectMethods', () => {
213
232
  const q = User.all();
214
233
 
215
234
  const query = q.select('id', messageRelation.where({ text: 'text' }));
216
- const eq: AssertEqual<
235
+ assertType<
217
236
  Awaited<typeof query>,
218
237
  { id: number; messages: typeof Message['type'][] }[]
219
- > = true;
220
- expect(eq).toBe(true);
238
+ >();
221
239
 
222
240
  expectSql(
223
241
  query.toSql(),
@@ -251,6 +269,9 @@ describe('selectMethods', () => {
251
269
  });
252
270
 
253
271
  const [record] = await User.select('id', messageRelation);
272
+
273
+ assertType<typeof record, { id: number; messages: MessageRecord[] }>();
274
+
254
275
  expect(record.messages[0]).toMatchObject({
255
276
  updatedAt: now,
256
277
  createdAt: now,
@@ -264,6 +285,8 @@ describe('selectMethods', () => {
264
285
  it('should parse columns of the table', async () => {
265
286
  const q = User.select('createdAt');
266
287
 
288
+ assertType<Awaited<typeof q>, { createdAt: Date }[]>();
289
+
267
290
  expect((await q.all())[0].createdAt instanceof Date).toBe(true);
268
291
  expect((await q.take()).createdAt instanceof Date).toBe(true);
269
292
  expect((await q.rows())[0][0] instanceof Date).toBe(true);
@@ -273,6 +296,8 @@ describe('selectMethods', () => {
273
296
  it('should parse columns of the table, selected by column name and table name', async () => {
274
297
  const q = User.select('user.createdAt');
275
298
 
299
+ assertType<Awaited<typeof q>, { createdAt: Date }[]>();
300
+
276
301
  expect((await q.all())[0].createdAt instanceof Date).toBe(true);
277
302
  expect((await q.take()).createdAt instanceof Date).toBe(true);
278
303
  expect((await q.rows())[0][0] instanceof Date).toBe(true);
@@ -284,6 +309,8 @@ describe('selectMethods', () => {
284
309
  'user.createdAt',
285
310
  );
286
311
 
312
+ assertType<Awaited<typeof q>, { createdAt: Date }[]>();
313
+
287
314
  expect((await q.all())[0].createdAt instanceof Date).toBe(true);
288
315
  expect((await q.take()).createdAt instanceof Date).toBe(true);
289
316
  expect((await q.rows())[0][0] instanceof Date).toBe(true);
@@ -296,11 +323,10 @@ describe('selectMethods', () => {
296
323
 
297
324
  const query = q.select({ aliasedId: 'id', aliasedName: 'name' });
298
325
 
299
- const eq: AssertEqual<
326
+ assertType<
300
327
  Awaited<typeof query>,
301
328
  { aliasedId: number; aliasedName: string }[]
302
- > = true;
303
- expect(eq).toBe(true);
329
+ >();
304
330
 
305
331
  expectSql(
306
332
  query.toSql(),
@@ -320,11 +346,10 @@ describe('selectMethods', () => {
320
346
  aliasedName: 'user.name',
321
347
  });
322
348
 
323
- const eq: AssertEqual<
349
+ assertType<
324
350
  Awaited<typeof query>,
325
351
  { aliasedId: number; aliasedName: string }[]
326
- > = true;
327
- expect(eq).toBe(true);
352
+ >();
328
353
 
329
354
  expectSql(
330
355
  query.toSql(),
@@ -338,14 +363,18 @@ describe('selectMethods', () => {
338
363
 
339
364
  it('should select joined columns', () => {
340
365
  const q = User.all();
366
+ const query = q.join(Profile, 'profile.userId', '=', 'user.id').select({
367
+ aliasedId: 'user.id',
368
+ aliasedUserId: 'profile.userId',
369
+ });
370
+
371
+ assertType<
372
+ Awaited<typeof query>,
373
+ { aliasedId: number; aliasedUserId: number }[]
374
+ >();
375
+
341
376
  expectSql(
342
- q
343
- .join(Profile, 'profile.userId', '=', 'user.id')
344
- .select({
345
- aliasedId: 'user.id',
346
- aliasedUserId: 'profile.userId',
347
- })
348
- .toSql(),
377
+ query.toSql(),
349
378
  `
350
379
  SELECT "user"."id" AS "aliasedId", "profile"."userId" AS "aliasedUserId"
351
380
  FROM "user"
@@ -357,14 +386,18 @@ describe('selectMethods', () => {
357
386
 
358
387
  it('should select joined columns with alias', () => {
359
388
  const q = User.all();
389
+ const query = q.join(Profile.as('p'), 'p.userId', '=', 'user.id').select({
390
+ aliasedId: 'user.id',
391
+ aliasedUserId: 'p.userId',
392
+ });
393
+
394
+ assertType<
395
+ Awaited<typeof query>,
396
+ { aliasedId: number; aliasedUserId: number }[]
397
+ >();
398
+
360
399
  expectSql(
361
- q
362
- .join(Profile.as('p'), 'p.userId', '=', 'user.id')
363
- .select({
364
- aliasedId: 'user.id',
365
- aliasedUserId: 'p.userId',
366
- })
367
- .toSql(),
400
+ query.toSql(),
368
401
  `
369
402
  SELECT "user"."id" AS "aliasedId", "p"."userId" AS "aliasedUserId"
370
403
  FROM "user"
@@ -376,8 +409,12 @@ describe('selectMethods', () => {
376
409
 
377
410
  it('can select raw', () => {
378
411
  const q = User.all();
412
+ const query = q.select({ one: raw('1') });
413
+
414
+ assertType<Awaited<typeof query>, { one: unknown }[]>();
415
+
379
416
  expectSql(
380
- q.select({ one: raw('1') }).toSql(),
417
+ query.toSql(),
381
418
  `
382
419
  SELECT 1 AS "one" FROM "user"
383
420
  `,
@@ -387,8 +424,12 @@ describe('selectMethods', () => {
387
424
 
388
425
  it('can select subquery', () => {
389
426
  const q = User.all();
427
+ const query = q.select({ subquery: User.all() });
428
+
429
+ assertType<Awaited<typeof query>, { subquery: UserRecord[] }[]>();
430
+
390
431
  expectSql(
391
- q.select({ subquery: User.all() }).toSql(),
432
+ query.toSql(),
392
433
  `
393
434
  SELECT
394
435
  (
@@ -406,6 +447,8 @@ describe('selectMethods', () => {
406
447
  it('should select all columns', () => {
407
448
  const query = User.select('id', 'name').selectAll();
408
449
 
450
+ assertType<Awaited<typeof query>, UserRecord[]>();
451
+
409
452
  expectSql(query.toSql(), `SELECT * FROM "user"`);
410
453
  });
411
454
  });
@@ -418,6 +461,8 @@ describe('selectMethods', () => {
418
461
  date: 'createdAt',
419
462
  });
420
463
 
464
+ assertType<Awaited<typeof q>, { date: Date }[]>();
465
+
421
466
  expect((await q.all())[0].date instanceof Date).toBe(true);
422
467
  expect((await q.take()).date instanceof Date).toBe(true);
423
468
  expect((await q.rows())[0][0] instanceof Date).toBe(true);
@@ -428,6 +473,8 @@ describe('selectMethods', () => {
428
473
  date: 'user.createdAt',
429
474
  });
430
475
 
476
+ assertType<Awaited<typeof q>, { date: Date }[]>();
477
+
431
478
  expect((await q.all())[0].date instanceof Date).toBe(true);
432
479
  expect((await q.take()).date instanceof Date).toBe(true);
433
480
  expect((await q.rows())[0][0] instanceof Date).toBe(true);
@@ -438,6 +485,8 @@ describe('selectMethods', () => {
438
485
  date: 'user.createdAt',
439
486
  });
440
487
 
488
+ assertType<Awaited<typeof q>, { date: Date }[]>();
489
+
441
490
  expect((await q.all())[0].date instanceof Date).toBe(true);
442
491
  expect((await q.take()).date instanceof Date).toBe(true);
443
492
  expect((await q.rows())[0][0] instanceof Date).toBe(true);
@@ -448,6 +497,8 @@ describe('selectMethods', () => {
448
497
  users: User.all(),
449
498
  });
450
499
 
500
+ assertType<Awaited<typeof q>, { users: UserRecord[] }[]>();
501
+
451
502
  expect((await q.all())[0].users[0].createdAt instanceof Date).toBe(true);
452
503
  expect((await q.take()).users[0].createdAt instanceof Date).toBe(true);
453
504
  expect((await q.rows())[0][0][0].createdAt instanceof Date).toBe(true);
@@ -458,6 +509,8 @@ describe('selectMethods', () => {
458
509
  user: User.take(),
459
510
  });
460
511
 
512
+ assertType<Awaited<typeof q>, { user: UserRecord }[]>();
513
+
461
514
  expect((await q.all())[0].user.createdAt instanceof Date).toBe(true);
462
515
  expect((await q.take()).user.createdAt instanceof Date).toBe(true);
463
516
  expect((await q.rows())[0][0].createdAt instanceof Date).toBe(true);
@@ -471,6 +524,8 @@ describe('selectMethods', () => {
471
524
  ),
472
525
  });
473
526
 
527
+ assertType<Awaited<typeof q>, { date: Date }[]>();
528
+
474
529
  expect((await q.all())[0].date instanceof Date).toBe(true);
475
530
  expect((await q.take()).date instanceof Date).toBe(true);
476
531
  expect((await q.rows())[0][0] instanceof Date).toBe(true);
package/src/test-utils.ts CHANGED
@@ -28,6 +28,7 @@ export const db = createDb({
28
28
  },
29
29
  });
30
30
 
31
+ export type UserRecord = typeof User['type'];
31
32
  export const User = db('user', (t) => ({
32
33
  id: t.serial().primaryKey(),
33
34
  name: t.text(),
@@ -47,6 +48,7 @@ export const User = db('user', (t) => ({
47
48
  updatedAt: t.timestamp(),
48
49
  }));
49
50
 
51
+ export type ProfileRecord = typeof Profile['type'];
50
52
  export const Profile = db('profile', (t) => ({
51
53
  id: t.serial().primaryKey(),
52
54
  userId: t.integer().foreignKey('user', 'id'),
@@ -62,6 +64,7 @@ export const Chat = db('chat', (t) => ({
62
64
  updatedAt: t.timestamp(),
63
65
  }));
64
66
 
67
+ export type MessageRecord = typeof Message['type'];
65
68
  export const Message = db('message', (t) => ({
66
69
  id: t.serial().primaryKey(),
67
70
  chatId: t.integer().foreignKey('chat', 'id'),
@@ -110,6 +113,12 @@ export type AssertEqual<T, Expected> = [T] extends [Expected]
110
113
  : false
111
114
  : false;
112
115
 
116
+ export const assertType = <T, Expected>(
117
+ ..._: AssertEqual<T, Expected> extends true ? [] : ['invalid type']
118
+ ) => {
119
+ // noop
120
+ };
121
+
113
122
  export const insert = async <
114
123
  T extends Record<string, unknown> & { id: number },
115
124
  >(