bunsane 0.1.2 → 0.1.3

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 (48) hide show
  1. package/TODO.md +1 -1
  2. package/bun.lock +156 -150
  3. package/core/App.ts +188 -31
  4. package/core/ArcheType.ts +1044 -26
  5. package/core/ComponentRegistry.ts +172 -29
  6. package/core/Components.ts +102 -24
  7. package/core/Decorators.ts +0 -1
  8. package/core/Entity.ts +55 -7
  9. package/core/EntityInterface.ts +4 -0
  10. package/core/EntityManager.ts +4 -4
  11. package/core/Query.ts +169 -3
  12. package/core/RequestLoaders.ts +101 -12
  13. package/core/SchedulerManager.ts +3 -4
  14. package/core/metadata/definitions/ArcheType.ts +9 -0
  15. package/core/metadata/definitions/Component.ts +16 -0
  16. package/core/metadata/definitions/gqlObject.ts +10 -0
  17. package/core/metadata/getMetadataStorage.ts +14 -0
  18. package/core/metadata/index.ts +17 -0
  19. package/core/metadata/metadata-storage.ts +81 -0
  20. package/database/DatabaseHelper.ts +22 -20
  21. package/database/sqlHelpers.ts +0 -2
  22. package/gql/ArchetypeOperations.ts +281 -0
  23. package/gql/Generator.ts +252 -62
  24. package/gql/helpers.ts +5 -5
  25. package/gql/index.ts +19 -17
  26. package/gql/types.ts +58 -11
  27. package/index.ts +93 -82
  28. package/package.json +39 -37
  29. package/plugins/index.ts +13 -0
  30. package/scheduler/index.ts +87 -0
  31. package/service/Service.ts +4 -0
  32. package/service/ServiceRegistry.ts +5 -1
  33. package/service/index.ts +1 -1
  34. package/swagger/decorators.ts +65 -0
  35. package/swagger/generator.ts +100 -0
  36. package/swagger/index.ts +2 -0
  37. package/tests/bench/insert.bench.ts +1 -0
  38. package/tests/bench/relations.bench.ts +1 -0
  39. package/tests/bench/sorting.bench.ts +1 -0
  40. package/tests/component-hooks-simple.test.ts +117 -0
  41. package/tests/component-hooks.test.ts +83 -31
  42. package/tests/component.test.ts +1 -0
  43. package/tests/hooks.test.ts +1 -0
  44. package/tests/query.test.ts +46 -4
  45. package/tests/relations.test.ts +1 -0
  46. package/types/app.types.ts +0 -0
  47. package/upload/index.ts +0 -2
  48. package/core/processors/ImageProcessor.ts +0 -423
package/gql/Generator.ts CHANGED
@@ -1,18 +1,29 @@
1
- import { GraphQLSchema, GraphQLError } from "graphql";
1
+ import { GraphQLSchema, GraphQLError, printSchema } from "graphql";
2
2
  import { createSchema } from "graphql-yoga";
3
3
  import { logger as MainLogger } from "core/Logger";
4
4
  import type { GraphQLType } from "./helpers";
5
+ import { generateArchetypeOperations } from "./ArchetypeOperations";
6
+ import { getArchetypeSchema } from "../core/ArcheType";
7
+ import BaseArcheType from "../core/ArcheType";
8
+ import { getMetadataStorage } from "../core/metadata";
9
+ import type { BaseService } from "service";
10
+ import { type ZodType } from "zod";
11
+ import { ZodWeaver } from "@gqloom/zod";
12
+ import { weave } from "@gqloom/core";
13
+ import * as z from "zod";
14
+
5
15
  const logger = MainLogger.child({ scope: "GraphQLGenerator" });
6
16
  export interface GraphQLObjectTypeMeta {
7
17
  name: string;
8
18
  fields: Record<string, GraphQLType>;
9
19
  }
10
20
 
11
- export interface GraphQLOperationMeta {
21
+ export interface GraphQLOperationMeta<T extends BaseArcheType | BaseArcheType[] | string = string> {
12
22
  type: "Query" | "Mutation";
23
+ propertyKey?: string;
13
24
  name?: string;
14
- input?: Record<string, GraphQLType>;
15
- output: GraphQLType | Record<string, GraphQLType>;
25
+ input?: Record<string, GraphQLType> | any;
26
+ output: GraphQLType | Record<string, GraphQLType> | T;
16
27
  }
17
28
 
18
29
  export interface GraphQLFieldMeta {
@@ -20,8 +31,9 @@ export interface GraphQLFieldMeta {
20
31
  field: string;
21
32
  }
22
33
 
34
+
23
35
  export function GraphQLObjectType(meta: GraphQLObjectTypeMeta) {
24
- return (target: any) => {
36
+ return (target: BaseService) => {
25
37
  if (!target.__graphqlObjectType) target.__graphqlObjectType = [];
26
38
  target.__graphqlObjectType.push(meta);
27
39
  }
@@ -34,14 +46,14 @@ export function GraphQLScalarType(name: string) {
34
46
  }
35
47
  }
36
48
 
37
- export function GraphQLOperation(meta: GraphQLOperationMeta) {
38
- return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
49
+ export function GraphQLOperation<T extends BaseArcheType | BaseArcheType[] | string = string>(meta: GraphQLOperationMeta<T>) {
50
+ return function (target: BaseService, propertyKey: string, descriptor: PropertyDescriptor) {
39
51
  if (!target.__graphqlOperations) target.__graphqlOperations = [];
40
52
  const operationName = meta.name ?? propertyKey;
41
53
  if (!operationName) {
42
54
  throw new Error("GraphQLOperation: Operation name is required (either meta.name or propertyKey must be defined)");
43
55
  }
44
- const operationMeta = { ...meta, name: operationName, propertyKey };
56
+ const operationMeta = { ...meta, name: operationName, propertyKey } as GraphQLOperationMeta<any>;
45
57
  target.__graphqlOperations.push(operationMeta);
46
58
  };
47
59
  }
@@ -53,7 +65,55 @@ export function GraphQLField(meta: GraphQLFieldMeta) {
53
65
  };
54
66
  }
55
67
 
56
- export function generateGraphQLSchema(services: any[]): { schema: GraphQLSchema | null; resolvers: any } {
68
+ /**
69
+ * Helper function to get the registered GraphQL type name from an archetype instance.
70
+ * This respects the custom name set via @ArcheType("CustomName") decorator.
71
+ * Falls back to inferring from class name if not found in registry.
72
+ */
73
+ function getArchetypeTypeName(archetypeInstance: any): string | null {
74
+ if (!archetypeInstance || !(archetypeInstance instanceof BaseArcheType)) {
75
+ return null;
76
+ }
77
+
78
+ const storage = getMetadataStorage();
79
+ const className = archetypeInstance.constructor.name;
80
+
81
+ // Look up the archetype metadata by class name to get the custom name
82
+ const archetypeMetadata = storage.archetypes.find(a => a.target?.name === className);
83
+
84
+ if (archetypeMetadata?.name) {
85
+ // Use the custom name from @ArcheType("CustomName") decorator
86
+ logger.trace(`Found custom archetype name: ${archetypeMetadata.name} for class ${className}`);
87
+
88
+ // Ensure schema is generated and cached
89
+ try {
90
+ if (!getArchetypeSchema(archetypeMetadata.name)) {
91
+ archetypeInstance.getZodObjectSchema();
92
+ }
93
+ } catch (error) {
94
+ logger.warn(`Failed to generate schema for archetype ${archetypeMetadata.name}: ${error}`);
95
+ }
96
+
97
+ return archetypeMetadata.name;
98
+ }
99
+
100
+ // Fallback: infer from class name
101
+ const inferredName = className.replace(/ArcheType$/, '');
102
+ logger.trace(`Using inferred archetype name: ${inferredName} for class ${className}`);
103
+
104
+ try {
105
+ if (!getArchetypeSchema(inferredName)) {
106
+ archetypeInstance.getZodObjectSchema();
107
+ }
108
+ } catch (error) {
109
+ logger.warn(`Failed to generate schema for archetype ${inferredName}: ${error}`);
110
+ }
111
+
112
+ return inferredName;
113
+ }
114
+
115
+ export function generateGraphQLSchema(services: any[], options?: { enableArchetypeOperations?: boolean }): { schema: GraphQLSchema | null; resolvers: any } {
116
+ logger.trace(`generateGraphQLSchema called with ${services.length} services`);
57
117
  let typeDefs = `
58
118
  `;
59
119
  const scalarTypes: Set<string> = new Set();
@@ -61,8 +121,53 @@ export function generateGraphQLSchema(services: any[]): { schema: GraphQLSchema
61
121
  const queryFields: string[] = [];
62
122
  const mutationFields: string[] = [];
63
123
 
124
+ // PRE-GENERATE ALL ARCHETYPE SCHEMAS
125
+ // Scan all services for archetype instances and generate their schemas upfront
126
+ logger.trace(`Pre-generating archetype schemas from service operations...`);
127
+ services.forEach(service => {
128
+ const operations = service.__graphqlOperations || service.constructor.prototype.__graphqlOperations;
129
+ if (operations) {
130
+ operations.forEach((op: any) => {
131
+ const { output } = op;
132
+ // Check if output is an archetype or array of archetypes
133
+ if (Array.isArray(output) && output[0] instanceof BaseArcheType) {
134
+ const archetypeInstance = output[0];
135
+ getArchetypeTypeName(archetypeInstance); // This will cache the schema
136
+ } else if (output instanceof BaseArcheType) {
137
+ getArchetypeTypeName(output); // This will cache the schema
138
+ }
139
+ });
140
+ }
141
+ });
142
+ logger.trace(`Completed pre-generation of archetype schemas`);
143
+
144
+ // Generate archetype operations if enabled
145
+ if (options?.enableArchetypeOperations !== false) {
146
+ try {
147
+ const archetypeOps = generateArchetypeOperations();
148
+ typeDefs += archetypeOps.typeDefs;
149
+ queryFields.push(...archetypeOps.queryFields);
150
+ mutationFields.push(...archetypeOps.mutationFields);
151
+ Object.assign(resolvers, archetypeOps.resolvers);
152
+ logger.trace(`Added archetype operations: ${archetypeOps.queryFields.length} queries, ${archetypeOps.mutationFields.length} mutations`);
153
+ } catch (error) {
154
+ logger.error(`Failed to generate archetype operations: ${error}`);
155
+ }
156
+ } else {
157
+ // Still generate type definitions even if operations are disabled
158
+ try {
159
+ const archetypeOps = generateArchetypeOperations();
160
+ typeDefs += archetypeOps.typeDefs;
161
+ logger.trace(`Added archetype type definitions (without operations)`);
162
+ } catch (error) {
163
+ logger.error(`Failed to generate archetype type definitions: ${error}`);
164
+ }
165
+ }
166
+
64
167
  services.forEach(service => {
65
168
  logger.trace(`Processing service: ${service.constructor.name}`);
169
+ // Check if service has graphql operations (either on instance or prototype)
170
+ const operations = service.__graphqlOperations || service.constructor.prototype.__graphqlOperations;
66
171
  if(service.constructor.__graphqlScalarTypes) {
67
172
  for (const scalarName of service.constructor.__graphqlScalarTypes) {
68
173
  if (!scalarTypes.has(scalarName)) {
@@ -77,71 +182,152 @@ export function generateGraphQLSchema(services: any[]): { schema: GraphQLSchema
77
182
  typeDefs += `type ${name} {\n${Object.entries(fields).map(([k, v]) => ` ${k}: ${v}`).join('\n')}\n}\n`;
78
183
  }
79
184
  }
80
- if (service.__graphqlOperations) {
81
- service.__graphqlOperations.forEach((op: any) => {
82
- const { type, name, input, output, propertyKey } = op;
83
- if (!resolvers[type]) resolvers[type] = {};
84
- let fieldDef = `${name}`;
85
- if (input) {
86
- const inputName = `${name}Input`;
87
- typeDefs += `input ${inputName} {\n${Object.entries(input).map(([k, v]) => ` ${k}: ${v}`).join('\n')}\n}\n`;
88
- fieldDef += `(input: ${inputName}!)`;
89
- resolvers[type][name] = async (_: any, args: any, context: any, info: any) => {
90
- try {
91
- return await service[propertyKey](args.input || args, context, info);
92
- } catch (error) {
93
- logger.error(`Error in ${type}.${name}:`);
94
- logger.error(error);
95
- if (error instanceof GraphQLError) {
96
- throw error;
97
- }
98
- throw new GraphQLError(`Internal error in ${name}`, {
99
- extensions: {
100
- code: "INTERNAL_ERROR",
101
- originalError: process.env.NODE_ENV === 'development' ? error : undefined
185
+ if (operations) {
186
+ logger.trace(`Processing ${operations.length} operations for ${service.constructor.name}`);
187
+ operations.forEach((op: any) => {
188
+ try {
189
+ let { type, name, input, output, propertyKey } = op;
190
+ if (!resolvers[type]) resolvers[type] = {};
191
+ let fieldDef = `${name}`;
192
+ if (input) {
193
+ const inputName = `${name}Input`;
194
+ // Check if input is a Zod schema
195
+ if (input && typeof input === 'object' && '_def' in input) {
196
+ // It's a Zod schema - use GQLoom's weave to generate GraphQL type
197
+ try {
198
+ // Add __typename to input zod object
199
+ input = input.extend({ __typename: z.literal(inputName).nullish() });
200
+ logger.trace(`Weaving Zod schema for ${name}`);
201
+ const gqlInputSchema = weave(ZodWeaver, input as ZodType) as GraphQLSchema;
202
+ const schemaString = printSchema(gqlInputSchema);
203
+ logger.trace(`Schema string for ${name}: ${schemaString}`);
204
+ // Extract the type definition and convert it to an input type
205
+ // The schema will contain "type <TypeName> { ... }", we need to replace with "input <inputName> { ... }"
206
+ const typeMatch = schemaString.match(/type\s+(\w+)\s*\{([^}]*)\}/s);
207
+ if (typeMatch) {
208
+ const fields = typeMatch[2];
209
+ typeDefs += `input ${inputName} {${fields}}\n`;
210
+ logger.trace(`Successfully generated input type ${inputName}`);
211
+ } else {
212
+ logger.warn(`Could not extract type from Zod schema for ${name}, schema: ${schemaString}`);
213
+ typeDefs += `input ${inputName} { _placeholder: String }\n`;
102
214
  }
103
- });
215
+ } catch (error) {
216
+ logger.error(`Failed to weave Zod schema for ${name}: ${error}`);
217
+ logger.error(`Error stack: ${error instanceof Error ? error.stack : 'No stack'}`);
218
+ // Fallback: generate basic input type
219
+ typeDefs += `input ${inputName} { _placeholder: String }\n`;
220
+ }
221
+ } else {
222
+ // Legacy Record<string, GraphQLType> format
223
+ typeDefs += `input ${inputName} {\n${Object.entries(input).map(([k, v]) => ` ${k}: ${v}`).join('\n')}\n}\n`;
104
224
  }
105
- };
106
- } else {
107
- resolvers[type][name] = async (_: any, args: any, context: any, info: any) => {
108
- try {
109
- return await service[propertyKey]({}, context, info);
110
- } catch (error) {
111
- logger.error(`Error in ${type}.${name}:`);
112
- logger.error(error);
113
- if (error instanceof GraphQLError) {
114
- throw error;
225
+ fieldDef += `(input: ${inputName}!)`;
226
+
227
+ // Store the Zod schema for validation if it's a Zod type
228
+ const zodSchema = (input && typeof input === 'object' && '_def' in input) ? input as ZodType : null;
229
+
230
+ resolvers[type][name] = async (_: any, args: any, context: any, info: any) => {
231
+ try {
232
+ const inputArgs = args.input || args;
233
+
234
+ // Automatically validate with Zod schema if provided
235
+ if (zodSchema) {
236
+ try {
237
+ const validated = zodSchema.parse(inputArgs);
238
+ return await service[propertyKey](validated, context, info);
239
+ } catch (error) {
240
+ if (error instanceof z.ZodError) {
241
+ // Let handleGraphQLError convert Zod errors to user-friendly messages
242
+ const { handleGraphQLError } = await import("../core/ErrorHandler");
243
+ handleGraphQLError(error);
244
+ }
245
+ throw error;
246
+ }
247
+ } else {
248
+ return await service[propertyKey](inputArgs, context, info);
249
+ }
250
+ } catch (error) {
251
+ logger.error(`Error in ${type}.${name}:`);
252
+ logger.error(error);
253
+ if (error instanceof GraphQLError) {
254
+ throw error;
255
+ }
256
+ throw new GraphQLError(`Internal error in ${name}`, {
257
+ extensions: {
258
+ code: "INTERNAL_ERROR",
259
+ originalError: process.env.NODE_ENV === 'development' ? error : undefined
260
+ }
261
+ });
115
262
  }
116
- throw new GraphQLError(`Internal error in ${name}`, {
117
- extensions: {
118
- code: "INTERNAL_ERROR",
119
- originalError: process.env.NODE_ENV === 'development' ? error : undefined
263
+ };
264
+ } else {
265
+ resolvers[type][name] = async (_: any, args: any, context: any, info: any) => {
266
+ try {
267
+ return await service[propertyKey]({}, context, info);
268
+ } catch (error) {
269
+ logger.error(`Error in ${type}.${name}:`);
270
+ logger.error(error);
271
+ if (error instanceof GraphQLError) {
272
+ throw error;
120
273
  }
121
- });
274
+ throw new GraphQLError(`Internal error in ${name}`, {
275
+ extensions: {
276
+ code: "INTERNAL_ERROR",
277
+ originalError: process.env.NODE_ENV === 'development' ? error : undefined
278
+ }
279
+ });
280
+ }
281
+ };
282
+ }
283
+ if (typeof output === 'string') {
284
+ fieldDef += `: ${output}`;
285
+ } else if (Array.isArray(output)) {
286
+ // Handle array of archetypes: [serviceAreaArcheType]
287
+ const archetypeInstance = output[0];
288
+ const typeName = getArchetypeTypeName(archetypeInstance);
289
+ if (typeName) {
290
+ fieldDef += `: [${typeName}]`;
291
+ } else {
292
+ logger.warn(`Invalid array output type for ${name}, expected archetype instance`);
293
+ fieldDef += `: [Any]`;
122
294
  }
123
- };
124
- }
125
- if (typeof output === 'string') {
126
- fieldDef += `: ${output}`;
127
- } else if (typeof output === 'object') {
128
- const outputName = `${name}Output`;
129
- typeDefs += `type ${outputName} {\n${Object.entries(output).map(([k, v]) => ` ${k}: ${v}`).join('\n')}\n}\n`;
130
- fieldDef += `: ${outputName}`;
131
- }
132
- if (type === 'Query') {
133
- queryFields.push(fieldDef);
134
- } else if (type === 'Mutation') {
135
- mutationFields.push(fieldDef);
295
+ } else if (output instanceof BaseArcheType) {
296
+ // Handle single archetype instance: serviceAreaArcheType
297
+ const typeName = getArchetypeTypeName(output);
298
+ if (typeName) {
299
+ fieldDef += `: ${typeName}`;
300
+ } else {
301
+ logger.warn(`Could not determine type name for archetype in ${name}`);
302
+ fieldDef += `: Any`;
303
+ }
304
+ } else if (typeof output === 'object') {
305
+ const outputName = `${name}Output`;
306
+ typeDefs += `type ${outputName} {\n${Object.entries(output).map(([k, v]) => ` ${k}: ${v}`).join('\n')}\n}\n`;
307
+ fieldDef += `: ${outputName}`;
308
+ }
309
+ if (type === 'Query') {
310
+ queryFields.push(fieldDef);
311
+ logger.trace(`Added query field: ${fieldDef}`);
312
+ } else if (type === 'Mutation') {
313
+ mutationFields.push(fieldDef);
314
+ logger.trace(`Added mutation field: ${fieldDef}`);
315
+ }
316
+ } catch (opError) {
317
+ logger.error(`Failed to process operation ${op.name || 'unknown'} in ${service.constructor.name}: ${opError}`);
318
+ logger.error(`Error stack: ${opError instanceof Error ? opError.stack : 'No stack'}`);
136
319
  }
137
320
  });
321
+
322
+ logger.trace(`Completed processing operations for ${service.constructor.name}`);
138
323
  }
139
324
  });
140
325
 
141
326
  // Process field resolvers
142
327
  services.forEach(service => {
143
- if (service.__graphqlFields) {
144
- service.__graphqlFields.forEach((fieldMeta: any) => {
328
+ const fields = service.__graphqlFields || service.constructor.prototype.__graphqlFields;
329
+ if (fields) {
330
+ fields.forEach((fieldMeta: any) => {
145
331
  const { type, field, propertyKey } = fieldMeta;
146
332
  if (!resolvers[type]) resolvers[type] = {};
147
333
  resolvers[type][field] = async (parent: any, args: any, context: any, info: any) => {
@@ -172,11 +358,15 @@ export function generateGraphQLSchema(services: any[]): { schema: GraphQLSchema
172
358
  typeDefs += `type Mutation {\n${mutationFields.map(f => ` ${f}`).join('\n')}\n}\n`;
173
359
  }
174
360
 
361
+ logger.trace(`Query fields count: ${queryFields.length}, Mutation fields count: ${mutationFields.length}`);
175
362
  logger.trace(`System Type Defs: ${typeDefs}`);
176
363
  let schema : GraphQLSchema | null = null;
177
364
  // Check if typeDefs contains actual schema definitions, not just whitespace
178
365
  if(typeDefs.trim() !== "" && (queryFields.length > 0 || mutationFields.length > 0 || scalarTypes.size > 0)) {
366
+ logger.trace(`Creating schema with resolvers: ${Object.keys(resolvers).join(', ')}`);
179
367
  schema = createSchema({ typeDefs, resolvers });
368
+ } else {
369
+ logger.warn(`No schema generated - queryFields: ${queryFields.length}, mutationFields: ${mutationFields.length}, scalarTypes: ${scalarTypes.size}`);
180
370
  }
181
371
  return { schema, resolvers };
182
372
  }
package/gql/helpers.ts CHANGED
@@ -14,15 +14,15 @@ export function isValidGraphQLType(type: string): type is GraphQLType {
14
14
  }
15
15
 
16
16
  export type TypeFromGraphQL<T extends GraphQLType> =
17
- T extends GraphQLFieldTypes.ID_REQUIRED | GraphQLFieldTypes.ID_OPTIONAL ? string :
17
+ T extends GraphQLFieldTypes.ID_REQUIRED | GraphQLFieldTypes.ID ? string :
18
18
  T extends GraphQLFieldTypes.STRING_REQUIRED ? string :
19
- T extends GraphQLFieldTypes.STRING_OPTIONAL ? string | null :
19
+ T extends GraphQLFieldTypes.STRING ? string | null :
20
20
  T extends GraphQLFieldTypes.INT_REQUIRED ? number :
21
- T extends GraphQLFieldTypes.INT_OPTIONAL ? number | null :
21
+ T extends GraphQLFieldTypes.INT ? number | null :
22
22
  T extends GraphQLFieldTypes.BOOLEAN_REQUIRED ? boolean :
23
- T extends GraphQLFieldTypes.BOOLEAN_OPTIONAL ? boolean | null :
23
+ T extends GraphQLFieldTypes.BOOLEAN ? boolean | null :
24
24
  T extends GraphQLFieldTypes.FLOAT_REQUIRED ? number :
25
- T extends GraphQLFieldTypes.FLOAT_OPTIONAL ? number | null :
25
+ T extends GraphQLFieldTypes.FLOAT ? number | null :
26
26
  T extends `[${string}]` | `[${string}]!` ? any[] :
27
27
  any;
28
28
 
package/gql/index.ts CHANGED
@@ -104,26 +104,28 @@ const maskError = (error: any, message: string): GraphQLError => {
104
104
  return error instanceof GraphQLError ? error : new GraphQLError(message, { originalError: error });
105
105
  };
106
106
 
107
- export function createYogaInstance(schema?: GraphQLSchema, plugins: Plugin[] = []) {
107
+ export function createYogaInstance(schema?: GraphQLSchema, plugins: Plugin[] = [], contextFactory?: (context: any) => any) {
108
+ const yogaConfig: any = {
109
+ plugins,
110
+ maskedErrors: {
111
+ maskError,
112
+ },
113
+ };
114
+
115
+ // Add context factory if provided
116
+ if (contextFactory) {
117
+ yogaConfig.context = contextFactory;
118
+ }
119
+
108
120
  if (schema) {
109
- return createYoga({
110
- schema,
111
- plugins,
112
- maskedErrors: {
113
- maskError,
114
- },
115
- });
121
+ yogaConfig.schema = schema;
122
+ return createYoga(yogaConfig);
116
123
  } else {
117
- return createYoga({
118
- schema: createSchema({
119
- typeDefs: staticTypeDefs,
120
- resolvers: staticResolvers,
121
- }),
122
- plugins,
123
- maskedErrors: {
124
- maskError,
125
- },
124
+ yogaConfig.schema = createSchema({
125
+ typeDefs: staticTypeDefs,
126
+ resolvers: staticResolvers,
126
127
  });
128
+ return createYoga(yogaConfig);
127
129
  }
128
130
  }
129
131
 
package/gql/types.ts CHANGED
@@ -1,23 +1,40 @@
1
+
2
+
1
3
  export enum GraphQLScalar {
2
4
  ID = "ID",
3
- String = "String",
4
- Int = "Int",
5
- Float = "Float",
6
- Boolean = "Boolean",
7
- Date = "Date",
5
+ INT = "Int",
6
+ FLOAT = "Float",
7
+ STRING = "String",
8
+ BOOLEAN = "Boolean",
9
+ }
10
+
11
+ export interface GraphQLObject {
12
+ name: string;
13
+ fields: GraphQLField[];
8
14
  }
9
15
 
16
+ export type GraphQLType = GraphQLScalar | string;
17
+ export interface GraphQLField {
18
+ name: string;
19
+ type: GraphQLType;
20
+ isList?: boolean;
21
+ isRequired?: boolean;
22
+ }
23
+
24
+
25
+
26
+ // TODO: Remove this when we have a better way to define GraphQL type
10
27
  export enum GraphQLFieldTypes {
28
+ ID = "ID",
11
29
  ID_REQUIRED = "ID!",
12
- ID_OPTIONAL = "ID",
13
- STRING_REQUIRED = "String!",
14
- STRING_OPTIONAL = "String",
30
+ INT = "Int",
15
31
  INT_REQUIRED = "Int!",
16
- INT_OPTIONAL = "Int",
32
+ FLOAT = "Float",
17
33
  FLOAT_REQUIRED = "Float!",
18
- FLOAT_OPTIONAL = "Float",
34
+ STRING = "String",
35
+ STRING_REQUIRED = "String!",
36
+ BOOLEAN = "Boolean",
19
37
  BOOLEAN_REQUIRED = "Boolean!",
20
- BOOLEAN_OPTIONAL = "Boolean",
21
38
  }
22
39
 
23
40
  export const GraphQLList = {
@@ -25,4 +42,34 @@ export const GraphQLList = {
25
42
  ofRequired: (type: string) => `[${type}]!`,
26
43
  } as const;
27
44
 
45
+ // Utils for building GraphQL Enums
46
+ import { GraphQLEnumType } from 'graphql';
47
+
48
+ type EnumObject = {
49
+ [index: string]: string;
50
+ };
51
+
52
+ type EnumObjectResult = {
53
+ [index: string]: {
54
+ value: string;
55
+ };
56
+ };
57
+ export const enumBuilderValues = <T extends EnumObject>(
58
+ constants: T,
59
+ ): EnumObjectResult =>
60
+ Object.keys(constants).reduce(
61
+ (prev, curr) => ({
62
+ ...prev,
63
+ [curr]: {
64
+ value: constants[curr],
65
+ },
66
+ }),
67
+ {},
68
+ );
69
+
28
70
 
71
+ export const graphqlEnumBuilder = <T extends EnumObject>(name: string, values: T) =>
72
+ new GraphQLEnumType({
73
+ name,
74
+ values: enumBuilderValues(values),
75
+ });