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.
- package/CHANGELOG.md +20 -0
- package/dist/index.d.ts +59 -54
- package/dist/index.esm.js +777 -547
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +776 -546
- package/dist/index.js.map +1 -1
- package/jest-setup.ts +4 -0
- package/package.json +8 -4
- package/src/appCodeUpdater/appCodeUpdater.ts +19 -0
- package/src/appCodeUpdater/fileChanges.ts +41 -0
- package/src/appCodeUpdater/testUtils.ts +31 -0
- package/src/appCodeUpdater/tsUtils.ts +137 -0
- package/src/appCodeUpdater/updateMainFile.test.ts +230 -0
- package/src/appCodeUpdater/updateMainFile.ts +163 -0
- package/src/appCodeUpdater/updateTableFile.ts +19 -0
- package/src/index.ts +5 -1
- package/src/orm.test.ts +13 -13
- package/src/orm.ts +21 -21
- package/src/relations/belongsTo.test.ts +1 -1
- package/src/relations/belongsTo.ts +291 -186
- package/src/relations/hasAndBelongsToMany.test.ts +1 -1
- package/src/relations/hasAndBelongsToMany.ts +292 -218
- package/src/relations/hasMany.test.ts +16 -10
- package/src/relations/hasMany.ts +243 -172
- package/src/relations/hasOne.test.ts +10 -10
- package/src/relations/hasOne.ts +211 -138
- package/src/relations/relations.ts +85 -77
- package/src/relations/utils.ts +154 -4
- package/src/repo.test.ts +29 -29
- package/src/repo.ts +6 -6
- package/src/{model.test.ts → table.test.ts} +15 -15
- package/src/{model.ts → table.ts} +17 -17
- package/src/test-utils/test-db.ts +15 -15
- package/src/test-utils/{test-models.ts → test-tables.ts} +42 -42
- package/src/transaction.test.ts +1 -1
- package/src/transaction.ts +4 -4
- package/src/utils.ts +9 -0
- package/tsconfig.json +1 -0
package/src/relations/hasOne.ts
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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
|
|
138
|
+
type TableWithQueryMethod = Record<
|
|
72
139
|
string,
|
|
73
140
|
(params: Record<string, unknown>) => Query
|
|
74
141
|
>;
|
|
75
142
|
|
|
76
|
-
const throughRelation = getThroughRelation(
|
|
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 = (
|
|
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
|
-
|
|
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
|
+
};
|