pqb 0.0.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 (122) hide show
  1. package/dist/index.d.ts +3630 -0
  2. package/dist/index.esm.js +4587 -0
  3. package/dist/index.esm.js.map +1 -0
  4. package/dist/index.js +4691 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +59 -0
  7. package/rollup.config.js +35 -0
  8. package/src/adapter.test.ts +10 -0
  9. package/src/adapter.ts +171 -0
  10. package/src/columnSchema/array.ts +21 -0
  11. package/src/columnSchema/boolean.ts +10 -0
  12. package/src/columnSchema/columnType.test.ts +129 -0
  13. package/src/columnSchema/columnType.ts +77 -0
  14. package/src/columnSchema/columnTypes.ts +145 -0
  15. package/src/columnSchema/columnsSchema.test.ts +32 -0
  16. package/src/columnSchema/columnsSchema.ts +100 -0
  17. package/src/columnSchema/commonMethods.ts +130 -0
  18. package/src/columnSchema/dateTime.ts +104 -0
  19. package/src/columnSchema/enum.ts +13 -0
  20. package/src/columnSchema/index.ts +11 -0
  21. package/src/columnSchema/json/array.ts +55 -0
  22. package/src/columnSchema/json/discriminatedUnion.ts +91 -0
  23. package/src/columnSchema/json/enum.ts +29 -0
  24. package/src/columnSchema/json/instanceOf.ts +16 -0
  25. package/src/columnSchema/json/intersection.ts +23 -0
  26. package/src/columnSchema/json/lazy.ts +22 -0
  27. package/src/columnSchema/json/literal.ts +12 -0
  28. package/src/columnSchema/json/map.ts +29 -0
  29. package/src/columnSchema/json/nativeEnum.ts +30 -0
  30. package/src/columnSchema/json/nullable.ts +33 -0
  31. package/src/columnSchema/json/nullish.ts +30 -0
  32. package/src/columnSchema/json/object.ts +206 -0
  33. package/src/columnSchema/json/optional.ts +28 -0
  34. package/src/columnSchema/json/record.ts +40 -0
  35. package/src/columnSchema/json/scalarTypes.ts +117 -0
  36. package/src/columnSchema/json/set.ts +34 -0
  37. package/src/columnSchema/json/tuple.ts +40 -0
  38. package/src/columnSchema/json/typeBase.ts +202 -0
  39. package/src/columnSchema/json/union.ts +16 -0
  40. package/src/columnSchema/json.ts +64 -0
  41. package/src/columnSchema/number.ts +122 -0
  42. package/src/columnSchema/string.ts +222 -0
  43. package/src/columnSchema/utils.ts +27 -0
  44. package/src/common.ts +86 -0
  45. package/src/db.test.ts +67 -0
  46. package/src/db.ts +212 -0
  47. package/src/errors.ts +7 -0
  48. package/src/index.ts +18 -0
  49. package/src/operators.test.ts +608 -0
  50. package/src/operators.ts +177 -0
  51. package/src/query.ts +292 -0
  52. package/src/queryDataUtils.ts +50 -0
  53. package/src/queryMethods/aggregate.test.ts +583 -0
  54. package/src/queryMethods/aggregate.ts +878 -0
  55. package/src/queryMethods/callbacks.test.ts +69 -0
  56. package/src/queryMethods/callbacks.ts +55 -0
  57. package/src/queryMethods/clear.test.ts +64 -0
  58. package/src/queryMethods/clear.ts +58 -0
  59. package/src/queryMethods/columnInfo.test.ts +45 -0
  60. package/src/queryMethods/columnInfo.ts +67 -0
  61. package/src/queryMethods/delete.test.ts +135 -0
  62. package/src/queryMethods/delete.ts +50 -0
  63. package/src/queryMethods/for.test.ts +57 -0
  64. package/src/queryMethods/for.ts +99 -0
  65. package/src/queryMethods/from.test.ts +66 -0
  66. package/src/queryMethods/from.ts +58 -0
  67. package/src/queryMethods/get.test.ts +66 -0
  68. package/src/queryMethods/get.ts +88 -0
  69. package/src/queryMethods/having.test.ts +247 -0
  70. package/src/queryMethods/having.ts +99 -0
  71. package/src/queryMethods/insert.test.ts +555 -0
  72. package/src/queryMethods/insert.ts +453 -0
  73. package/src/queryMethods/join.test.ts +150 -0
  74. package/src/queryMethods/join.ts +508 -0
  75. package/src/queryMethods/json.test.ts +398 -0
  76. package/src/queryMethods/json.ts +259 -0
  77. package/src/queryMethods/log.test.ts +172 -0
  78. package/src/queryMethods/log.ts +123 -0
  79. package/src/queryMethods/queryMethods.test.ts +629 -0
  80. package/src/queryMethods/queryMethods.ts +428 -0
  81. package/src/queryMethods/select.test.ts +479 -0
  82. package/src/queryMethods/select.ts +249 -0
  83. package/src/queryMethods/then.ts +236 -0
  84. package/src/queryMethods/transaction.test.ts +66 -0
  85. package/src/queryMethods/transaction.ts +66 -0
  86. package/src/queryMethods/union.test.ts +59 -0
  87. package/src/queryMethods/union.ts +89 -0
  88. package/src/queryMethods/update.test.ts +417 -0
  89. package/src/queryMethods/update.ts +350 -0
  90. package/src/queryMethods/upsert.test.ts +56 -0
  91. package/src/queryMethods/upsert.ts +43 -0
  92. package/src/queryMethods/where.test.ts +1594 -0
  93. package/src/queryMethods/where.ts +450 -0
  94. package/src/queryMethods/window.test.ts +66 -0
  95. package/src/queryMethods/window.ts +108 -0
  96. package/src/queryMethods/with.test.ts +191 -0
  97. package/src/queryMethods/with.ts +92 -0
  98. package/src/quote.ts +36 -0
  99. package/src/relations.ts +194 -0
  100. package/src/sql/aggregate.ts +80 -0
  101. package/src/sql/columnInfo.ts +22 -0
  102. package/src/sql/common.ts +42 -0
  103. package/src/sql/delete.ts +41 -0
  104. package/src/sql/distinct.ts +19 -0
  105. package/src/sql/fromAndAs.ts +51 -0
  106. package/src/sql/having.ts +140 -0
  107. package/src/sql/index.ts +2 -0
  108. package/src/sql/insert.ts +102 -0
  109. package/src/sql/join.ts +242 -0
  110. package/src/sql/orderBy.ts +41 -0
  111. package/src/sql/select.ts +153 -0
  112. package/src/sql/toSql.ts +153 -0
  113. package/src/sql/truncate.ts +13 -0
  114. package/src/sql/types.ts +355 -0
  115. package/src/sql/update.ts +62 -0
  116. package/src/sql/where.ts +314 -0
  117. package/src/sql/window.ts +38 -0
  118. package/src/sql/with.ts +32 -0
  119. package/src/test-utils.ts +172 -0
  120. package/src/utils.ts +140 -0
  121. package/tsconfig.build.json +6 -0
  122. package/tsconfig.json +8 -0
@@ -0,0 +1,350 @@
1
+ import { Query, SetQueryReturnsRowCount } from '../query';
2
+ import { pushQueryArray, pushQueryValue } from '../queryDataUtils';
3
+ import { isRaw, RawExpression } from '../common';
4
+ import {
5
+ BelongsToNestedUpdate,
6
+ HasOneNestedUpdate,
7
+ NestedUpdateOneItem,
8
+ Relation,
9
+ } from '../relations';
10
+ import { WhereArg, WhereResult } from './where';
11
+ import { MaybeArray } from '../utils';
12
+ import { InsertData } from './insert';
13
+ import { parseResult, queryMethodByReturnType } from './then';
14
+
15
+ export type UpdateData<T extends Query> = {
16
+ [K in keyof T['type']]?: T['type'][K] | RawExpression;
17
+ } & (T['relations'] extends Record<string, Relation>
18
+ ? {
19
+ [K in keyof T['relations']]?: T['relations'][K]['type'] extends 'belongsTo'
20
+ ?
21
+ | { disconnect: boolean }
22
+ | { set: WhereArg<T['relations'][K]['model']> }
23
+ | { delete: boolean }
24
+ | { update: UpdateData<T['relations'][K]['model']> }
25
+ | (T['returnType'] extends 'one' | 'oneOrThrow'
26
+ ?
27
+ | {
28
+ create: InsertData<
29
+ T['relations'][K]['nestedCreateQuery']
30
+ >;
31
+ }
32
+ | {
33
+ upsert: {
34
+ update: UpdateData<T['relations'][K]['model']>;
35
+ create: InsertData<
36
+ T['relations'][K]['nestedCreateQuery']
37
+ >;
38
+ };
39
+ }
40
+ : never)
41
+ : T['relations'][K]['type'] extends 'hasOne'
42
+ ?
43
+ | { disconnect: boolean }
44
+ | { delete: boolean }
45
+ | { update: UpdateData<T['relations'][K]['model']> }
46
+ | (T['returnType'] extends 'one' | 'oneOrThrow'
47
+ ?
48
+ | { set: WhereArg<T['relations'][K]['model']> }
49
+ | {
50
+ upsert: {
51
+ update: UpdateData<T['relations'][K]['model']>;
52
+ create: InsertData<
53
+ T['relations'][K]['nestedCreateQuery']
54
+ >;
55
+ };
56
+ }
57
+ | {
58
+ create: InsertData<
59
+ T['relations'][K]['nestedCreateQuery']
60
+ >;
61
+ }
62
+ : never)
63
+ : T['relations'][K]['type'] extends 'hasMany'
64
+ ?
65
+ | { disconnect: MaybeArray<WhereArg<T['relations'][K]['model']>> }
66
+ | { delete: MaybeArray<WhereArg<T['relations'][K]['model']>> }
67
+ | {
68
+ update: {
69
+ where: MaybeArray<WhereArg<T['relations'][K]['model']>>;
70
+ data: UpdateData<T['relations'][K]['model']>;
71
+ };
72
+ }
73
+ | (T['returnType'] extends 'one' | 'oneOrThrow'
74
+ ?
75
+ | { set: MaybeArray<WhereArg<T['relations'][K]['model']>> }
76
+ | {
77
+ create: InsertData<
78
+ T['relations'][K]['nestedCreateQuery']
79
+ >[];
80
+ }
81
+ : never)
82
+ : T['relations'][K]['type'] extends 'hasAndBelongsToMany'
83
+ ?
84
+ | { disconnect: MaybeArray<WhereArg<T['relations'][K]['model']>> }
85
+ | {
86
+ set: MaybeArray<WhereArg<T['relations'][K]['model']>>;
87
+ }
88
+ | { delete: MaybeArray<WhereArg<T['relations'][K]['model']>> }
89
+ | {
90
+ update: {
91
+ where: MaybeArray<WhereArg<T['relations'][K]['model']>>;
92
+ data: UpdateData<T['relations'][K]['model']>;
93
+ };
94
+ }
95
+ | {
96
+ create: InsertData<T['relations'][K]['nestedCreateQuery']>[];
97
+ }
98
+ : never;
99
+ }
100
+ : // eslint-disable-next-line @typescript-eslint/ban-types
101
+ {});
102
+
103
+ type UpdateArgs<T extends Query, ForceAll extends boolean> = (
104
+ T['hasWhere'] extends true ? true : ForceAll
105
+ ) extends true
106
+ ? [update: RawExpression | UpdateData<T>, forceAll?: ForceAll]
107
+ : [update: RawExpression | UpdateData<T>, forceAll: true];
108
+
109
+ type UpdateResult<T extends Query> = T['hasSelect'] extends false
110
+ ? SetQueryReturnsRowCount<T>
111
+ : T;
112
+
113
+ type ChangeCountArg<T extends Query> =
114
+ | keyof T['shape']
115
+ | Partial<Record<keyof T['shape'], number>>;
116
+
117
+ const applyCountChange = <T extends Query>(
118
+ self: T,
119
+ op: string,
120
+ data: ChangeCountArg<T>,
121
+ ) => {
122
+ self.query.type = 'update';
123
+
124
+ let map: Record<string, { op: string; arg: number }>;
125
+ if (typeof data === 'object') {
126
+ map = {};
127
+ for (const key in data) {
128
+ map[key] = { op, arg: data[key] as number };
129
+ }
130
+ } else {
131
+ map = { [data as string]: { op, arg: 1 } };
132
+ }
133
+
134
+ pushQueryValue(self, 'data', map);
135
+ return self as unknown as UpdateResult<T>;
136
+ };
137
+
138
+ export class Update {
139
+ update<T extends Query, ForceAll extends boolean = false>(
140
+ this: T,
141
+ ...args: UpdateArgs<T, ForceAll>
142
+ ): UpdateResult<T> {
143
+ const q = this.clone() as T;
144
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
145
+ return q._update(...(args as any));
146
+ }
147
+
148
+ _update<T extends Query, ForceAll extends boolean = false>(
149
+ this: T,
150
+ ...args: UpdateArgs<T, ForceAll>
151
+ ): UpdateResult<T> {
152
+ const [data, forceAll] = args as unknown as [
153
+ Record<string, unknown>,
154
+ boolean | undefined,
155
+ ];
156
+ const { query } = this;
157
+ query.type = 'update';
158
+
159
+ if (!query.and?.length && !query.or?.length && !forceAll) {
160
+ throw new Error(
161
+ 'No where conditions or forceAll flag provided to update',
162
+ );
163
+ }
164
+
165
+ if (isRaw(data)) {
166
+ pushQueryValue(this, 'data', data);
167
+ } else {
168
+ const relations = this.relations as Record<string, Relation>;
169
+
170
+ const prependRelations: Record<string, Record<string, unknown>> = {};
171
+ const appendRelations: Record<string, Record<string, unknown>> = {};
172
+
173
+ const originalReturnType = this.query.returnType;
174
+
175
+ const update: Record<string, unknown> = { ...data };
176
+ for (const key in data) {
177
+ if (relations[key]) {
178
+ delete update[key];
179
+ if (relations[key].type === 'belongsTo') {
180
+ prependRelations[key] = data[key] as Record<string, unknown>;
181
+ } else {
182
+ if (!query.select?.includes('*')) {
183
+ const primaryKey = relations[key].primaryKey;
184
+ if (!query.select?.includes(primaryKey)) {
185
+ this._select(primaryKey);
186
+ }
187
+ }
188
+ appendRelations[key] = data[key] as Record<string, unknown>;
189
+ }
190
+ }
191
+ }
192
+
193
+ const state: {
194
+ updateLater?: Record<string, unknown>;
195
+ updateLaterPromises?: Promise<void>[];
196
+ } = {};
197
+
198
+ const prependRelationKeys = Object.keys(prependRelations);
199
+ if (prependRelationKeys.length) {
200
+ const willSetKeys = prependRelationKeys.some((relationName) => {
201
+ const data = prependRelations[relationName] as NestedUpdateOneItem;
202
+
203
+ return (
204
+ relations[relationName] as {
205
+ nestedUpdate: BelongsToNestedUpdate;
206
+ }
207
+ ).nestedUpdate(this, update, data, state);
208
+ });
209
+
210
+ if (!willSetKeys && !Object.keys(update).length) {
211
+ delete this.query.type;
212
+ }
213
+ } else if (!Object.keys(update).length) {
214
+ delete this.query.type;
215
+ }
216
+
217
+ const appendRelationKeys = Object.keys(appendRelations);
218
+
219
+ let resultOfTypeAll: Record<string, unknown>[] | undefined;
220
+
221
+ if (
222
+ state?.updateLater ||
223
+ (appendRelationKeys.length && originalReturnType !== 'all')
224
+ ) {
225
+ this.query.returnType = 'all';
226
+
227
+ if (state?.updateLater) {
228
+ this.schema.primaryKeys.forEach((key: string) => {
229
+ if (!query.select?.includes('*') && !query.select?.includes(key)) {
230
+ this._select(key);
231
+ }
232
+ });
233
+ }
234
+
235
+ const { handleResult } = this.query;
236
+ this.query.handleResult = async (q, queryResult) => {
237
+ resultOfTypeAll = (await handleResult(q, queryResult)) as Record<
238
+ string,
239
+ unknown
240
+ >[];
241
+
242
+ if (state?.updateLater) {
243
+ await Promise.all(state.updateLaterPromises as Promise<void>[]);
244
+
245
+ const t = this.__model.clone().transacting(q);
246
+ const keys = this.schema.primaryKeys as string[];
247
+ (
248
+ t._whereIn as unknown as (
249
+ keys: string[],
250
+ values: unknown[][],
251
+ ) => Query
252
+ )(
253
+ keys,
254
+ resultOfTypeAll.map((item) => keys.map((key) => item[key])),
255
+ );
256
+
257
+ await (t as WhereResult<Query>)._update(state.updateLater);
258
+
259
+ resultOfTypeAll.forEach((item) =>
260
+ Object.assign(item, state.updateLater),
261
+ );
262
+ }
263
+
264
+ if (queryMethodByReturnType[originalReturnType] === 'arrays') {
265
+ queryResult.rows.forEach(
266
+ (row, i) =>
267
+ ((queryResult.rows as unknown as unknown[][])[i] =
268
+ Object.values(row)),
269
+ );
270
+ }
271
+
272
+ return parseResult(q, originalReturnType, queryResult);
273
+ };
274
+ }
275
+
276
+ if (appendRelationKeys.length) {
277
+ pushQueryArray(
278
+ this,
279
+ 'afterQuery',
280
+ appendRelationKeys.map((relationName) => {
281
+ return (q: Query, result: Record<string, unknown>[]) => {
282
+ const all = resultOfTypeAll || result;
283
+ return (
284
+ relations[relationName].nestedUpdate as HasOneNestedUpdate
285
+ )?.(q, all, appendRelations[relationName] as NestedUpdateOneItem);
286
+ };
287
+ }),
288
+ );
289
+ }
290
+
291
+ if (prependRelationKeys.length || appendRelationKeys.length) {
292
+ query.wrapInTransaction = true;
293
+ }
294
+
295
+ pushQueryValue(this, 'data', update);
296
+ }
297
+
298
+ if (!query.select) {
299
+ this.query.returnType = 'rowCount';
300
+ }
301
+
302
+ return this as unknown as UpdateResult<T>;
303
+ }
304
+
305
+ updateOrThrow<T extends Query, ForceAll extends boolean = false>(
306
+ this: T,
307
+ ...args: UpdateArgs<T, ForceAll>
308
+ ): UpdateResult<T> {
309
+ const q = this.clone() as T;
310
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
311
+ return q._updateOrThrow(...(args as any));
312
+ }
313
+
314
+ _updateOrThrow<T extends Query, ForceAll extends boolean = false>(
315
+ this: T,
316
+ ...args: UpdateArgs<T, ForceAll>
317
+ ): UpdateResult<T> {
318
+ this.query.throwOnNotFound = true;
319
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
320
+ return this._update(...(args as any)) as unknown as UpdateResult<T>;
321
+ }
322
+
323
+ increment<T extends Query>(
324
+ this: T,
325
+ data: ChangeCountArg<T>,
326
+ ): UpdateResult<T> {
327
+ return this.clone()._increment(data) as unknown as UpdateResult<T>;
328
+ }
329
+
330
+ _increment<T extends Query>(
331
+ this: T,
332
+ data: ChangeCountArg<T>,
333
+ ): UpdateResult<T> {
334
+ return applyCountChange(this, '+', data);
335
+ }
336
+
337
+ decrement<T extends Query>(
338
+ this: T,
339
+ data: ChangeCountArg<T>,
340
+ ): UpdateResult<T> {
341
+ return this.clone()._decrement(data) as unknown as UpdateResult<T>;
342
+ }
343
+
344
+ _decrement<T extends Query>(
345
+ this: T,
346
+ data: ChangeCountArg<T>,
347
+ ): UpdateResult<T> {
348
+ return applyCountChange(this, '-', data);
349
+ }
350
+ }
@@ -0,0 +1,56 @@
1
+ import { AssertEqual, User, userData, useTestDatabase } from '../test-utils';
2
+
3
+ describe('upsert', () => {
4
+ useTestDatabase();
5
+
6
+ it('should return void by default', () => {
7
+ const query = User.find(1).upsert({
8
+ update: { name: 'name' },
9
+ create: userData,
10
+ });
11
+
12
+ const eq: AssertEqual<Awaited<typeof query>, void> = true;
13
+ expect(eq).toBe(true);
14
+ });
15
+
16
+ it('should update record if exists', async () => {
17
+ const { id } = await User.create(userData);
18
+
19
+ const user = await User.selectAll()
20
+ .find(id)
21
+ .upsert({
22
+ update: {
23
+ name: 'updated',
24
+ },
25
+ create: userData,
26
+ });
27
+
28
+ expect(user.name).toBe('updated');
29
+ });
30
+
31
+ it('should create record if not exists', async () => {
32
+ const user = await User.selectAll()
33
+ .find(123)
34
+ .upsert({
35
+ update: {
36
+ name: 'updated',
37
+ },
38
+ create: { ...userData, name: 'created' },
39
+ });
40
+
41
+ expect(user.name).toBe('created');
42
+ });
43
+
44
+ it('should throw if more than one row was updated', async () => {
45
+ await User.create([userData, userData]);
46
+
47
+ await expect(
48
+ User.findBy({ name: userData.name }).upsert({
49
+ update: {
50
+ name: 'updated',
51
+ },
52
+ create: userData,
53
+ }),
54
+ ).rejects.toThrow();
55
+ });
56
+ });
@@ -0,0 +1,43 @@
1
+ import { Query, SetQueryReturnsOne, SetQueryReturnsVoid } from '../query';
2
+ import { UpdateData } from './update';
3
+ import { InsertData } from './insert';
4
+ import { WhereResult } from './where';
5
+ import { MoreThanOneRowError } from '../errors';
6
+
7
+ export type UpsertData<T extends Query> = {
8
+ update: UpdateData<T>;
9
+ create: InsertData<T>;
10
+ };
11
+
12
+ export type UpsertResult<T extends Query> = T['hasSelect'] extends true
13
+ ? SetQueryReturnsOne<T>
14
+ : SetQueryReturnsVoid<T>;
15
+
16
+ export type UpsertThis = WhereResult<Query> & {
17
+ returnType: 'one' | 'oneOrThrow';
18
+ };
19
+
20
+ export class QueryUpsert {
21
+ upsert<T extends UpsertThis>(this: T, data: UpsertData<T>): UpsertResult<T> {
22
+ return this.clone()._upsert(data);
23
+ }
24
+
25
+ _upsert<T extends UpsertThis>(this: T, data: UpsertData<T>): UpsertResult<T> {
26
+ this._update<WhereResult<Query>>(data.update);
27
+ this.query.returnType = 'one';
28
+ this.query.wrapInTransaction = true;
29
+ const { handleResult } = this.query;
30
+ this.query.handleResult = async (q, queryResult) => {
31
+ if (queryResult.rowCount === 0) {
32
+ return q.insert(data.create);
33
+ } else if (queryResult.rowCount > 1) {
34
+ throw new MoreThanOneRowError(
35
+ `Only one row was expected to find for upsert, found ${queryResult.rowCount} rows.`,
36
+ );
37
+ }
38
+
39
+ return handleResult(q, queryResult);
40
+ };
41
+ return this as unknown as UpsertResult<T>;
42
+ }
43
+ }