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,66 @@
1
+ import { describe, expect, it, mock, spyOn } from 'bun:test'
2
+ import { defaultSessionHandler } from './defaultSessionHandler'
3
+ import { Session } from '../authentication/Session'
4
+
5
+ describe('defaultSessionHandler', () => {
6
+ it('should set refreshToken cookie expiration to refresh expiry', async () => {
7
+ const refreshExpiry = new Date('2030-01-01T00:00:00.000Z')
8
+ const accessExpiry = new Date('2029-01-01T00:00:00.000Z')
9
+
10
+ const spyMeFromAccessToken = spyOn(
11
+ Session.prototype,
12
+ 'meFromAccessToken',
13
+ ).mockResolvedValue({
14
+ user: { id: 'userId' } as any,
15
+ sessionId: 'sessionId',
16
+ accessToken: 'newAccess',
17
+ refreshToken: 'newRefresh',
18
+ })
19
+
20
+ const spyGetAccessTokenExpireAt = spyOn(
21
+ Session.prototype,
22
+ 'getAccessTokenExpireAt',
23
+ ).mockReturnValue(accessExpiry)
24
+ const spyGetRefreshTokenExpireAt = spyOn(
25
+ Session.prototype,
26
+ 'getRefreshTokenExpireAt',
27
+ ).mockReturnValue(refreshExpiry)
28
+
29
+ const setCookie = mock(() => {})
30
+
31
+ const wabe: any = {
32
+ config: {
33
+ rootKey: 'root',
34
+ authentication: {
35
+ session: { cookieSession: true, jwtSecret: 'dev' },
36
+ },
37
+ },
38
+ }
39
+
40
+ const handler = defaultSessionHandler(wabe)
41
+
42
+ const ctx: any = {
43
+ request: new Request('http://localhost/graphql', {
44
+ headers: new Headers({
45
+ 'Wabe-Access-Token': 'oldAccess',
46
+ }),
47
+ }),
48
+ res: {
49
+ setCookie,
50
+ },
51
+ }
52
+
53
+ await handler(ctx)
54
+
55
+ const refreshCookie = setCookie.mock.calls.find(
56
+ (call: unknown) =>
57
+ Array.isArray(call) && call.length > 0 && call[0] === 'refreshToken',
58
+ ) as [string, string, { expires?: Date }] | undefined
59
+
60
+ expect(refreshCookie?.[2]?.expires).toEqual(refreshExpiry)
61
+
62
+ spyMeFromAccessToken.mockRestore()
63
+ spyGetAccessTokenExpireAt.mockRestore()
64
+ spyGetRefreshTokenExpireAt.mockRestore()
65
+ })
66
+ })
@@ -0,0 +1,115 @@
1
+ import type { Wabe, WobeCustomContext } from '.'
2
+ import { timingSafeEqual } from 'node:crypto'
3
+ import { Session } from '../authentication/Session'
4
+ import { getCookieInRequestHeaders } from '../utils'
5
+ import type { DevWabeTypes } from '../utils/helper'
6
+
7
+ export const defaultSessionHandler =
8
+ (wabe: Wabe<DevWabeTypes>) =>
9
+ async (ctx: WobeCustomContext<DevWabeTypes>) => {
10
+ const headers = ctx.request.headers
11
+ const isGraphQLCall = ctx.request.url.includes('/graphql')
12
+
13
+ const headerRootKey = Buffer.from(headers.get('Wabe-Root-Key') || '')
14
+ const rootKey = Buffer.from(wabe.config.rootKey)
15
+
16
+ if (
17
+ headerRootKey.length === rootKey.length &&
18
+ timingSafeEqual(rootKey, headerRootKey)
19
+ ) {
20
+ ctx.wabe = {
21
+ isRoot: true,
22
+ wabe,
23
+ response: ctx.res,
24
+ isGraphQLCall,
25
+ }
26
+ return
27
+ }
28
+
29
+ const getAccessToken = () => {
30
+ if (headers.get('Wabe-Access-Token'))
31
+ return { accessToken: headers.get('Wabe-Access-Token') }
32
+
33
+ const isCookieSession =
34
+ !!wabe.config.authentication?.session?.cookieSession
35
+
36
+ if (isCookieSession)
37
+ return {
38
+ accessToken: getCookieInRequestHeaders(
39
+ 'accessToken',
40
+ ctx.request.headers,
41
+ ),
42
+ }
43
+
44
+ return { accessToken: null }
45
+ }
46
+
47
+ const { accessToken } = getAccessToken()
48
+
49
+ if (!accessToken) {
50
+ ctx.wabe = {
51
+ isRoot: false,
52
+ wabe,
53
+ response: ctx.res,
54
+ isGraphQLCall,
55
+ }
56
+ return
57
+ }
58
+
59
+ const getCsrfToken = () => {
60
+ if (headers.get('Wabe-Csrf-Token'))
61
+ return { csrfToken: headers.get('Wabe-Csrf-Token') || '' }
62
+
63
+ return { csrfToken: '' }
64
+ }
65
+
66
+ const { csrfToken } = getCsrfToken()
67
+
68
+ const session = new Session()
69
+
70
+ const {
71
+ user,
72
+ sessionId,
73
+ accessToken: newAccessToken,
74
+ refreshToken: newRefreshToken,
75
+ } = await session.meFromAccessToken(
76
+ { accessToken, csrfToken },
77
+ {
78
+ wabe,
79
+ isRoot: true,
80
+ isGraphQLCall,
81
+ },
82
+ )
83
+
84
+ ctx.wabe = {
85
+ isRoot: false,
86
+ sessionId,
87
+ user,
88
+ wabe,
89
+ response: ctx.res,
90
+ isGraphQLCall,
91
+ }
92
+
93
+ if (
94
+ wabe.config.authentication?.session?.cookieSession &&
95
+ newAccessToken &&
96
+ newRefreshToken &&
97
+ newAccessToken !== accessToken
98
+ ) {
99
+ ctx.res.setCookie('accessToken', newAccessToken, {
100
+ httpOnly: true,
101
+ path: '/',
102
+ expires: session.getAccessTokenExpireAt(wabe.config),
103
+ sameSite: 'None',
104
+ secure: true,
105
+ })
106
+
107
+ ctx.res.setCookie('refreshToken', newRefreshToken, {
108
+ httpOnly: true,
109
+ path: '/',
110
+ expires: session.getRefreshTokenExpireAt(wabe.config),
111
+ sameSite: 'None',
112
+ secure: true,
113
+ })
114
+ }
115
+ }
@@ -0,0 +1,476 @@
1
+ import { type GraphQLSchema, printSchema } from 'graphql'
2
+ import { writeFile, readFile } from 'node:fs/promises'
3
+ import type {
4
+ ClassInterface,
5
+ EnumInterface,
6
+ MutationResolver,
7
+ QueryResolver,
8
+ ScalarInterface,
9
+ SchemaInterface,
10
+ TypeField,
11
+ TypeResolver,
12
+ WabeObject,
13
+ WabePrimaryTypes,
14
+ } from '../schema'
15
+ import { firstLetterUpperCase, type DevWabeTypes } from '../utils/helper'
16
+ import { firstLetterInUpperCase } from '../utils'
17
+
18
+ const wabePrimaryTypesToTypescriptTypes: Record<WabePrimaryTypes, string> = {
19
+ Boolean: 'boolean',
20
+ Int: 'number',
21
+ Float: 'number',
22
+ String: 'string',
23
+ Email: 'string',
24
+ Phone: 'string',
25
+ Date: 'Date',
26
+ File: '{url: string, name: string}',
27
+ Hash: 'string',
28
+ }
29
+
30
+ const wabeTypesToTypescriptTypes = ({
31
+ field,
32
+ isInput = false,
33
+ }: {
34
+ field: TypeField<DevWabeTypes>
35
+ isInput?: boolean
36
+ }) => {
37
+ switch (field.type) {
38
+ case 'Date':
39
+ if (isInput) return 'Date'
40
+ return 'string'
41
+ case 'Boolean':
42
+ case 'Int':
43
+ case 'Float':
44
+ case 'String':
45
+ case 'Email':
46
+ case 'Phone':
47
+ case 'File':
48
+ case 'Hash':
49
+ return wabePrimaryTypesToTypescriptTypes[field.type]
50
+ case 'Array':
51
+ if (field.typeValue === 'Object') return `Array<${field.object.name}>`
52
+ return `Array<${wabePrimaryTypesToTypescriptTypes[field.typeValue]}>`
53
+ case 'Pointer':
54
+ return field.class
55
+ case 'Relation':
56
+ return `Array<${field.class}>`
57
+ case 'Object':
58
+ return `${field.object.name}`
59
+ default:
60
+ return field.type
61
+ }
62
+ }
63
+
64
+ const generateWabeObject = ({
65
+ object,
66
+ isInput = false,
67
+ prefix = '',
68
+ }: {
69
+ object: WabeObject<DevWabeTypes>
70
+ prefix?: string
71
+ isInput?: boolean
72
+ }): Record<string, Record<string, string>> => {
73
+ const objectName = object.name
74
+
75
+ return Object.entries(object.fields).reduce(
76
+ (acc, [fieldName, field]) => {
77
+ const type = wabeTypesToTypescriptTypes({ field, isInput })
78
+
79
+ const objectNameWithPrefix = `${prefix}${firstLetterUpperCase(objectName)}`
80
+
81
+ if (
82
+ field.type === 'Object' ||
83
+ (field.type === 'Array' && field.typeValue === 'Object')
84
+ ) {
85
+ const subObject = generateWabeObject({
86
+ object: field.object,
87
+ isInput,
88
+ prefix: objectNameWithPrefix,
89
+ })
90
+
91
+ const isArray = field.type === 'Array'
92
+
93
+ return {
94
+ ...acc,
95
+ ...subObject,
96
+ ...{
97
+ [objectNameWithPrefix]: {
98
+ ...acc[objectNameWithPrefix],
99
+ [`${fieldName}${field.required ? '' : 'undefined'}`]: `${
100
+ isArray ? 'Array<' : ''
101
+ }${objectNameWithPrefix}${firstLetterUpperCase(field.object.name)}${
102
+ isArray ? '>' : ''
103
+ }`,
104
+ },
105
+ },
106
+ }
107
+ }
108
+
109
+ return {
110
+ ...acc,
111
+ ...{
112
+ [objectNameWithPrefix]: {
113
+ ...acc[objectNameWithPrefix],
114
+ [`${fieldName}${field.required ? '' : 'undefined'}`]: `${type}`,
115
+ },
116
+ },
117
+ }
118
+ },
119
+ {} as Record<string, Record<string, string>>,
120
+ )
121
+ }
122
+
123
+ const generateWabeTypes = (classes: ClassInterface<DevWabeTypes>[]) => {
124
+ const wabeTypes = classes.reduce(
125
+ (acc, classType) => {
126
+ const { name, fields } = classType
127
+
128
+ const objectsToLoad: Array<Record<string, Record<string, string>>> = []
129
+
130
+ const currentClass = Object.entries(fields).reduce(
131
+ (acc2, [name, field]) => {
132
+ const type = wabeTypesToTypescriptTypes({ field })
133
+
134
+ if (
135
+ field.type === 'Object' ||
136
+ (field.type === 'Array' && field.typeValue === 'Object')
137
+ ) {
138
+ const wabeObject = generateWabeObject({ object: field.object })
139
+
140
+ objectsToLoad.push(wabeObject)
141
+ }
142
+
143
+ return {
144
+ ...acc2,
145
+ [`${name}${field.required ? '' : 'undefined'}`]: type,
146
+ }
147
+ },
148
+ {} as Record<string, string>,
149
+ )
150
+
151
+ const objects = objectsToLoad.reduce((acc2, object) => {
152
+ return {
153
+ ...acc2,
154
+ ...object,
155
+ }
156
+ }, {})
157
+
158
+ return {
159
+ ...acc,
160
+ ...objects,
161
+ [name]: { id: 'string', ...currentClass },
162
+ }
163
+ },
164
+ {} as Record<string, Record<string, string>>,
165
+ )
166
+
167
+ return wabeTypes
168
+ }
169
+
170
+ const generateWabeWhereTypes = (classes: ClassInterface<DevWabeTypes>[]) => {
171
+ const wabeTypes = classes.reduce(
172
+ (acc, classType) => {
173
+ const { name, fields } = classType
174
+
175
+ const completeName = `Where${firstLetterUpperCase(name)}`
176
+
177
+ const objectsToLoad: Array<Record<string, Record<string, string>>> = []
178
+
179
+ const currentClass = Object.entries(fields).reduce(
180
+ (acc2, [name, field]) => {
181
+ const type = wabeTypesToTypescriptTypes({ field, isInput: true })
182
+
183
+ if (
184
+ field.type === 'Object' ||
185
+ (field.type === 'Array' && field.typeValue === 'Object')
186
+ ) {
187
+ const wabeObject = generateWabeObject({
188
+ object: field.object,
189
+ isInput: true,
190
+ })
191
+
192
+ objectsToLoad.push(wabeObject)
193
+ }
194
+
195
+ return {
196
+ ...acc2,
197
+ [`${name}${field.required ? '' : 'undefined'}`]: type,
198
+ }
199
+ },
200
+ {} as Record<string, string>,
201
+ )
202
+
203
+ const objects = objectsToLoad.reduce((acc2, object) => {
204
+ return {
205
+ ...acc2,
206
+ ...object,
207
+ }
208
+ }, {})
209
+
210
+ return {
211
+ ...acc,
212
+ ...objects,
213
+ [completeName]: { id: 'string', ...currentClass },
214
+ }
215
+ },
216
+ {} as Record<string, Record<string, string>>,
217
+ )
218
+
219
+ return wabeTypes
220
+ }
221
+
222
+ const generateWabeEnumTypes = (enums: EnumInterface[]) => {
223
+ return Object.values(enums).reduce(
224
+ (acc, { name, values }) => {
225
+ return {
226
+ ...acc,
227
+ [name]: values,
228
+ }
229
+ },
230
+ {} as Record<string, Record<string, string>>,
231
+ )
232
+ }
233
+
234
+ const generateWabeScalarTypes = (scalars: ScalarInterface[]) => {
235
+ return Object.values(scalars).reduce(
236
+ (acc, { name }) => {
237
+ return {
238
+ ...acc,
239
+ // For the moment we will just use string as the type
240
+ // Suppose all scalars are string
241
+ [name]: 'string',
242
+ }
243
+ },
244
+ {} as Record<string, string>,
245
+ )
246
+ }
247
+
248
+ const generateWabeMutationOrQueryInput = (
249
+ mutationOrQueryName: string,
250
+ resolver: MutationResolver<any> | QueryResolver<any>,
251
+ isMutation: boolean,
252
+ ) => {
253
+ const objectsToLoad: Array<Record<string, Record<string, string>>> = []
254
+
255
+ const mutationNameWithFirstLetterUpperCase =
256
+ firstLetterUpperCase(mutationOrQueryName)
257
+
258
+ const mutationObject = Object.entries(
259
+ (isMutation ? resolver.args?.input : resolver.args) || {},
260
+ ).reduce(
261
+ (acc, [name, field]) => {
262
+ let type = wabeTypesToTypescriptTypes({ field, isInput: true })
263
+
264
+ if (field.type === 'Object') {
265
+ type = firstLetterInUpperCase(name)
266
+
267
+ const wabeObject = generateWabeObject({
268
+ object: {
269
+ ...field.object,
270
+ name: type,
271
+ },
272
+ prefix: mutationNameWithFirstLetterUpperCase,
273
+ })
274
+
275
+ objectsToLoad.push(wabeObject)
276
+
277
+ return {
278
+ ...acc,
279
+ [`${name}${field.required ? '' : 'undefined'}`]: `${mutationNameWithFirstLetterUpperCase}${type}`,
280
+ }
281
+ }
282
+
283
+ return {
284
+ ...acc,
285
+ [`${name}${field.required ? '' : 'undefined'}`]: type,
286
+ }
287
+ },
288
+ {} as Record<string, string>,
289
+ )
290
+
291
+ const objects = objectsToLoad.reduce((acc2, object) => {
292
+ return {
293
+ ...acc2,
294
+ ...object,
295
+ }
296
+ }, {})
297
+
298
+ return {
299
+ ...(isMutation
300
+ ? {
301
+ [`${firstLetterInUpperCase(mutationOrQueryName)}Input`]:
302
+ mutationObject,
303
+ }
304
+ : {}),
305
+ [`${isMutation ? 'Mutation' : 'Query'}${firstLetterInUpperCase(
306
+ mutationOrQueryName,
307
+ )}Args`]: isMutation
308
+ ? {
309
+ input: `${firstLetterInUpperCase(mutationOrQueryName)}Input`,
310
+ }
311
+ : mutationObject,
312
+ ...objects,
313
+ }
314
+ }
315
+
316
+ const generateWabeMutationsAndQueriesTypes = (resolver: TypeResolver<any>) => {
317
+ const mutationsObject = Object.entries(resolver.mutations || {}).reduce(
318
+ (acc, [mutationName, mutation]) => {
319
+ return {
320
+ ...acc,
321
+ ...generateWabeMutationOrQueryInput(mutationName, mutation, true),
322
+ }
323
+ },
324
+ {},
325
+ )
326
+
327
+ const queriesObject = Object.entries(resolver.queries || {}).reduce(
328
+ (acc, [queryName, query]) => {
329
+ return {
330
+ ...acc,
331
+ ...generateWabeMutationOrQueryInput(queryName, query, false),
332
+ }
333
+ },
334
+ {},
335
+ )
336
+
337
+ return {
338
+ ...mutationsObject,
339
+ ...queriesObject,
340
+ }
341
+ }
342
+
343
+ const wabeClassRecordToString = (
344
+ wabeClass: Record<string, Record<string, string>>,
345
+ ) => {
346
+ return Object.entries(wabeClass).reduce((acc, [className, fields]) => {
347
+ return `${acc}export type ${className} = {\n${Object.entries(fields)
348
+ .map(
349
+ ([fieldName, fieldType]) =>
350
+ `\t${fieldName.replace('undefined', '?')}: ${fieldType}`,
351
+ )
352
+ .join(',\n')}\n}\n\n`
353
+ }, '')
354
+ }
355
+
356
+ const wabeEnumRecordToString = (
357
+ wabeEnum: Record<string, Record<string, string>>,
358
+ ) => {
359
+ return Object.entries(wabeEnum).reduce((acc, [enumName, values]) => {
360
+ return `${acc}export enum ${enumName} {\n${Object.entries(values)
361
+ .map(([valueName, value]) => `\t${valueName} = "${value}"`)
362
+ .join(',\n')}\n}\n\n`
363
+ }, '')
364
+ }
365
+
366
+ const wabeScalarRecordToString = (wabeScalar: Record<string, string>) => {
367
+ return Object.entries(wabeScalar).reduce((acc, [scalarName, scalarType]) => {
368
+ return `${acc}export type ${scalarName} = ${scalarType}\n\n`
369
+ }, '')
370
+ }
371
+
372
+ const generateWabeDevTypes = ({
373
+ scalars,
374
+ enums,
375
+ classes,
376
+ }: {
377
+ enums?: EnumInterface[]
378
+ scalars?: ScalarInterface[]
379
+ classes: ClassInterface<DevWabeTypes>[]
380
+ }) => {
381
+ // Scalars
382
+ const listOfScalars = scalars?.map((scalar) => `"${scalar.name}"`) || []
383
+
384
+ const wabeScalarType =
385
+ listOfScalars.length > 0
386
+ ? `export type WabeSchemaScalars = ${listOfScalars.join(' | ')}`
387
+ : 'export type WabeSchemaScalars = ""'
388
+
389
+ // Enums
390
+ const wabeEnumsGlobalTypes =
391
+ enums?.map((wabeEnum) => `${wabeEnum.name}: ${wabeEnum.name}`) || []
392
+
393
+ const wabeEnumsGlobalTypesString =
394
+ wabeEnumsGlobalTypes.length > 0
395
+ ? `export type WabeSchemaEnums = {\n\t${wabeEnumsGlobalTypes.join(
396
+ ',\n\t',
397
+ )}\n}`
398
+ : ''
399
+
400
+ // Classes
401
+ const allNames = classes
402
+ .map((schema) => `${schema.name}: ${schema.name}`)
403
+ .filter((schema) => schema)
404
+
405
+ const globalWabeTypeString = `export type WabeSchemaTypes = {\n\t${allNames.join(
406
+ ',\n\t',
407
+ )}\n}`
408
+
409
+ // Where
410
+ const allWhereNames = classes
411
+ .map(
412
+ (schema) => `${schema.name}: Where${firstLetterUpperCase(schema.name)}`,
413
+ )
414
+ .filter((schema) => schema)
415
+
416
+ const globalWabeWhereTypeString = `export type WabeSchemaWhereTypes = {\n\t${allWhereNames.join(
417
+ ',\n\t',
418
+ )}\n}`
419
+
420
+ return `${wabeScalarType}\n\n${wabeEnumsGlobalTypesString}\n\n${globalWabeTypeString}\n\n${globalWabeWhereTypeString}`
421
+ }
422
+
423
+ export const generateCodegen = async ({
424
+ schema,
425
+ path,
426
+ graphqlSchema,
427
+ }: {
428
+ schema: SchemaInterface<any>
429
+ path: string
430
+ graphqlSchema: GraphQLSchema
431
+ }) => {
432
+ const graphqlSchemaContent = printSchema(graphqlSchema)
433
+
434
+ const wabeClasses = generateWabeTypes(schema.classes || [])
435
+ const wabeWhereTypes = generateWabeWhereTypes(schema.classes || [])
436
+ const mutationsAndQueries = generateWabeMutationsAndQueriesTypes(
437
+ schema.resolvers || {},
438
+ )
439
+
440
+ const wabeEnumsInString = wabeEnumRecordToString(
441
+ generateWabeEnumTypes(schema.enums || []),
442
+ )
443
+ const wabeScalarsInString = wabeScalarRecordToString(
444
+ generateWabeScalarTypes(schema.scalars || []),
445
+ )
446
+ const wabeObjectsInString = wabeClassRecordToString({
447
+ ...wabeClasses,
448
+ ...wabeWhereTypes,
449
+ ...mutationsAndQueries,
450
+ })
451
+
452
+ const wabeDevTypes = generateWabeDevTypes({
453
+ scalars: schema.scalars,
454
+ enums: schema.enums,
455
+ classes: schema.classes || [],
456
+ })
457
+
458
+ const wabeTsContent = `${wabeEnumsInString}${wabeScalarsInString}${wabeObjectsInString}${wabeDevTypes}`
459
+
460
+ try {
461
+ const contentOfGraphqlSchema = (
462
+ await readFile(`${path}/schema.graphql`)
463
+ ).toString()
464
+
465
+ // We will need to find a better way to avoid infinite loop of loading
466
+ // Better solution will be that bun implements watch ignores)
467
+ if (
468
+ !process.env.CODEGEN &&
469
+ contentOfGraphqlSchema === graphqlSchemaContent.toString()
470
+ )
471
+ return
472
+ } catch {}
473
+
474
+ await writeFile(`${path}/wabe.ts`, wabeTsContent)
475
+ await writeFile(`${path}/schema.graphql`, graphqlSchemaContent)
476
+ }