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,795 @@
1
+ import { AuthenticationProvider, SecondaryFactor } from '../authentication'
2
+ import { refreshResolver } from '../authentication/resolvers/refreshResolver'
3
+ import { signOutResolver } from '../authentication/resolvers/signOutResolver'
4
+ import { verifyChallengeResolver } from '../authentication/resolvers/verifyChallenge'
5
+ import type { WabeConfig, WabeTypes } from '../server'
6
+ import { defaultMutations, defaultQueries } from './defaultResolvers'
7
+ import type { HookObject } from '../hooks/HookObject'
8
+ import { signUpWithResolver } from '../authentication/resolvers/signUpWithResolver'
9
+ import { signInWithResolver } from '../authentication/resolvers/signInWithResolver'
10
+
11
+ export type WabePrimaryTypes =
12
+ | 'String'
13
+ | 'Int'
14
+ | 'Float'
15
+ | 'Boolean'
16
+ | 'Email'
17
+ | 'Phone'
18
+ | 'Date'
19
+ | 'File'
20
+ | 'Hash'
21
+
22
+ export type WabeCustomTypes = 'Array' | 'Object'
23
+
24
+ export type WabeRelationTypes = 'Pointer' | 'Relation'
25
+
26
+ export type WabeFieldTypes =
27
+ | WabeCustomTypes
28
+ | WabePrimaryTypes
29
+ | WabeRelationTypes
30
+
31
+ export type WabeObject<T extends WabeTypes> = {
32
+ name: string
33
+ fields: SchemaFields<T>
34
+ description?: string
35
+ required?: boolean
36
+ }
37
+
38
+ type FieldBase<T extends WabeTypes> = {
39
+ required?: boolean
40
+ description?: string
41
+ protected?: {
42
+ authorizedRoles: Array<T['enums']['RoleEnum'] | 'rootOnly'>
43
+ protectedOperations: Array<'create' | 'read' | 'update'>
44
+ }
45
+ }
46
+
47
+ type TypeFieldBase<U, K extends WabeFieldTypes> = {
48
+ type: K
49
+ defaultValue?: U
50
+ }
51
+
52
+ type TypeFieldArray<T extends WabeTypes> = {
53
+ type: 'Array'
54
+ requiredValue?: boolean
55
+ defaultValue?: any[]
56
+ } & (
57
+ | {
58
+ // For the moment we only keep object and not array because we don't
59
+ // support array of array
60
+ typeValue: WabePrimaryTypes
61
+ }
62
+ | { typeValue: 'Object'; object: WabeObject<T> }
63
+ )
64
+
65
+ type TypeFieldObject<T extends WabeTypes> = {
66
+ type: 'Object'
67
+ object: WabeObject<T>
68
+ defaultValue?: any
69
+ }
70
+
71
+ type TypeFieldPointer<T extends WabeTypes> = {
72
+ type: 'Pointer'
73
+ class: keyof T['types']
74
+ }
75
+
76
+ type TypeFieldRelation<T extends WabeTypes> = {
77
+ type: 'Relation'
78
+ class: keyof T['types']
79
+ }
80
+
81
+ type TypeFieldFile = {
82
+ type: 'File'
83
+ }
84
+
85
+ type TypeFieldCustomScalars<T extends WabeTypes> = {
86
+ type: T['scalars']
87
+ required?: boolean
88
+ defaultValue?: any
89
+ }
90
+
91
+ type TypeFieldCustomEnums<T extends WabeTypes> = {
92
+ type: keyof T['enums']
93
+ defaultValue?: any
94
+ }
95
+
96
+ export type TypeField<T extends WabeTypes> = (
97
+ | TypeFieldBase<string, 'String'>
98
+ | TypeFieldBase<number, 'Int'>
99
+ | TypeFieldBase<number, 'Float'>
100
+ | TypeFieldBase<boolean, 'Boolean'>
101
+ | TypeFieldBase<Date, 'Date'>
102
+ | TypeFieldBase<string, 'Email'>
103
+ | TypeFieldBase<string, 'Phone'>
104
+ | TypeFieldBase<string, 'Hash'>
105
+ | TypeFieldArray<T>
106
+ | TypeFieldObject<T>
107
+ | TypeFieldPointer<T>
108
+ | TypeFieldRelation<T>
109
+ | TypeFieldFile
110
+ | TypeFieldCustomScalars<T>
111
+ | TypeFieldCustomEnums<T>
112
+ ) &
113
+ FieldBase<T>
114
+
115
+ export type SchemaFields<T extends WabeTypes> = Record<string, TypeField<T>>
116
+
117
+ export type ResolverType<T extends WabeTypes> = {
118
+ required?: boolean
119
+ description?: string
120
+ resolve: (...args: any) => any
121
+ } & (
122
+ | { type: WabePrimaryTypes | T['enums'] | T['scalars'] }
123
+ | { type: 'Object'; outputObject: ClassInterface<T> }
124
+ | {
125
+ type: 'Array'
126
+ typeValue: WabePrimaryTypes
127
+ typeValueRequired?: boolean
128
+ }
129
+ | {
130
+ type: 'Array'
131
+ typeValue: 'Object'
132
+ outputObject: ClassInterface<T>
133
+ typeValueRequired?: boolean
134
+ }
135
+ )
136
+
137
+ export type QueryResolver<T extends WabeTypes> = {
138
+ args?: SchemaFields<T>
139
+ } & ResolverType<T>
140
+
141
+ export type MutationResolver<T extends WabeTypes> = {
142
+ args?: { input: SchemaFields<T> }
143
+ } & ResolverType<T>
144
+
145
+ export type TypeResolver<T extends WabeTypes> = {
146
+ queries?: {
147
+ [key: string]: QueryResolver<T>
148
+ }
149
+ mutations?: {
150
+ [key: string]: MutationResolver<T>
151
+ }
152
+ }
153
+
154
+ export type PermissionsOperations = 'create' | 'read' | 'update' | 'delete'
155
+
156
+ export interface PermissionProperties<T extends WabeTypes> {
157
+ requireAuthentication?: boolean
158
+ /**
159
+ * An empty array means that none role is authorized (except root client)
160
+ */
161
+ authorizedRoles?: Array<T['enums']['RoleEnum'] | 'everyone'>
162
+ }
163
+
164
+ /**
165
+ * ACL properties
166
+ * Callback to define the ACL object before insert of the object in the database
167
+ * Can be done with a beforeCreate hook but for simplicity we can define it here
168
+ */
169
+ export type ACLProperties = (
170
+ hookObject: HookObject<any, any>,
171
+ ) => void | Promise<void>
172
+
173
+ export type ClassPermissions<T extends WabeTypes> = Partial<
174
+ Record<PermissionsOperations, PermissionProperties<T>> & {
175
+ acl: ACLProperties
176
+ }
177
+ >
178
+
179
+ export type SearchableFields = Array<string>
180
+
181
+ export type ClassIndexes = Array<{
182
+ field: string
183
+ order: 'ASC' | 'DESC'
184
+ unique?: boolean
185
+ }>
186
+
187
+ export interface ClassInterface<T extends WabeTypes> {
188
+ name: string
189
+ fields: SchemaFields<T>
190
+ description?: string
191
+ permissions?: ClassPermissions<T>
192
+ searchableFields?: SearchableFields
193
+ indexes?: ClassIndexes
194
+ }
195
+
196
+ export interface ScalarInterface {
197
+ name: string
198
+ description?: string
199
+ parseValue?: (value: any) => any
200
+ serialize?: (value: any) => any
201
+ parseLiteral?: (ast: any) => any
202
+ }
203
+
204
+ export interface EnumInterface {
205
+ name: string
206
+ values: Record<string, string>
207
+ description?: string
208
+ }
209
+
210
+ export interface SchemaInterface<T extends WabeTypes> {
211
+ classes?: ClassInterface<T>[]
212
+ scalars?: ScalarInterface[]
213
+ enums?: EnumInterface[]
214
+ resolvers?: TypeResolver<T>
215
+ }
216
+
217
+ export class Schema<T extends WabeTypes> {
218
+ public schema: SchemaInterface<T>
219
+ private config: WabeConfig<T>
220
+
221
+ constructor(config: WabeConfig<T>) {
222
+ this.config = config
223
+ // TODO : Add default scalars here
224
+ this.schema = {
225
+ ...config.schema,
226
+ classes: this.defaultClass(config.schema),
227
+ enums: [...(config.schema?.enums || []), ...this.defaultEnum()],
228
+ resolvers: this.mergeResolvers(this.defaultResolvers()),
229
+ }
230
+ }
231
+
232
+ defaultEnum(): EnumInterface[] {
233
+ return [
234
+ {
235
+ name: 'AuthenticationProvider',
236
+ values: Object.fromEntries(
237
+ Object.values(AuthenticationProvider).map((key) => [key, key]),
238
+ ),
239
+ },
240
+ {
241
+ name: 'SecondaryFactor',
242
+ values: Object.fromEntries(
243
+ Object.values(SecondaryFactor).map((key) => [key, key]),
244
+ ),
245
+ },
246
+ ]
247
+ }
248
+
249
+ mergeResolvers(defaultResolvers: TypeResolver<T>): TypeResolver<T> {
250
+ return {
251
+ mutations: {
252
+ ...(this.config.schema?.resolvers?.mutations || {}),
253
+ ...defaultResolvers.mutations,
254
+ },
255
+ queries: {
256
+ ...(this.config.schema?.resolvers?.queries || {}),
257
+ ...defaultResolvers.queries,
258
+ },
259
+ }
260
+ }
261
+
262
+ defaultResolvers(): TypeResolver<T> {
263
+ const customAuthenticationConfig =
264
+ this.config.authentication?.customAuthenticationMethods || []
265
+
266
+ const allPrimaryAuthenticationMethodsInput = customAuthenticationConfig
267
+ .filter((authenticationMethod) => !authenticationMethod.isSecondaryFactor)
268
+ .reduce(
269
+ (acc, authenticationMethod) => {
270
+ acc[authenticationMethod.name] = {
271
+ type: 'Object',
272
+ object: {
273
+ name: authenticationMethod.name,
274
+ fields: authenticationMethod.input,
275
+ },
276
+ }
277
+
278
+ return acc
279
+ },
280
+ {} as SchemaFields<T>,
281
+ )
282
+
283
+ const allSecondaryFactorAuthenticationMethodsInput =
284
+ customAuthenticationConfig
285
+ .filter(
286
+ (authenticationMethod) => authenticationMethod.isSecondaryFactor,
287
+ )
288
+ .reduce(
289
+ (acc, authenticationMethod) => {
290
+ acc[authenticationMethod.name] = {
291
+ type: 'Object',
292
+ object: {
293
+ name: authenticationMethod.name,
294
+ fields: authenticationMethod.input,
295
+ },
296
+ }
297
+
298
+ return acc
299
+ },
300
+ {} as SchemaFields<T>,
301
+ )
302
+
303
+ const authenticationInputObject: TypeFieldObject<T> = {
304
+ type: 'Object',
305
+ object: {
306
+ name: 'Authentication',
307
+ fields: allPrimaryAuthenticationMethodsInput,
308
+ },
309
+ }
310
+
311
+ const secondaryFactorAuthenticationInputObject: TypeFieldObject<T> = {
312
+ type: 'Object',
313
+ object: {
314
+ name: 'SecondaryFactorAuthentication',
315
+ fields: allSecondaryFactorAuthenticationMethodsInput,
316
+ },
317
+ }
318
+
319
+ const authenticationInput: TypeField<T> = {
320
+ type: 'Object',
321
+ object: {
322
+ name: 'Authentication',
323
+ fields: authenticationInputObject.object.fields,
324
+ required: true,
325
+ },
326
+ required: true,
327
+ }
328
+
329
+ return {
330
+ queries: defaultQueries,
331
+ mutations: {
332
+ ...defaultMutations,
333
+ ...(customAuthenticationConfig.length > 0
334
+ ? {
335
+ signInWith: {
336
+ type: 'Object',
337
+ outputObject: {
338
+ name: 'SignInWithOutput',
339
+ fields: {
340
+ user: {
341
+ type: 'Pointer',
342
+ class: 'User',
343
+ },
344
+ accessToken: {
345
+ type: 'String',
346
+ },
347
+ refreshToken: {
348
+ type: 'String',
349
+ },
350
+ csrfToken: {
351
+ type: 'String',
352
+ required: false,
353
+ },
354
+ srp: {
355
+ type: 'Object',
356
+ object: {
357
+ name: 'SRPOutputSignInWith',
358
+ fields: {
359
+ salt: {
360
+ type: 'String',
361
+ },
362
+ serverPublic: {
363
+ type: 'String',
364
+ },
365
+ },
366
+ },
367
+ },
368
+ },
369
+ },
370
+ args: {
371
+ input: {
372
+ authentication: authenticationInput,
373
+ },
374
+ },
375
+ resolve: signInWithResolver,
376
+ },
377
+ signUpWith: {
378
+ type: 'Object',
379
+ outputObject: {
380
+ name: 'SignUpWithOutput',
381
+ fields: {
382
+ id: { type: 'String' },
383
+ accessToken: {
384
+ type: 'String',
385
+ required: true,
386
+ },
387
+ refreshToken: {
388
+ type: 'String',
389
+ required: true,
390
+ },
391
+ csrfToken: {
392
+ type: 'String',
393
+ required: false,
394
+ },
395
+ },
396
+ },
397
+ args: {
398
+ input: {
399
+ authentication: authenticationInput,
400
+ },
401
+ },
402
+ resolve: signUpWithResolver,
403
+ },
404
+ signOut: {
405
+ type: 'Boolean',
406
+ resolve: signOutResolver,
407
+ },
408
+ refresh: {
409
+ type: 'Object',
410
+ args: {
411
+ input: {
412
+ accessToken: {
413
+ type: 'String',
414
+ required: true,
415
+ },
416
+ refreshToken: {
417
+ type: 'String',
418
+ required: true,
419
+ },
420
+ },
421
+ },
422
+ outputObject: {
423
+ name: 'RefreshSessionOutput',
424
+ fields: {
425
+ accessToken: {
426
+ type: 'String',
427
+ required: true,
428
+ },
429
+ refreshToken: {
430
+ type: 'String',
431
+ required: true,
432
+ },
433
+ },
434
+ },
435
+ resolve: refreshResolver,
436
+ },
437
+ verifyChallenge: {
438
+ type: 'Object',
439
+ outputObject: {
440
+ name: 'VerifyChallengeOutput',
441
+ fields: {
442
+ accessToken: {
443
+ type: 'String',
444
+ },
445
+ srp: {
446
+ type: 'Object',
447
+ object: {
448
+ name: 'SRPOutputVerifyChallenge',
449
+ fields: {
450
+ serverSessionProof: {
451
+ type: 'String',
452
+ required: true,
453
+ },
454
+ },
455
+ },
456
+ },
457
+ },
458
+ },
459
+ args: {
460
+ input: {
461
+ secondFA: secondaryFactorAuthenticationInputObject,
462
+ },
463
+ },
464
+ resolve: verifyChallengeResolver,
465
+ },
466
+ }
467
+ : {}),
468
+ },
469
+ }
470
+ }
471
+
472
+ sessionClass(): ClassInterface<T> {
473
+ return {
474
+ name: '_Session',
475
+ fields: {
476
+ user: {
477
+ type: 'Pointer',
478
+ required: true,
479
+ class: 'User',
480
+ },
481
+ accessTokenEncrypted: {
482
+ type: 'String',
483
+ required: true,
484
+ },
485
+ accessTokenExpiresAt: {
486
+ type: 'Date',
487
+ required: true,
488
+ },
489
+ refreshTokenEncrypted: {
490
+ type: 'String',
491
+ required: true,
492
+ },
493
+ refreshTokenExpiresAt: {
494
+ type: 'Date',
495
+ required: true,
496
+ },
497
+ },
498
+ }
499
+ }
500
+
501
+ roleClass(): ClassInterface<T> {
502
+ return {
503
+ name: 'Role',
504
+ fields: {
505
+ name: {
506
+ type: 'String',
507
+ required: true,
508
+ },
509
+ users: {
510
+ type: 'Relation',
511
+ class: 'User',
512
+ },
513
+ },
514
+ permissions: {
515
+ create: {
516
+ authorizedRoles: [],
517
+ requireAuthentication: true,
518
+ },
519
+ read: {
520
+ authorizedRoles: ['everyone'],
521
+ requireAuthentication: true,
522
+ },
523
+ update: {
524
+ authorizedRoles: [],
525
+ requireAuthentication: true,
526
+ },
527
+ delete: {
528
+ authorizedRoles: [],
529
+ requireAuthentication: true,
530
+ },
531
+ },
532
+ }
533
+ }
534
+
535
+ internalConfigClass(): ClassInterface<T> {
536
+ return {
537
+ name: '_InternalConfig',
538
+ fields: {
539
+ configKey: {
540
+ type: 'String',
541
+ required: true,
542
+ },
543
+ configValue: {
544
+ type: 'String',
545
+ required: true,
546
+ },
547
+ description: {
548
+ type: 'String',
549
+ },
550
+ },
551
+ // Only root key
552
+ permissions: {
553
+ create: {
554
+ authorizedRoles: [],
555
+ requireAuthentication: true,
556
+ },
557
+ read: {
558
+ authorizedRoles: [],
559
+ requireAuthentication: true,
560
+ },
561
+ update: {
562
+ authorizedRoles: [],
563
+ requireAuthentication: true,
564
+ },
565
+ delete: {
566
+ authorizedRoles: [],
567
+ requireAuthentication: true,
568
+ },
569
+ },
570
+ }
571
+ }
572
+
573
+ userClass(): ClassInterface<T> {
574
+ const customAuthenticationConfig =
575
+ this.config.authentication?.customAuthenticationMethods || []
576
+
577
+ const allAuthenticationDataToStoreObject = customAuthenticationConfig
578
+ .filter(
579
+ (authenticationMethod) =>
580
+ authenticationMethod.dataToStore &&
581
+ !authenticationMethod.isSecondaryFactor,
582
+ )
583
+ .reduce(
584
+ (acc, authenticationMethod) => {
585
+ if (authenticationMethod.dataToStore)
586
+ acc[authenticationMethod.name] = {
587
+ type: 'Object',
588
+ object: {
589
+ name: authenticationMethod.name,
590
+ fields: authenticationMethod.dataToStore,
591
+ },
592
+ }
593
+
594
+ return acc
595
+ },
596
+ {} as SchemaFields<T>,
597
+ )
598
+
599
+ const authenticationObject: TypeFieldObject<T> = {
600
+ type: 'Object',
601
+ object: {
602
+ name: 'Authentication',
603
+ fields: allAuthenticationDataToStoreObject,
604
+ },
605
+ }
606
+
607
+ const fields: SchemaFields<T> = {
608
+ ...(customAuthenticationConfig.length > 0
609
+ ? { authentication: authenticationObject }
610
+ : {}),
611
+ provider: {
612
+ type: 'AuthenticationProvider',
613
+ },
614
+ isOauth: {
615
+ type: 'Boolean',
616
+ },
617
+ email: {
618
+ type: 'Email',
619
+ },
620
+ verifiedEmail: {
621
+ type: 'Boolean',
622
+ },
623
+ role: {
624
+ type: 'Pointer',
625
+ class: 'Role',
626
+ protected: {
627
+ authorizedRoles: ['rootOnly'],
628
+ protectedOperations: ['create', 'update'],
629
+ },
630
+ },
631
+ sessions: {
632
+ type: 'Relation',
633
+ class: '_Session',
634
+ protected: {
635
+ authorizedRoles: ['rootOnly'],
636
+ protectedOperations: ['create', 'update'],
637
+ },
638
+ },
639
+ secondFA: {
640
+ type: 'Object',
641
+ object: {
642
+ name: 'SecondFA',
643
+ fields: {
644
+ enabled: {
645
+ type: 'Boolean',
646
+ required: true,
647
+ },
648
+ provider: {
649
+ type: 'SecondaryFactor',
650
+ required: true,
651
+ },
652
+ },
653
+ },
654
+ },
655
+ }
656
+
657
+ return {
658
+ name: 'User',
659
+ fields,
660
+ permissions: {
661
+ delete: {
662
+ authorizedRoles: [],
663
+ requireAuthentication: true,
664
+ },
665
+ update: {
666
+ authorizedRoles: [],
667
+ requireAuthentication: true,
668
+ },
669
+ create: {
670
+ requireAuthentication: false,
671
+ },
672
+ read: {
673
+ authorizedRoles: [],
674
+ requireAuthentication: true,
675
+ },
676
+ },
677
+ }
678
+ }
679
+
680
+ defaultFields(): SchemaFields<T> {
681
+ return {
682
+ acl: {
683
+ type: 'Object',
684
+ object: {
685
+ name: 'ACLObject',
686
+ fields: {
687
+ users: {
688
+ type: 'Array',
689
+ typeValue: 'Object',
690
+ object: {
691
+ name: 'UsersACL',
692
+ fields: {
693
+ userId: {
694
+ type: 'String',
695
+ required: true,
696
+ },
697
+ read: {
698
+ type: 'Boolean',
699
+ required: true,
700
+ },
701
+ write: {
702
+ type: 'Boolean',
703
+ required: true,
704
+ },
705
+ },
706
+ },
707
+ },
708
+ roles: {
709
+ type: 'Array',
710
+ typeValue: 'Object',
711
+ object: {
712
+ name: 'RolesACL',
713
+ fields: {
714
+ roleId: {
715
+ type: 'String',
716
+ required: true,
717
+ },
718
+ read: {
719
+ type: 'Boolean',
720
+ required: true,
721
+ },
722
+ write: {
723
+ type: 'Boolean',
724
+ required: true,
725
+ },
726
+ },
727
+ },
728
+ },
729
+ },
730
+ },
731
+ protected: {
732
+ authorizedRoles: ['rootOnly'],
733
+ protectedOperations: ['create', 'update'],
734
+ },
735
+ },
736
+ createdAt: {
737
+ type: 'Date',
738
+ },
739
+ updatedAt: {
740
+ type: 'Date',
741
+ },
742
+ search: {
743
+ type: 'Array',
744
+ typeValue: 'String',
745
+ },
746
+ }
747
+ }
748
+
749
+ mergeClass(newClass: ClassInterface<T>[]): ClassInterface<T>[] {
750
+ const allUniqueClassName = [
751
+ ...new Set(newClass.map((classItem) => classItem.name)),
752
+ ]
753
+
754
+ return allUniqueClassName.map((uniqueClass) => {
755
+ const allClassWithSameName = newClass.filter(
756
+ (localClass) => localClass.name === uniqueClass,
757
+ )
758
+
759
+ return allClassWithSameName.reduce(
760
+ (acc, classItem) => {
761
+ return {
762
+ ...acc,
763
+ ...classItem,
764
+ fields: {
765
+ // We merge fields that have the same name and then we add the new fields
766
+ ...acc.fields,
767
+ ...classItem.fields,
768
+ ...this.defaultFields(),
769
+ },
770
+ permissions:
771
+ classItem.permissions || acc.permissions
772
+ ? {
773
+ // Order is important because we put the provided schema before so we always consider
774
+ // the provided schema as the source of truth
775
+ ...classItem.permissions,
776
+ ...acc.permissions,
777
+ }
778
+ : undefined,
779
+ }
780
+ },
781
+ allClassWithSameName[0] as ClassInterface<T>,
782
+ )
783
+ })
784
+ }
785
+
786
+ defaultClass(schema?: SchemaInterface<T>): ClassInterface<T>[] {
787
+ return this.mergeClass([
788
+ ...(schema?.classes || []),
789
+ this.userClass(),
790
+ this.sessionClass(),
791
+ this.roleClass(),
792
+ this.internalConfigClass(),
793
+ ])
794
+ }
795
+ }