wabe 0.6.9 → 0.6.10

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 (158) hide show
  1. package/README.md +138 -32
  2. package/bucket/b.txt +1 -0
  3. package/dev/index.ts +215 -0
  4. package/dist/authentication/Session.d.ts +4 -1
  5. package/dist/authentication/interface.d.ts +16 -0
  6. package/dist/email/interface.d.ts +1 -1
  7. package/dist/graphql/resolvers.d.ts +4 -2
  8. package/dist/hooks/index.d.ts +1 -0
  9. package/dist/index.d.ts +0 -1
  10. package/dist/index.js +8713 -8867
  11. package/dist/server/index.d.ts +4 -2
  12. package/dist/utils/crypto.d.ts +7 -0
  13. package/dist/utils/helper.d.ts +4 -1
  14. package/generated/schema.graphql +16 -14
  15. package/generated/wabe.ts +4 -4
  16. package/package.json +15 -15
  17. package/src/authentication/OTP.test.ts +69 -0
  18. package/src/authentication/OTP.ts +66 -0
  19. package/src/authentication/Session.test.ts +665 -0
  20. package/src/authentication/Session.ts +529 -0
  21. package/src/authentication/defaultAuthentication.ts +214 -0
  22. package/src/authentication/index.ts +3 -0
  23. package/src/authentication/interface.ts +157 -0
  24. package/src/authentication/oauth/GitHub.test.ts +105 -0
  25. package/src/authentication/oauth/GitHub.ts +133 -0
  26. package/src/authentication/oauth/Google.test.ts +105 -0
  27. package/src/authentication/oauth/Google.ts +110 -0
  28. package/src/authentication/oauth/Oauth2Client.test.ts +225 -0
  29. package/src/authentication/oauth/Oauth2Client.ts +140 -0
  30. package/src/authentication/oauth/index.ts +2 -0
  31. package/src/authentication/oauth/utils.test.ts +35 -0
  32. package/src/authentication/oauth/utils.ts +28 -0
  33. package/src/authentication/providers/EmailOTP.test.ts +138 -0
  34. package/src/authentication/providers/EmailOTP.ts +93 -0
  35. package/src/authentication/providers/EmailPassword.test.ts +187 -0
  36. package/src/authentication/providers/EmailPassword.ts +130 -0
  37. package/src/authentication/providers/EmailPasswordSRP.test.ts +206 -0
  38. package/src/authentication/providers/EmailPasswordSRP.ts +184 -0
  39. package/src/authentication/providers/GitHub.ts +30 -0
  40. package/src/authentication/providers/Google.ts +30 -0
  41. package/src/authentication/providers/OAuth.test.ts +185 -0
  42. package/src/authentication/providers/OAuth.ts +112 -0
  43. package/src/authentication/providers/PhonePassword.test.ts +187 -0
  44. package/src/authentication/providers/PhonePassword.ts +129 -0
  45. package/src/authentication/providers/QRCodeOTP.test.ts +79 -0
  46. package/src/authentication/providers/QRCodeOTP.ts +65 -0
  47. package/src/authentication/providers/index.ts +6 -0
  48. package/src/authentication/resolvers/refreshResolver.test.ts +37 -0
  49. package/src/authentication/resolvers/refreshResolver.ts +20 -0
  50. package/src/authentication/resolvers/signInWithResolver.inte.test.ts +59 -0
  51. package/src/authentication/resolvers/signInWithResolver.test.ts +307 -0
  52. package/src/authentication/resolvers/signInWithResolver.ts +102 -0
  53. package/src/authentication/resolvers/signOutResolver.test.ts +41 -0
  54. package/src/authentication/resolvers/signOutResolver.ts +22 -0
  55. package/src/authentication/resolvers/signUpWithResolver.test.ts +186 -0
  56. package/src/authentication/resolvers/signUpWithResolver.ts +69 -0
  57. package/src/authentication/resolvers/verifyChallenge.test.ts +136 -0
  58. package/src/authentication/resolvers/verifyChallenge.ts +69 -0
  59. package/src/authentication/roles.test.ts +59 -0
  60. package/src/authentication/roles.ts +40 -0
  61. package/src/authentication/utils.test.ts +99 -0
  62. package/src/authentication/utils.ts +43 -0
  63. package/src/cache/InMemoryCache.test.ts +62 -0
  64. package/src/cache/InMemoryCache.ts +45 -0
  65. package/src/cron/index.test.ts +17 -0
  66. package/src/cron/index.ts +46 -0
  67. package/src/database/DatabaseController.test.ts +625 -0
  68. package/src/database/DatabaseController.ts +983 -0
  69. package/src/database/index.test.ts +1230 -0
  70. package/src/database/index.ts +9 -0
  71. package/src/database/interface.ts +312 -0
  72. package/src/email/DevAdapter.ts +8 -0
  73. package/src/email/EmailController.test.ts +29 -0
  74. package/src/email/EmailController.ts +13 -0
  75. package/src/email/index.ts +2 -0
  76. package/src/email/interface.ts +36 -0
  77. package/src/email/templates/sendOtpCode.ts +120 -0
  78. package/src/file/FileController.ts +28 -0
  79. package/src/file/FileDevAdapter.ts +54 -0
  80. package/src/file/hookDeleteFile.ts +27 -0
  81. package/src/file/hookReadFile.ts +70 -0
  82. package/src/file/hookUploadFile.ts +53 -0
  83. package/src/file/index.test.ts +979 -0
  84. package/src/file/index.ts +2 -0
  85. package/src/file/interface.ts +42 -0
  86. package/src/graphql/GraphQLSchema.test.ts +4399 -0
  87. package/src/graphql/GraphQLSchema.ts +928 -0
  88. package/src/graphql/index.ts +2 -0
  89. package/src/graphql/parseGraphqlSchema.ts +94 -0
  90. package/src/graphql/parser.test.ts +217 -0
  91. package/src/graphql/parser.ts +566 -0
  92. package/src/graphql/pointerAndRelationFunction.ts +200 -0
  93. package/src/graphql/resolvers.ts +467 -0
  94. package/src/graphql/tests/aggregation.test.ts +1123 -0
  95. package/src/graphql/tests/e2e.test.ts +596 -0
  96. package/src/graphql/tests/scalars.test.ts +250 -0
  97. package/src/graphql/types.ts +219 -0
  98. package/src/hooks/HookObject.test.ts +122 -0
  99. package/src/hooks/HookObject.ts +168 -0
  100. package/src/hooks/authentication.ts +76 -0
  101. package/src/hooks/createUser.test.ts +77 -0
  102. package/src/hooks/createUser.ts +10 -0
  103. package/src/hooks/defaultFields.test.ts +187 -0
  104. package/src/hooks/defaultFields.ts +40 -0
  105. package/src/hooks/deleteSession.test.ts +181 -0
  106. package/src/hooks/deleteSession.ts +20 -0
  107. package/src/hooks/hashFieldHook.test.ts +163 -0
  108. package/src/hooks/hashFieldHook.ts +97 -0
  109. package/src/hooks/index.test.ts +207 -0
  110. package/src/hooks/index.ts +430 -0
  111. package/src/hooks/permissions.test.ts +424 -0
  112. package/src/hooks/permissions.ts +113 -0
  113. package/src/hooks/protected.test.ts +551 -0
  114. package/src/hooks/protected.ts +72 -0
  115. package/src/hooks/searchableFields.test.ts +166 -0
  116. package/src/hooks/searchableFields.ts +98 -0
  117. package/src/hooks/session.test.ts +138 -0
  118. package/src/hooks/session.ts +78 -0
  119. package/src/hooks/setEmail.test.ts +216 -0
  120. package/src/hooks/setEmail.ts +35 -0
  121. package/src/hooks/setupAcl.test.ts +589 -0
  122. package/src/hooks/setupAcl.ts +29 -0
  123. package/src/index.ts +9 -0
  124. package/src/schema/Schema.test.ts +484 -0
  125. package/src/schema/Schema.ts +795 -0
  126. package/src/schema/defaultResolvers.ts +94 -0
  127. package/src/schema/index.ts +1 -0
  128. package/src/schema/resolvers/meResolver.test.ts +62 -0
  129. package/src/schema/resolvers/meResolver.ts +14 -0
  130. package/src/schema/resolvers/newFile.ts +0 -0
  131. package/src/schema/resolvers/resetPassword.test.ts +345 -0
  132. package/src/schema/resolvers/resetPassword.ts +64 -0
  133. package/src/schema/resolvers/sendEmail.test.ts +118 -0
  134. package/src/schema/resolvers/sendEmail.ts +21 -0
  135. package/src/schema/resolvers/sendOtpCode.test.ts +153 -0
  136. package/src/schema/resolvers/sendOtpCode.ts +52 -0
  137. package/src/security.test.ts +3461 -0
  138. package/src/server/defaultSessionHandler.test.ts +66 -0
  139. package/src/server/defaultSessionHandler.ts +115 -0
  140. package/src/server/generateCodegen.ts +476 -0
  141. package/src/server/index.test.ts +552 -0
  142. package/src/server/index.ts +354 -0
  143. package/src/server/interface.ts +11 -0
  144. package/src/server/routes/authHandler.ts +187 -0
  145. package/src/server/routes/index.ts +40 -0
  146. package/src/utils/crypto.test.ts +41 -0
  147. package/src/utils/crypto.ts +121 -0
  148. package/src/utils/export.ts +13 -0
  149. package/src/utils/helper.ts +195 -0
  150. package/src/utils/index.test.ts +11 -0
  151. package/src/utils/index.ts +201 -0
  152. package/src/utils/preload.ts +8 -0
  153. package/src/utils/testHelper.ts +117 -0
  154. package/tsconfig.json +32 -0
  155. package/bunfig.toml +0 -4
  156. package/dist/ai/index.d.ts +0 -1
  157. package/dist/ai/interface.d.ts +0 -9
  158. /package/dist/server/{defaultHandlers.d.ts → defaultSessionHandler.d.ts} +0 -0
@@ -0,0 +1,983 @@
1
+ import type { WabeTypes } from '../..'
2
+ import { OperationType, initializeHook } from '../hooks'
3
+ import type { SchemaInterface } from '../schema'
4
+ import type { WabeContext } from '../server/interface'
5
+ import { contextWithRoot } from '../utils/export'
6
+ import { notEmpty } from '../utils/export'
7
+ import type {
8
+ CountOptions,
9
+ CreateObjectOptions,
10
+ CreateObjectsOptions,
11
+ DatabaseAdapter,
12
+ DeleteObjectOptions,
13
+ DeleteObjectsOptions,
14
+ GetObjectOptions,
15
+ GetObjectsOptions,
16
+ OutputType,
17
+ UpdateObjectOptions,
18
+ UpdateObjectsOptions,
19
+ WhereType,
20
+ } from './interface'
21
+
22
+ export type Select = Record<string, boolean>
23
+ type SelectWithObject = Record<string, object | boolean>
24
+
25
+ export class DatabaseController<T extends WabeTypes> {
26
+ public adapter: DatabaseAdapter<T>
27
+
28
+ constructor(adapter: DatabaseAdapter<T>) {
29
+ this.adapter = adapter
30
+ }
31
+
32
+ _getSelectMinusPointersAndRelations({
33
+ className,
34
+ context,
35
+ select,
36
+ }: {
37
+ className: keyof T['types']
38
+ context: WabeContext<T>
39
+ select?: SelectWithObject
40
+ }): {
41
+ // We associated the fieldName with a className and a select object
42
+ pointers: Record<string, { className: string; select: Select }>
43
+ selectWithoutPointers: Select
44
+ } {
45
+ const realClass = context.wabe.config.schema?.classes?.find(
46
+ // @ts-expect-error
47
+ (c) => c.name.toLowerCase() === className.toLowerCase(),
48
+ )
49
+
50
+ if (!realClass) throw new Error('Class not found in schema')
51
+
52
+ if (!select) return { pointers: {}, selectWithoutPointers: {} }
53
+
54
+ const pointerOrRelationFields = Object.keys(realClass.fields).filter(
55
+ (fieldName) =>
56
+ realClass.fields[fieldName]?.type === 'Pointer' ||
57
+ realClass.fields[fieldName]?.type === 'Relation',
58
+ )
59
+
60
+ return Object.entries(select).reduce(
61
+ (acc, [fieldName, value]) => {
62
+ // If not pointer or relation
63
+ if (!pointerOrRelationFields.includes(fieldName))
64
+ return {
65
+ ...acc,
66
+ selectWithoutPointers: {
67
+ ...acc.selectWithoutPointers,
68
+ [fieldName]: true,
69
+ },
70
+ }
71
+
72
+ // @ts-expect-error
73
+ const classOfPointerOrRelation = realClass.fields[fieldName].class
74
+
75
+ // Pointer or relation
76
+ return {
77
+ ...acc,
78
+ pointers: {
79
+ ...acc.pointers,
80
+ [fieldName]: {
81
+ className: classOfPointerOrRelation,
82
+ // If we set value to true we want all the fields of the pointer if we
83
+ // set an object we just want some fields
84
+ select: value === true ? undefined : value,
85
+ },
86
+ },
87
+ }
88
+ },
89
+ { pointers: {}, selectWithoutPointers: {} },
90
+ )
91
+ }
92
+
93
+ _isRelationField({
94
+ pointerField,
95
+ currentClassName,
96
+ context,
97
+ originClassName,
98
+ }: {
99
+ pointerField: string
100
+ originClassName: string
101
+ context: WabeContext<T>
102
+ currentClassName?: string
103
+ }) {
104
+ if (!currentClassName) return false
105
+
106
+ return context.wabe.config.schema?.classes?.some(
107
+ (c) =>
108
+ c.name.toLowerCase() === originClassName.toLowerCase() &&
109
+ Object.entries(c.fields).find(
110
+ ([fieldName, field]) =>
111
+ fieldName === pointerField &&
112
+ field.type === 'Relation' &&
113
+ // @ts-expect-error
114
+ field.class.toLowerCase() === currentClassName.toLowerCase(),
115
+ ),
116
+ )
117
+ }
118
+
119
+ _isPointerField({
120
+ pointerField,
121
+ currentClassName,
122
+ context,
123
+ originClassName,
124
+ }: {
125
+ originClassName: string
126
+ context: WabeContext<T>
127
+ pointerField: string
128
+ currentClassName?: string
129
+ }) {
130
+ if (!currentClassName) return false
131
+
132
+ return context.wabe.config.schema?.classes?.some(
133
+ (c) =>
134
+ c.name.toLowerCase() === originClassName.toLowerCase() &&
135
+ Object.entries(c.fields).find(
136
+ ([fieldName, field]) =>
137
+ fieldName === pointerField &&
138
+ field.type === 'Pointer' &&
139
+ // @ts-expect-error
140
+ field.class.toLowerCase() === currentClassName.toLowerCase(),
141
+ ),
142
+ )
143
+ }
144
+
145
+ async _getWhereObjectWithPointerOrRelation<U extends keyof T['types']>(
146
+ className: U,
147
+ where: WhereType<T, U>,
148
+ context: WabeContext<T>,
149
+ ) {
150
+ const whereKeys = Object.keys(where) as Array<keyof WhereType<T, U>>
151
+
152
+ const realClass = context.wabe.config.schema?.classes?.find(
153
+ // @ts-expect-error
154
+ (c) => c.name.toLowerCase() === className.toLowerCase(),
155
+ )
156
+
157
+ const newWhereObject = await whereKeys.reduce(async (acc, whereKey) => {
158
+ const currentAcc = await acc
159
+
160
+ const typedWhereKey = whereKey as string
161
+
162
+ const field = realClass?.fields[typedWhereKey]
163
+
164
+ if (typedWhereKey === 'AND' || typedWhereKey === 'OR') {
165
+ const newWhere = await Promise.all(
166
+ (where[typedWhereKey] as any).map((whereObject: any) =>
167
+ this._getWhereObjectWithPointerOrRelation(
168
+ className,
169
+ whereObject,
170
+ context,
171
+ ),
172
+ ),
173
+ )
174
+
175
+ return {
176
+ ...currentAcc,
177
+ [typedWhereKey]: newWhere,
178
+ }
179
+ }
180
+
181
+ if (field?.type !== 'Pointer' && field?.type !== 'Relation') return acc
182
+
183
+ // @ts-expect-error
184
+ const fieldTargetClass = field.class
185
+
186
+ const defaultWhere = where[typedWhereKey]
187
+
188
+ const objects = await this.getObjects({
189
+ className: fieldTargetClass,
190
+ // @ts-expect-error
191
+ select: { id: true },
192
+ // @ts-expect-error
193
+ where: defaultWhere,
194
+ context,
195
+ })
196
+
197
+ return {
198
+ ...acc,
199
+ // If we don't found any object we just execute the query with the default where
200
+ // Without any transformation for pointer or relation
201
+ [typedWhereKey]: {
202
+ in: objects.map((object) => object?.id).filter(notEmpty),
203
+ },
204
+ }
205
+ }, Promise.resolve({}))
206
+
207
+ return {
208
+ ...where,
209
+ ...newWhereObject,
210
+ }
211
+ }
212
+
213
+ _buildWhereWithACL<K extends keyof T['types']>(
214
+ where: WhereType<T, K>,
215
+ context: WabeContext<T>,
216
+ operation: 'write' | 'read',
217
+ ): WhereType<T, K> {
218
+ if (context.isRoot) return where
219
+
220
+ const roleId = context.user?.role?.id
221
+ const userId = context.user?.id
222
+
223
+ // If we have an user we good right we return
224
+ // If we don't have user we check role
225
+ // If the role is good we return
226
+
227
+ // @ts-expect-error
228
+ return {
229
+ AND: [
230
+ { ...where },
231
+ // If the user is not connected we need to have a null acl
232
+ !userId
233
+ ? {
234
+ acl: { equalTo: null },
235
+ }
236
+ : undefined,
237
+ // If we have user or role we need to check the acl
238
+ userId || roleId
239
+ ? {
240
+ OR: [
241
+ {
242
+ acl: { equalTo: null },
243
+ },
244
+ userId
245
+ ? {
246
+ acl: {
247
+ users: {
248
+ contains: {
249
+ userId,
250
+ [operation]: true,
251
+ },
252
+ },
253
+ },
254
+ }
255
+ : undefined,
256
+ roleId
257
+ ? {
258
+ AND: [
259
+ {
260
+ acl: {
261
+ users: {
262
+ notContains: {
263
+ userId,
264
+ },
265
+ },
266
+ },
267
+ },
268
+ {
269
+ acl: {
270
+ roles: {
271
+ contains: {
272
+ roleId,
273
+ [operation]: true,
274
+ },
275
+ },
276
+ },
277
+ },
278
+ ],
279
+ }
280
+ : undefined,
281
+ ].filter(notEmpty),
282
+ }
283
+ : undefined,
284
+ ].filter(notEmpty),
285
+ }
286
+ }
287
+
288
+ _getFinalObjectWithPointerAndRelation({
289
+ pointers,
290
+ context,
291
+ originClassName,
292
+ object,
293
+ _skipHooks,
294
+ }: {
295
+ originClassName: string
296
+ pointers: Record<string, { className: string; select: Select }>
297
+ context: WabeContext<any>
298
+ object: Record<string, any>
299
+ _skipHooks?: boolean
300
+ }) {
301
+ return Object.entries(pointers).reduce(
302
+ async (
303
+ acc,
304
+ [pointerField, { className: currentClassName, select: currentSelect }],
305
+ ) => {
306
+ const accObject = await acc
307
+
308
+ const isPointer = this._isPointerField({
309
+ originClassName,
310
+ context,
311
+ currentClassName,
312
+ pointerField,
313
+ })
314
+
315
+ if (isPointer) {
316
+ if (!object[pointerField])
317
+ return {
318
+ ...accObject,
319
+ [pointerField]: null,
320
+ }
321
+
322
+ const objectOfPointerClass = await this.getObject({
323
+ className: currentClassName,
324
+ id: object[pointerField],
325
+ context,
326
+ // @ts-expect-error
327
+ select: currentSelect,
328
+ _skipHooks,
329
+ })
330
+
331
+ return {
332
+ ...accObject,
333
+ [pointerField]: objectOfPointerClass,
334
+ }
335
+ }
336
+
337
+ const isRelation = this._isRelationField({
338
+ originClassName,
339
+ context,
340
+ currentClassName,
341
+ pointerField,
342
+ })
343
+
344
+ if (isRelation && object[pointerField]) {
345
+ const selectWithoutTotalCount = Object.entries(
346
+ currentSelect || {},
347
+ ).reduce(
348
+ (acc2, [key, value]) => {
349
+ if (key === 'totalCount') return acc2
350
+
351
+ return {
352
+ ...acc2,
353
+ [key]: value,
354
+ }
355
+ },
356
+ {} as Record<string, any>,
357
+ )
358
+
359
+ const relationObjects = await this.getObjects({
360
+ className: currentClassName,
361
+ select: selectWithoutTotalCount,
362
+ // @ts-expect-error
363
+ where: { id: { in: object[pointerField] } },
364
+ context,
365
+ _skipHooks,
366
+ })
367
+
368
+ return {
369
+ ...accObject,
370
+ [pointerField]: context.isGraphQLCall
371
+ ? {
372
+ totalCount: relationObjects.length,
373
+ edges: relationObjects.map((object: any) => ({
374
+ node: object,
375
+ })),
376
+ }
377
+ : relationObjects,
378
+ }
379
+ }
380
+
381
+ return accObject
382
+ },
383
+ Promise.resolve({} as Record<string, any>),
384
+ )
385
+ }
386
+
387
+ async close() {
388
+ await this.adapter.close()
389
+ }
390
+
391
+ createClassIfNotExist(
392
+ className: string,
393
+ schema: SchemaInterface<T>,
394
+ ): Promise<any> {
395
+ return this.adapter.createClassIfNotExist(className, schema)
396
+ }
397
+
398
+ initializeDatabase(schema: SchemaInterface<T>): Promise<void> {
399
+ return this.adapter.initializeDatabase(schema)
400
+ }
401
+
402
+ async count<K extends keyof T['types']>({
403
+ className,
404
+ context,
405
+ where,
406
+ }: CountOptions<T, K>): Promise<number> {
407
+ const whereWithACLCondition = this._buildWhereWithACL(
408
+ where || {},
409
+ context,
410
+ 'read',
411
+ )
412
+
413
+ const hook = initializeHook({
414
+ className,
415
+ context,
416
+ select: {},
417
+ })
418
+
419
+ await hook?.runOnSingleObject({
420
+ operationType: OperationType.BeforeRead,
421
+ })
422
+
423
+ const count = await this.adapter.count({
424
+ className,
425
+ context,
426
+ where: whereWithACLCondition,
427
+ })
428
+
429
+ await hook?.runOnSingleObject({
430
+ operationType: OperationType.AfterRead,
431
+ })
432
+
433
+ return count
434
+ }
435
+
436
+ async clearDatabase(): Promise<void> {
437
+ await this.adapter.clearDatabase()
438
+ }
439
+
440
+ async getObject<K extends keyof T['types'], U extends keyof T['types'][K]>({
441
+ select,
442
+ className,
443
+ context,
444
+ _skipHooks,
445
+ id,
446
+ where,
447
+ }: GetObjectOptions<T, K, U>): Promise<OutputType<T, K, U>> {
448
+ const { pointers, selectWithoutPointers } =
449
+ this._getSelectMinusPointersAndRelations({
450
+ className,
451
+ context,
452
+ select: select as SelectWithObject,
453
+ })
454
+
455
+ const hook = !_skipHooks
456
+ ? initializeHook({
457
+ className,
458
+ context,
459
+ select: selectWithoutPointers,
460
+ })
461
+ : undefined
462
+
463
+ await hook?.runOnSingleObject({
464
+ operationType: OperationType.BeforeRead,
465
+ id,
466
+ })
467
+
468
+ const whereWithACLCondition = this._buildWhereWithACL(
469
+ where || {},
470
+ context,
471
+ 'read',
472
+ )
473
+
474
+ const selectWithPointersAndRelationsToGetId = Object.keys(pointers).reduce(
475
+ (acc, fieldName) => {
476
+ acc[fieldName] = true
477
+
478
+ return acc
479
+ },
480
+ selectWithoutPointers,
481
+ )
482
+
483
+ const objectToReturn = await this.adapter.getObject({
484
+ className,
485
+ id,
486
+ context: contextWithRoot(context),
487
+ // @ts-expect-error
488
+ select: !select ? undefined : selectWithPointersAndRelationsToGetId,
489
+ where: whereWithACLCondition,
490
+ })
491
+
492
+ const finalObject = {
493
+ ...objectToReturn,
494
+ ...(await this._getFinalObjectWithPointerAndRelation({
495
+ context,
496
+ // @ts-expect-error
497
+ originClassName: className,
498
+ pointers,
499
+ // @ts-expect-error
500
+ object: objectToReturn,
501
+ _skipHooks,
502
+ })),
503
+ }
504
+
505
+ const afterReadResult = await hook?.runOnSingleObject({
506
+ operationType: OperationType.AfterRead,
507
+ id,
508
+ // @ts-expect-error
509
+ object: finalObject,
510
+ })
511
+
512
+ return afterReadResult?.object || finalObject
513
+ }
514
+
515
+ async getObjects<
516
+ K extends keyof T['types'],
517
+ U extends keyof T['types'][K],
518
+ W extends keyof T['types'][K],
519
+ >({
520
+ className,
521
+ select,
522
+ context,
523
+ where,
524
+ _skipHooks,
525
+ first,
526
+ offset,
527
+ order,
528
+ }: GetObjectsOptions<T, K, U, W>): Promise<OutputType<T, K, W>[]> {
529
+ const { pointers, selectWithoutPointers } =
530
+ this._getSelectMinusPointersAndRelations({
531
+ className,
532
+ context,
533
+ select: select as SelectWithObject,
534
+ })
535
+
536
+ const whereWithPointer = await this._getWhereObjectWithPointerOrRelation(
537
+ className,
538
+ where || {},
539
+ context,
540
+ )
541
+
542
+ const whereWithACLCondition = this._buildWhereWithACL(
543
+ whereWithPointer || {},
544
+ context,
545
+ 'read',
546
+ )
547
+
548
+ const selectWithPointersAndRelationsToGetId = Object.keys(pointers).reduce(
549
+ (acc, fieldName) => {
550
+ acc[fieldName] = true
551
+
552
+ return acc
553
+ },
554
+ selectWithoutPointers,
555
+ )
556
+
557
+ const hook = !_skipHooks
558
+ ? initializeHook({
559
+ className,
560
+ select: selectWithoutPointers,
561
+ context,
562
+ })
563
+ : undefined
564
+
565
+ await hook?.runOnMultipleObjects({
566
+ operationType: OperationType.BeforeRead,
567
+ where: whereWithACLCondition,
568
+ })
569
+
570
+ const objectsToReturn = await this.adapter.getObjects({
571
+ className,
572
+ context: contextWithRoot(context),
573
+ first,
574
+ offset,
575
+ where: whereWithACLCondition,
576
+ // @ts-expect-error
577
+ select: !select ? undefined : selectWithPointersAndRelationsToGetId,
578
+ order,
579
+ })
580
+
581
+ const objectsWithPointers = await Promise.all(
582
+ objectsToReturn.map(async (object) => {
583
+ return {
584
+ ...object,
585
+ ...(await this._getFinalObjectWithPointerAndRelation({
586
+ // @ts-expect-error
587
+ object,
588
+ context,
589
+ // @ts-expect-error
590
+ originClassName: className,
591
+ pointers,
592
+ _skipHooks,
593
+ })),
594
+ }
595
+ }),
596
+ )
597
+
598
+ const afterReadResults = await hook?.runOnMultipleObjects({
599
+ operationType: OperationType.AfterRead,
600
+ // @ts-expect-error
601
+ objects: objectsWithPointers,
602
+ })
603
+
604
+ return (afterReadResults?.objects ||
605
+ objectsWithPointers) as unknown as Promise<OutputType<T, K, W>[]>
606
+ }
607
+
608
+ async createObject<
609
+ K extends keyof T['types'],
610
+ U extends keyof T['types'][K],
611
+ W extends keyof T['types'][K],
612
+ >({
613
+ className,
614
+ context,
615
+ data,
616
+ select,
617
+ }: CreateObjectOptions<T, K, U, W>): Promise<OutputType<T, K, W>> {
618
+ // Here data.file is null but should not be
619
+ const hook = initializeHook({
620
+ className,
621
+ context,
622
+ newData: data,
623
+ // @ts-expect-error
624
+ select,
625
+ })
626
+
627
+ const { newData } = await hook.runOnSingleObject({
628
+ operationType: OperationType.BeforeCreate,
629
+ })
630
+
631
+ const res = await this.adapter.createObject({
632
+ className,
633
+ context,
634
+ select,
635
+ data: newData || data,
636
+ })
637
+
638
+ await hook.runOnSingleObject({
639
+ operationType: OperationType.AfterCreate,
640
+ id: res.id,
641
+ })
642
+
643
+ if (select && Object.keys(select).length === 0) return null
644
+
645
+ return this.getObject({
646
+ className,
647
+ context: {
648
+ ...context,
649
+ // @ts-expect-error
650
+ user: className === 'User' ? res : context.user,
651
+ },
652
+ select,
653
+ id: res.id,
654
+ })
655
+ }
656
+
657
+ async createObjects<
658
+ K extends keyof T['types'],
659
+ U extends keyof T['types'][K],
660
+ W extends keyof T['types'][K],
661
+ X extends keyof T['types'][K],
662
+ >({
663
+ data,
664
+ select,
665
+ className,
666
+ context,
667
+ first,
668
+ offset,
669
+ order,
670
+ }: CreateObjectsOptions<T, K, U, W, X>): Promise<OutputType<T, K, W>[]> {
671
+ if (data.length === 0) return []
672
+
673
+ const hooks = await Promise.all(
674
+ data.map((newData) =>
675
+ initializeHook({
676
+ className,
677
+ context,
678
+ newData,
679
+ // @ts-expect-error
680
+ select,
681
+ }),
682
+ ),
683
+ )
684
+
685
+ const arrayOfComputedData = (
686
+ await Promise.all(
687
+ hooks.map(
688
+ async (hook) =>
689
+ (
690
+ await hook.runOnMultipleObjects({
691
+ operationType: OperationType.BeforeCreate,
692
+ })
693
+ )?.newData[0],
694
+ ),
695
+ )
696
+ ).filter(notEmpty)
697
+
698
+ const listOfIds = await this.adapter.createObjects({
699
+ className,
700
+ select,
701
+ context,
702
+ data: arrayOfComputedData,
703
+ first,
704
+ offset,
705
+ order,
706
+ })
707
+
708
+ const ids = listOfIds.map(({ id }) => id)
709
+
710
+ await Promise.all(
711
+ hooks.map((hook) =>
712
+ hook.runOnMultipleObjects({
713
+ operationType: OperationType.AfterCreate,
714
+ ids,
715
+ }),
716
+ ),
717
+ )
718
+
719
+ if (select && Object.keys(select).length === 0) return []
720
+
721
+ return this.getObjects({
722
+ className,
723
+ context: {
724
+ ...context,
725
+ // @ts-expect-error
726
+ user: className === 'User' ? data[0] : context.user,
727
+ },
728
+ select,
729
+ // @ts-expect-error
730
+ where: { id: { in: ids } },
731
+ first,
732
+ offset,
733
+ order,
734
+ })
735
+ }
736
+
737
+ async updateObject<
738
+ K extends keyof T['types'],
739
+ U extends keyof T['types'][K],
740
+ W extends keyof T['types'][K],
741
+ >({
742
+ id,
743
+ className,
744
+ context,
745
+ data,
746
+ select,
747
+ _skipHooks,
748
+ }: UpdateObjectOptions<T, K, U, W>): Promise<OutputType<T, K, W>> {
749
+ const hook = !_skipHooks
750
+ ? initializeHook({
751
+ className,
752
+ context,
753
+ newData: data,
754
+ // @ts-expect-error
755
+ select,
756
+ })
757
+ : undefined
758
+
759
+ const resultsAfterBeforeUpdate = await hook?.runOnSingleObject({
760
+ operationType: OperationType.BeforeUpdate,
761
+ id,
762
+ })
763
+
764
+ const whereWithACLCondition = this._buildWhereWithACL({}, context, 'write')
765
+
766
+ await this.adapter.updateObject({
767
+ className,
768
+ select,
769
+ id,
770
+ context,
771
+ data: resultsAfterBeforeUpdate?.newData || data,
772
+ where: whereWithACLCondition,
773
+ })
774
+
775
+ await hook?.runOnSingleObject({
776
+ operationType: OperationType.AfterUpdate,
777
+ id,
778
+ originalObject: resultsAfterBeforeUpdate?.object,
779
+ })
780
+
781
+ if (select && Object.keys(select).length === 0) return null
782
+
783
+ return this.getObject({
784
+ className,
785
+ context,
786
+ select,
787
+ id,
788
+ })
789
+ }
790
+
791
+ async updateObjects<
792
+ K extends keyof T['types'],
793
+ U extends keyof T['types'][K],
794
+ W extends keyof T['types'][K],
795
+ X extends keyof T['types'][K],
796
+ >({
797
+ className,
798
+ where,
799
+ context,
800
+ select,
801
+ data,
802
+ first,
803
+ offset,
804
+ order,
805
+ _skipHooks,
806
+ }: UpdateObjectsOptions<T, K, U, W, X>): Promise<OutputType<T, K, W>[]> {
807
+ const whereObject = await this._getWhereObjectWithPointerOrRelation(
808
+ className,
809
+ where || {},
810
+ context,
811
+ )
812
+
813
+ const hook = !_skipHooks
814
+ ? initializeHook({
815
+ className,
816
+ context,
817
+ newData: data,
818
+ // @ts-expect-error
819
+ select,
820
+ })
821
+ : undefined
822
+
823
+ const whereWithACLCondition = this._buildWhereWithACL(
824
+ whereObject,
825
+ context,
826
+ 'write',
827
+ )
828
+
829
+ const resultsAfterBeforeUpdate = await hook?.runOnMultipleObjects({
830
+ operationType: OperationType.BeforeUpdate,
831
+ where: whereWithACLCondition,
832
+ })
833
+
834
+ const objects = await this.adapter.updateObjects({
835
+ className,
836
+ context,
837
+ select,
838
+ data: resultsAfterBeforeUpdate?.newData[0] || data,
839
+ where: whereWithACLCondition,
840
+ first,
841
+ offset,
842
+ order,
843
+ })
844
+
845
+ const objectsId = objects.map((object) => object?.id).filter(notEmpty)
846
+
847
+ await hook?.runOnMultipleObjects({
848
+ operationType: OperationType.AfterUpdate,
849
+ ids: objectsId,
850
+ originalObjects: resultsAfterBeforeUpdate?.objects || [],
851
+ })
852
+
853
+ if (select && Object.keys(select).length === 0) return []
854
+
855
+ return this.getObjects({
856
+ className,
857
+ context,
858
+ select,
859
+ // @ts-expect-error
860
+ where: { id: { in: objectsId } },
861
+ first,
862
+ offset,
863
+ order,
864
+ })
865
+ }
866
+
867
+ async deleteObject<
868
+ K extends keyof T['types'],
869
+ U extends keyof T['types'][K],
870
+ >({
871
+ context,
872
+ className,
873
+ id,
874
+ select,
875
+ }: DeleteObjectOptions<T, K, U>): Promise<OutputType<T, K, U>> {
876
+ const hook = initializeHook({
877
+ className,
878
+ context,
879
+ // @ts-expect-error
880
+ select,
881
+ })
882
+
883
+ const whereWithACLCondition = this._buildWhereWithACL({}, context, 'write')
884
+
885
+ let objectBeforeDelete = null
886
+
887
+ if (select && Object.keys(select).length > 0)
888
+ objectBeforeDelete = await this.getObject({
889
+ className,
890
+ select,
891
+ id,
892
+ context,
893
+ })
894
+
895
+ const resultOfBeforeDelete = await hook.runOnSingleObject({
896
+ operationType: OperationType.BeforeDelete,
897
+ id,
898
+ })
899
+
900
+ await this.adapter.deleteObject({
901
+ className,
902
+ context,
903
+ select,
904
+ id,
905
+ where: whereWithACLCondition,
906
+ })
907
+
908
+ await hook.runOnSingleObject({
909
+ operationType: OperationType.AfterDelete,
910
+ originalObject: resultOfBeforeDelete.object,
911
+ })
912
+
913
+ return objectBeforeDelete
914
+ }
915
+
916
+ async deleteObjects<
917
+ K extends keyof T['types'],
918
+ U extends keyof T['types'][K],
919
+ W extends keyof T['types'][K],
920
+ >({
921
+ className,
922
+ context,
923
+ select,
924
+ where,
925
+ first,
926
+ offset,
927
+ order,
928
+ }: DeleteObjectsOptions<T, K, U, W>): Promise<OutputType<T, K, W>[]> {
929
+ const whereObject = await this._getWhereObjectWithPointerOrRelation(
930
+ className,
931
+ where || {},
932
+ context,
933
+ )
934
+
935
+ const hook = initializeHook({
936
+ className,
937
+ context,
938
+ // @ts-expect-error
939
+ select,
940
+ })
941
+
942
+ const whereWithACLCondition = this._buildWhereWithACL(
943
+ whereObject,
944
+ context,
945
+ 'write',
946
+ )
947
+
948
+ let objectsBeforeDelete: OutputType<T, K, W>[] = []
949
+
950
+ if (select && Object.keys(select).length > 0)
951
+ objectsBeforeDelete = await this.getObjects({
952
+ className,
953
+ where,
954
+ select,
955
+ context,
956
+ first,
957
+ offset,
958
+ order,
959
+ })
960
+
961
+ const resultOfBeforeDelete = await hook.runOnMultipleObjects({
962
+ operationType: OperationType.BeforeDelete,
963
+ where: whereWithACLCondition,
964
+ })
965
+
966
+ await this.adapter.deleteObjects({
967
+ className,
968
+ context,
969
+ select,
970
+ first,
971
+ offset,
972
+ where: whereWithACLCondition,
973
+ order,
974
+ })
975
+
976
+ await hook.runOnMultipleObjects({
977
+ operationType: OperationType.AfterDelete,
978
+ originalObjects: resultOfBeforeDelete.objects,
979
+ })
980
+
981
+ return objectsBeforeDelete
982
+ }
983
+ }