bunsane 0.1.0 → 0.1.2

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 (82) hide show
  1. package/.github/workflows/deploy-docs.yml +57 -0
  2. package/LICENSE.md +1 -1
  3. package/README.md +2 -28
  4. package/TODO.md +8 -1
  5. package/bun.lock +3 -0
  6. package/config/upload.config.ts +135 -0
  7. package/core/App.ts +168 -4
  8. package/core/ArcheType.ts +122 -0
  9. package/core/BatchLoader.ts +100 -0
  10. package/core/ComponentRegistry.ts +4 -3
  11. package/core/Components.ts +2 -2
  12. package/core/Decorators.ts +15 -8
  13. package/core/Entity.ts +193 -14
  14. package/core/EntityCache.ts +15 -0
  15. package/core/EntityHookManager.ts +855 -0
  16. package/core/EntityManager.ts +12 -2
  17. package/core/ErrorHandler.ts +64 -7
  18. package/core/FileValidator.ts +284 -0
  19. package/core/Query.ts +503 -85
  20. package/core/RequestContext.ts +24 -0
  21. package/core/RequestLoaders.ts +89 -0
  22. package/core/SchedulerManager.ts +710 -0
  23. package/core/UploadManager.ts +261 -0
  24. package/core/components/UploadComponent.ts +206 -0
  25. package/core/decorators/EntityHooks.ts +190 -0
  26. package/core/decorators/ScheduledTask.ts +83 -0
  27. package/core/events/EntityLifecycleEvents.ts +177 -0
  28. package/core/processors/ImageProcessor.ts +423 -0
  29. package/core/storage/LocalStorageProvider.ts +290 -0
  30. package/core/storage/StorageProvider.ts +112 -0
  31. package/database/DatabaseHelper.ts +183 -58
  32. package/database/index.ts +5 -5
  33. package/database/sqlHelpers.ts +7 -0
  34. package/docs/README.md +149 -0
  35. package/docs/_coverpage.md +36 -0
  36. package/docs/_sidebar.md +23 -0
  37. package/docs/api/core.md +568 -0
  38. package/docs/api/hooks.md +554 -0
  39. package/docs/api/index.md +222 -0
  40. package/docs/api/query.md +678 -0
  41. package/docs/api/service.md +744 -0
  42. package/docs/core-concepts/archetypes.md +512 -0
  43. package/docs/core-concepts/components.md +498 -0
  44. package/docs/core-concepts/entity.md +314 -0
  45. package/docs/core-concepts/hooks.md +683 -0
  46. package/docs/core-concepts/query.md +588 -0
  47. package/docs/core-concepts/services.md +647 -0
  48. package/docs/examples/code-examples.md +425 -0
  49. package/docs/getting-started.md +337 -0
  50. package/docs/index.html +97 -0
  51. package/gql/Generator.ts +58 -35
  52. package/gql/decorators/Upload.ts +176 -0
  53. package/gql/helpers.ts +67 -0
  54. package/gql/index.ts +65 -31
  55. package/gql/types.ts +1 -1
  56. package/index.ts +79 -11
  57. package/package.json +19 -10
  58. package/rest/Generator.ts +3 -0
  59. package/rest/index.ts +22 -0
  60. package/service/Service.ts +1 -1
  61. package/service/ServiceRegistry.ts +10 -6
  62. package/service/index.ts +12 -1
  63. package/tests/bench/insert.bench.ts +59 -0
  64. package/tests/bench/relations.bench.ts +269 -0
  65. package/tests/bench/sorting.bench.ts +415 -0
  66. package/tests/component-hooks.test.ts +1409 -0
  67. package/tests/component.test.ts +338 -0
  68. package/tests/errorHandling.test.ts +155 -0
  69. package/tests/hooks.test.ts +666 -0
  70. package/tests/query-sorting.test.ts +101 -0
  71. package/tests/relations.test.ts +169 -0
  72. package/tests/scheduler.test.ts +724 -0
  73. package/tsconfig.json +35 -34
  74. package/types/graphql.types.ts +87 -0
  75. package/types/hooks.types.ts +141 -0
  76. package/types/scheduler.types.ts +165 -0
  77. package/types/upload.types.ts +184 -0
  78. package/upload/index.ts +140 -0
  79. package/utils/UploadHelper.ts +305 -0
  80. package/utils/cronParser.ts +366 -0
  81. package/utils/errorMessages.ts +151 -0
  82. package/core/Events.ts +0 -0
@@ -0,0 +1,337 @@
1
+ # Getting Started with BunSane
2
+
3
+ This guide will walk you through installing and setting up BunSane for your first project.
4
+
5
+ ## 📋 Prerequisites
6
+
7
+ Before you begin, ensure you have the following installed:
8
+
9
+ - **Bun Runtime**: Version 1.0 or later ([Download Bun](https://bun.sh/))
10
+ - **PostgreSQL**: Version 12 or later ([Download PostgreSQL](https://www.postgresql.org/download/))
11
+ - **Node.js**: Version 18+ (for some development tools, though Bun is the primary runtime)
12
+
13
+ ## 🚀 Installation
14
+
15
+ ### 1. Install BunSane
16
+
17
+ ```bash
18
+ bun install bunsane
19
+ ```
20
+
21
+ ### 2. Verify Installation
22
+
23
+ ```bash
24
+ bun run --version
25
+ # Should show Bun version 1.x.x
26
+ ```
27
+
28
+ ## ⚙️ Configuration
29
+
30
+ ### TypeScript Configuration
31
+
32
+ BunSane requires experimental decorators to be enabled. Update your `tsconfig.json`:
33
+
34
+ ```json
35
+ {
36
+ "compilerOptions": {
37
+ "target": "ES2022",
38
+ "module": "ESNext",
39
+ "moduleResolution": "bundler",
40
+ "experimentalDecorators": true,
41
+ "emitDecoratorMetadata": true,
42
+ "strict": true,
43
+ "esModuleInterop": true,
44
+ "skipLibCheck": true,
45
+ "forceConsistentCasingInFileNames": true,
46
+ "allowSyntheticDefaultImports": true,
47
+ "resolveJsonModule": true,
48
+ "isolatedModules": true,
49
+ "noEmit": true,
50
+ "types": ["bun-types"]
51
+ },
52
+ "include": ["src/**/*", "index.ts"],
53
+ "exclude": ["node_modules"]
54
+ }
55
+ ```
56
+
57
+ ### Database Setup
58
+
59
+ Create a PostgreSQL database for your application:
60
+
61
+ ```sql
62
+ -- Create database
63
+ CREATE DATABASE bunsane_app;
64
+
65
+ -- Create user (optional)
66
+ CREATE USER bunsane_user WITH PASSWORD 'your_password';
67
+
68
+ -- Grant permissions
69
+ GRANT ALL PRIVILEGES ON DATABASE bunsane_app TO bunsane_user;
70
+ ```
71
+
72
+ ### Environment Configuration
73
+
74
+ Create a `.env` file in your project root:
75
+
76
+ ```env
77
+ # Database Configuration
78
+ DATABASE_URL=postgresql://username:password@localhost:5432/bunsane_app
79
+
80
+ # Application Configuration
81
+ NODE_ENV=development
82
+ PORT=3000
83
+
84
+ # Optional: Logging
85
+ LOG_LEVEL=info
86
+ ```
87
+
88
+ ## 🏗️ Your First BunSane Application
89
+
90
+ ### Project Structure
91
+
92
+ Create the following directory structure:
93
+
94
+ ```
95
+ my-bunsane-app/
96
+ ├── src/
97
+ │ ├── services/
98
+ │ └── helpers/
99
+ ├── index.ts
100
+ ├── package.json
101
+ ├── tsconfig.json
102
+ └── .env
103
+ ```
104
+
105
+ ### 1. Create Your First Component
106
+
107
+ ```typescript
108
+ // src/services/UserService.ts
109
+ import { Component, CompData, BaseComponent, ArcheType, Entity, GraphQLObjectType, GraphQLOperation, GraphQLFieldTypes, GraphQLField } from 'bunsane';
110
+
111
+ @Component
112
+ export class UserProfile extends BaseComponent {
113
+ @CompData()
114
+ name: string = '';
115
+
116
+ @CompData()
117
+ email: string = '';
118
+
119
+ @CompData({ indexed: true })
120
+ username: string = '';
121
+ }
122
+
123
+ @Component
124
+ export class UserPreferences extends BaseComponent {
125
+ @CompData()
126
+ theme: 'light' | 'dark' = 'light';
127
+
128
+ @CompData()
129
+ notifications: boolean = true;
130
+ }
131
+ ```
132
+
133
+ ### 2. Create an ArcheType
134
+
135
+ ```typescript
136
+ // src/services/UserService.ts (continued)
137
+ import { ArcheType } from 'bunsane';
138
+
139
+ export const UserArcheType = new ArcheType([
140
+ UserProfile,
141
+ UserPreferences
142
+ ]);
143
+ ```
144
+
145
+ ### 3. Create a Service
146
+
147
+ ```typescript
148
+ // src/services/UserService.ts (continued)
149
+ import { BaseService, GraphQLObjectType, GraphQLOperation, GraphQLFieldTypes, GraphQLField } from 'bunsane';
150
+
151
+ const userFields = {
152
+ id: GraphQLFieldTypes.ID_REQUIRED,
153
+ name: GraphQLFieldTypes.STRING_OPTIONAL,
154
+ email: GraphQLFieldTypes.STRING_REQUIRED,
155
+ username: GraphQLFieldTypes.STRING_OPTIONAL
156
+ };
157
+
158
+ const userInputs = {
159
+ createUser: {
160
+ name: GraphQLFieldTypes.STRING_REQUIRED,
161
+ email: GraphQLFieldTypes.STRING_REQUIRED,
162
+ username: GraphQLFieldTypes.STRING_REQUIRED
163
+ },
164
+ getUser: {
165
+ id: GraphQLFieldTypes.ID_REQUIRED
166
+ }
167
+ };
168
+
169
+ @GraphQLObjectType({
170
+ name: "User",
171
+ fields: userFields
172
+ })
173
+ export default class UserService extends BaseService {
174
+ @GraphQLOperation({
175
+ type: "Mutation",
176
+ input: userInputs.createUser,
177
+ output: "User"
178
+ })
179
+ async createUser(args: { name: string; email: string; username: string }) {
180
+ const userEntity = UserArcheType.fill(args).createEntity();
181
+ await userEntity.save();
182
+ return await UserArcheType.Unwrap(userEntity);
183
+ }
184
+
185
+ @GraphQLOperation({
186
+ type: "Query",
187
+ input: userInputs.getUser,
188
+ output: "User"
189
+ })
190
+ async getUser(args: { id: string }) {
191
+ const entity = await Entity.FindById(args.id);
192
+ if (!entity) return null;
193
+ return await UserArcheType.Unwrap(entity);
194
+ }
195
+
196
+ @GraphQLField({ type: "User", field: "id" })
197
+ idResolver(parent: Entity) {
198
+ return parent.id;
199
+ }
200
+
201
+ @GraphQLField({ type: "User", field: "name" })
202
+ async nameResolver(parent: Entity) {
203
+ const profile = await parent.get(UserProfile);
204
+ return profile?.name ?? "";
205
+ }
206
+
207
+ @GraphQLField({ type: "User", field: "email" })
208
+ async emailResolver(parent: Entity) {
209
+ const profile = await parent.get(UserProfile);
210
+ return profile?.email ?? "";
211
+ }
212
+
213
+ @GraphQLField({ type: "User", field: "username" })
214
+ async usernameResolver(parent: Entity) {
215
+ const profile = await parent.get(UserProfile);
216
+ return profile?.username ?? "";
217
+ }
218
+ }
219
+ ```
220
+
221
+ ### 4. Set Up the Application
222
+
223
+ ```typescript
224
+ // index.ts
225
+ import { App } from 'bunsane';
226
+ import UserService from './src/services/UserService';
227
+
228
+ async function main() {
229
+ // Services are automatically registered when imported
230
+ // No manual registration needed
231
+
232
+ // Create and start the application
233
+ const app = new App({
234
+ port: 3000,
235
+ databaseUrl: process.env.DATABASE_URL
236
+ });
237
+
238
+ await app.start();
239
+
240
+ console.log('🚀 BunSane server running on http://localhost:3000');
241
+ }
242
+
243
+ main().catch(console.error);
244
+ ```
245
+
246
+ ### 5. Run Your Application
247
+
248
+ ```bash
249
+ bun run index.ts
250
+ ```
251
+
252
+ ## 🧪 Testing Your Setup
253
+
254
+ ### GraphQL API Testing
255
+
256
+ Your service now exposes GraphQL endpoints. Visit `http://localhost:3000/graphql` to access the GraphQL playground.
257
+
258
+ **Create User Mutation:**
259
+ ```graphql
260
+ mutation CreateUser($input: CreateUserInput!) {
261
+ createUser(input: $input) {
262
+ id
263
+ name
264
+ email
265
+ username
266
+ }
267
+ }
268
+ ```
269
+
270
+ With variables:
271
+ ```json
272
+ {
273
+ "input": {
274
+ "name": "John Doe",
275
+ "email": "john.doe@example.com",
276
+ "username": "johndoe"
277
+ }
278
+ }
279
+ ```
280
+
281
+ **Get User Query:**
282
+ ```graphql
283
+ query GetUser($id: ID!) {
284
+ getUser(input: { id: $id }) {
285
+ id
286
+ name
287
+ email
288
+ username
289
+ }
290
+ }
291
+ ```
292
+
293
+ ### REST API Testing
294
+
295
+ You can also test REST endpoints using tools like curl or Postman:
296
+
297
+ ```bash
298
+ # Example curl request
299
+ curl -X GET http://localhost:3000/api/users
300
+ ```
301
+
302
+ ## 🔍 What's Next?
303
+
304
+ Congratulations! You now have a working BunSane application. Here's what you can explore next:
305
+
306
+ - **[Entity System](core-concepts/entity.md)** - Deep dive into entity management
307
+ - **[Component Architecture](core-concepts/components.md)** - Advanced component patterns
308
+ - **[Query System](core-concepts/query.md)** - Efficient data retrieval
309
+ - **[Lifecycle Hooks](core-concepts/hooks.md)** - Business logic integration
310
+ - **[Real Examples](examples/)** - Complete application tutorials
311
+
312
+ ## 🐛 Troubleshooting
313
+
314
+ ### Common Issues
315
+
316
+ **"Component not registered" error**
317
+ - Ensure all components are properly decorated with `@Component`
318
+ - Check that components are imported before use
319
+
320
+ **Database connection failed**
321
+ - Verify PostgreSQL is running
322
+ - Check DATABASE_URL format and credentials
323
+ - Ensure database exists and user has permissions
324
+
325
+ **Service not found error**
326
+ - Verify services extend `BaseService`
327
+ - Check that services are registered with `ServiceRegistry`
328
+
329
+ ### Getting Help
330
+
331
+ - [GitHub Issues](https://github.com/yaaruu/bunsane/issues) - Report bugs
332
+ - [GitHub Discussions](https://github.com/yaaruu/bunsane/discussions) - Ask questions
333
+ - [Documentation](https://yaaruu.github.io/bunsane/) - Complete reference
334
+
335
+ ---
336
+
337
+ *Ready to build something amazing? Let's continue with the [Entity System](core-concepts/entity.md)!* 🚀
@@ -0,0 +1,97 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>BunSane Framework Documentation</title>
6
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
7
+ <meta name="description" content="Professional documentation for BunSane - A batteries-included TypeScript API framework for Bun">
8
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
9
+ <link rel="icon" href="https://raw.githubusercontent.com/yaaruu/bunsane/main/BunSane.jpg" type="image/x-icon">
10
+ <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
11
+ <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/dark.css" title="dark" disabled>
12
+ <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/buble.css" title="buble" disabled>
13
+ <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/pure.css" title="pure" disabled>
14
+ <style>
15
+ .sidebar-nav ul li a {
16
+ font-size: 14px;
17
+ padding: 8px 15px;
18
+ }
19
+ .sidebar-nav ul li.active > a {
20
+ background-color: #f0f8ff;
21
+ border-left: 4px solid #007acc;
22
+ }
23
+ .content {
24
+ max-width: 900px;
25
+ }
26
+ .markdown-section h1 {
27
+ border-bottom: 2px solid #007acc;
28
+ padding-bottom: 10px;
29
+ }
30
+ .markdown-section h2 {
31
+ border-bottom: 1px solid #ddd;
32
+ padding-bottom: 5px;
33
+ }
34
+ .markdown-section code {
35
+ background-color: #f6f8fa;
36
+ padding: 2px 6px;
37
+ border-radius: 3px;
38
+ font-size: 0.9em;
39
+ }
40
+ .markdown-section pre code {
41
+ background-color: transparent;
42
+ padding: 0;
43
+ }
44
+ .markdown-section blockquote {
45
+ border-left: 4px solid #007acc;
46
+ background-color: #f0f8ff;
47
+ margin: 20px 0;
48
+ padding: 15px 20px;
49
+ }
50
+ </style>
51
+ </head>
52
+ <body>
53
+ <div id="app"></div>
54
+ <script>
55
+ window.$docsify = {
56
+ name: 'BunSane Framework',
57
+ repo: 'https://github.com/yaaruu/bunsane',
58
+ loadSidebar: true,
59
+ loadNavbar: true,
60
+ coverpage: true,
61
+ themeColor: '#007acc',
62
+ auto2top: true,
63
+ search: {
64
+ paths: 'auto',
65
+ placeholder: 'Search documentation...',
66
+ noData: 'No results found',
67
+ depth: 3
68
+ },
69
+ pagination: {
70
+ previousText: 'Previous',
71
+ nextText: 'Next',
72
+ crossChapter: true,
73
+ crossChapterText: true
74
+ },
75
+ plugins: [
76
+ function(hook, vm) {
77
+ hook.beforeEach(function(html) {
78
+ return html + '\n\n---\n\n*Last updated: ' + new Date().toLocaleDateString() + '*';
79
+ });
80
+ }
81
+ ]
82
+ }
83
+ </script>
84
+ <script src="//cdn.jsdelivr.net/npm/docsify@4/lib/docsify.min.js"></script>
85
+ <script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/search.min.js"></script>
86
+ <script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/emoji.min.js"></script>
87
+ <script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/zoom-image.min.js"></script>
88
+ <script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/external-script.min.js"></script>
89
+ <script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-typescript.min.js"></script>
90
+ <script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-bash.min.js"></script>
91
+ <script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-json.min.js"></script>
92
+ <script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-sql.min.js"></script>
93
+ <script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-graphql.min.js"></script>
94
+ <script src="//cdn.jsdelivr.net/npm/prismjs@1/plugins/line-numbers/prism-line-numbers.min.js"></script>
95
+ <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/prismjs@1/plugins/line-numbers/prism-line-numbers.css">
96
+ </body>
97
+ </html>
package/gql/Generator.ts CHANGED
@@ -1,72 +1,94 @@
1
1
  import { GraphQLSchema, GraphQLError } from "graphql";
2
- import {makeExecutableSchema} from "@graphql-tools/schema";
2
+ import { createSchema } from "graphql-yoga";
3
3
  import { logger as MainLogger } from "core/Logger";
4
+ import type { GraphQLType } from "./helpers";
4
5
  const logger = MainLogger.child({ scope: "GraphQLGenerator" });
5
- export interface GraphQLTypeMeta {
6
+ export interface GraphQLObjectTypeMeta {
6
7
  name: string;
7
- fields: Record<string, string>;
8
+ fields: Record<string, GraphQLType>;
8
9
  }
9
10
 
10
11
  export interface GraphQLOperationMeta {
11
12
  type: "Query" | "Mutation";
12
13
  name?: string;
13
- input?: Record<string, string>;
14
- output: Record<string, string> | string;
14
+ input?: Record<string, GraphQLType>;
15
+ output: GraphQLType | Record<string, GraphQLType>;
15
16
  }
16
17
 
17
18
  export interface GraphQLFieldMeta {
18
- type: string;
19
+ type: GraphQLType;
19
20
  field: string;
20
21
  }
21
22
 
22
- export function GraphQLType(meta: GraphQLTypeMeta) {
23
+ export function GraphQLObjectType(meta: GraphQLObjectTypeMeta) {
23
24
  return (target: any) => {
24
- target.__graphqlType = meta;
25
+ if (!target.__graphqlObjectType) target.__graphqlObjectType = [];
26
+ target.__graphqlObjectType.push(meta);
27
+ }
28
+ }
29
+
30
+ export function GraphQLScalarType(name: string) {
31
+ return (target: any) => {
32
+ if (!target.__graphqlScalarTypes) target.__graphqlScalarTypes = [];
33
+ target.__graphqlScalarTypes.push(name);
25
34
  }
26
35
  }
27
36
 
28
37
  export function GraphQLOperation(meta: GraphQLOperationMeta) {
29
- return function (target: any, context: ClassMethodDecoratorContext) {
38
+ return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
30
39
  if (!target.__graphqlOperations) target.__graphqlOperations = [];
31
- const operationName = meta.name ?? context;
40
+ const operationName = meta.name ?? propertyKey;
32
41
  if (!operationName) {
33
- throw new Error("GraphQLOperation: Operation name is required (either meta.name or context.name must be defined)");
42
+ throw new Error("GraphQLOperation: Operation name is required (either meta.name or propertyKey must be defined)");
34
43
  }
35
- const operationMeta = { ...meta, name: operationName, propertyKey: context};
44
+ const operationMeta = { ...meta, name: operationName, propertyKey };
36
45
  target.__graphqlOperations.push(operationMeta);
37
46
  };
38
47
  }
39
48
 
40
49
  export function GraphQLField(meta: GraphQLFieldMeta) {
41
- return function (target: any, context: ClassMethodDecoratorContext) {
50
+ return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
42
51
  if (!target.__graphqlFields) target.__graphqlFields = [];
43
- target.__graphqlFields.push({ ...meta, propertyKey: context });
52
+ target.__graphqlFields.push({ ...meta, propertyKey });
44
53
  };
45
54
  }
46
55
 
47
- export function generateGraphQLSchema(systems: any[]): { schema: GraphQLSchema | null; resolvers: any } {
48
- let typeDefs = "";
49
- const resolvers: any = { Query: {}, Mutation: {} };
56
+ export function generateGraphQLSchema(services: any[]): { schema: GraphQLSchema | null; resolvers: any } {
57
+ let typeDefs = `
58
+ `;
59
+ const scalarTypes: Set<string> = new Set();
60
+ const resolvers: any = {};
50
61
  const queryFields: string[] = [];
51
62
  const mutationFields: string[] = [];
52
63
 
53
- systems.forEach(system => {
54
- logger.trace(`Processing system: ${system.constructor.name}`);
55
- if (system.constructor.__graphqlType) {
56
- const { name, fields } = system.constructor.__graphqlType;
57
- typeDefs += `type ${name} {\n${Object.entries(fields).map(([k, v]) => ` ${k}: ${v}`).join('\n')}\n}\n`;
64
+ services.forEach(service => {
65
+ logger.trace(`Processing service: ${service.constructor.name}`);
66
+ if(service.constructor.__graphqlScalarTypes) {
67
+ for (const scalarName of service.constructor.__graphqlScalarTypes) {
68
+ if (!scalarTypes.has(scalarName)) {
69
+ scalarTypes.add(scalarName);
70
+ typeDefs += `scalar ${scalarName}\n`;
71
+ }
72
+ }
73
+ }
74
+ if (service.constructor.__graphqlObjectType) {
75
+ for (const meta of service.constructor.__graphqlObjectType) {
76
+ const { name, fields } = meta;
77
+ typeDefs += `type ${name} {\n${Object.entries(fields).map(([k, v]) => ` ${k}: ${v}`).join('\n')}\n}\n`;
78
+ }
58
79
  }
59
- if (system.__graphqlOperations) {
60
- system.__graphqlOperations.forEach((op: any) => {
80
+ if (service.__graphqlOperations) {
81
+ service.__graphqlOperations.forEach((op: any) => {
61
82
  const { type, name, input, output, propertyKey } = op;
83
+ if (!resolvers[type]) resolvers[type] = {};
62
84
  let fieldDef = `${name}`;
63
85
  if (input) {
64
86
  const inputName = `${name}Input`;
65
87
  typeDefs += `input ${inputName} {\n${Object.entries(input).map(([k, v]) => ` ${k}: ${v}`).join('\n')}\n}\n`;
66
88
  fieldDef += `(input: ${inputName}!)`;
67
- resolvers[type][name] = async (_: any, args: any, context: any) => {
89
+ resolvers[type][name] = async (_: any, args: any, context: any, info: any) => {
68
90
  try {
69
- return await system[propertyKey](args.input || args, context);
91
+ return await service[propertyKey](args.input || args, context, info);
70
92
  } catch (error) {
71
93
  logger.error(`Error in ${type}.${name}:`);
72
94
  logger.error(error);
@@ -82,9 +104,9 @@ export function generateGraphQLSchema(systems: any[]): { schema: GraphQLSchema |
82
104
  }
83
105
  };
84
106
  } else {
85
- resolvers[type][name] = async (_: any, args: any, context: any) => {
107
+ resolvers[type][name] = async (_: any, args: any, context: any, info: any) => {
86
108
  try {
87
- return await system[propertyKey]({}, context);
109
+ return await service[propertyKey]({}, context, info);
88
110
  } catch (error) {
89
111
  logger.error(`Error in ${type}.${name}:`);
90
112
  logger.error(error);
@@ -117,14 +139,14 @@ export function generateGraphQLSchema(systems: any[]): { schema: GraphQLSchema |
117
139
  });
118
140
 
119
141
  // Process field resolvers
120
- systems.forEach(system => {
121
- if (system.__graphqlFields) {
122
- system.__graphqlFields.forEach((fieldMeta: any) => {
142
+ services.forEach(service => {
143
+ if (service.__graphqlFields) {
144
+ service.__graphqlFields.forEach((fieldMeta: any) => {
123
145
  const { type, field, propertyKey } = fieldMeta;
124
146
  if (!resolvers[type]) resolvers[type] = {};
125
- resolvers[type][field] = async (parent: any, args: any, context: any) => {
147
+ resolvers[type][field] = async (parent: any, args: any, context: any, info: any) => {
126
148
  try {
127
- return await system[propertyKey](parent, args, context);
149
+ return await service[propertyKey](parent, args, context, info);
128
150
  } catch (error) {
129
151
  logger.error(`Error in ${type}.${field}:`);
130
152
  logger.error(error);
@@ -152,8 +174,9 @@ export function generateGraphQLSchema(systems: any[]): { schema: GraphQLSchema |
152
174
 
153
175
  logger.trace(`System Type Defs: ${typeDefs}`);
154
176
  let schema : GraphQLSchema | null = null;
155
- if(typeDefs !== "") {
156
- schema = makeExecutableSchema({ typeDefs, resolvers });
177
+ // Check if typeDefs contains actual schema definitions, not just whitespace
178
+ if(typeDefs.trim() !== "" && (queryFields.length > 0 || mutationFields.length > 0 || scalarTypes.size > 0)) {
179
+ schema = createSchema({ typeDefs, resolvers });
157
180
  }
158
181
  return { schema, resolvers };
159
182
  }