@constructive-io/graphql-codegen 2.18.0 → 2.20.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 (301) hide show
  1. package/README.md +1818 -113
  2. package/__tests__/codegen/input-types-generator.test.d.ts +1 -0
  3. package/__tests__/codegen/input-types-generator.test.js +635 -0
  4. package/cli/codegen/barrel.d.ts +27 -0
  5. package/cli/codegen/barrel.js +163 -0
  6. package/cli/codegen/client.d.ts +4 -0
  7. package/cli/codegen/client.js +170 -0
  8. package/cli/codegen/custom-mutations.d.ts +38 -0
  9. package/cli/codegen/custom-mutations.js +149 -0
  10. package/cli/codegen/custom-queries.d.ts +38 -0
  11. package/cli/codegen/custom-queries.js +358 -0
  12. package/cli/codegen/filters.d.ts +27 -0
  13. package/cli/codegen/filters.js +357 -0
  14. package/cli/codegen/gql-ast.d.ts +41 -0
  15. package/cli/codegen/gql-ast.js +329 -0
  16. package/cli/codegen/index.d.ts +71 -0
  17. package/cli/codegen/index.js +147 -0
  18. package/cli/codegen/mutations.d.ts +30 -0
  19. package/cli/codegen/mutations.js +410 -0
  20. package/cli/codegen/orm/barrel.d.ts +18 -0
  21. package/cli/codegen/orm/barrel.js +48 -0
  22. package/cli/codegen/orm/client-generator.d.ts +45 -0
  23. package/cli/codegen/orm/client-generator.js +646 -0
  24. package/cli/codegen/orm/custom-ops-generator.d.ts +30 -0
  25. package/cli/codegen/orm/custom-ops-generator.js +350 -0
  26. package/cli/codegen/orm/index.d.ts +38 -0
  27. package/cli/codegen/orm/index.js +88 -0
  28. package/cli/codegen/orm/input-types-generator.d.ts +21 -0
  29. package/cli/codegen/orm/input-types-generator.js +705 -0
  30. package/cli/codegen/orm/input-types-generator.test.d.ts +1 -0
  31. package/cli/codegen/orm/input-types-generator.test.js +75 -0
  32. package/cli/codegen/orm/model-generator.d.ts +32 -0
  33. package/cli/codegen/orm/model-generator.js +264 -0
  34. package/cli/codegen/orm/query-builder.d.ts +161 -0
  35. package/cli/codegen/orm/query-builder.js +366 -0
  36. package/cli/codegen/orm/select-types.d.ts +169 -0
  37. package/cli/codegen/orm/select-types.js +16 -0
  38. package/cli/codegen/orm/select-types.test.d.ts +11 -0
  39. package/cli/codegen/orm/select-types.test.js +22 -0
  40. package/cli/codegen/queries.d.ts +25 -0
  41. package/cli/codegen/queries.js +438 -0
  42. package/cli/codegen/scalars.d.ts +12 -0
  43. package/cli/codegen/scalars.js +71 -0
  44. package/cli/codegen/schema-gql-ast.d.ts +51 -0
  45. package/cli/codegen/schema-gql-ast.js +385 -0
  46. package/cli/codegen/ts-ast.d.ts +122 -0
  47. package/cli/codegen/ts-ast.js +280 -0
  48. package/cli/codegen/type-resolver.d.ts +96 -0
  49. package/cli/codegen/type-resolver.js +246 -0
  50. package/cli/codegen/types.d.ts +12 -0
  51. package/cli/codegen/types.js +69 -0
  52. package/cli/codegen/utils.d.ts +163 -0
  53. package/cli/codegen/utils.js +326 -0
  54. package/cli/commands/generate-orm.d.ts +37 -0
  55. package/cli/commands/generate-orm.js +195 -0
  56. package/cli/commands/generate.d.ts +39 -0
  57. package/cli/commands/generate.js +299 -0
  58. package/cli/commands/index.d.ts +7 -0
  59. package/cli/commands/index.js +12 -0
  60. package/cli/commands/init.d.ts +35 -0
  61. package/cli/commands/init.js +176 -0
  62. package/cli/index.d.ts +4 -0
  63. package/cli/index.js +291 -0
  64. package/cli/introspect/fetch-meta.d.ts +31 -0
  65. package/cli/introspect/fetch-meta.js +108 -0
  66. package/cli/introspect/fetch-schema.d.ts +21 -0
  67. package/cli/introspect/fetch-schema.js +86 -0
  68. package/cli/introspect/index.d.ts +8 -0
  69. package/cli/introspect/index.js +16 -0
  70. package/cli/introspect/meta-query.d.ts +111 -0
  71. package/cli/introspect/meta-query.js +191 -0
  72. package/cli/introspect/schema-query.d.ts +20 -0
  73. package/cli/introspect/schema-query.js +123 -0
  74. package/cli/introspect/transform-schema.d.ts +74 -0
  75. package/cli/introspect/transform-schema.js +269 -0
  76. package/cli/introspect/transform-schema.test.d.ts +1 -0
  77. package/cli/introspect/transform-schema.test.js +67 -0
  78. package/cli/introspect/transform.d.ts +21 -0
  79. package/cli/introspect/transform.js +216 -0
  80. package/cli/watch/cache.d.ts +45 -0
  81. package/cli/watch/cache.js +111 -0
  82. package/cli/watch/debounce.d.ts +19 -0
  83. package/cli/watch/debounce.js +89 -0
  84. package/cli/watch/hash.d.ts +17 -0
  85. package/cli/watch/hash.js +48 -0
  86. package/cli/watch/index.d.ts +10 -0
  87. package/cli/watch/index.js +22 -0
  88. package/cli/watch/orchestrator.d.ts +63 -0
  89. package/cli/watch/orchestrator.js +228 -0
  90. package/cli/watch/poller.d.ts +65 -0
  91. package/cli/watch/poller.js +203 -0
  92. package/cli/watch/types.d.ts +67 -0
  93. package/cli/watch/types.js +5 -0
  94. package/client/error.d.ts +95 -0
  95. package/client/error.js +255 -0
  96. package/client/execute.d.ts +57 -0
  97. package/client/execute.js +124 -0
  98. package/client/index.d.ts +6 -0
  99. package/client/index.js +18 -0
  100. package/client/typed-document.d.ts +31 -0
  101. package/client/typed-document.js +44 -0
  102. package/core/ast.d.ts +10 -0
  103. package/core/ast.js +593 -0
  104. package/core/custom-ast.d.ts +35 -0
  105. package/core/custom-ast.js +204 -0
  106. package/core/index.d.ts +8 -0
  107. package/core/index.js +33 -0
  108. package/core/meta-object/convert.d.ts +65 -0
  109. package/core/meta-object/convert.js +63 -0
  110. package/core/meta-object/format.json +93 -0
  111. package/core/meta-object/index.d.ts +2 -0
  112. package/core/meta-object/index.js +18 -0
  113. package/core/meta-object/validate.d.ts +9 -0
  114. package/core/meta-object/validate.js +34 -0
  115. package/core/query-builder.d.ts +46 -0
  116. package/core/query-builder.js +412 -0
  117. package/core/types.d.ts +139 -0
  118. package/core/types.js +28 -0
  119. package/esm/__tests__/codegen/input-types-generator.test.d.ts +1 -0
  120. package/esm/__tests__/codegen/input-types-generator.test.js +633 -0
  121. package/esm/cli/codegen/barrel.d.ts +27 -0
  122. package/esm/cli/codegen/barrel.js +156 -0
  123. package/esm/cli/codegen/client.d.ts +4 -0
  124. package/esm/cli/codegen/client.js +167 -0
  125. package/esm/cli/codegen/custom-mutations.d.ts +38 -0
  126. package/esm/cli/codegen/custom-mutations.js +145 -0
  127. package/esm/cli/codegen/custom-queries.d.ts +38 -0
  128. package/esm/cli/codegen/custom-queries.js +354 -0
  129. package/esm/cli/codegen/filters.d.ts +27 -0
  130. package/esm/cli/codegen/filters.js +351 -0
  131. package/esm/cli/codegen/gql-ast.d.ts +41 -0
  132. package/esm/cli/codegen/gql-ast.js +288 -0
  133. package/esm/cli/codegen/index.d.ts +71 -0
  134. package/esm/cli/codegen/index.js +124 -0
  135. package/esm/cli/codegen/mutations.d.ts +30 -0
  136. package/esm/cli/codegen/mutations.js +404 -0
  137. package/esm/cli/codegen/orm/barrel.d.ts +18 -0
  138. package/esm/cli/codegen/orm/barrel.js +44 -0
  139. package/esm/cli/codegen/orm/client-generator.d.ts +45 -0
  140. package/esm/cli/codegen/orm/client-generator.js +640 -0
  141. package/esm/cli/codegen/orm/custom-ops-generator.d.ts +30 -0
  142. package/esm/cli/codegen/orm/custom-ops-generator.js +346 -0
  143. package/esm/cli/codegen/orm/index.d.ts +38 -0
  144. package/esm/cli/codegen/orm/index.js +75 -0
  145. package/esm/cli/codegen/orm/input-types-generator.d.ts +21 -0
  146. package/esm/cli/codegen/orm/input-types-generator.js +700 -0
  147. package/esm/cli/codegen/orm/input-types-generator.test.d.ts +1 -0
  148. package/esm/cli/codegen/orm/input-types-generator.test.js +73 -0
  149. package/esm/cli/codegen/orm/model-generator.d.ts +32 -0
  150. package/esm/cli/codegen/orm/model-generator.js +260 -0
  151. package/esm/cli/codegen/orm/query-builder.d.ts +161 -0
  152. package/esm/cli/codegen/orm/query-builder.js +353 -0
  153. package/esm/cli/codegen/orm/select-types.d.ts +169 -0
  154. package/esm/cli/codegen/orm/select-types.js +15 -0
  155. package/esm/cli/codegen/orm/select-types.test.d.ts +11 -0
  156. package/esm/cli/codegen/orm/select-types.test.js +21 -0
  157. package/esm/cli/codegen/queries.d.ts +25 -0
  158. package/esm/cli/codegen/queries.js +433 -0
  159. package/esm/cli/codegen/scalars.d.ts +12 -0
  160. package/esm/cli/codegen/scalars.js +66 -0
  161. package/esm/cli/codegen/schema-gql-ast.d.ts +51 -0
  162. package/esm/cli/codegen/schema-gql-ast.js +343 -0
  163. package/esm/cli/codegen/ts-ast.d.ts +122 -0
  164. package/esm/cli/codegen/ts-ast.js +260 -0
  165. package/esm/cli/codegen/type-resolver.d.ts +96 -0
  166. package/esm/cli/codegen/type-resolver.js +224 -0
  167. package/esm/cli/codegen/types.d.ts +12 -0
  168. package/esm/cli/codegen/types.js +65 -0
  169. package/esm/cli/codegen/utils.d.ts +163 -0
  170. package/esm/cli/codegen/utils.js +288 -0
  171. package/esm/cli/commands/generate-orm.d.ts +37 -0
  172. package/esm/cli/commands/generate-orm.js +192 -0
  173. package/esm/cli/commands/generate.d.ts +39 -0
  174. package/esm/cli/commands/generate.js +262 -0
  175. package/esm/cli/commands/index.d.ts +7 -0
  176. package/esm/cli/commands/index.js +5 -0
  177. package/esm/cli/commands/init.d.ts +35 -0
  178. package/esm/cli/commands/init.js +138 -0
  179. package/esm/cli/index.d.ts +4 -0
  180. package/esm/cli/index.js +256 -0
  181. package/esm/cli/introspect/fetch-meta.d.ts +31 -0
  182. package/esm/cli/introspect/fetch-meta.js +104 -0
  183. package/esm/cli/introspect/fetch-schema.d.ts +21 -0
  184. package/esm/cli/introspect/fetch-schema.js +83 -0
  185. package/esm/cli/introspect/index.d.ts +8 -0
  186. package/esm/cli/introspect/index.js +6 -0
  187. package/esm/cli/introspect/meta-query.d.ts +111 -0
  188. package/esm/cli/introspect/meta-query.js +188 -0
  189. package/esm/cli/introspect/schema-query.d.ts +20 -0
  190. package/esm/cli/introspect/schema-query.js +120 -0
  191. package/esm/cli/introspect/transform-schema.d.ts +74 -0
  192. package/esm/cli/introspect/transform-schema.js +259 -0
  193. package/esm/cli/introspect/transform-schema.test.d.ts +1 -0
  194. package/esm/cli/introspect/transform-schema.test.js +65 -0
  195. package/esm/cli/introspect/transform.d.ts +21 -0
  196. package/esm/cli/introspect/transform.js +210 -0
  197. package/esm/cli/watch/cache.d.ts +45 -0
  198. package/esm/cli/watch/cache.js +73 -0
  199. package/esm/cli/watch/debounce.d.ts +19 -0
  200. package/esm/cli/watch/debounce.js +85 -0
  201. package/esm/cli/watch/hash.d.ts +17 -0
  202. package/esm/cli/watch/hash.js +43 -0
  203. package/esm/cli/watch/index.d.ts +10 -0
  204. package/esm/cli/watch/index.js +8 -0
  205. package/esm/cli/watch/orchestrator.d.ts +63 -0
  206. package/esm/cli/watch/orchestrator.js +223 -0
  207. package/esm/cli/watch/poller.d.ts +65 -0
  208. package/esm/cli/watch/poller.js +198 -0
  209. package/esm/cli/watch/types.d.ts +67 -0
  210. package/esm/cli/watch/types.js +4 -0
  211. package/esm/client/error.d.ts +95 -0
  212. package/esm/client/error.js +249 -0
  213. package/esm/client/execute.d.ts +57 -0
  214. package/esm/client/execute.js +120 -0
  215. package/esm/client/index.d.ts +6 -0
  216. package/esm/client/index.js +6 -0
  217. package/esm/client/typed-document.d.ts +31 -0
  218. package/esm/client/typed-document.js +40 -0
  219. package/esm/core/ast.d.ts +10 -0
  220. package/esm/core/ast.js +549 -0
  221. package/esm/core/custom-ast.d.ts +35 -0
  222. package/esm/core/custom-ast.js +161 -0
  223. package/esm/core/index.d.ts +8 -0
  224. package/esm/core/index.js +12 -0
  225. package/esm/core/meta-object/convert.d.ts +65 -0
  226. package/esm/core/meta-object/convert.js +60 -0
  227. package/esm/core/meta-object/format.json +93 -0
  228. package/esm/core/meta-object/index.d.ts +2 -0
  229. package/esm/core/meta-object/index.js +2 -0
  230. package/esm/core/meta-object/validate.d.ts +9 -0
  231. package/esm/core/meta-object/validate.js +28 -0
  232. package/esm/core/query-builder.d.ts +46 -0
  233. package/esm/core/query-builder.js +375 -0
  234. package/esm/core/types.d.ts +139 -0
  235. package/esm/core/types.js +24 -0
  236. package/esm/generators/field-selector.d.ts +30 -0
  237. package/esm/generators/field-selector.js +355 -0
  238. package/esm/generators/index.d.ts +6 -0
  239. package/esm/generators/index.js +9 -0
  240. package/esm/generators/mutations.d.ts +31 -0
  241. package/esm/generators/mutations.js +197 -0
  242. package/esm/generators/select.d.ts +50 -0
  243. package/esm/generators/select.js +636 -0
  244. package/esm/index.d.ts +12 -0
  245. package/esm/index.js +17 -3
  246. package/esm/react/index.d.ts +5 -0
  247. package/esm/react/index.js +6 -0
  248. package/esm/types/config.d.ts +199 -0
  249. package/esm/types/config.js +106 -0
  250. package/esm/types/index.d.ts +9 -0
  251. package/esm/types/index.js +4 -0
  252. package/esm/types/introspection.d.ts +121 -0
  253. package/esm/types/introspection.js +54 -0
  254. package/esm/types/mutation.d.ts +45 -0
  255. package/esm/types/mutation.js +4 -0
  256. package/esm/types/query.d.ts +82 -0
  257. package/esm/types/query.js +4 -0
  258. package/esm/types/schema.d.ts +253 -0
  259. package/esm/types/schema.js +5 -0
  260. package/esm/types/selection.d.ts +43 -0
  261. package/esm/types/selection.js +4 -0
  262. package/esm/utils/index.d.ts +4 -0
  263. package/esm/utils/index.js +4 -0
  264. package/generators/field-selector.d.ts +30 -0
  265. package/generators/field-selector.js +361 -0
  266. package/generators/index.d.ts +6 -0
  267. package/generators/index.js +27 -0
  268. package/generators/mutations.d.ts +31 -0
  269. package/generators/mutations.js +235 -0
  270. package/generators/select.d.ts +50 -0
  271. package/generators/select.js +679 -0
  272. package/index.d.ts +12 -3
  273. package/index.js +19 -3
  274. package/package.json +59 -38
  275. package/react/index.d.ts +5 -0
  276. package/react/index.js +9 -0
  277. package/types/config.d.ts +199 -0
  278. package/types/config.js +111 -0
  279. package/types/index.d.ts +9 -0
  280. package/types/index.js +10 -0
  281. package/types/introspection.d.ts +121 -0
  282. package/types/introspection.js +62 -0
  283. package/types/mutation.d.ts +45 -0
  284. package/types/mutation.js +5 -0
  285. package/types/query.d.ts +82 -0
  286. package/types/query.js +5 -0
  287. package/types/schema.d.ts +253 -0
  288. package/types/schema.js +6 -0
  289. package/types/selection.d.ts +43 -0
  290. package/types/selection.js +5 -0
  291. package/utils/index.d.ts +4 -0
  292. package/utils/index.js +7 -0
  293. package/codegen.d.ts +0 -13
  294. package/codegen.js +0 -293
  295. package/esm/codegen.js +0 -253
  296. package/esm/gql.js +0 -939
  297. package/esm/options.js +0 -27
  298. package/gql.d.ts +0 -188
  299. package/gql.js +0 -992
  300. package/options.d.ts +0 -45
  301. package/options.js +0 -31
@@ -0,0 +1,45 @@
1
+ /**
2
+ * ORM Client generator
3
+ *
4
+ * Generates the createClient() factory function and main client file.
5
+ *
6
+ * Example output:
7
+ * ```typescript
8
+ * import { OrmClient, OrmClientConfig } from './client';
9
+ * import { UserModel } from './models/user';
10
+ * import { queryOperations } from './query';
11
+ * import { mutationOperations } from './mutation';
12
+ *
13
+ * export function createClient(config: OrmClientConfig) {
14
+ * const client = new OrmClient(config);
15
+ * return {
16
+ * user: new UserModel(client),
17
+ * post: new PostModel(client),
18
+ * query: queryOperations(client),
19
+ * mutation: mutationOperations(client),
20
+ * };
21
+ * }
22
+ * ```
23
+ */
24
+ import type { CleanTable } from '../../../types/schema';
25
+ export interface GeneratedClientFile {
26
+ fileName: string;
27
+ content: string;
28
+ }
29
+ /**
30
+ * Generate the main client.ts file (OrmClient class)
31
+ * This is the runtime client that handles GraphQL execution
32
+ */
33
+ export declare function generateOrmClientFile(): GeneratedClientFile;
34
+ /**
35
+ * Generate the query-builder.ts file (runtime query builder)
36
+ */
37
+ export declare function generateQueryBuilderFile(): GeneratedClientFile;
38
+ /**
39
+ * Generate the select-types.ts file
40
+ */
41
+ export declare function generateSelectTypesFile(): GeneratedClientFile;
42
+ /**
43
+ * Generate the main index.ts with createClient factory
44
+ */
45
+ export declare function generateCreateClientFile(tables: CleanTable[], hasCustomQueries: boolean, hasCustomMutations: boolean): GeneratedClientFile;
@@ -0,0 +1,640 @@
1
+ import { createProject, createSourceFile, getFormattedOutput, createFileHeader, createImport, } from '../ts-ast';
2
+ import { getTableNames, lcFirst } from '../utils';
3
+ /**
4
+ * Generate the main client.ts file (OrmClient class)
5
+ * This is the runtime client that handles GraphQL execution
6
+ */
7
+ export function generateOrmClientFile() {
8
+ // This is runtime code that doesn't change based on schema
9
+ // We generate it as a static file
10
+ const content = `/**
11
+ * ORM Client - Runtime GraphQL executor
12
+ * @generated by @constructive-io/graphql-codegen
13
+ * DO NOT EDIT - changes will be overwritten
14
+ */
15
+
16
+ export interface OrmClientConfig {
17
+ endpoint: string;
18
+ headers?: Record<string, string>;
19
+ }
20
+
21
+ export interface GraphQLError {
22
+ message: string;
23
+ locations?: { line: number; column: number }[];
24
+ path?: (string | number)[];
25
+ extensions?: Record<string, unknown>;
26
+ }
27
+
28
+ /**
29
+ * Error thrown when GraphQL request fails
30
+ */
31
+ export class GraphQLRequestError extends Error {
32
+ constructor(
33
+ public readonly errors: GraphQLError[],
34
+ public readonly data: unknown = null
35
+ ) {
36
+ const messages = errors.map(e => e.message).join('; ');
37
+ super(\`GraphQL Error: \${messages}\`);
38
+ this.name = 'GraphQLRequestError';
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Discriminated union for query results
44
+ * Use .ok to check success, or use .unwrap() to get data or throw
45
+ */
46
+ export type QueryResult<T> =
47
+ | { ok: true; data: T; errors: undefined }
48
+ | { ok: false; data: null; errors: GraphQLError[] };
49
+
50
+ /**
51
+ * Legacy QueryResult type for backwards compatibility
52
+ * @deprecated Use QueryResult discriminated union instead
53
+ */
54
+ export interface LegacyQueryResult<T> {
55
+ data: T | null;
56
+ errors?: GraphQLError[];
57
+ }
58
+
59
+ export class OrmClient {
60
+ private endpoint: string;
61
+ private headers: Record<string, string>;
62
+
63
+ constructor(config: OrmClientConfig) {
64
+ this.endpoint = config.endpoint;
65
+ this.headers = config.headers ?? {};
66
+ }
67
+
68
+ async execute<T>(
69
+ document: string,
70
+ variables?: Record<string, unknown>
71
+ ): Promise<QueryResult<T>> {
72
+ const response = await fetch(this.endpoint, {
73
+ method: 'POST',
74
+ headers: {
75
+ 'Content-Type': 'application/json',
76
+ Accept: 'application/json',
77
+ ...this.headers,
78
+ },
79
+ body: JSON.stringify({
80
+ query: document,
81
+ variables: variables ?? {},
82
+ }),
83
+ });
84
+
85
+ if (!response.ok) {
86
+ return {
87
+ ok: false,
88
+ data: null,
89
+ errors: [{ message: \`HTTP \${response.status}: \${response.statusText}\` }],
90
+ };
91
+ }
92
+
93
+ const json = (await response.json()) as {
94
+ data?: T;
95
+ errors?: GraphQLError[];
96
+ };
97
+
98
+ // Return discriminated union based on presence of errors
99
+ if (json.errors && json.errors.length > 0) {
100
+ return {
101
+ ok: false,
102
+ data: null,
103
+ errors: json.errors,
104
+ };
105
+ }
106
+
107
+ return {
108
+ ok: true,
109
+ data: json.data as T,
110
+ errors: undefined,
111
+ };
112
+ }
113
+
114
+ setHeaders(headers: Record<string, string>): void {
115
+ this.headers = { ...this.headers, ...headers };
116
+ }
117
+
118
+ getEndpoint(): string {
119
+ return this.endpoint;
120
+ }
121
+ }
122
+ `;
123
+ return {
124
+ fileName: 'client.ts',
125
+ content,
126
+ };
127
+ }
128
+ /**
129
+ * Generate the query-builder.ts file (runtime query builder)
130
+ */
131
+ export function generateQueryBuilderFile() {
132
+ const content = `/**
133
+ * Query Builder - Builds and executes GraphQL operations
134
+ * @generated by @constructive-io/graphql-codegen
135
+ * DO NOT EDIT - changes will be overwritten
136
+ */
137
+
138
+ import { OrmClient, QueryResult, GraphQLRequestError } from './client';
139
+
140
+ export interface QueryBuilderConfig {
141
+ client: OrmClient;
142
+ operation: 'query' | 'mutation';
143
+ operationName: string;
144
+ fieldName: string;
145
+ document: string;
146
+ variables?: Record<string, unknown>;
147
+ }
148
+
149
+ export class QueryBuilder<TResult> {
150
+ private config: QueryBuilderConfig;
151
+
152
+ constructor(config: QueryBuilderConfig) {
153
+ this.config = config;
154
+ }
155
+
156
+ /**
157
+ * Execute the query and return a discriminated union result
158
+ * Use result.ok to check success, or .unwrap() to throw on error
159
+ */
160
+ async execute(): Promise<QueryResult<TResult>> {
161
+ return this.config.client.execute<TResult>(
162
+ this.config.document,
163
+ this.config.variables
164
+ );
165
+ }
166
+
167
+ /**
168
+ * Execute and unwrap the result, throwing GraphQLRequestError on failure
169
+ * @throws {GraphQLRequestError} If the query returns errors
170
+ */
171
+ async unwrap(): Promise<TResult> {
172
+ const result = await this.execute();
173
+ if (!result.ok) {
174
+ throw new GraphQLRequestError(result.errors, result.data);
175
+ }
176
+ return result.data;
177
+ }
178
+
179
+ /**
180
+ * Execute and unwrap, returning defaultValue on error instead of throwing
181
+ */
182
+ async unwrapOr<D>(defaultValue: D): Promise<TResult | D> {
183
+ const result = await this.execute();
184
+ if (!result.ok) {
185
+ return defaultValue;
186
+ }
187
+ return result.data;
188
+ }
189
+
190
+ /**
191
+ * Execute and unwrap, calling onError callback on failure
192
+ */
193
+ async unwrapOrElse<D>(onError: (errors: import('./client').GraphQLError[]) => D): Promise<TResult | D> {
194
+ const result = await this.execute();
195
+ if (!result.ok) {
196
+ return onError(result.errors);
197
+ }
198
+ return result.data;
199
+ }
200
+
201
+ toGraphQL(): string {
202
+ return this.config.document;
203
+ }
204
+
205
+ getVariables(): Record<string, unknown> | undefined {
206
+ return this.config.variables;
207
+ }
208
+ }
209
+
210
+ // ============================================================================
211
+ // Document Builders
212
+ // ============================================================================
213
+
214
+ export function buildSelections<T>(select: T): string {
215
+ if (!select) return '';
216
+
217
+ const fields: string[] = [];
218
+
219
+ for (const [key, value] of Object.entries(select)) {
220
+ if (value === false || value === undefined) continue;
221
+
222
+ if (value === true) {
223
+ fields.push(key);
224
+ continue;
225
+ }
226
+
227
+ if (typeof value === 'object' && value !== null) {
228
+ const nested = value as {
229
+ select?: Record<string, unknown>;
230
+ first?: number;
231
+ filter?: Record<string, unknown>;
232
+ orderBy?: string[];
233
+ // New: connection flag to differentiate connection types from regular objects
234
+ connection?: boolean;
235
+ };
236
+
237
+ if (nested.select) {
238
+ const nestedSelections = buildSelections(nested.select);
239
+
240
+ // Check if this is a connection type (has pagination args or explicit connection flag)
241
+ const isConnection = nested.connection === true || nested.first !== undefined || nested.filter !== undefined;
242
+
243
+ if (isConnection) {
244
+ // Connection type - wrap in nodes/totalCount/pageInfo
245
+ const args: string[] = [];
246
+ if (nested.first !== undefined) args.push(\`first: \${nested.first}\`);
247
+ if (nested.orderBy?.length) args.push(\`orderBy: [\${nested.orderBy.join(', ')}]\`);
248
+ const argsStr = args.length > 0 ? \`(\${args.join(', ')})\` : '';
249
+
250
+ fields.push(\`\${key}\${argsStr} {
251
+ nodes { \${nestedSelections} }
252
+ totalCount
253
+ pageInfo { hasNextPage hasPreviousPage startCursor endCursor }
254
+ }\`);
255
+ } else {
256
+ // Regular nested object - just wrap in braces
257
+ fields.push(\`\${key} { \${nestedSelections} }\`);
258
+ }
259
+ }
260
+ }
261
+ }
262
+
263
+ return fields.join('\\n ');
264
+ }
265
+
266
+ export function buildFindManyDocument<TSelect, TWhere>(
267
+ operationName: string,
268
+ queryField: string,
269
+ select: TSelect,
270
+ args: {
271
+ where?: TWhere;
272
+ orderBy?: string[];
273
+ first?: number;
274
+ last?: number;
275
+ after?: string;
276
+ before?: string;
277
+ offset?: number;
278
+ },
279
+ filterTypeName: string
280
+ ): { document: string; variables: Record<string, unknown> } {
281
+ const selections = select ? buildSelections(select) : 'id';
282
+
283
+ const varDefs: string[] = [];
284
+ const queryArgs: string[] = [];
285
+ const variables: Record<string, unknown> = {};
286
+
287
+ if (args.where) {
288
+ varDefs.push(\`$where: \${filterTypeName}\`);
289
+ queryArgs.push('filter: $where');
290
+ variables.where = args.where;
291
+ }
292
+ if (args.orderBy?.length) {
293
+ varDefs.push(\`$orderBy: [\${operationName}sOrderBy!]\`);
294
+ queryArgs.push('orderBy: $orderBy');
295
+ variables.orderBy = args.orderBy;
296
+ }
297
+ if (args.first !== undefined) {
298
+ varDefs.push('$first: Int');
299
+ queryArgs.push('first: $first');
300
+ variables.first = args.first;
301
+ }
302
+ if (args.last !== undefined) {
303
+ varDefs.push('$last: Int');
304
+ queryArgs.push('last: $last');
305
+ variables.last = args.last;
306
+ }
307
+ if (args.after) {
308
+ varDefs.push('$after: Cursor');
309
+ queryArgs.push('after: $after');
310
+ variables.after = args.after;
311
+ }
312
+ if (args.before) {
313
+ varDefs.push('$before: Cursor');
314
+ queryArgs.push('before: $before');
315
+ variables.before = args.before;
316
+ }
317
+ if (args.offset !== undefined) {
318
+ varDefs.push('$offset: Int');
319
+ queryArgs.push('offset: $offset');
320
+ variables.offset = args.offset;
321
+ }
322
+
323
+ const varDefsStr = varDefs.length > 0 ? \`(\${varDefs.join(', ')})\` : '';
324
+ const queryArgsStr = queryArgs.length > 0 ? \`(\${queryArgs.join(', ')})\` : '';
325
+
326
+ const document = \`query \${operationName}Query\${varDefsStr} {
327
+ \${queryField}\${queryArgsStr} {
328
+ nodes { \${selections} }
329
+ totalCount
330
+ pageInfo { hasNextPage hasPreviousPage startCursor endCursor }
331
+ }
332
+ }\`;
333
+
334
+ return { document, variables };
335
+ }
336
+
337
+ export function buildFindFirstDocument<TSelect, TWhere>(
338
+ operationName: string,
339
+ queryField: string,
340
+ select: TSelect,
341
+ args: { where?: TWhere },
342
+ filterTypeName: string
343
+ ): { document: string; variables: Record<string, unknown> } {
344
+ const selections = select ? buildSelections(select) : 'id';
345
+
346
+ const varDefs: string[] = ['$first: Int'];
347
+ const queryArgs: string[] = ['first: $first'];
348
+ const variables: Record<string, unknown> = { first: 1 };
349
+
350
+ if (args.where) {
351
+ varDefs.push(\`$where: \${filterTypeName}\`);
352
+ queryArgs.push('filter: $where');
353
+ variables.where = args.where;
354
+ }
355
+
356
+ const document = \`query \${operationName}Query(\${varDefs.join(', ')}) {
357
+ \${queryField}(\${queryArgs.join(', ')}) {
358
+ nodes { \${selections} }
359
+ }
360
+ }\`;
361
+
362
+ return { document, variables };
363
+ }
364
+
365
+ export function buildCreateDocument<TSelect, TData>(
366
+ operationName: string,
367
+ mutationField: string,
368
+ entityField: string,
369
+ select: TSelect,
370
+ data: TData,
371
+ inputTypeName: string
372
+ ): { document: string; variables: Record<string, unknown> } {
373
+ const selections = select ? buildSelections(select) : 'id';
374
+
375
+ const document = \`mutation \${operationName}Mutation($input: \${inputTypeName}!) {
376
+ \${mutationField}(input: $input) {
377
+ \${entityField} { \${selections} }
378
+ }
379
+ }\`;
380
+
381
+ return {
382
+ document,
383
+ variables: { input: { [entityField]: data } },
384
+ };
385
+ }
386
+
387
+ export function buildUpdateDocument<TSelect, TWhere extends { id: string }, TData>(
388
+ operationName: string,
389
+ mutationField: string,
390
+ entityField: string,
391
+ select: TSelect,
392
+ where: TWhere,
393
+ data: TData,
394
+ inputTypeName: string
395
+ ): { document: string; variables: Record<string, unknown> } {
396
+ const selections = select ? buildSelections(select) : 'id';
397
+
398
+ const document = \`mutation \${operationName}Mutation($input: \${inputTypeName}!) {
399
+ \${mutationField}(input: $input) {
400
+ \${entityField} { \${selections} }
401
+ }
402
+ }\`;
403
+
404
+ return {
405
+ document,
406
+ variables: { input: { id: where.id, patch: data } },
407
+ };
408
+ }
409
+
410
+ export function buildDeleteDocument<TWhere extends { id: string }>(
411
+ operationName: string,
412
+ mutationField: string,
413
+ entityField: string,
414
+ where: TWhere,
415
+ inputTypeName: string
416
+ ): { document: string; variables: Record<string, unknown> } {
417
+ const document = \`mutation \${operationName}Mutation($input: \${inputTypeName}!) {
418
+ \${mutationField}(input: $input) {
419
+ \${entityField} { id }
420
+ }
421
+ }\`;
422
+
423
+ return {
424
+ document,
425
+ variables: { input: { id: where.id } },
426
+ };
427
+ }
428
+
429
+ export function buildCustomDocument<TSelect, TArgs>(
430
+ operationType: 'query' | 'mutation',
431
+ operationName: string,
432
+ fieldName: string,
433
+ select: TSelect,
434
+ args: TArgs,
435
+ variableDefinitions: Array<{ name: string; type: string }>
436
+ ): { document: string; variables: Record<string, unknown> } {
437
+ const selections = select ? buildSelections(select) : '';
438
+
439
+ const varDefs = variableDefinitions.map(v => \`$\${v.name}: \${v.type}\`);
440
+ const fieldArgs = variableDefinitions.map(v => \`\${v.name}: $\${v.name}\`);
441
+
442
+ const varDefsStr = varDefs.length > 0 ? \`(\${varDefs.join(', ')})\` : '';
443
+ const fieldArgsStr = fieldArgs.length > 0 ? \`(\${fieldArgs.join(', ')})\` : '';
444
+ const selectionsBlock = selections ? \` { \${selections} }\` : '';
445
+
446
+ const opType = operationType === 'query' ? 'query' : 'mutation';
447
+ const document = \`\${opType} \${operationName}\${varDefsStr} {
448
+ \${fieldName}\${fieldArgsStr}\${selectionsBlock}
449
+ }\`;
450
+
451
+ return { document, variables: (args ?? {}) as Record<string, unknown> };
452
+ }
453
+ `;
454
+ return {
455
+ fileName: 'query-builder.ts',
456
+ content,
457
+ };
458
+ }
459
+ /**
460
+ * Generate the select-types.ts file
461
+ */
462
+ export function generateSelectTypesFile() {
463
+ const content = `/**
464
+ * Type utilities for select inference
465
+ * @generated by @constructive-io/graphql-codegen
466
+ * DO NOT EDIT - changes will be overwritten
467
+ */
468
+
469
+ export interface ConnectionResult<T> {
470
+ nodes: T[];
471
+ totalCount: number;
472
+ pageInfo: PageInfo;
473
+ }
474
+
475
+ export interface PageInfo {
476
+ hasNextPage: boolean;
477
+ hasPreviousPage: boolean;
478
+ startCursor?: string | null;
479
+ endCursor?: string | null;
480
+ }
481
+
482
+ export interface FindManyArgs<TSelect, TWhere, TOrderBy> {
483
+ select?: TSelect;
484
+ where?: TWhere;
485
+ orderBy?: TOrderBy[];
486
+ first?: number;
487
+ last?: number;
488
+ after?: string;
489
+ before?: string;
490
+ offset?: number;
491
+ }
492
+
493
+ export interface FindFirstArgs<TSelect, TWhere> {
494
+ select?: TSelect;
495
+ where?: TWhere;
496
+ }
497
+
498
+ export interface CreateArgs<TSelect, TData> {
499
+ data: TData;
500
+ select?: TSelect;
501
+ }
502
+
503
+ export interface UpdateArgs<TSelect, TWhere, TData> {
504
+ where: TWhere;
505
+ data: TData;
506
+ select?: TSelect;
507
+ }
508
+
509
+ export interface DeleteArgs<TWhere> {
510
+ where: TWhere;
511
+ }
512
+
513
+ /**
514
+ * Infer result type from select configuration
515
+ */
516
+ export type InferSelectResult<TEntity, TSelect> = TSelect extends undefined
517
+ ? TEntity
518
+ : {
519
+ [K in keyof TSelect as TSelect[K] extends false | undefined ? never : K]: TSelect[K] extends true
520
+ ? K extends keyof TEntity
521
+ ? TEntity[K]
522
+ : never
523
+ : TSelect[K] extends { select: infer NestedSelect }
524
+ ? K extends keyof TEntity
525
+ ? NonNullable<TEntity[K]> extends ConnectionResult<infer NodeType>
526
+ ? ConnectionResult<InferSelectResult<NodeType, NestedSelect>>
527
+ : InferSelectResult<NonNullable<TEntity[K]>, NestedSelect> | (null extends TEntity[K] ? null : never)
528
+ : never
529
+ : K extends keyof TEntity
530
+ ? TEntity[K]
531
+ : never;
532
+ };
533
+ `;
534
+ return {
535
+ fileName: 'select-types.ts',
536
+ content,
537
+ };
538
+ }
539
+ /**
540
+ * Generate the main index.ts with createClient factory
541
+ */
542
+ export function generateCreateClientFile(tables, hasCustomQueries, hasCustomMutations) {
543
+ const project = createProject();
544
+ const sourceFile = createSourceFile(project, 'index.ts');
545
+ // Add file header
546
+ sourceFile.insertText(0, createFileHeader('ORM Client - createClient factory') + '\n\n');
547
+ // Add imports
548
+ const imports = [
549
+ createImport({
550
+ moduleSpecifier: './client',
551
+ namedImports: ['OrmClient'],
552
+ typeOnlyNamedImports: ['OrmClientConfig'],
553
+ }),
554
+ ];
555
+ // Import models
556
+ for (const table of tables) {
557
+ const { typeName } = getTableNames(table);
558
+ const modelName = `${typeName}Model`;
559
+ const fileName = lcFirst(typeName);
560
+ imports.push(createImport({
561
+ moduleSpecifier: `./models/${fileName}`,
562
+ namedImports: [modelName],
563
+ }));
564
+ }
565
+ // Import custom operations
566
+ if (hasCustomQueries) {
567
+ imports.push(createImport({
568
+ moduleSpecifier: './query',
569
+ namedImports: ['createQueryOperations'],
570
+ }));
571
+ }
572
+ if (hasCustomMutations) {
573
+ imports.push(createImport({
574
+ moduleSpecifier: './mutation',
575
+ namedImports: ['createMutationOperations'],
576
+ }));
577
+ }
578
+ sourceFile.addImportDeclarations(imports);
579
+ // Re-export types and classes
580
+ sourceFile.addStatements('\n// Re-export types and classes');
581
+ sourceFile.addStatements("export type { OrmClientConfig, QueryResult, GraphQLError } from './client';");
582
+ sourceFile.addStatements("export { GraphQLRequestError } from './client';");
583
+ sourceFile.addStatements("export { QueryBuilder } from './query-builder';");
584
+ sourceFile.addStatements("export * from './select-types';");
585
+ // Generate createClient function
586
+ sourceFile.addStatements('\n// ============================================================================');
587
+ sourceFile.addStatements('// Client Factory');
588
+ sourceFile.addStatements('// ============================================================================\n');
589
+ // Build the return object
590
+ const modelEntries = tables.map((table) => {
591
+ const { typeName, singularName } = getTableNames(table);
592
+ return `${singularName}: new ${typeName}Model(client)`;
593
+ });
594
+ let returnObject = modelEntries.join(',\n ');
595
+ if (hasCustomQueries) {
596
+ returnObject += ',\n query: createQueryOperations(client)';
597
+ }
598
+ if (hasCustomMutations) {
599
+ returnObject += ',\n mutation: createMutationOperations(client)';
600
+ }
601
+ sourceFile.addFunction({
602
+ name: 'createClient',
603
+ isExported: true,
604
+ parameters: [{ name: 'config', type: 'OrmClientConfig' }],
605
+ statements: `const client = new OrmClient(config);
606
+
607
+ return {
608
+ ${returnObject},
609
+ };`,
610
+ docs: [
611
+ {
612
+ description: `Create an ORM client instance
613
+
614
+ @example
615
+ \`\`\`typescript
616
+ const db = createClient({
617
+ endpoint: 'https://api.example.com/graphql',
618
+ headers: { Authorization: 'Bearer token' },
619
+ });
620
+
621
+ // Query users
622
+ const users = await db.user.findMany({
623
+ select: { id: true, name: true },
624
+ first: 10,
625
+ }).execute();
626
+
627
+ // Create a user
628
+ const newUser = await db.user.create({
629
+ data: { name: 'John', email: 'john@example.com' },
630
+ select: { id: true },
631
+ }).execute();
632
+ \`\`\``,
633
+ },
634
+ ],
635
+ });
636
+ return {
637
+ fileName: 'index.ts',
638
+ content: getFormattedOutput(sourceFile),
639
+ };
640
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Custom operations generator for ORM client
3
+ *
4
+ * Generates db.query.* and db.mutation.* namespaces for non-table operations
5
+ * like login, register, currentUser, etc.
6
+ *
7
+ * Example output:
8
+ * ```typescript
9
+ * // query/index.ts
10
+ * export function createQueryOperations(client: OrmClient) {
11
+ * return {
12
+ * currentUser: (args?: { select?: CurrentUserSelect }) =>
13
+ * new QueryBuilder({ ... }),
14
+ * };
15
+ * }
16
+ * ```
17
+ */
18
+ import type { CleanOperation } from '../../../types/schema';
19
+ export interface GeneratedCustomOpsFile {
20
+ fileName: string;
21
+ content: string;
22
+ }
23
+ /**
24
+ * Generate the query/index.ts file for custom query operations
25
+ */
26
+ export declare function generateCustomQueryOpsFile(operations: CleanOperation[]): GeneratedCustomOpsFile;
27
+ /**
28
+ * Generate the mutation/index.ts file for custom mutation operations
29
+ */
30
+ export declare function generateCustomMutationOpsFile(operations: CleanOperation[]): GeneratedCustomOpsFile;