orchid-orm 1.3.15 → 1.4.16

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 (38) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/index.d.ts +59 -54
  3. package/dist/index.esm.js +777 -547
  4. package/dist/index.esm.js.map +1 -1
  5. package/dist/index.js +776 -546
  6. package/dist/index.js.map +1 -1
  7. package/jest-setup.ts +4 -0
  8. package/package.json +8 -4
  9. package/src/appCodeUpdater/appCodeUpdater.ts +19 -0
  10. package/src/appCodeUpdater/fileChanges.ts +41 -0
  11. package/src/appCodeUpdater/testUtils.ts +31 -0
  12. package/src/appCodeUpdater/tsUtils.ts +137 -0
  13. package/src/appCodeUpdater/updateMainFile.test.ts +230 -0
  14. package/src/appCodeUpdater/updateMainFile.ts +163 -0
  15. package/src/appCodeUpdater/updateTableFile.ts +19 -0
  16. package/src/index.ts +5 -1
  17. package/src/orm.test.ts +13 -13
  18. package/src/orm.ts +21 -21
  19. package/src/relations/belongsTo.test.ts +1 -1
  20. package/src/relations/belongsTo.ts +291 -186
  21. package/src/relations/hasAndBelongsToMany.test.ts +1 -1
  22. package/src/relations/hasAndBelongsToMany.ts +292 -218
  23. package/src/relations/hasMany.test.ts +16 -10
  24. package/src/relations/hasMany.ts +243 -172
  25. package/src/relations/hasOne.test.ts +10 -10
  26. package/src/relations/hasOne.ts +211 -138
  27. package/src/relations/relations.ts +85 -77
  28. package/src/relations/utils.ts +154 -4
  29. package/src/repo.test.ts +29 -29
  30. package/src/repo.ts +6 -6
  31. package/src/{model.test.ts → table.test.ts} +15 -15
  32. package/src/{model.ts → table.ts} +17 -17
  33. package/src/test-utils/test-db.ts +15 -15
  34. package/src/test-utils/{test-models.ts → test-tables.ts} +42 -42
  35. package/src/transaction.test.ts +1 -1
  36. package/src/transaction.ts +4 -4
  37. package/src/utils.ts +9 -0
  38. package/tsconfig.json +1 -0
@@ -1,25 +1,33 @@
1
1
  import {
2
2
  addQueryOn,
3
+ CreateCtx,
3
4
  getQueryAs,
4
- HasOneNestedInsert,
5
- HasOneNestedUpdate,
6
5
  HasOneRelation,
7
6
  InsertQueryData,
8
7
  isQueryReturnsAll,
9
8
  JoinCallback,
10
9
  Query,
11
10
  QueryBase,
11
+ UpdateCtx,
12
+ VirtualColumn,
12
13
  WhereArg,
13
14
  WhereResult,
14
15
  } from 'pqb';
15
- import { Model } from '../model';
16
+ import { Table } from '../table';
16
17
  import {
17
18
  RelationData,
18
19
  RelationInfo,
19
20
  RelationThunkBase,
20
21
  RelationThunks,
21
22
  } from './relations';
22
- import { getSourceRelation, getThroughRelation } from './utils';
23
+ import {
24
+ getSourceRelation,
25
+ getThroughRelation,
26
+ hasRelationHandleCreate,
27
+ hasRelationHandleUpdate,
28
+ NestedInsertOneItem,
29
+ NestedUpdateOneItem,
30
+ } from './utils';
23
31
 
24
32
  export interface HasOne extends RelationThunkBase {
25
33
  type: 'hasOne';
@@ -28,7 +36,7 @@ export interface HasOne extends RelationThunkBase {
28
36
  }
29
37
 
30
38
  export type HasOneInfo<
31
- T extends Model,
39
+ T extends Table,
32
40
  Relations extends RelationThunks,
33
41
  Relation extends HasOne,
34
42
  > = {
@@ -53,8 +61,67 @@ export type HasOneInfo<
53
61
  chainedDelete: true;
54
62
  };
55
63
 
64
+ type State = {
65
+ query: Query;
66
+ primaryKey: string;
67
+ foreignKey: string;
68
+ };
69
+
70
+ export type HasOneNestedInsert = (
71
+ query: Query,
72
+ data: [
73
+ selfData: Record<string, unknown>,
74
+ relationData: NestedInsertOneItem,
75
+ ][],
76
+ ) => Promise<void>;
77
+
78
+ export type HasOneNestedUpdate = (
79
+ query: Query,
80
+ data: Record<string, unknown>[],
81
+ relationData: NestedUpdateOneItem,
82
+ ) => Promise<void>;
83
+
84
+ class HasOneVirtualColumn extends VirtualColumn {
85
+ private readonly nestedInsert: HasOneNestedInsert;
86
+ private readonly nestedUpdate: HasOneNestedUpdate;
87
+
88
+ constructor(private key: string, private state: State) {
89
+ super();
90
+ this.nestedInsert = nestedInsert(state);
91
+ this.nestedUpdate = nestedUpdate(state);
92
+ }
93
+
94
+ create(
95
+ q: Query,
96
+ ctx: CreateCtx,
97
+ item: Record<string, unknown>,
98
+ rowIndex: number,
99
+ ) {
100
+ hasRelationHandleCreate(
101
+ q,
102
+ ctx,
103
+ item,
104
+ rowIndex,
105
+ this.key,
106
+ this.state.primaryKey,
107
+ this.nestedInsert,
108
+ );
109
+ }
110
+
111
+ update(q: Query, ctx: UpdateCtx, set: Record<string, unknown>) {
112
+ hasRelationHandleUpdate(
113
+ q,
114
+ ctx,
115
+ set,
116
+ this.key,
117
+ this.state.primaryKey,
118
+ this.nestedUpdate,
119
+ );
120
+ }
121
+ }
122
+
56
123
  export const makeHasOneMethod = (
57
- model: Query,
124
+ table: Query,
58
125
  relation: HasOne,
59
126
  relationName: string,
60
127
  query: Query,
@@ -68,12 +135,12 @@ export const makeHasOneMethod = (
68
135
  if ('through' in relation.options) {
69
136
  const { through, source } = relation.options;
70
137
 
71
- type ModelWithQueryMethod = Record<
138
+ type TableWithQueryMethod = Record<
72
139
  string,
73
140
  (params: Record<string, unknown>) => Query
74
141
  >;
75
142
 
76
- const throughRelation = getThroughRelation(model, through);
143
+ const throughRelation = getThroughRelation(table, through);
77
144
  const sourceRelation = getSourceRelation(throughRelation, source);
78
145
  const sourceQuery = sourceRelation
79
146
  .joinQuery(throughRelation.query, sourceRelation.query)
@@ -84,7 +151,7 @@ export const makeHasOneMethod = (
84
151
  return {
85
152
  returns: 'one',
86
153
  method: (params: Record<string, unknown>) => {
87
- const throughQuery = (model as unknown as ModelWithQueryMethod)[
154
+ const throughQuery = (table as unknown as TableWithQueryMethod)[
88
155
  through
89
156
  ](params);
90
157
 
@@ -93,8 +160,6 @@ export const makeHasOneMethod = (
93
160
  whereExistsCallback as unknown as JoinCallback<Query, Query>,
94
161
  );
95
162
  },
96
- nestedInsert: undefined,
97
- nestedUpdate: undefined,
98
163
  joinQuery(fromQuery, toQuery) {
99
164
  return toQuery.whereExists<Query, Query>(
100
165
  throughRelation.joinQuery(fromQuery, throughRelation.query),
@@ -124,6 +189,7 @@ export const makeHasOneMethod = (
124
189
  }
125
190
 
126
191
  const { primaryKey, foreignKey } = relation.options;
192
+ const state: State = { query, primaryKey, foreignKey };
127
193
 
128
194
  const fromQuerySelect = [{ selectAs: { [foreignKey]: primaryKey } }];
129
195
 
@@ -133,133 +199,7 @@ export const makeHasOneMethod = (
133
199
  const values = { [foreignKey]: params[primaryKey] };
134
200
  return query.where(values)._defaults(values);
135
201
  },
136
- nestedInsert: (async (q, data) => {
137
- const connect = data.filter(
138
- (
139
- item,
140
- ): item is [
141
- Record<string, unknown>,
142
- (
143
- | {
144
- connect: WhereArg<QueryBase>;
145
- }
146
- | {
147
- connectOrCreate: {
148
- where: WhereArg<QueryBase>;
149
- create: Record<string, unknown>;
150
- };
151
- }
152
- ),
153
- ] => Boolean(item[1].connect || item[1].connectOrCreate),
154
- );
155
-
156
- const t = query.transacting(q);
157
-
158
- let connected: number[];
159
- if (connect.length) {
160
- connected = await Promise.all(
161
- connect.map(([selfData, item]) => {
162
- const data = { [foreignKey]: selfData[primaryKey] };
163
- return 'connect' in item
164
- ? (
165
- t.where(item.connect) as WhereResult<Query> & {
166
- hasSelect: false;
167
- }
168
- )._updateOrThrow(data)
169
- : (
170
- t.where(item.connectOrCreate.where) as WhereResult<Query> & {
171
- hasSelect: false;
172
- }
173
- )._update(data);
174
- }),
175
- );
176
- } else {
177
- connected = [];
178
- }
179
-
180
- let connectedI = 0;
181
- const create = data.filter(
182
- (
183
- item,
184
- ): item is [
185
- Record<string, unknown>,
186
- (
187
- | { create: Record<string, unknown> }
188
- | {
189
- connectOrCreate: {
190
- where: WhereArg<QueryBase>;
191
- create: Record<string, unknown>;
192
- };
193
- }
194
- ),
195
- ] => {
196
- if (item[1].connectOrCreate) {
197
- return !connected[connectedI++];
198
- }
199
- return Boolean(item[1].create);
200
- },
201
- );
202
-
203
- if (create.length) {
204
- await t._count()._createMany(
205
- create.map(([selfData, item]) => ({
206
- [foreignKey]: selfData[primaryKey],
207
- ...('create' in item ? item.create : item.connectOrCreate.create),
208
- })),
209
- );
210
- }
211
- }) as HasOneNestedInsert,
212
- nestedUpdate: (async (q, data, params) => {
213
- if (
214
- (params.set || params.create || params.upsert) &&
215
- isQueryReturnsAll(q)
216
- ) {
217
- const key = params.set ? 'set' : params.create ? 'create' : 'upsert';
218
- throw new Error(`\`${key}\` option is not allowed in a batch update`);
219
- }
220
-
221
- const t = query.transacting(q);
222
- const ids = data.map((item) => item[primaryKey]);
223
- const currentRelationsQuery = t.where({
224
- [foreignKey]: { in: ids },
225
- });
226
-
227
- if (params.create || params.disconnect || params.set) {
228
- await currentRelationsQuery._update({ [foreignKey]: null });
229
-
230
- if (params.create) {
231
- await t._count()._create({
232
- ...params.create,
233
- [foreignKey]: data[0][primaryKey],
234
- });
235
- }
236
- if (params.set) {
237
- await t
238
- ._where<Query>(params.set)
239
- ._update({ [foreignKey]: data[0][primaryKey] });
240
- }
241
- } else if (params.update) {
242
- await currentRelationsQuery._update<WhereResult<Query>>(params.update);
243
- } else if (params.delete) {
244
- await currentRelationsQuery._delete();
245
- } else if (params.upsert) {
246
- const { update, create } = params.upsert;
247
- const updatedIds: unknown[] = await currentRelationsQuery
248
- ._pluck(foreignKey)
249
- ._update<WhereResult<Query & { hasSelect: true }>>(update);
250
-
251
- if (updatedIds.length < ids.length) {
252
- await t.createMany(
253
- ids
254
- .filter((id) => !updatedIds.includes(id))
255
- .map((id) => ({
256
- ...create,
257
- [foreignKey]: id,
258
- })),
259
- );
260
- }
261
- }
262
- }) as HasOneNestedUpdate,
202
+ virtualColumn: new HasOneVirtualColumn(relationName, state),
263
203
  joinQuery(fromQuery, toQuery) {
264
204
  return addQueryOn(toQuery, fromQuery, toQuery, foreignKey, primaryKey);
265
205
  },
@@ -276,3 +216,136 @@ export const makeHasOneMethod = (
276
216
  },
277
217
  };
278
218
  };
219
+
220
+ const nestedInsert = ({ query, primaryKey, foreignKey }: State) => {
221
+ return (async (q, data) => {
222
+ const connect = data.filter(
223
+ (
224
+ item,
225
+ ): item is [
226
+ Record<string, unknown>,
227
+ (
228
+ | {
229
+ connect: WhereArg<QueryBase>;
230
+ }
231
+ | {
232
+ connectOrCreate: {
233
+ where: WhereArg<QueryBase>;
234
+ create: Record<string, unknown>;
235
+ };
236
+ }
237
+ ),
238
+ ] => Boolean(item[1].connect || item[1].connectOrCreate),
239
+ );
240
+
241
+ const t = query.transacting(q);
242
+
243
+ let connected: number[];
244
+ if (connect.length) {
245
+ connected = await Promise.all(
246
+ connect.map(([selfData, item]) => {
247
+ const data = { [foreignKey]: selfData[primaryKey] };
248
+ return 'connect' in item
249
+ ? (
250
+ t.where(item.connect) as WhereResult<Query> & {
251
+ hasSelect: false;
252
+ }
253
+ )._updateOrThrow(data)
254
+ : (
255
+ t.where(item.connectOrCreate.where) as WhereResult<Query> & {
256
+ hasSelect: false;
257
+ }
258
+ )._update(data);
259
+ }),
260
+ );
261
+ } else {
262
+ connected = [];
263
+ }
264
+
265
+ let connectedI = 0;
266
+ const create = data.filter(
267
+ (
268
+ item,
269
+ ): item is [
270
+ Record<string, unknown>,
271
+ (
272
+ | { create: Record<string, unknown> }
273
+ | {
274
+ connectOrCreate: {
275
+ where: WhereArg<QueryBase>;
276
+ create: Record<string, unknown>;
277
+ };
278
+ }
279
+ ),
280
+ ] => {
281
+ if (item[1].connectOrCreate) {
282
+ return !connected[connectedI++];
283
+ }
284
+ return Boolean(item[1].create);
285
+ },
286
+ );
287
+
288
+ if (create.length) {
289
+ await t._count()._createMany(
290
+ create.map(([selfData, item]) => ({
291
+ [foreignKey]: selfData[primaryKey],
292
+ ...('create' in item ? item.create : item.connectOrCreate.create),
293
+ })),
294
+ );
295
+ }
296
+ }) as HasOneNestedInsert;
297
+ };
298
+
299
+ const nestedUpdate = ({ query, primaryKey, foreignKey }: State) => {
300
+ return (async (q, data, params) => {
301
+ if (
302
+ (params.set || params.create || params.upsert) &&
303
+ isQueryReturnsAll(q)
304
+ ) {
305
+ const key = params.set ? 'set' : params.create ? 'create' : 'upsert';
306
+ throw new Error(`\`${key}\` option is not allowed in a batch update`);
307
+ }
308
+
309
+ const t = query.transacting(q);
310
+ const ids = data.map((item) => item[primaryKey]);
311
+ const currentRelationsQuery = t.where({
312
+ [foreignKey]: { in: ids },
313
+ });
314
+
315
+ if (params.create || params.disconnect || params.set) {
316
+ await currentRelationsQuery._update({ [foreignKey]: null });
317
+
318
+ if (params.create) {
319
+ await t._count()._create({
320
+ ...params.create,
321
+ [foreignKey]: data[0][primaryKey],
322
+ });
323
+ }
324
+ if (params.set) {
325
+ await t
326
+ ._where<Query>(params.set)
327
+ ._update({ [foreignKey]: data[0][primaryKey] });
328
+ }
329
+ } else if (params.update) {
330
+ await currentRelationsQuery._update<WhereResult<Query>>(params.update);
331
+ } else if (params.delete) {
332
+ await currentRelationsQuery._delete();
333
+ } else if (params.upsert) {
334
+ const { update, create } = params.upsert;
335
+ const updatedIds: unknown[] = await currentRelationsQuery
336
+ ._pluck(foreignKey)
337
+ ._update<WhereResult<Query & { hasSelect: true }>>(update);
338
+
339
+ if (updatedIds.length < ids.length) {
340
+ await t.createMany(
341
+ ids
342
+ .filter((id) => !updatedIds.includes(id))
343
+ .map((id) => ({
344
+ ...create,
345
+ [foreignKey]: id,
346
+ })),
347
+ );
348
+ }
349
+ }
350
+ }) as HasOneNestedUpdate;
351
+ };