bunsane 0.1.4 → 0.2.0

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 (257) hide show
  1. package/.claude/settings.local.json +47 -0
  2. package/.claude/skills/update-memory.md +74 -0
  3. package/.prettierrc +4 -0
  4. package/.serena/memories/architectural-decision-no-dependency-injection.md +76 -0
  5. package/.serena/memories/architecture.md +154 -0
  6. package/.serena/memories/cache-interface-refactoring-2026-01-24.md +165 -0
  7. package/.serena/memories/code_style_and_conventions.md +76 -0
  8. package/.serena/memories/project_overview.md +43 -0
  9. package/.serena/memories/schema-dsl-plan.md +107 -0
  10. package/.serena/memories/suggested_commands.md +80 -0
  11. package/.serena/memories/typescript-compilation-status.md +54 -0
  12. package/.serena/project.yml +114 -0
  13. package/TODO.md +1 -7
  14. package/bun.lock +150 -4
  15. package/bunfig.toml +10 -0
  16. package/config/cache.config.ts +77 -0
  17. package/config/upload.config.ts +4 -5
  18. package/core/App.ts +870 -123
  19. package/core/ArcheType.ts +2268 -377
  20. package/core/BatchLoader.ts +181 -71
  21. package/core/Config.ts +153 -0
  22. package/core/Decorators.ts +4 -1
  23. package/core/Entity.ts +621 -92
  24. package/core/EntityHookManager.ts +1 -1
  25. package/core/EntityInterface.ts +3 -1
  26. package/core/EntityManager.ts +1 -13
  27. package/core/ErrorHandler.ts +8 -2
  28. package/core/Logger.ts +9 -0
  29. package/core/Middleware.ts +34 -0
  30. package/core/RequestContext.ts +5 -1
  31. package/core/RequestLoaders.ts +227 -93
  32. package/core/SchedulerManager.ts +193 -52
  33. package/core/cache/CacheAnalytics.ts +399 -0
  34. package/core/cache/CacheFactory.ts +145 -0
  35. package/core/cache/CacheManager.ts +520 -0
  36. package/core/cache/CacheProvider.ts +34 -0
  37. package/core/cache/CacheWarmer.ts +157 -0
  38. package/core/cache/CompressionUtils.ts +110 -0
  39. package/core/cache/MemoryCache.ts +251 -0
  40. package/core/cache/MultiLevelCache.ts +180 -0
  41. package/core/cache/NoOpCache.ts +53 -0
  42. package/core/cache/RedisCache.ts +464 -0
  43. package/core/cache/TTLStrategy.ts +254 -0
  44. package/core/cache/index.ts +6 -0
  45. package/core/components/BaseComponent.ts +120 -0
  46. package/core/{ComponentRegistry.ts → components/ComponentRegistry.ts} +148 -54
  47. package/core/components/Decorators.ts +88 -0
  48. package/core/components/Interfaces.ts +7 -0
  49. package/core/components/index.ts +5 -0
  50. package/core/decorators/EntityHooks.ts +0 -3
  51. package/core/decorators/IndexedField.ts +26 -0
  52. package/core/decorators/ScheduledTask.ts +0 -47
  53. package/core/events/EntityLifecycleEvents.ts +1 -1
  54. package/core/health.ts +112 -0
  55. package/core/metadata/definitions/ArcheType.ts +14 -0
  56. package/core/metadata/definitions/Component.ts +9 -0
  57. package/core/metadata/definitions/gqlObject.ts +1 -1
  58. package/core/metadata/index.ts +42 -1
  59. package/core/metadata/metadata-storage.ts +28 -2
  60. package/core/middleware/AccessLog.ts +59 -0
  61. package/core/middleware/RequestId.ts +38 -0
  62. package/core/middleware/SecurityHeaders.ts +62 -0
  63. package/core/middleware/index.ts +3 -0
  64. package/core/scheduler/DistributedLock.ts +266 -0
  65. package/core/scheduler/index.ts +15 -0
  66. package/core/validateEnv.ts +92 -0
  67. package/database/DatabaseHelper.ts +416 -40
  68. package/database/IndexingStrategy.ts +342 -0
  69. package/database/PreparedStatementCache.ts +226 -0
  70. package/database/index.ts +32 -7
  71. package/database/sqlHelpers.ts +14 -2
  72. package/endpoints/archetypes.ts +362 -0
  73. package/endpoints/components.ts +58 -0
  74. package/endpoints/entity.ts +80 -0
  75. package/endpoints/index.ts +27 -0
  76. package/endpoints/query.ts +93 -0
  77. package/endpoints/stats.ts +76 -0
  78. package/endpoints/tables.ts +212 -0
  79. package/endpoints/types.ts +155 -0
  80. package/gql/ArchetypeOperations.ts +32 -86
  81. package/gql/Generator.ts +27 -315
  82. package/gql/GeneratorV2.ts +37 -0
  83. package/gql/builders/InputTypeBuilder.ts +99 -0
  84. package/gql/builders/ResolverBuilder.ts +234 -0
  85. package/gql/builders/TypeDefBuilder.ts +105 -0
  86. package/gql/builders/index.ts +3 -0
  87. package/gql/decorators/Upload.ts +1 -1
  88. package/gql/depthLimit.ts +85 -0
  89. package/gql/graph/GraphNode.ts +224 -0
  90. package/gql/graph/SchemaGraph.ts +278 -0
  91. package/gql/helpers.ts +8 -2
  92. package/gql/index.ts +56 -4
  93. package/gql/middleware.ts +79 -0
  94. package/gql/orchestration/GraphQLSchemaOrchestrator.ts +241 -0
  95. package/gql/orchestration/index.ts +1 -0
  96. package/gql/scanner/ServiceScanner.ts +347 -0
  97. package/gql/schema/index.ts +458 -0
  98. package/gql/strategies/TypeGenerationStrategy.ts +329 -0
  99. package/gql/types.ts +1 -0
  100. package/gql/utils/TypeSignature.ts +220 -0
  101. package/gql/utils/index.ts +1 -0
  102. package/gql/visitors/ArchetypePreprocessorVisitor.ts +80 -0
  103. package/gql/visitors/DeduplicationVisitor.ts +82 -0
  104. package/gql/visitors/GraphVisitor.ts +78 -0
  105. package/gql/visitors/ResolverGeneratorVisitor.ts +122 -0
  106. package/gql/visitors/SchemaGeneratorVisitor.ts +851 -0
  107. package/gql/visitors/TypeCollectorVisitor.ts +79 -0
  108. package/gql/visitors/VisitorComposer.ts +96 -0
  109. package/gql/visitors/index.ts +7 -0
  110. package/package.json +59 -37
  111. package/plugins/index.ts +2 -2
  112. package/query/CTENode.ts +97 -0
  113. package/query/ComponentInclusionNode.ts +689 -0
  114. package/query/FilterBuilder.ts +127 -0
  115. package/query/FilterBuilderRegistry.ts +202 -0
  116. package/query/OrNode.ts +517 -0
  117. package/query/OrQuery.ts +42 -0
  118. package/query/Query.ts +1022 -0
  119. package/query/QueryContext.ts +170 -0
  120. package/query/QueryDAG.ts +122 -0
  121. package/query/QueryNode.ts +65 -0
  122. package/query/SourceNode.ts +53 -0
  123. package/query/builders/FullTextSearchBuilder.ts +236 -0
  124. package/query/index.ts +21 -0
  125. package/scheduler/index.ts +40 -8
  126. package/service/Service.ts +2 -1
  127. package/service/ServiceRegistry.ts +6 -5
  128. package/{core/storage → storage}/LocalStorageProvider.ts +2 -2
  129. package/storage/S3StorageProvider.ts +316 -0
  130. package/{core/storage → storage}/StorageProvider.ts +7 -3
  131. package/studio/bun.lock +482 -0
  132. package/studio/index.html +13 -0
  133. package/studio/package.json +39 -0
  134. package/studio/postcss.config.js +6 -0
  135. package/studio/src/components/DataTable.tsx +211 -0
  136. package/studio/src/components/Layout.tsx +13 -0
  137. package/studio/src/components/PageContainer.tsx +9 -0
  138. package/studio/src/components/PageHeader.tsx +13 -0
  139. package/studio/src/components/SearchBar.tsx +57 -0
  140. package/studio/src/components/Sidebar.tsx +294 -0
  141. package/studio/src/components/ui/button.tsx +56 -0
  142. package/studio/src/components/ui/checkbox.tsx +26 -0
  143. package/studio/src/components/ui/input.tsx +25 -0
  144. package/studio/src/hooks/useDataTable.ts +131 -0
  145. package/studio/src/index.css +36 -0
  146. package/studio/src/lib/api.ts +186 -0
  147. package/studio/src/lib/utils.ts +13 -0
  148. package/studio/src/main.tsx +17 -0
  149. package/studio/src/pages/ArcheType.tsx +239 -0
  150. package/studio/src/pages/Components.tsx +124 -0
  151. package/studio/src/pages/EntityInspector.tsx +302 -0
  152. package/studio/src/pages/QueryRunner.tsx +246 -0
  153. package/studio/src/pages/Table.tsx +94 -0
  154. package/studio/src/pages/Welcome.tsx +241 -0
  155. package/studio/src/routes.tsx +45 -0
  156. package/studio/src/store/archeTypeSettings.ts +30 -0
  157. package/studio/src/store/studio.ts +65 -0
  158. package/studio/src/utils/columnHelpers.tsx +114 -0
  159. package/studio/studio-instructions.md +81 -0
  160. package/studio/tailwind.config.js +77 -0
  161. package/studio/tsconfig.json +24 -0
  162. package/studio/utils.ts +54 -0
  163. package/studio/vite.config.js +19 -0
  164. package/swagger/generator.ts +1 -1
  165. package/tests/e2e/http.test.ts +126 -0
  166. package/tests/fixtures/archetypes/TestUserArchetype.ts +21 -0
  167. package/tests/fixtures/components/TestOrder.ts +23 -0
  168. package/tests/fixtures/components/TestProduct.ts +23 -0
  169. package/tests/fixtures/components/TestUser.ts +20 -0
  170. package/tests/fixtures/components/index.ts +6 -0
  171. package/tests/graphql/SchemaGeneration.test.ts +90 -0
  172. package/tests/graphql/builders/ResolverBuilder.test.ts +223 -0
  173. package/tests/graphql/builders/TypeDefBuilder.test.ts +153 -0
  174. package/tests/integration/archetype/ArcheType.persistence.test.ts +241 -0
  175. package/tests/integration/cache/CacheInvalidation.test.ts +259 -0
  176. package/tests/integration/entity/Entity.persistence.test.ts +333 -0
  177. package/tests/integration/query/Query.exec.test.ts +523 -0
  178. package/tests/pglite-setup.ts +61 -0
  179. package/tests/setup.ts +164 -0
  180. package/tests/stress/BenchmarkRunner.ts +203 -0
  181. package/tests/stress/DataSeeder.ts +190 -0
  182. package/tests/stress/StressTestReporter.ts +229 -0
  183. package/tests/stress/cursor-perf-test.ts +171 -0
  184. package/tests/stress/fixtures/StressTestComponents.ts +58 -0
  185. package/tests/stress/index.ts +7 -0
  186. package/tests/stress/scenarios/query-benchmarks.test.ts +285 -0
  187. package/tests/unit/BatchLoader.test.ts +82 -0
  188. package/tests/unit/archetype/ArcheType.test.ts +107 -0
  189. package/tests/unit/cache/CacheManager.test.ts +347 -0
  190. package/tests/unit/cache/MemoryCache.test.ts +260 -0
  191. package/tests/unit/cache/RedisCache.test.ts +411 -0
  192. package/tests/unit/entity/Entity.components.test.ts +244 -0
  193. package/tests/unit/entity/Entity.test.ts +345 -0
  194. package/tests/unit/gql/depthLimit.test.ts +203 -0
  195. package/tests/unit/gql/operationMiddleware.test.ts +293 -0
  196. package/tests/unit/health/Health.test.ts +129 -0
  197. package/tests/unit/middleware/AccessLog.test.ts +37 -0
  198. package/tests/unit/middleware/Middleware.test.ts +98 -0
  199. package/tests/unit/middleware/RequestId.test.ts +54 -0
  200. package/tests/unit/middleware/SecurityHeaders.test.ts +66 -0
  201. package/tests/unit/query/FilterBuilder.test.ts +111 -0
  202. package/tests/unit/query/Query.test.ts +308 -0
  203. package/tests/unit/scheduler/DistributedLock.test.ts +274 -0
  204. package/tests/unit/schema/schema-integration.test.ts +426 -0
  205. package/tests/unit/schema/schema.test.ts +580 -0
  206. package/tests/unit/storage/S3StorageProvider.test.ts +571 -0
  207. package/tests/unit/upload/RestUpload.test.ts +267 -0
  208. package/tests/unit/validateEnv.test.ts +82 -0
  209. package/tests/utils/entity-tracker.ts +57 -0
  210. package/tests/utils/index.ts +13 -0
  211. package/tests/utils/test-context.ts +149 -0
  212. package/tsconfig.json +5 -1
  213. package/types/archetype.types.ts +6 -0
  214. package/types/hooks.types.ts +1 -1
  215. package/types/query.types.ts +110 -0
  216. package/types/scheduler.types.ts +68 -7
  217. package/types/upload.types.ts +1 -0
  218. package/{core → upload}/FileValidator.ts +10 -1
  219. package/upload/RestUpload.ts +130 -0
  220. package/{core/components → upload}/UploadComponent.ts +11 -11
  221. package/{core → upload}/UploadManager.ts +3 -3
  222. package/upload/index.ts +23 -7
  223. package/utils/UploadHelper.ts +27 -6
  224. package/utils/cronParser.ts +16 -6
  225. package/.github/workflows/deploy-docs.yml +0 -57
  226. package/core/Components.ts +0 -202
  227. package/core/EntityCache.ts +0 -15
  228. package/core/Query.ts +0 -880
  229. package/docs/README.md +0 -149
  230. package/docs/_coverpage.md +0 -36
  231. package/docs/_sidebar.md +0 -23
  232. package/docs/api/core.md +0 -568
  233. package/docs/api/hooks.md +0 -554
  234. package/docs/api/index.md +0 -222
  235. package/docs/api/query.md +0 -678
  236. package/docs/api/service.md +0 -744
  237. package/docs/core-concepts/archetypes.md +0 -512
  238. package/docs/core-concepts/components.md +0 -498
  239. package/docs/core-concepts/entity.md +0 -314
  240. package/docs/core-concepts/hooks.md +0 -683
  241. package/docs/core-concepts/query.md +0 -588
  242. package/docs/core-concepts/services.md +0 -647
  243. package/docs/examples/code-examples.md +0 -425
  244. package/docs/getting-started.md +0 -337
  245. package/docs/index.html +0 -97
  246. package/tests/bench/insert.bench.ts +0 -60
  247. package/tests/bench/relations.bench.ts +0 -270
  248. package/tests/bench/sorting.bench.ts +0 -416
  249. package/tests/component-hooks-simple.test.ts +0 -117
  250. package/tests/component-hooks.test.ts +0 -1461
  251. package/tests/component.test.ts +0 -339
  252. package/tests/errorHandling.test.ts +0 -155
  253. package/tests/hooks.test.ts +0 -667
  254. package/tests/query-sorting.test.ts +0 -101
  255. package/tests/query.test.ts +0 -81
  256. package/tests/relations.test.ts +0 -170
  257. package/tests/scheduler.test.ts +0 -724
@@ -0,0 +1,278 @@
1
+ import { GraphNode, NodeType } from './GraphNode';
2
+
3
+ /**
4
+ * Directed graph data structure for managing GraphQL schema components and their dependencies.
5
+ * Provides topological sorting and dependency resolution for schema generation.
6
+ */
7
+ export class SchemaGraph {
8
+ private nodes: Map<string, GraphNode> = new Map();
9
+ private adjacencyList: Map<string, Set<string>> = new Map(); // nodeId -> set of dependent nodeIds
10
+
11
+ /**
12
+ * Add a node to the graph
13
+ */
14
+ addNode(node: GraphNode): void {
15
+ if (this.nodes.has(node.id)) {
16
+ throw new Error(`Node with id '${node.id}' already exists in graph`);
17
+ }
18
+
19
+ this.nodes.set(node.id, node);
20
+ this.adjacencyList.set(node.id, new Set());
21
+
22
+ // Add reverse edges for dependencies (what depends on this node)
23
+ for (const depId of node.dependencies) {
24
+ if (!this.adjacencyList.has(depId)) {
25
+ this.adjacencyList.set(depId, new Set());
26
+ }
27
+ this.adjacencyList.get(depId)!.add(node.id);
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Get a node by its ID
33
+ */
34
+ getNode(nodeId: string): GraphNode | undefined {
35
+ return this.nodes.get(nodeId);
36
+ }
37
+
38
+ /**
39
+ * Check if a node exists in the graph
40
+ */
41
+ hasNode(nodeId: string): boolean {
42
+ return this.nodes.has(nodeId);
43
+ }
44
+
45
+ /**
46
+ * Remove a node from the graph
47
+ */
48
+ removeNode(nodeId: string): boolean {
49
+ const node = this.nodes.get(nodeId);
50
+ if (!node) {
51
+ return false;
52
+ }
53
+
54
+ // Remove from nodes map
55
+ this.nodes.delete(nodeId);
56
+
57
+ // Remove from adjacency list
58
+ this.adjacencyList.delete(nodeId);
59
+
60
+ // Remove this node from other nodes' dependency lists
61
+ for (const [otherNodeId, dependents] of this.adjacencyList) {
62
+ dependents.delete(nodeId);
63
+ }
64
+
65
+ return true;
66
+ }
67
+
68
+ /**
69
+ * Get all nodes in the graph
70
+ */
71
+ getAllNodes(): GraphNode[] {
72
+ return Array.from(this.nodes.values());
73
+ }
74
+
75
+ /**
76
+ * Get nodes of a specific type
77
+ */
78
+ getNodesByType(nodeType: NodeType): GraphNode[] {
79
+ return this.getAllNodes().filter(node => node.nodeType === nodeType);
80
+ }
81
+
82
+ /**
83
+ * Get direct dependencies of a node (nodes this node depends on)
84
+ */
85
+ getDependencies(nodeId: string): GraphNode[] {
86
+ const node = this.getNode(nodeId);
87
+ if (!node) {
88
+ return [];
89
+ }
90
+
91
+ return node.dependencies
92
+ .map(depId => this.getNode(depId))
93
+ .filter((dep): dep is GraphNode => dep !== undefined);
94
+ }
95
+
96
+ /**
97
+ * Get nodes that depend on the given node (reverse dependencies)
98
+ */
99
+ getDependents(nodeId: string): GraphNode[] {
100
+ const dependents = this.adjacencyList.get(nodeId);
101
+ if (!dependents) {
102
+ return [];
103
+ }
104
+
105
+ return Array.from(dependents)
106
+ .map(depId => this.getNode(depId))
107
+ .filter((dep): dep is GraphNode => dep !== undefined);
108
+ }
109
+
110
+ /**
111
+ * Add a dependency edge between two nodes
112
+ */
113
+ addDependency(fromNodeId: string, toNodeId: string): void {
114
+ const fromNode = this.getNode(fromNodeId);
115
+ const toNode = this.getNode(toNodeId);
116
+
117
+ if (!fromNode) {
118
+ throw new Error(`Source node '${fromNodeId}' does not exist`);
119
+ }
120
+ if (!toNode) {
121
+ throw new Error(`Target node '${toNodeId}' does not exist`);
122
+ }
123
+
124
+ fromNode.addDependency(toNodeId);
125
+
126
+ // Update adjacency list for reverse lookup
127
+ if (!this.adjacencyList.has(toNodeId)) {
128
+ this.adjacencyList.set(toNodeId, new Set());
129
+ }
130
+ this.adjacencyList.get(toNodeId)!.add(fromNodeId);
131
+ }
132
+
133
+ /**
134
+ * Remove a dependency edge between two nodes
135
+ */
136
+ removeDependency(fromNodeId: string, toNodeId: string): void {
137
+ const fromNode = this.getNode(fromNodeId);
138
+ if (!fromNode) {
139
+ return;
140
+ }
141
+
142
+ fromNode.removeDependency(toNodeId);
143
+
144
+ // Update adjacency list
145
+ const dependents = this.adjacencyList.get(toNodeId);
146
+ if (dependents) {
147
+ dependents.delete(fromNodeId);
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Perform topological sort of the graph
153
+ * Returns nodes in dependency order (dependencies first)
154
+ */
155
+ topologicalSort(): GraphNode[] {
156
+ const visited = new Set<string>();
157
+ const visiting = new Set<string>(); // For cycle detection
158
+ const result: GraphNode[] = [];
159
+
160
+ const visit = (nodeId: string): void => {
161
+ if (visited.has(nodeId)) {
162
+ return;
163
+ }
164
+
165
+ if (visiting.has(nodeId)) {
166
+ throw new Error(`Cycle detected in graph involving node '${nodeId}'`);
167
+ }
168
+
169
+ visiting.add(nodeId);
170
+
171
+ // Visit all dependencies first
172
+ const node = this.getNode(nodeId);
173
+ if (node) {
174
+ for (const depId of node.dependencies) {
175
+ if (this.hasNode(depId)) {
176
+ visit(depId);
177
+ }
178
+ }
179
+ }
180
+
181
+ visiting.delete(nodeId);
182
+ visited.add(nodeId);
183
+
184
+ if (node) {
185
+ result.push(node);
186
+ }
187
+ };
188
+
189
+ // Visit all nodes
190
+ for (const nodeId of this.nodes.keys()) {
191
+ if (!visited.has(nodeId)) {
192
+ visit(nodeId);
193
+ }
194
+ }
195
+
196
+ return result;
197
+ }
198
+
199
+ /**
200
+ * Get nodes in dependency order (topologically sorted)
201
+ */
202
+ getNodesInDependencyOrder(): GraphNode[] {
203
+ return this.topologicalSort();
204
+ }
205
+
206
+ /**
207
+ * Clear all nodes and edges from the graph
208
+ */
209
+ clear(): void {
210
+ this.nodes.clear();
211
+ this.adjacencyList.clear();
212
+ }
213
+
214
+ /**
215
+ * Get graph statistics
216
+ */
217
+ getStats(): {
218
+ nodeCount: number;
219
+ edgeCount: number;
220
+ nodesByType: Record<NodeType, number>;
221
+ } {
222
+ const nodesByType = Object.values(NodeType).reduce((acc, type) => {
223
+ acc[type] = 0;
224
+ return acc;
225
+ }, {} as Record<NodeType, number>);
226
+
227
+ for (const node of this.nodes.values()) {
228
+ nodesByType[node.nodeType]++;
229
+ }
230
+
231
+ let edgeCount = 0;
232
+ for (const node of this.nodes.values()) {
233
+ edgeCount += node.dependencies.length;
234
+ }
235
+
236
+ return {
237
+ nodeCount: this.nodes.size,
238
+ edgeCount,
239
+ nodesByType
240
+ };
241
+ }
242
+
243
+ /**
244
+ * Validate the graph structure
245
+ * Checks for missing dependencies and other consistency issues
246
+ */
247
+ validate(): { isValid: boolean; errors: string[] } {
248
+ const errors: string[] = [];
249
+
250
+ for (const [nodeId, node] of this.nodes) {
251
+ // Check that all dependencies exist
252
+ for (const depId of node.dependencies) {
253
+ if (!this.hasNode(depId)) {
254
+ errors.push(`Node '${nodeId}' depends on non-existent node '${depId}'`);
255
+ }
256
+ }
257
+ }
258
+
259
+ // Check for cycles (this will throw an error if cycles exist)
260
+ try {
261
+ this.topologicalSort();
262
+ } catch (error) {
263
+ errors.push(`Graph contains cycles: ${(error as Error).message}`);
264
+ }
265
+
266
+ return {
267
+ isValid: errors.length === 0,
268
+ errors
269
+ };
270
+ }
271
+
272
+ /**
273
+ * Get the number of nodes in the graph
274
+ */
275
+ size(): number {
276
+ return this.nodes.size;
277
+ }
278
+ }
package/gql/helpers.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { GraphQLFieldTypes } from './types';
2
+ import { z } from 'zod';
2
3
 
3
4
  export type GraphQLType =
4
5
  | GraphQLFieldTypes // e.g., "ID!", "String"
@@ -12,7 +13,7 @@ export function isValidGraphQLType(type: string): type is GraphQLType {
12
13
  return enumValues.includes(type as GraphQLFieldTypes) ||
13
14
  /^(\w+|\[\w+\])(!)?$/.test(type); // Simple regex for custom types/lists
14
15
  }
15
-
16
+ /** @deprecated */
16
17
  export type TypeFromGraphQL<T extends GraphQLType> =
17
18
  T extends GraphQLFieldTypes.ID_REQUIRED | GraphQLFieldTypes.ID ? string :
18
19
  T extends GraphQLFieldTypes.STRING_REQUIRED ? string :
@@ -26,6 +27,11 @@ export type TypeFromGraphQL<T extends GraphQLType> =
26
27
  T extends `[${string}]` | `[${string}]!` ? any[] :
27
28
  any;
28
29
 
30
+ /**
31
+ *
32
+ * @deprecated
33
+ * @reason Use `ArcheType.getInputSchema()` instead.
34
+ */
29
35
  export type ResolverInput<T extends Record<string, GraphQLType>> = {
30
36
  [K in keyof T]: TypeFromGraphQL<T[K]>;
31
37
  };
@@ -64,4 +70,4 @@ function isPathSelected(selectionSet: any, path: string[]): boolean {
64
70
  }
65
71
  }
66
72
  return false;
67
- }
73
+ }
package/gql/index.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  import {createSchema, createYoga, type Plugin} from 'graphql-yoga';
2
+ import { useValidationRule } from '@envelop/core';
2
3
  import { GraphQLSchema, GraphQLError } from 'graphql';
3
- import { GraphQLObjectType, GraphQLField, GraphQLOperation, GraphQLScalarType } from './Generator';
4
+ import { depthLimitRule } from './depthLimit';
5
+ import { GraphQLObjectType, GraphQLField, GraphQLOperation, GraphQLScalarType, GraphQLSubscription } from './Generator';
4
6
  import {GraphQLFieldTypes} from "./types"
5
- import {logger as MainLogger} from "core/Logger"
7
+ import {logger as MainLogger} from "../core/Logger"
6
8
  import { isFieldRequested } from './helpers';
9
+ import * as z from "zod";
7
10
 
8
11
  const logger = MainLogger.child({scope: "GQL"});
9
12
 
@@ -19,11 +22,16 @@ export {
19
22
  GraphQLObjectType,
20
23
  GraphQLField,
21
24
  GraphQLOperation,
25
+ GraphQLSubscription,
22
26
  GraphQLFieldTypes,
23
27
  isValidGraphQLType,
24
28
  GraphQLScalarType,
25
29
  isFieldRequested
26
30
  }
31
+ export { GraphQLSchemaOrchestrator } from "./orchestration";
32
+ export { generateGraphQLSchemaV2 } from "./GeneratorV2";
33
+ export { Middleware, composeOperationMiddleware } from "./middleware";
34
+ export type { OperationMiddleware } from "./middleware";
27
35
  export type {
28
36
  GraphQLType,
29
37
  TypeFromGraphQL,
@@ -90,6 +98,21 @@ const maskError = (error: any, message: string): GraphQLError => {
90
98
  }
91
99
  });
92
100
  }
101
+
102
+ // Handle GraphQL validation errors for missing required fields
103
+ if (error.message.includes('was not provided')) {
104
+ const match = error.message.match(/Field "([^"]+)" of required type "([^"]+)" was not provided/);
105
+ if (match) {
106
+ const fieldName = match[1];
107
+ return new GraphQLError(`Missing required field: ${fieldName}`, {
108
+ extensions: {
109
+ code: 'VALIDATION_ERROR',
110
+ field: fieldName,
111
+ originalMessage: error.message
112
+ }
113
+ });
114
+ }
115
+ }
93
116
 
94
117
  if (process.env.NODE_ENV === 'production') {
95
118
  logger.error("GraphQL Error:", error);
@@ -104,14 +127,41 @@ const maskError = (error: any, message: string): GraphQLError => {
104
127
  return error instanceof GraphQLError ? error : new GraphQLError(message, { originalError: error });
105
128
  };
106
129
 
107
- export function createYogaInstance(schema?: GraphQLSchema, plugins: Plugin[] = [], contextFactory?: (context: any) => any) {
130
+ export interface YogaInstanceOptions {
131
+ cors?: {
132
+ origin?: string | string[] | ((origin: string) => boolean);
133
+ credentials?: boolean;
134
+ allowedHeaders?: string[];
135
+ methods?: string[];
136
+ };
137
+ maxDepth?: number;
138
+ }
139
+
140
+ export function createYogaInstance(
141
+ schema?: GraphQLSchema,
142
+ plugins: Plugin[] = [],
143
+ contextFactory?: (context: any) => any,
144
+ options?: YogaInstanceOptions
145
+ ) {
146
+ // Prepend depth limit plugin if configured
147
+ const allPlugins: Plugin[] = [];
148
+ if (options?.maxDepth) {
149
+ allPlugins.push(useValidationRule(depthLimitRule(options.maxDepth)) as Plugin);
150
+ }
151
+ allPlugins.push(...plugins);
152
+
108
153
  const yogaConfig: any = {
109
- plugins,
154
+ plugins: allPlugins,
110
155
  maskedErrors: {
111
156
  maskError,
112
157
  },
113
158
  };
114
159
 
160
+ // Add CORS if provided
161
+ if (options?.cors) {
162
+ yogaConfig.cors = options.cors;
163
+ }
164
+
115
165
  // Add context factory if provided
116
166
  if (contextFactory) {
117
167
  yogaConfig.context = contextFactory;
@@ -129,4 +179,6 @@ export function createYogaInstance(schema?: GraphQLSchema, plugins: Plugin[] = [
129
179
  }
130
180
  }
131
181
 
182
+ export const Upload = z.union([z.literal("Upload"), z.any()]);
183
+
132
184
  export const yoga = createYogaInstance();
@@ -0,0 +1,79 @@
1
+ import { GraphQLError } from "graphql";
2
+
3
+ /**
4
+ * Middleware function for GraphQL operations (onion model).
5
+ *
6
+ * Call `next()` to continue to the next middleware or the resolver.
7
+ * Throw a `GraphQLError` to short-circuit the chain.
8
+ * Return the result of `next()` (or transform it).
9
+ */
10
+ export type OperationMiddleware = (
11
+ args: any,
12
+ context: any,
13
+ info: any,
14
+ next: () => Promise<any>,
15
+ ) => Promise<any>;
16
+
17
+ /**
18
+ * Compose an array of OperationMiddleware into a single chain.
19
+ * The final `handler` is the original service method.
20
+ */
21
+ export function composeOperationMiddleware(
22
+ middlewares: OperationMiddleware[],
23
+ handler: (...args: any[]) => Promise<any>,
24
+ thisArg: any,
25
+ ): (args: any, context: any, info: any) => Promise<any> {
26
+ return (args: any, context: any, info: any) => {
27
+ let index = 0;
28
+ const dispatch = (): Promise<any> => {
29
+ if (index >= middlewares.length) {
30
+ return handler.call(thisArg, args, context, info);
31
+ }
32
+ const mw = middlewares[index++]!;
33
+ return mw(args, context, info, dispatch);
34
+ };
35
+ return dispatch();
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Decorator that attaches an ordered chain of middleware to a GraphQL operation.
41
+ *
42
+ * Middleware execute in array order (left-to-right), wrapping the resolver
43
+ * in an onion model identical to HTTP middleware.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const Authenticate: OperationMiddleware = async (args, ctx, info, next) => {
48
+ * if (!ctx.user) throw new GraphQLError("Unauthenticated", {
49
+ * extensions: { code: "UNAUTHENTICATED", http: { status: 401 } }
50
+ * });
51
+ * return next();
52
+ * };
53
+ *
54
+ * function Authorize(...permissions: string[]): OperationMiddleware {
55
+ * return async (args, ctx, info, next) => {
56
+ * if (!permissions.every(p => ctx.user.permissions?.includes(p)))
57
+ * throw new GraphQLError("Forbidden", {
58
+ * extensions: { code: "FORBIDDEN", http: { status: 403 } }
59
+ * });
60
+ * return next();
61
+ * };
62
+ * }
63
+ *
64
+ * class UserService extends BaseService {
65
+ * @Middleware([Authenticate, Authorize("users.read")])
66
+ * @GraphQLOperation({ type: "Query", output: "User", input: { id: "ID!" } })
67
+ * async getUser(args, context, info) { ... }
68
+ * }
69
+ * ```
70
+ */
71
+ export function Middleware(middlewares: OperationMiddleware[]) {
72
+ return function (_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
73
+ const original = descriptor.value;
74
+ descriptor.value = function (this: any, args: any, context: any, info: any) {
75
+ const chain = composeOperationMiddleware(middlewares, original, this);
76
+ return chain(args, context, info);
77
+ };
78
+ };
79
+ }