pqb 0.3.3 → 0.3.4

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.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Postgres query builder",
5
5
  "homepage": "https://porm.netlify.app/guide/query-builder.html",
6
6
  "repository": {
@@ -1,6 +1,7 @@
1
1
  import { ColumnInput, ColumnOutput, ColumnType } from './columnType';
2
2
  import { Operators } from '../columnsOperators';
3
- import { SetOptional, SomeIsTrue, UnionToIntersection } from '../utils';
3
+ import { SetOptional, SomeIsTrue } from '../utils';
4
+ import { StringKey } from '../common';
4
5
 
5
6
  export type ColumnsShape = Record<string, ColumnType>;
6
7
 
@@ -53,63 +54,22 @@ export abstract class PluckResultColumnType<
53
54
  C extends ColumnType,
54
55
  > extends ColumnType<C['type'][], typeof Operators.any> {}
55
56
 
56
- type UnionKeyofToOvlds<S, U> = UnionToIntersection<
57
- U extends keyof S ? (f: U) => void : never
58
- >;
59
-
60
- type PopKeyofColumnShapeUnion<
61
- S extends ColumnsShape,
62
- U extends keyof S,
63
- > = UnionKeyofToOvlds<S, U> extends (a: infer A extends keyof S) => void
64
- ? A
65
- : never;
66
-
67
- type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;
68
-
69
- export type UnionToArray<
70
- S extends ColumnsShape,
71
- T extends keyof S,
72
- A extends [...(keyof S)[]] = [],
73
- > = IsUnion<T> extends true
74
- ? UnionToArray<
75
- S,
76
- Exclude<T, PopKeyofColumnShapeUnion<S, T>>,
77
- [PopKeyofColumnShapeUnion<S, T>, ...A]
78
- >
79
- : [T, ...A];
80
-
81
- type GetPrimaryKeys<S extends ColumnsShape> = UnionToArray<
82
- S,
83
- { [K in keyof S]: S[K] extends { isPrimaryKey: true } ? K : never }[keyof S]
57
+ // resolves in string literal of single primary key
58
+ // if table has two or more primary keys it will resolve in never
59
+ export type SinglePrimaryKey<Shape extends ColumnsShape> = StringKey<
60
+ {
61
+ [K in keyof Shape]: Shape[K]['isPrimaryKey'] extends true
62
+ ? [
63
+ {
64
+ [S in keyof Shape]: Shape[S]['isPrimaryKey'] extends true
65
+ ? S extends K
66
+ ? never
67
+ : S
68
+ : never;
69
+ }[keyof Shape],
70
+ ] extends [never]
71
+ ? K
72
+ : never
73
+ : never;
74
+ }[keyof Shape]
84
75
  >;
85
-
86
- type GetPrimaryTypes<
87
- S extends ColumnsShape,
88
- Keys extends [...(keyof S | string)[]] = GetPrimaryKeys<S>,
89
- > = GetTypesFromKeys<S, Keys>;
90
-
91
- type GetTypesFromKeys<
92
- S extends ColumnsShape,
93
- T extends [...(keyof S)[]],
94
- > = T extends [
95
- infer Head extends keyof S,
96
- ...infer Tail extends [...(keyof S)[]],
97
- ]
98
- ? [GetTypeFromKey<S, Head>, ...GetTypesFromKeys<S, Tail>]
99
- : [];
100
-
101
- type GetTypeFromKey<S extends ColumnsShape, T extends keyof S> = S[T]['type'];
102
-
103
- export class TableSchema<Shape extends ColumnsShape> {
104
- primaryKeys: string extends keyof Shape ? string[] : GetPrimaryKeys<Shape>;
105
- primaryTypes!: GetPrimaryTypes<Shape>;
106
-
107
- constructor(public shape: Shape) {
108
- this.primaryKeys = Object.entries(this.shape)
109
- .filter(([, column]) => {
110
- return column.isPrimaryKey;
111
- })
112
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
113
- .map(([key]) => key) as any;
114
- }
115
- }
package/src/db.ts CHANGED
@@ -18,10 +18,10 @@ import { AdapterOptions, Adapter } from './adapter';
18
18
  import {
19
19
  ColumnsShape,
20
20
  ColumnShapeOutput,
21
- TableSchema,
22
21
  ColumnShapeInput,
23
22
  ColumnTypesBase,
24
23
  getColumnTypes,
24
+ SinglePrimaryKey,
25
25
  } from './columnSchema';
26
26
  import { applyMixins, pushOrNewArray } from './utils';
27
27
  import { StringKey } from './common';
@@ -51,7 +51,8 @@ export interface Db<
51
51
  onQueryBuilder: Query['onQueryBuilder'];
52
52
  table: Table;
53
53
  shape: Shape;
54
- schema: TableSchema<Shape>;
54
+ singlePrimaryKey: SinglePrimaryKey<Shape>;
55
+ primaryKeys: Query['primaryKeys'];
55
56
  type: ColumnShapeOutput<Shape>;
56
57
  inputType: ColumnShapeInput<Shape>;
57
58
  query: QueryData;
@@ -113,7 +114,14 @@ export class Db<
113
114
  this.query.schema = options.schema;
114
115
  }
115
116
 
116
- this.schema = new TableSchema(shape);
117
+ this.primaryKeys = Object.keys(shape).filter(
118
+ (key) => shape[key].isPrimaryKey,
119
+ );
120
+ if (this.primaryKeys.length === 1) {
121
+ this.singlePrimaryKey = this
122
+ .primaryKeys[0] as unknown as SinglePrimaryKey<Shape>;
123
+ }
124
+
117
125
  const columns = Object.keys(
118
126
  shape,
119
127
  ) as unknown as (keyof ColumnShapeOutput<Shape>)[];
package/src/query.ts CHANGED
@@ -8,12 +8,7 @@ import {
8
8
  getValueKey,
9
9
  } from './queryMethods';
10
10
  import { QueryData } from './sql';
11
- import {
12
- ColumnShapeOutput,
13
- ColumnsShape,
14
- ColumnType,
15
- TableSchema,
16
- } from './columnSchema';
11
+ import { ColumnShapeOutput, ColumnsShape, ColumnType } from './columnSchema';
17
12
  import { EmptyObject, Spread } from './utils';
18
13
  import { AliasOrTable, RawExpression, StringKey } from './common';
19
14
  import { Db } from './db';
@@ -51,12 +46,8 @@ export type Query = QueryMethods & {
51
46
  onQueryBuilder: typeof OnQueryBuilder;
52
47
  table?: string;
53
48
  shape: ColumnsShape;
54
- schema: Omit<TableSchema<ColumnsShape>, 'primaryKeys' | 'primaryTypes'> & {
55
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
- primaryKeys: any[];
57
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
- primaryTypes: any[];
59
- };
49
+ singlePrimaryKey: string;
50
+ primaryKeys: string[];
60
51
  type: Record<string, unknown>;
61
52
  inputType: Record<string, unknown>;
62
53
  query: QueryData;
@@ -4,13 +4,13 @@ import {
4
4
  adapter,
5
5
  User,
6
6
  Profile,
7
- AssertEqual,
8
7
  useTestDatabase,
9
8
  db,
10
9
  expectSql,
11
10
  userData,
12
11
  now,
13
12
  assertType,
13
+ UserRecord,
14
14
  } from '../test-utils';
15
15
  import { NumberColumn } from '../columnSchema';
16
16
  import { NotFoundError } from '../errors';
@@ -25,8 +25,7 @@ describe('queryMethods', () => {
25
25
  expect(cloned.table).toBe(User.table);
26
26
  expect(cloned.shape).toBe(User.shape);
27
27
 
28
- const eq: AssertEqual<typeof User, typeof cloned> = true;
29
- expect(eq).toBe(true);
28
+ assertType<typeof User, typeof cloned>();
30
29
  });
31
30
  });
32
31
 
@@ -35,9 +34,7 @@ describe('queryMethods', () => {
35
34
  const sql = User.toSql();
36
35
  expectSql(sql, `SELECT * FROM "user"`);
37
36
 
38
- const eq: AssertEqual<typeof sql, { text: string; values: unknown[] }> =
39
- true;
40
- expect(eq).toBe(true);
37
+ assertType<typeof sql, { text: string; values: unknown[] }>();
41
38
  });
42
39
  });
43
40
 
@@ -60,8 +57,7 @@ describe('queryMethods', () => {
60
57
  .then((res) => res.rows[0]);
61
58
 
62
59
  const user = await q.take();
63
- const eq: AssertEqual<typeof user, typeof User.type> = true;
64
- expect(eq).toBe(true);
60
+ assertType<typeof user, UserRecord>();
65
61
 
66
62
  expect(user).toEqual({
67
63
  ...expected,
@@ -88,8 +84,7 @@ describe('queryMethods', () => {
88
84
  .then((res) => res.rows[0]);
89
85
 
90
86
  const user = await q.takeOptional();
91
- const eq: AssertEqual<typeof user, typeof User.type | undefined> = true;
92
- expect(eq).toBe(true);
87
+ assertType<typeof user, UserRecord | undefined>();
93
88
 
94
89
  expect(user).toEqual({
95
90
  ...expected,
@@ -100,8 +95,7 @@ describe('queryMethods', () => {
100
95
 
101
96
  it('should return undefined if not found', async () => {
102
97
  const user = await User.takeOptional();
103
- const eq: AssertEqual<typeof user, typeof User.type | undefined> = true;
104
- expect(eq).toBe(true);
98
+ assertType<typeof user, UserRecord | undefined>();
105
99
 
106
100
  expect(user).toBe(undefined);
107
101
  });
@@ -128,16 +122,14 @@ describe('queryMethods', () => {
128
122
  const result = await User.pluck('createdAt');
129
123
  expect(result).toEqual([now, now, now]);
130
124
 
131
- const eq: AssertEqual<typeof result, Date[]> = true;
132
- expect(eq).toBe(true);
125
+ assertType<typeof result, Date[]>();
133
126
  });
134
127
 
135
128
  it('should support raw expression', async () => {
136
129
  const result = await User.pluck(raw<NumberColumn>('123'));
137
130
  expect(result).toEqual([123, 123, 123]);
138
131
 
139
- const eq: AssertEqual<typeof result, number[]> = true;
140
- expect(eq).toBe(true);
132
+ assertType<typeof result, number[]>();
141
133
  });
142
134
  });
143
135
 
@@ -228,8 +220,7 @@ describe('queryMethods', () => {
228
220
  const q = User.all();
229
221
  const query = q.find(1);
230
222
 
231
- const eq: AssertEqual<Awaited<typeof query>, typeof User.type> = true;
232
- expect(eq).toBe(true);
223
+ assertType<Awaited<typeof query>, UserRecord>();
233
224
 
234
225
  expectSql(
235
226
  query.toSql(),
@@ -247,8 +238,7 @@ describe('queryMethods', () => {
247
238
  const q = User.all();
248
239
  const query = q.find(raw('$1 + $2', 1, 2));
249
240
 
250
- const eq: AssertEqual<Awaited<typeof query>, typeof User.type> = true;
251
- expect(eq).toBe(true);
241
+ assertType<Awaited<typeof query>, UserRecord>();
252
242
 
253
243
  expectSql(
254
244
  query.toSql(),
@@ -268,11 +258,7 @@ describe('queryMethods', () => {
268
258
  const q = User.all();
269
259
  const query = q.findOptional(1);
270
260
 
271
- const eq: AssertEqual<
272
- Awaited<typeof query>,
273
- typeof User.type | undefined
274
- > = true;
275
- expect(eq).toBe(true);
261
+ assertType<Awaited<typeof query>, UserRecord | undefined>();
276
262
 
277
263
  expectSql(
278
264
  query.toSql(),
@@ -290,11 +276,7 @@ describe('queryMethods', () => {
290
276
  const q = User.all();
291
277
  const query = q.findOptional(raw('$1 + $2', 1, 2));
292
278
 
293
- const eq: AssertEqual<
294
- Awaited<typeof query>,
295
- typeof User.type | undefined
296
- > = true;
297
- expect(eq).toBe(true);
279
+ assertType<Awaited<typeof query>, UserRecord | undefined>();
298
280
 
299
281
  expectSql(
300
282
  query.toSql(),
@@ -336,11 +318,7 @@ describe('queryMethods', () => {
336
318
  const q = User.all();
337
319
  const query = q.findByOptional({ name: 's' });
338
320
 
339
- const eq: AssertEqual<
340
- Awaited<typeof query>,
341
- typeof User.type | undefined
342
- > = true;
343
- expect(eq).toBe(true);
321
+ assertType<Awaited<typeof query>, UserRecord | undefined>();
344
322
 
345
323
  expectSql(
346
324
  query.toSql(),
@@ -354,11 +332,7 @@ describe('queryMethods', () => {
354
332
  const q = User.all();
355
333
  const query = q.findByOptional({ name: raw(`'string'`) });
356
334
 
357
- const eq: AssertEqual<
358
- Awaited<typeof query>,
359
- typeof User.type | undefined
360
- > = true;
361
- expect(eq).toBe(true);
335
+ assertType<Awaited<typeof query>, UserRecord | undefined>();
362
336
 
363
337
  expectSql(
364
338
  query.toSql(),
@@ -13,12 +13,7 @@ import {
13
13
  SetQueryTableAlias,
14
14
  SetQueryWindows,
15
15
  } from '../query';
16
- import {
17
- applyMixins,
18
- EmptyObject,
19
- getClonedQueryData,
20
- GetTypesOrRaw,
21
- } from '../utils';
16
+ import { applyMixins, EmptyObject, getClonedQueryData } from '../utils';
22
17
  import {
23
18
  SelectItem,
24
19
  SelectQueryData,
@@ -189,35 +184,33 @@ export class QueryMethods {
189
184
 
190
185
  find<T extends Query>(
191
186
  this: T,
192
- ...args: GetTypesOrRaw<T['schema']['primaryTypes']>
187
+ value: T['shape'][T['singlePrimaryKey']]['type'] | RawExpression,
193
188
  ): SetQueryReturnsOne<WhereResult<T>> {
194
- return this.clone()._find(...args);
189
+ return this.clone()._find(value);
195
190
  }
196
191
 
197
192
  _find<T extends Query>(
198
193
  this: T,
199
- ...args: GetTypesOrRaw<T['schema']['primaryTypes']>
194
+ value: T['shape'][T['singlePrimaryKey']]['type'] | RawExpression,
200
195
  ): SetQueryReturnsOne<WhereResult<T>> {
201
- const conditions: Partial<T['type']> = {};
202
- this.schema.primaryKeys.forEach((key: string, i: number) => {
203
- conditions[key as keyof T['type']] = args[i];
204
- });
205
- return this._where(conditions as WhereArg<T>)._take();
196
+ return this._where({
197
+ [this.singlePrimaryKey]: value,
198
+ } as WhereArg<T>)._take();
206
199
  }
207
200
 
208
201
  findOptional<T extends Query>(
209
202
  this: T,
210
- ...args: GetTypesOrRaw<T['schema']['primaryTypes']>
203
+ value: T['shape'][T['singlePrimaryKey']]['type'] | RawExpression,
211
204
  ): SetQueryReturnsOneOptional<WhereResult<T>> {
212
- return this.clone()._findOptional(...args);
205
+ return this.clone()._findOptional(value);
213
206
  }
214
207
 
215
208
  _findOptional<T extends Query>(
216
209
  this: T,
217
- ...args: GetTypesOrRaw<T['schema']['primaryTypes']>
210
+ value: T['shape'][T['singlePrimaryKey']]['type'] | RawExpression,
218
211
  ): SetQueryReturnsOneOptional<WhereResult<T>> {
219
212
  return this._find(
220
- ...args,
213
+ value,
221
214
  ).takeOptional() as unknown as SetQueryReturnsOneOptional<WhereResult<T>>;
222
215
  }
223
216
 
@@ -232,11 +232,13 @@ export class Update {
232
232
  query.returnType = 'all';
233
233
 
234
234
  if (state?.updateLater) {
235
- this.schema.primaryKeys.forEach((key: string) => {
236
- if (!query.select?.includes('*') && !query.select?.includes(key)) {
237
- this._select(key as StringKey<keyof T['selectable']>);
238
- }
239
- });
235
+ if (!query.select?.includes('*')) {
236
+ this.primaryKeys.forEach((key) => {
237
+ if (!query.select?.includes(key)) {
238
+ this._select(key as StringKey<keyof T['selectable']>);
239
+ }
240
+ });
241
+ }
240
242
  }
241
243
 
242
244
  const { handleResult } = query;
@@ -250,7 +252,7 @@ export class Update {
250
252
  await Promise.all(state.updateLaterPromises as Promise<void>[]);
251
253
 
252
254
  const t = this.__model.clone().transacting(q);
253
- const keys = this.schema.primaryKeys as string[];
255
+ const keys = this.primaryKeys;
254
256
  (
255
257
  t._whereIn as unknown as (
256
258
  keys: string[],
package/src/utils.test.ts CHANGED
@@ -1,7 +1,5 @@
1
1
  import { assertType } from './test-utils';
2
2
  import {
3
- GetTypeOrRaw,
4
- GetTypesOrRaw,
5
3
  makeRegexToFindInSql,
6
4
  MaybeArray,
7
5
  pushOrNewArray,
@@ -9,7 +7,6 @@ import {
9
7
  SetOptional,
10
8
  SomeIsTrue,
11
9
  } from './utils';
12
- import { RawExpression } from './common';
13
10
 
14
11
  describe('utils', () => {
15
12
  describe('SomeIsTrue', () => {
@@ -45,21 +42,6 @@ describe('utils', () => {
45
42
  });
46
43
  });
47
44
 
48
- describe('GetTypesOrRaw', () => {
49
- it('should add each element to union with RawExpression', () => {
50
- assertType<
51
- GetTypesOrRaw<[number, string]>,
52
- [number | RawExpression, string | RawExpression]
53
- >();
54
- });
55
- });
56
-
57
- describe('GetTypeOrRaw', () => {
58
- it('should add type to union with RawExpression', () => {
59
- assertType<GetTypeOrRaw<number>, number | RawExpression>();
60
- });
61
- });
62
-
63
45
  describe('makeRegexToFindWordInSql', () => {
64
46
  it('should return a proper regex', () => {
65
47
  const regex = makeRegexToFindInSql('\\bupdatedAt\\b');
package/src/utils.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { RawExpression } from './common';
2
1
  import { QueryData, toSqlCacheKey } from './sql';
3
2
 
4
3
  export type SomeIsTrue<T extends unknown[]> = T extends [
@@ -16,28 +15,7 @@ export type SetOptional<T, K extends PropertyKey> = Omit<T, K> & {
16
15
  [P in K]?: P extends keyof T ? T[P] : never;
17
16
  };
18
17
 
19
- export type GetTypesOrRaw<T extends [...unknown[]]> = T extends [
20
- infer Head,
21
- ...infer Tail,
22
- ]
23
- ? [GetTypeOrRaw<Head>, ...GetTypesOrRaw<Tail>]
24
- : [];
25
-
26
- export type GetTypeOrRaw<T> = T | RawExpression;
27
-
28
- // credits goes to https://stackoverflow.com/a/50375286
29
- export type UnionToIntersection<U> =
30
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
- (U extends any ? (k: U) => void : never) extends (k: infer I) => void
32
- ? I
33
- : never;
34
-
35
18
  // Converts union to overloaded function
36
- export type UnionToOvlds<U> = UnionToIntersection<
37
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
- U extends any ? (f: U) => void : never
39
- >;
40
-
41
19
  type OptionalPropertyNames<T> = {
42
20
  // eslint-disable-next-line @typescript-eslint/ban-types
43
21
  [K in keyof T]-?: {} extends { [P in K]: T[K] } ? K : never;
@@ -1,32 +0,0 @@
1
- import { AssertEqual } from '../test-utils';
2
- import { TableSchema } from './columnsSchema';
3
- import { columnTypes } from './columnTypes';
4
-
5
- describe('columnsSchema', () => {
6
- describe('schema methods', () => {
7
- const createSchema = () => {
8
- return new TableSchema({
9
- a: columnTypes.integer().primaryKey(),
10
- b: columnTypes.decimal().primaryKey(),
11
- c: columnTypes.text(),
12
- });
13
- };
14
-
15
- describe('.primaryKeys', () => {
16
- it('should be array of primary key names', () => {
17
- const schema = createSchema();
18
- const eq: AssertEqual<typeof schema.primaryKeys, ['a', 'b']> = true;
19
- expect(eq).toBe(true);
20
- expect(schema.primaryKeys).toEqual(['a', 'b']);
21
- });
22
- });
23
-
24
- describe('.primaryTypes', () => {
25
- const schema = createSchema();
26
- const eq: AssertEqual<typeof schema.primaryTypes, [number, number]> =
27
- true;
28
- expect(eq).toBe(true);
29
- expect(schema.primaryTypes).toEqual(undefined);
30
- });
31
- });
32
- });