@zenstackhq/tanstack-query 2.21.0 → 3.0.0-beta.17
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/.turbo/turbo-build.log +31 -0
- package/LICENSE +1 -1
- package/dist/react.cjs +1238 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +696 -0
- package/dist/react.d.ts +696 -0
- package/dist/react.js +1195 -0
- package/dist/react.js.map +1 -0
- package/eslint.config.js +4 -0
- package/package.json +56 -109
- package/scripts/generate.ts +27 -0
- package/src/react.ts +531 -0
- package/src/utils/common.ts +457 -0
- package/src/utils/mutator.ts +441 -0
- package/src/utils/nested-read-visitor.ts +61 -0
- package/src/utils/nested-write-visitor.ts +359 -0
- package/src/utils/query-analysis.ts +116 -0
- package/src/utils/serialization.ts +39 -0
- package/src/utils/types.ts +19 -0
- package/test/react-query.test.tsx +1787 -0
- package/test/schemas/basic/input.ts +70 -0
- package/test/schemas/basic/models.ts +12 -0
- package/test/schemas/basic/schema-lite.ts +124 -0
- package/test/schemas/basic/schema.zmodel +25 -0
- package/tsconfig.json +7 -0
- package/tsconfig.test.json +8 -0
- package/tsup.config.ts +13 -0
- package/vitest.config.ts +11 -0
- package/README.md +0 -5
- package/generator.d.ts +0 -6
- package/generator.js +0 -578
- package/generator.js.map +0 -1
- package/index.d.ts +0 -4
- package/index.js +0 -22
- package/index.js.map +0 -1
- package/runtime/common-CXlL7vTW.d.mts +0 -121
- package/runtime/common-CXlL7vTW.d.ts +0 -121
- package/runtime/index.d.mts +0 -20
- package/runtime/index.d.ts +0 -20
- package/runtime/index.js +0 -44
- package/runtime/index.js.map +0 -1
- package/runtime/index.mjs +0 -21
- package/runtime/index.mjs.map +0 -1
- package/runtime/react.d.mts +0 -322
- package/runtime/react.d.ts +0 -322
- package/runtime/react.js +0 -408
- package/runtime/react.js.map +0 -1
- package/runtime/react.mjs +0 -380
- package/runtime/react.mjs.map +0 -1
- package/runtime/svelte.d.mts +0 -322
- package/runtime/svelte.d.ts +0 -322
- package/runtime/svelte.js +0 -407
- package/runtime/svelte.js.map +0 -1
- package/runtime/svelte.mjs +0 -379
- package/runtime/svelte.mjs.map +0 -1
- package/runtime/vue.d.mts +0 -330
- package/runtime/vue.d.ts +0 -330
- package/runtime/vue.js +0 -418
- package/runtime/vue.js.map +0 -1
- package/runtime/vue.mjs +0 -390
- package/runtime/vue.mjs.map +0 -1
- package/runtime-v5/angular.d.mts +0 -59
- package/runtime-v5/angular.d.ts +0 -59
- package/runtime-v5/angular.js +0 -425
- package/runtime-v5/angular.js.map +0 -1
- package/runtime-v5/angular.mjs +0 -397
- package/runtime-v5/angular.mjs.map +0 -1
- package/runtime-v5/common-CXlL7vTW.d.mts +0 -121
- package/runtime-v5/common-CXlL7vTW.d.ts +0 -121
- package/runtime-v5/index.d.mts +0 -20
- package/runtime-v5/index.d.ts +0 -20
- package/runtime-v5/index.js +0 -44
- package/runtime-v5/index.js.map +0 -1
- package/runtime-v5/index.mjs +0 -21
- package/runtime-v5/index.mjs.map +0 -1
- package/runtime-v5/react.d.mts +0 -474
- package/runtime-v5/react.d.ts +0 -474
- package/runtime-v5/react.js +0 -440
- package/runtime-v5/react.js.map +0 -1
- package/runtime-v5/react.mjs +0 -412
- package/runtime-v5/react.mjs.map +0 -1
- package/runtime-v5/svelte.d.mts +0 -386
- package/runtime-v5/svelte.d.ts +0 -386
- package/runtime-v5/svelte.js +0 -436
- package/runtime-v5/svelte.js.map +0 -1
- package/runtime-v5/svelte.mjs +0 -408
- package/runtime-v5/svelte.mjs.map +0 -1
- package/runtime-v5/vue.d.mts +0 -330
- package/runtime-v5/vue.d.ts +0 -330
- package/runtime-v5/vue.js +0 -420
- package/runtime-v5/vue.js.map +0 -1
- package/runtime-v5/vue.mjs +0 -392
- package/runtime-v5/vue.mjs.map +0 -1
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { enumerate } from '@zenstackhq/common-helpers';
|
|
2
|
+
import type { FieldDef, SchemaDef } from '@zenstackhq/schema';
|
|
3
|
+
import { ORMWriteActions, type MaybePromise, type ORMWriteActionType } from './types';
|
|
4
|
+
|
|
5
|
+
type NestingPathItem = { field?: FieldDef; model: string; where: any; unique: boolean };
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Context for visiting
|
|
9
|
+
*/
|
|
10
|
+
export type NestedWriteVisitorContext = {
|
|
11
|
+
/**
|
|
12
|
+
* Parent data, can be used to replace fields
|
|
13
|
+
*/
|
|
14
|
+
parent: any;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Current field, undefined if toplevel
|
|
18
|
+
*/
|
|
19
|
+
field?: FieldDef;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A top-down path of all nested update conditions and corresponding field till now
|
|
23
|
+
*/
|
|
24
|
+
nestingPath: NestingPathItem[];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* NestedWriteVisitor's callback actions. A call back function should return true or void to indicate
|
|
29
|
+
* that the visitor should continue traversing its children, or false to stop. It can also return an object
|
|
30
|
+
* to let the visitor traverse it instead of its original children.
|
|
31
|
+
*/
|
|
32
|
+
export type NestedWriterVisitorCallback = {
|
|
33
|
+
create?: (model: string, data: any, context: NestedWriteVisitorContext) => MaybePromise<boolean | object | void>;
|
|
34
|
+
|
|
35
|
+
createMany?: (
|
|
36
|
+
model: string,
|
|
37
|
+
args: { data: any; skipDuplicates?: boolean },
|
|
38
|
+
context: NestedWriteVisitorContext,
|
|
39
|
+
) => MaybePromise<boolean | object | void>;
|
|
40
|
+
|
|
41
|
+
connectOrCreate?: (
|
|
42
|
+
model: string,
|
|
43
|
+
args: { where: object; create: any },
|
|
44
|
+
context: NestedWriteVisitorContext,
|
|
45
|
+
) => MaybePromise<boolean | object | void>;
|
|
46
|
+
|
|
47
|
+
connect?: (
|
|
48
|
+
model: string,
|
|
49
|
+
args: object,
|
|
50
|
+
context: NestedWriteVisitorContext,
|
|
51
|
+
) => MaybePromise<boolean | object | void>;
|
|
52
|
+
|
|
53
|
+
disconnect?: (
|
|
54
|
+
model: string,
|
|
55
|
+
args: object,
|
|
56
|
+
context: NestedWriteVisitorContext,
|
|
57
|
+
) => MaybePromise<boolean | object | void>;
|
|
58
|
+
|
|
59
|
+
set?: (model: string, args: object, context: NestedWriteVisitorContext) => MaybePromise<boolean | object | void>;
|
|
60
|
+
|
|
61
|
+
update?: (model: string, args: object, context: NestedWriteVisitorContext) => MaybePromise<boolean | object | void>;
|
|
62
|
+
|
|
63
|
+
updateMany?: (
|
|
64
|
+
model: string,
|
|
65
|
+
args: { where?: object; data: any },
|
|
66
|
+
context: NestedWriteVisitorContext,
|
|
67
|
+
) => MaybePromise<boolean | object | void>;
|
|
68
|
+
|
|
69
|
+
upsert?: (
|
|
70
|
+
model: string,
|
|
71
|
+
args: { where: object; create: any; update: any },
|
|
72
|
+
context: NestedWriteVisitorContext,
|
|
73
|
+
) => MaybePromise<boolean | object | void>;
|
|
74
|
+
|
|
75
|
+
delete?: (
|
|
76
|
+
model: string,
|
|
77
|
+
args: object | boolean,
|
|
78
|
+
context: NestedWriteVisitorContext,
|
|
79
|
+
) => MaybePromise<boolean | object | void>;
|
|
80
|
+
|
|
81
|
+
deleteMany?: (
|
|
82
|
+
model: string,
|
|
83
|
+
args: any | object,
|
|
84
|
+
context: NestedWriteVisitorContext,
|
|
85
|
+
) => MaybePromise<boolean | object | void>;
|
|
86
|
+
|
|
87
|
+
field?: (
|
|
88
|
+
field: FieldDef,
|
|
89
|
+
action: ORMWriteActionType,
|
|
90
|
+
data: any,
|
|
91
|
+
context: NestedWriteVisitorContext,
|
|
92
|
+
) => MaybePromise<void>;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Recursive visitor for nested write (create/update) payload.
|
|
97
|
+
*/
|
|
98
|
+
export class NestedWriteVisitor {
|
|
99
|
+
constructor(
|
|
100
|
+
private readonly schema: SchemaDef,
|
|
101
|
+
private readonly callback: NestedWriterVisitorCallback,
|
|
102
|
+
) {}
|
|
103
|
+
|
|
104
|
+
private isWriteAction(value: string): value is ORMWriteActionType {
|
|
105
|
+
return ORMWriteActions.includes(value as ORMWriteActionType);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Start visiting
|
|
110
|
+
*
|
|
111
|
+
* @see NestedWriterVisitorCallback
|
|
112
|
+
*/
|
|
113
|
+
async visit(model: string, action: ORMWriteActionType, args: any): Promise<void> {
|
|
114
|
+
if (!args) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let topData = args;
|
|
119
|
+
|
|
120
|
+
switch (action) {
|
|
121
|
+
// create has its data wrapped in 'data' field
|
|
122
|
+
case 'create':
|
|
123
|
+
topData = topData.data;
|
|
124
|
+
break;
|
|
125
|
+
|
|
126
|
+
case 'delete':
|
|
127
|
+
case 'deleteMany':
|
|
128
|
+
topData = topData.where;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
await this.doVisit(model, action, topData, undefined, undefined, []);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private async doVisit(
|
|
136
|
+
model: string,
|
|
137
|
+
action: ORMWriteActionType,
|
|
138
|
+
data: any,
|
|
139
|
+
parent: any,
|
|
140
|
+
field: FieldDef | undefined,
|
|
141
|
+
nestingPath: NestingPathItem[],
|
|
142
|
+
): Promise<void> {
|
|
143
|
+
if (!data) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const toplevel = field == undefined;
|
|
148
|
+
|
|
149
|
+
const context = { parent, field, nestingPath: [...nestingPath] };
|
|
150
|
+
const pushNewContext = (field: FieldDef | undefined, model: string, where: any, unique = false) => {
|
|
151
|
+
return { ...context, nestingPath: [...context.nestingPath, { field, model, where, unique }] };
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// visit payload
|
|
155
|
+
switch (action) {
|
|
156
|
+
case 'create':
|
|
157
|
+
for (const item of this.enumerateReverse(data)) {
|
|
158
|
+
const newContext = pushNewContext(field, model, {});
|
|
159
|
+
let callbackResult: any;
|
|
160
|
+
if (this.callback.create) {
|
|
161
|
+
callbackResult = await this.callback.create(model, item, newContext);
|
|
162
|
+
}
|
|
163
|
+
if (callbackResult !== false) {
|
|
164
|
+
const subPayload = typeof callbackResult === 'object' ? callbackResult : item;
|
|
165
|
+
await this.visitSubPayload(model, action, subPayload, newContext.nestingPath);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
|
|
170
|
+
case 'createMany':
|
|
171
|
+
case 'createManyAndReturn':
|
|
172
|
+
{
|
|
173
|
+
const newContext = pushNewContext(field, model, {});
|
|
174
|
+
let callbackResult: any;
|
|
175
|
+
if (this.callback.createMany) {
|
|
176
|
+
callbackResult = await this.callback.createMany(model, data, newContext);
|
|
177
|
+
}
|
|
178
|
+
if (callbackResult !== false) {
|
|
179
|
+
const subPayload = typeof callbackResult === 'object' ? callbackResult : data.data;
|
|
180
|
+
await this.visitSubPayload(model, action, subPayload, newContext.nestingPath);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
break;
|
|
184
|
+
|
|
185
|
+
case 'connectOrCreate':
|
|
186
|
+
for (const item of this.enumerateReverse(data)) {
|
|
187
|
+
const newContext = pushNewContext(field, model, item.where);
|
|
188
|
+
let callbackResult: any;
|
|
189
|
+
if (this.callback.connectOrCreate) {
|
|
190
|
+
callbackResult = await this.callback.connectOrCreate(model, item, newContext);
|
|
191
|
+
}
|
|
192
|
+
if (callbackResult !== false) {
|
|
193
|
+
const subPayload = typeof callbackResult === 'object' ? callbackResult : item.create;
|
|
194
|
+
await this.visitSubPayload(model, action, subPayload, newContext.nestingPath);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
|
|
199
|
+
case 'connect':
|
|
200
|
+
if (this.callback.connect) {
|
|
201
|
+
for (const item of this.enumerateReverse(data)) {
|
|
202
|
+
const newContext = pushNewContext(field, model, item, true);
|
|
203
|
+
await this.callback.connect(model, item, newContext);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
break;
|
|
207
|
+
|
|
208
|
+
case 'disconnect':
|
|
209
|
+
// disconnect has two forms:
|
|
210
|
+
// if relation is to-many, the payload is a unique filter object
|
|
211
|
+
// if relation is to-one, the payload can only be boolean `true`
|
|
212
|
+
if (this.callback.disconnect) {
|
|
213
|
+
for (const item of this.enumerateReverse(data)) {
|
|
214
|
+
const newContext = pushNewContext(field, model, item, typeof item === 'object');
|
|
215
|
+
await this.callback.disconnect(model, item, newContext);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
break;
|
|
219
|
+
|
|
220
|
+
case 'set':
|
|
221
|
+
if (this.callback.set) {
|
|
222
|
+
for (const item of this.enumerateReverse(data)) {
|
|
223
|
+
const newContext = pushNewContext(field, model, item, true);
|
|
224
|
+
await this.callback.set(model, item, newContext);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
|
|
229
|
+
case 'update':
|
|
230
|
+
for (const item of this.enumerateReverse(data)) {
|
|
231
|
+
const newContext = pushNewContext(field, model, item.where);
|
|
232
|
+
let callbackResult: any;
|
|
233
|
+
if (this.callback.update) {
|
|
234
|
+
callbackResult = await this.callback.update(model, item, newContext);
|
|
235
|
+
}
|
|
236
|
+
if (callbackResult !== false) {
|
|
237
|
+
const subPayload =
|
|
238
|
+
typeof callbackResult === 'object'
|
|
239
|
+
? callbackResult
|
|
240
|
+
: typeof item.data === 'object'
|
|
241
|
+
? item.data
|
|
242
|
+
: item;
|
|
243
|
+
await this.visitSubPayload(model, action, subPayload, newContext.nestingPath);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
break;
|
|
247
|
+
|
|
248
|
+
case 'updateMany':
|
|
249
|
+
case 'updateManyAndReturn':
|
|
250
|
+
for (const item of this.enumerateReverse(data)) {
|
|
251
|
+
const newContext = pushNewContext(field, model, item.where);
|
|
252
|
+
let callbackResult: any;
|
|
253
|
+
if (this.callback.updateMany) {
|
|
254
|
+
callbackResult = await this.callback.updateMany(model, item, newContext);
|
|
255
|
+
}
|
|
256
|
+
if (callbackResult !== false) {
|
|
257
|
+
const subPayload = typeof callbackResult === 'object' ? callbackResult : item;
|
|
258
|
+
await this.visitSubPayload(model, action, subPayload, newContext.nestingPath);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
break;
|
|
262
|
+
|
|
263
|
+
case 'upsert': {
|
|
264
|
+
for (const item of this.enumerateReverse(data)) {
|
|
265
|
+
const newContext = pushNewContext(field, model, item.where);
|
|
266
|
+
let callbackResult: any;
|
|
267
|
+
if (this.callback.upsert) {
|
|
268
|
+
callbackResult = await this.callback.upsert(model, item, newContext);
|
|
269
|
+
}
|
|
270
|
+
if (callbackResult !== false) {
|
|
271
|
+
if (typeof callbackResult === 'object') {
|
|
272
|
+
await this.visitSubPayload(model, action, callbackResult, newContext.nestingPath);
|
|
273
|
+
} else {
|
|
274
|
+
await this.visitSubPayload(model, action, item.create, newContext.nestingPath);
|
|
275
|
+
await this.visitSubPayload(model, action, item.update, newContext.nestingPath);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
case 'delete': {
|
|
283
|
+
if (this.callback.delete) {
|
|
284
|
+
for (const item of this.enumerateReverse(data)) {
|
|
285
|
+
const newContext = pushNewContext(field, model, toplevel ? item.where : item);
|
|
286
|
+
await this.callback.delete(model, item, newContext);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
case 'deleteMany':
|
|
293
|
+
if (this.callback.deleteMany) {
|
|
294
|
+
for (const item of this.enumerateReverse(data)) {
|
|
295
|
+
const newContext = pushNewContext(field, model, toplevel ? item.where : item);
|
|
296
|
+
await this.callback.deleteMany(model, item, newContext);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
break;
|
|
300
|
+
|
|
301
|
+
default: {
|
|
302
|
+
throw new Error(`unhandled action type ${action}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private async visitSubPayload(
|
|
308
|
+
model: string,
|
|
309
|
+
action: ORMWriteActionType,
|
|
310
|
+
payload: any,
|
|
311
|
+
nestingPath: NestingPathItem[],
|
|
312
|
+
) {
|
|
313
|
+
for (const item of enumerate(payload)) {
|
|
314
|
+
if (!item || typeof item !== 'object') {
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
for (const field of Object.keys(item)) {
|
|
318
|
+
const fieldDef = this.schema.models[model]?.fields[field];
|
|
319
|
+
if (!fieldDef) {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (fieldDef.relation) {
|
|
324
|
+
if (item[field]) {
|
|
325
|
+
// recurse into nested payloads
|
|
326
|
+
for (const [subAction, subData] of Object.entries<any>(item[field])) {
|
|
327
|
+
if (this.isWriteAction(subAction) && subData) {
|
|
328
|
+
await this.doVisit(fieldDef.type, subAction, subData, item[field], fieldDef, [
|
|
329
|
+
...nestingPath,
|
|
330
|
+
]);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
// visit plain field
|
|
336
|
+
if (this.callback.field) {
|
|
337
|
+
await this.callback.field(fieldDef, action, item[field], {
|
|
338
|
+
parent: item,
|
|
339
|
+
nestingPath,
|
|
340
|
+
field: fieldDef,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// enumerate a (possible) array in reverse order, so that the enumeration
|
|
349
|
+
// callback can safely delete the current item
|
|
350
|
+
private *enumerateReverse(data: any) {
|
|
351
|
+
if (Array.isArray(data)) {
|
|
352
|
+
for (let i = data.length - 1; i >= 0; i--) {
|
|
353
|
+
yield data[i];
|
|
354
|
+
}
|
|
355
|
+
} else {
|
|
356
|
+
yield data;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { SchemaDef } from '@zenstackhq/schema';
|
|
2
|
+
import { NestedReadVisitor } from './nested-read-visitor';
|
|
3
|
+
import { NestedWriteVisitor } from './nested-write-visitor';
|
|
4
|
+
import type { ORMWriteActionType } from './types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Gets models read (including nested ones) given a query args.
|
|
8
|
+
* @param model
|
|
9
|
+
* @param targetModels
|
|
10
|
+
* @param schema
|
|
11
|
+
* @param args
|
|
12
|
+
* @returns
|
|
13
|
+
*/
|
|
14
|
+
export function getReadModels(model: string, schema: SchemaDef, args: any) {
|
|
15
|
+
const result = new Set<string>();
|
|
16
|
+
result.add(model);
|
|
17
|
+
const visitor = new NestedReadVisitor(schema, {
|
|
18
|
+
field: (model) => {
|
|
19
|
+
result.add(model);
|
|
20
|
+
return true;
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
visitor.visit(model, args);
|
|
24
|
+
return [...result];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Gets mutated models (including nested ones) given a mutation args.
|
|
29
|
+
*/
|
|
30
|
+
export async function getMutatedModels(
|
|
31
|
+
model: string,
|
|
32
|
+
operation: ORMWriteActionType,
|
|
33
|
+
mutationArgs: any,
|
|
34
|
+
schema: SchemaDef,
|
|
35
|
+
) {
|
|
36
|
+
const result = new Set<string>();
|
|
37
|
+
result.add(model);
|
|
38
|
+
|
|
39
|
+
if (mutationArgs) {
|
|
40
|
+
const addModel = (model: string) => void result.add(model);
|
|
41
|
+
|
|
42
|
+
// add models that are cascaded deleted recursively
|
|
43
|
+
const addCascades = (model: string) => {
|
|
44
|
+
const cascades = new Set<string>();
|
|
45
|
+
const visited = new Set<string>();
|
|
46
|
+
collectDeleteCascades(model, schema, cascades, visited);
|
|
47
|
+
cascades.forEach((m) => addModel(m));
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const visitor = new NestedWriteVisitor(schema, {
|
|
51
|
+
create: addModel,
|
|
52
|
+
createMany: addModel,
|
|
53
|
+
connectOrCreate: addModel,
|
|
54
|
+
connect: addModel,
|
|
55
|
+
disconnect: addModel,
|
|
56
|
+
set: addModel,
|
|
57
|
+
update: addModel,
|
|
58
|
+
updateMany: addModel,
|
|
59
|
+
upsert: addModel,
|
|
60
|
+
delete: (model) => {
|
|
61
|
+
addModel(model);
|
|
62
|
+
addCascades(model);
|
|
63
|
+
},
|
|
64
|
+
deleteMany: (model) => {
|
|
65
|
+
addModel(model);
|
|
66
|
+
addCascades(model);
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
await visitor.visit(model, operation, mutationArgs);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// include delegate base models recursively
|
|
73
|
+
result.forEach((m) => {
|
|
74
|
+
getBaseRecursively(m, schema, result);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return [...result];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function collectDeleteCascades(model: string, schema: SchemaDef, result: Set<string>, visited: Set<string>) {
|
|
81
|
+
if (visited.has(model)) {
|
|
82
|
+
// break circle
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
visited.add(model);
|
|
86
|
+
|
|
87
|
+
const modelDef = schema.models[model];
|
|
88
|
+
if (!modelDef) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const [modelName, modelDef] of Object.entries(schema.models)) {
|
|
93
|
+
if (!modelDef) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
for (const fieldDef of Object.values(modelDef.fields)) {
|
|
97
|
+
if (fieldDef.relation?.onDelete === 'Cascade' && fieldDef.type === model) {
|
|
98
|
+
if (!result.has(modelName)) {
|
|
99
|
+
result.add(modelName);
|
|
100
|
+
}
|
|
101
|
+
collectDeleteCascades(modelName, schema, result, visited);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function getBaseRecursively(model: string, schema: SchemaDef, result: Set<string>) {
|
|
108
|
+
const modelDef = schema.models[model];
|
|
109
|
+
if (!modelDef) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (modelDef.baseModel) {
|
|
113
|
+
result.add(modelDef.baseModel);
|
|
114
|
+
getBaseRecursively(modelDef.baseModel, schema, result);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import Decimal from 'decimal.js';
|
|
3
|
+
import SuperJSON from 'superjson';
|
|
4
|
+
|
|
5
|
+
SuperJSON.registerCustom<Decimal, string>(
|
|
6
|
+
{
|
|
7
|
+
isApplicable: (v): v is Decimal =>
|
|
8
|
+
v instanceof Decimal ||
|
|
9
|
+
// interop with decimal.js
|
|
10
|
+
v?.toStringTag === '[object Decimal]',
|
|
11
|
+
serialize: (v) => v.toJSON(),
|
|
12
|
+
deserialize: (v) => new Decimal(v),
|
|
13
|
+
},
|
|
14
|
+
'Decimal'
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
SuperJSON.registerCustom<Buffer, string>(
|
|
18
|
+
{
|
|
19
|
+
isApplicable: (v): v is Buffer => Buffer.isBuffer(v),
|
|
20
|
+
serialize: (v) => v.toString('base64'),
|
|
21
|
+
deserialize: (v) => Buffer.from(v, 'base64'),
|
|
22
|
+
},
|
|
23
|
+
'Bytes'
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Serialize the given value with superjson
|
|
28
|
+
*/
|
|
29
|
+
export function serialize(value: unknown): { data: unknown; meta: unknown } {
|
|
30
|
+
const { json, meta } = SuperJSON.serialize(value);
|
|
31
|
+
return { data: json, meta };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Deserialize the given value with superjson using the given metadata
|
|
36
|
+
*/
|
|
37
|
+
export function deserialize(value: unknown, meta: any): unknown {
|
|
38
|
+
return SuperJSON.deserialize({ json: value as any, meta });
|
|
39
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type MaybePromise<T> = T | Promise<T> | PromiseLike<T>;
|
|
2
|
+
|
|
3
|
+
export const ORMWriteActions = [
|
|
4
|
+
'create',
|
|
5
|
+
'createMany',
|
|
6
|
+
'createManyAndReturn',
|
|
7
|
+
'connectOrCreate',
|
|
8
|
+
'update',
|
|
9
|
+
'updateMany',
|
|
10
|
+
'updateManyAndReturn',
|
|
11
|
+
'upsert',
|
|
12
|
+
'connect',
|
|
13
|
+
'disconnect',
|
|
14
|
+
'set',
|
|
15
|
+
'delete',
|
|
16
|
+
'deleteMany',
|
|
17
|
+
] as const;
|
|
18
|
+
|
|
19
|
+
export type ORMWriteActionType = (typeof ORMWriteActions)[number];
|