@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,223 @@
1
+ /**
2
+ * Watch mode orchestrator
3
+ *
4
+ * Coordinates schema polling, change detection, and code regeneration
5
+ */
6
+ import { SchemaPoller } from './poller';
7
+ import { debounce } from './debounce';
8
+ import { generateCommand } from '../commands/generate';
9
+ import { generateOrmCommand } from '../commands/generate-orm';
10
+ /**
11
+ * Main watch orchestrator class
12
+ */
13
+ export class WatchOrchestrator {
14
+ options;
15
+ watchOptions;
16
+ poller;
17
+ status;
18
+ debouncedRegenerate;
19
+ isShuttingDown = false;
20
+ constructor(options) {
21
+ this.options = options;
22
+ this.watchOptions = this.buildWatchOptions();
23
+ this.poller = new SchemaPoller(this.watchOptions);
24
+ this.status = {
25
+ isRunning: false,
26
+ pollCount: 0,
27
+ regenerateCount: 0,
28
+ lastPollTime: null,
29
+ lastRegenTime: null,
30
+ lastError: null,
31
+ currentHash: null,
32
+ };
33
+ // Create debounced regenerate function
34
+ this.debouncedRegenerate = debounce(() => this.regenerate(), options.config.watch.debounce);
35
+ // Set up event handlers
36
+ this.setupEventHandlers();
37
+ }
38
+ buildWatchOptions() {
39
+ const { config, verbose, authorization } = this.options;
40
+ return {
41
+ endpoint: config.endpoint,
42
+ authorization,
43
+ headers: config.headers,
44
+ pollInterval: config.watch.pollInterval,
45
+ debounce: config.watch.debounce,
46
+ touchFile: config.watch.touchFile,
47
+ clearScreen: config.watch.clearScreen,
48
+ verbose,
49
+ };
50
+ }
51
+ setupEventHandlers() {
52
+ this.poller.on('poll-start', () => {
53
+ this.status.pollCount++;
54
+ if (this.watchOptions.verbose) {
55
+ this.log('Polling endpoint...');
56
+ }
57
+ });
58
+ this.poller.on('poll-success', (event) => {
59
+ this.status.lastPollTime = event.timestamp;
60
+ this.status.lastError = null;
61
+ if (this.watchOptions.verbose) {
62
+ this.log(`Poll complete (${event.duration}ms)`);
63
+ }
64
+ });
65
+ this.poller.on('poll-error', (event) => {
66
+ this.status.lastError = event.error ?? 'Unknown error';
67
+ this.logError(`Poll failed: ${event.error}`);
68
+ });
69
+ this.poller.on('schema-changed', (event) => {
70
+ this.status.currentHash = event.hash ?? null;
71
+ this.log(`Schema changed! (${event.duration}ms)`);
72
+ this.debouncedRegenerate();
73
+ });
74
+ this.poller.on('schema-unchanged', () => {
75
+ if (this.watchOptions.verbose) {
76
+ this.log('Schema unchanged');
77
+ }
78
+ });
79
+ }
80
+ /**
81
+ * Start watch mode
82
+ */
83
+ async start() {
84
+ if (this.status.isRunning) {
85
+ return;
86
+ }
87
+ this.status.isRunning = true;
88
+ this.setupSignalHandlers();
89
+ // Clear screen on start if configured
90
+ if (this.watchOptions.clearScreen) {
91
+ this.clearScreen();
92
+ }
93
+ this.logHeader();
94
+ // Do initial generation (always generate on start, regardless of cache)
95
+ this.log('Running initial generation...');
96
+ await this.regenerate();
97
+ // Seed the in-memory cache with current schema hash (silent, no events)
98
+ // This prevents the first poll from triggering another regeneration
99
+ await this.poller.seedCache();
100
+ // Start polling loop
101
+ this.poller.start();
102
+ this.log(`Watching for schema changes (poll interval: ${this.watchOptions.pollInterval}ms)`);
103
+ }
104
+ /**
105
+ * Stop watch mode
106
+ */
107
+ async stop() {
108
+ if (!this.status.isRunning) {
109
+ return;
110
+ }
111
+ this.isShuttingDown = true;
112
+ this.debouncedRegenerate.cancel();
113
+ this.poller.stop();
114
+ this.status.isRunning = false;
115
+ this.log('Watch mode stopped');
116
+ }
117
+ /**
118
+ * Get current watch status
119
+ */
120
+ getStatus() {
121
+ return { ...this.status };
122
+ }
123
+ async regenerate() {
124
+ if (this.isShuttingDown) {
125
+ return;
126
+ }
127
+ const startTime = Date.now();
128
+ if (this.watchOptions.clearScreen) {
129
+ this.clearScreen();
130
+ this.logHeader();
131
+ }
132
+ this.log('Regenerating...');
133
+ try {
134
+ let result;
135
+ if (this.options.generatorType === 'generate') {
136
+ result = await generateCommand({
137
+ endpoint: this.options.config.endpoint,
138
+ output: this.options.outputDir ?? this.options.config.output,
139
+ authorization: this.options.authorization,
140
+ verbose: this.watchOptions.verbose,
141
+ skipCustomOperations: this.options.skipCustomOperations,
142
+ });
143
+ }
144
+ else {
145
+ result = await generateOrmCommand({
146
+ endpoint: this.options.config.endpoint,
147
+ output: this.options.outputDir ?? this.options.config.orm?.output,
148
+ authorization: this.options.authorization,
149
+ verbose: this.watchOptions.verbose,
150
+ skipCustomOperations: this.options.skipCustomOperations,
151
+ });
152
+ }
153
+ const duration = Date.now() - startTime;
154
+ if (result.success) {
155
+ this.status.regenerateCount++;
156
+ this.status.lastRegenTime = Date.now();
157
+ this.logSuccess(`Generated in ${duration}ms`);
158
+ if (result.tables && result.tables.length > 0) {
159
+ this.log(` Tables: ${result.tables.length}`);
160
+ }
161
+ if (result.filesWritten && result.filesWritten.length > 0) {
162
+ this.log(` Files: ${result.filesWritten.length}`);
163
+ }
164
+ }
165
+ else {
166
+ this.logError(`Generation failed: ${result.message}`);
167
+ if (result.errors) {
168
+ result.errors.forEach((e) => this.logError(` ${e}`));
169
+ }
170
+ }
171
+ }
172
+ catch (err) {
173
+ const message = err instanceof Error ? err.message : 'Unknown error';
174
+ this.logError(`Generation error: ${message}`);
175
+ }
176
+ this.log(`\nWatching for changes...`);
177
+ }
178
+ setupSignalHandlers() {
179
+ const shutdown = async (signal) => {
180
+ this.log(`\nReceived ${signal}, shutting down...`);
181
+ await this.stop();
182
+ process.exit(0);
183
+ };
184
+ process.on('SIGINT', () => shutdown('SIGINT'));
185
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
186
+ }
187
+ clearScreen() {
188
+ // ANSI escape codes for clearing screen
189
+ process.stdout.write('\x1B[2J\x1B[0f');
190
+ }
191
+ logHeader() {
192
+ const generatorName = this.options.generatorType === 'generate'
193
+ ? 'React Query hooks'
194
+ : 'ORM client';
195
+ console.log(`\n${'─'.repeat(50)}`);
196
+ console.log(`graphql-codegen watch mode (${generatorName})`);
197
+ console.log(`Endpoint: ${this.options.config.endpoint}`);
198
+ console.log(`${'─'.repeat(50)}\n`);
199
+ }
200
+ log(message) {
201
+ const timestamp = new Date().toLocaleTimeString();
202
+ console.log(`[${timestamp}] ${message}`);
203
+ }
204
+ logSuccess(message) {
205
+ const timestamp = new Date().toLocaleTimeString();
206
+ console.log(`[${timestamp}] ✓ ${message}`);
207
+ }
208
+ logError(message) {
209
+ const timestamp = new Date().toLocaleTimeString();
210
+ console.error(`[${timestamp}] ✗ ${message}`);
211
+ }
212
+ }
213
+ /**
214
+ * Start watch mode for a generator
215
+ */
216
+ export async function startWatch(options) {
217
+ const orchestrator = new WatchOrchestrator(options);
218
+ await orchestrator.start();
219
+ // Keep the process alive
220
+ await new Promise(() => {
221
+ // This promise never resolves - the process will exit via signal handlers
222
+ });
223
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Schema polling logic with EventEmitter pattern
3
+ *
4
+ * Uses in-memory hash comparison for efficiency.
5
+ * No file I/O during normal polling - only touchFile is optional file write.
6
+ */
7
+ import { EventEmitter } from 'node:events';
8
+ import type { MetaQueryResponse } from '../introspect/meta-query';
9
+ import type { IntrospectionQueryResponse } from '../../types/introspection';
10
+ import { SchemaCache } from './cache';
11
+ import type { PollResult, WatchOptions } from './types';
12
+ /**
13
+ * Schema poller that periodically introspects a GraphQL endpoint
14
+ * and emits events when the schema changes.
15
+ *
16
+ * Uses in-memory caching for hash comparison - no file I/O overhead.
17
+ */
18
+ export declare class SchemaPoller extends EventEmitter {
19
+ private options;
20
+ private cache;
21
+ private pollTimer;
22
+ private isPolling;
23
+ private consecutiveErrors;
24
+ private readonly MAX_CONSECUTIVE_ERRORS;
25
+ constructor(options: WatchOptions);
26
+ /**
27
+ * Start the polling loop
28
+ */
29
+ start(): void;
30
+ /**
31
+ * Stop the polling loop
32
+ */
33
+ stop(): void;
34
+ /**
35
+ * Perform a single poll operation
36
+ */
37
+ poll(): Promise<PollResult>;
38
+ /**
39
+ * Perform a single poll without starting the loop (for initial generation)
40
+ */
41
+ pollOnce(): Promise<PollResult>;
42
+ /**
43
+ * Seed the cache with current schema hash without emitting events.
44
+ * Call this after initial generation to prevent first poll from triggering regeneration.
45
+ */
46
+ seedCache(): Promise<void>;
47
+ /**
48
+ * Get the current cached schema hash
49
+ */
50
+ getCurrentHash(): string | null;
51
+ /**
52
+ * Get the cache instance for direct access
53
+ */
54
+ getCache(): SchemaCache;
55
+ /**
56
+ * Check if the poller is currently running
57
+ */
58
+ isRunning(): boolean;
59
+ private handleError;
60
+ private createEvent;
61
+ }
62
+ /**
63
+ * Utility to compute schema hash without full poll
64
+ */
65
+ export declare function computeSchemaHash(meta: MetaQueryResponse, schema: IntrospectionQueryResponse): Promise<string>;
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Schema polling logic with EventEmitter pattern
3
+ *
4
+ * Uses in-memory hash comparison for efficiency.
5
+ * No file I/O during normal polling - only touchFile is optional file write.
6
+ */
7
+ import { EventEmitter } from 'node:events';
8
+ import { fetchMeta } from '../introspect/fetch-meta';
9
+ import { fetchSchema } from '../introspect/fetch-schema';
10
+ import { SchemaCache, touchFile } from './cache';
11
+ import { hashObject, combineHashes } from './hash';
12
+ /**
13
+ * Schema poller that periodically introspects a GraphQL endpoint
14
+ * and emits events when the schema changes.
15
+ *
16
+ * Uses in-memory caching for hash comparison - no file I/O overhead.
17
+ */
18
+ export class SchemaPoller extends EventEmitter {
19
+ options;
20
+ cache;
21
+ pollTimer = null;
22
+ isPolling = false;
23
+ consecutiveErrors = 0;
24
+ MAX_CONSECUTIVE_ERRORS = 5;
25
+ constructor(options) {
26
+ super();
27
+ this.options = options;
28
+ this.cache = new SchemaCache();
29
+ }
30
+ /**
31
+ * Start the polling loop
32
+ */
33
+ start() {
34
+ if (this.pollTimer) {
35
+ return; // Already running
36
+ }
37
+ // Do an immediate poll
38
+ this.poll();
39
+ // Set up interval
40
+ this.pollTimer = setInterval(() => {
41
+ this.poll();
42
+ }, this.options.pollInterval);
43
+ }
44
+ /**
45
+ * Stop the polling loop
46
+ */
47
+ stop() {
48
+ if (this.pollTimer) {
49
+ clearInterval(this.pollTimer);
50
+ this.pollTimer = null;
51
+ }
52
+ }
53
+ /**
54
+ * Perform a single poll operation
55
+ */
56
+ async poll() {
57
+ // Prevent concurrent polls
58
+ if (this.isPolling) {
59
+ return { success: false, changed: false, error: 'Poll already in progress' };
60
+ }
61
+ this.isPolling = true;
62
+ const startTime = Date.now();
63
+ this.emit('poll-start', this.createEvent('poll-start'));
64
+ try {
65
+ // Fetch both _meta and __schema
66
+ const [metaResult, schemaResult] = await Promise.all([
67
+ fetchMeta({
68
+ endpoint: this.options.endpoint,
69
+ authorization: this.options.authorization,
70
+ headers: this.options.headers,
71
+ timeout: 30000,
72
+ }),
73
+ fetchSchema({
74
+ endpoint: this.options.endpoint,
75
+ authorization: this.options.authorization,
76
+ headers: this.options.headers,
77
+ timeout: 30000,
78
+ }),
79
+ ]);
80
+ const duration = Date.now() - startTime;
81
+ // Check for errors
82
+ if (!metaResult.success) {
83
+ return this.handleError(`_meta fetch failed: ${metaResult.error}`, duration);
84
+ }
85
+ if (!schemaResult.success) {
86
+ return this.handleError(`__schema fetch failed: ${schemaResult.error}`, duration);
87
+ }
88
+ const meta = metaResult.data;
89
+ const schema = schemaResult.data;
90
+ // Check if schema changed
91
+ const { changed, newHash } = await this.cache.hasChanged(meta, schema);
92
+ // Reset error counter on success
93
+ this.consecutiveErrors = 0;
94
+ if (changed) {
95
+ // Update in-memory hash
96
+ this.cache.updateHash(newHash);
97
+ // Touch trigger file if configured (only file I/O in watch mode)
98
+ if (this.options.touchFile) {
99
+ touchFile(this.options.touchFile);
100
+ }
101
+ this.emit('schema-changed', this.createEvent('schema-changed', { hash: newHash, duration }));
102
+ return { success: true, changed: true, hash: newHash, meta, schema };
103
+ }
104
+ this.emit('schema-unchanged', this.createEvent('schema-unchanged', { duration }));
105
+ this.emit('poll-success', this.createEvent('poll-success', { duration }));
106
+ return { success: true, changed: false, hash: newHash, meta, schema };
107
+ }
108
+ catch (err) {
109
+ const duration = Date.now() - startTime;
110
+ const error = err instanceof Error ? err.message : 'Unknown error';
111
+ return this.handleError(error, duration);
112
+ }
113
+ finally {
114
+ this.isPolling = false;
115
+ }
116
+ }
117
+ /**
118
+ * Perform a single poll without starting the loop (for initial generation)
119
+ */
120
+ async pollOnce() {
121
+ return this.poll();
122
+ }
123
+ /**
124
+ * Seed the cache with current schema hash without emitting events.
125
+ * Call this after initial generation to prevent first poll from triggering regeneration.
126
+ */
127
+ async seedCache() {
128
+ try {
129
+ const [metaResult, schemaResult] = await Promise.all([
130
+ fetchMeta({
131
+ endpoint: this.options.endpoint,
132
+ authorization: this.options.authorization,
133
+ headers: this.options.headers,
134
+ timeout: 30000,
135
+ }),
136
+ fetchSchema({
137
+ endpoint: this.options.endpoint,
138
+ authorization: this.options.authorization,
139
+ headers: this.options.headers,
140
+ timeout: 30000,
141
+ }),
142
+ ]);
143
+ if (metaResult.success && schemaResult.success) {
144
+ const { newHash } = await this.cache.hasChanged(metaResult.data, schemaResult.data);
145
+ this.cache.updateHash(newHash);
146
+ }
147
+ }
148
+ catch {
149
+ // Silently fail - cache will be seeded on first successful poll
150
+ }
151
+ }
152
+ /**
153
+ * Get the current cached schema hash
154
+ */
155
+ getCurrentHash() {
156
+ return this.cache.getHash();
157
+ }
158
+ /**
159
+ * Get the cache instance for direct access
160
+ */
161
+ getCache() {
162
+ return this.cache;
163
+ }
164
+ /**
165
+ * Check if the poller is currently running
166
+ */
167
+ isRunning() {
168
+ return this.pollTimer !== null;
169
+ }
170
+ handleError(error, duration) {
171
+ this.consecutiveErrors++;
172
+ this.emit('poll-error', this.createEvent('poll-error', { error, duration }));
173
+ // Slow down polling after multiple consecutive errors
174
+ if (this.consecutiveErrors >= this.MAX_CONSECUTIVE_ERRORS && this.pollTimer) {
175
+ this.stop();
176
+ const newInterval = this.options.pollInterval * 2;
177
+ this.pollTimer = setInterval(() => {
178
+ this.poll();
179
+ }, newInterval);
180
+ }
181
+ return { success: false, changed: false, error };
182
+ }
183
+ createEvent(type, extra) {
184
+ return {
185
+ type,
186
+ timestamp: Date.now(),
187
+ ...extra,
188
+ };
189
+ }
190
+ }
191
+ /**
192
+ * Utility to compute schema hash without full poll
193
+ */
194
+ export async function computeSchemaHash(meta, schema) {
195
+ const metaHash = await hashObject(meta);
196
+ const schemaHash = await hashObject(schema);
197
+ return combineHashes(metaHash, schemaHash);
198
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Watch mode types
3
+ */
4
+ import type { MetaQueryResponse } from '../introspect/meta-query';
5
+ import type { IntrospectionQueryResponse } from '../../types/introspection';
6
+ /**
7
+ * Result of a schema poll operation
8
+ */
9
+ export interface PollResult {
10
+ success: boolean;
11
+ /** Whether the schema changed since last poll */
12
+ changed: boolean;
13
+ /** Error message if poll failed */
14
+ error?: string;
15
+ /** The new hash if successful */
16
+ hash?: string;
17
+ /** Meta response if successful */
18
+ meta?: MetaQueryResponse;
19
+ /** Schema response if successful */
20
+ schema?: IntrospectionQueryResponse;
21
+ }
22
+ /**
23
+ * Watch mode options passed to the watch orchestrator
24
+ */
25
+ export interface WatchOptions {
26
+ /** GraphQL endpoint URL */
27
+ endpoint: string;
28
+ /** Authorization header value */
29
+ authorization?: string;
30
+ /** Additional headers */
31
+ headers?: Record<string, string>;
32
+ /** Polling interval in ms */
33
+ pollInterval: number;
34
+ /** Debounce delay in ms */
35
+ debounce: number;
36
+ /** File to touch on schema change (optional) */
37
+ touchFile: string | null;
38
+ /** Clear terminal on regeneration */
39
+ clearScreen: boolean;
40
+ /** Verbose output */
41
+ verbose: boolean;
42
+ }
43
+ /**
44
+ * Events emitted by the SchemaPoller
45
+ */
46
+ export type PollEventType = 'poll-start' | 'poll-success' | 'poll-error' | 'schema-changed' | 'schema-unchanged';
47
+ /**
48
+ * Event handler signature for poll events
49
+ */
50
+ export type PollEventHandler = (event: PollEvent) => void;
51
+ /**
52
+ * Poll event payload
53
+ */
54
+ export interface PollEvent {
55
+ type: PollEventType;
56
+ timestamp: number;
57
+ /** Error message if type is 'poll-error' */
58
+ error?: string;
59
+ /** New hash if schema changed */
60
+ hash?: string;
61
+ /** Duration of the poll in ms */
62
+ duration?: number;
63
+ }
64
+ /**
65
+ * Generator type for watch mode
66
+ */
67
+ export type GeneratorType = 'generate' | 'generate-orm';
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Watch mode types
3
+ */
4
+ export {};
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Error handling for GraphQL operations
3
+ * Provides consistent error types and parsing for PostGraphile responses
4
+ */
5
+ export declare const DataErrorType: {
6
+ readonly NETWORK_ERROR: "NETWORK_ERROR";
7
+ readonly TIMEOUT_ERROR: "TIMEOUT_ERROR";
8
+ readonly VALIDATION_FAILED: "VALIDATION_FAILED";
9
+ readonly REQUIRED_FIELD_MISSING: "REQUIRED_FIELD_MISSING";
10
+ readonly INVALID_MUTATION_DATA: "INVALID_MUTATION_DATA";
11
+ readonly QUERY_GENERATION_FAILED: "QUERY_GENERATION_FAILED";
12
+ readonly QUERY_EXECUTION_FAILED: "QUERY_EXECUTION_FAILED";
13
+ readonly UNAUTHORIZED: "UNAUTHORIZED";
14
+ readonly FORBIDDEN: "FORBIDDEN";
15
+ readonly TABLE_NOT_FOUND: "TABLE_NOT_FOUND";
16
+ readonly BAD_REQUEST: "BAD_REQUEST";
17
+ readonly NOT_FOUND: "NOT_FOUND";
18
+ readonly GRAPHQL_ERROR: "GRAPHQL_ERROR";
19
+ readonly UNIQUE_VIOLATION: "UNIQUE_VIOLATION";
20
+ readonly FOREIGN_KEY_VIOLATION: "FOREIGN_KEY_VIOLATION";
21
+ readonly NOT_NULL_VIOLATION: "NOT_NULL_VIOLATION";
22
+ readonly CHECK_VIOLATION: "CHECK_VIOLATION";
23
+ readonly EXCLUSION_VIOLATION: "EXCLUSION_VIOLATION";
24
+ readonly UNKNOWN_ERROR: "UNKNOWN_ERROR";
25
+ };
26
+ export type DataErrorType = (typeof DataErrorType)[keyof typeof DataErrorType];
27
+ export interface DataErrorOptions {
28
+ tableName?: string;
29
+ fieldName?: string;
30
+ constraint?: string;
31
+ originalError?: Error;
32
+ code?: string;
33
+ context?: Record<string, unknown>;
34
+ }
35
+ /**
36
+ * Standard error class for data layer operations
37
+ */
38
+ export declare class DataError extends Error {
39
+ readonly type: DataErrorType;
40
+ readonly code?: string;
41
+ readonly originalError?: Error;
42
+ readonly context?: Record<string, unknown>;
43
+ readonly tableName?: string;
44
+ readonly fieldName?: string;
45
+ readonly constraint?: string;
46
+ constructor(type: DataErrorType, message: string, options?: DataErrorOptions);
47
+ getUserMessage(): string;
48
+ isRetryable(): boolean;
49
+ }
50
+ export declare const PG_ERROR_CODES: {
51
+ readonly UNIQUE_VIOLATION: "23505";
52
+ readonly FOREIGN_KEY_VIOLATION: "23503";
53
+ readonly NOT_NULL_VIOLATION: "23502";
54
+ readonly CHECK_VIOLATION: "23514";
55
+ readonly EXCLUSION_VIOLATION: "23P01";
56
+ readonly NUMERIC_VALUE_OUT_OF_RANGE: "22003";
57
+ readonly STRING_DATA_RIGHT_TRUNCATION: "22001";
58
+ readonly INVALID_TEXT_REPRESENTATION: "22P02";
59
+ readonly DATETIME_FIELD_OVERFLOW: "22008";
60
+ readonly UNDEFINED_TABLE: "42P01";
61
+ readonly UNDEFINED_COLUMN: "42703";
62
+ readonly INSUFFICIENT_PRIVILEGE: "42501";
63
+ };
64
+ export declare const createError: {
65
+ network: (originalError?: Error) => DataError;
66
+ timeout: (originalError?: Error) => DataError;
67
+ unauthorized: (message?: string) => DataError;
68
+ forbidden: (message?: string) => DataError;
69
+ badRequest: (message: string, code?: string) => DataError;
70
+ notFound: (message?: string) => DataError;
71
+ graphql: (message: string, code?: string) => DataError;
72
+ uniqueViolation: (message: string, fieldName?: string, constraint?: string) => DataError;
73
+ foreignKeyViolation: (message: string, fieldName?: string, constraint?: string) => DataError;
74
+ notNullViolation: (message: string, fieldName?: string, constraint?: string) => DataError;
75
+ unknown: (originalError: Error) => DataError;
76
+ };
77
+ export interface GraphQLError {
78
+ message: string;
79
+ extensions?: {
80
+ code?: string;
81
+ } & Record<string, unknown>;
82
+ locations?: Array<{
83
+ line: number;
84
+ column: number;
85
+ }>;
86
+ path?: Array<string | number>;
87
+ }
88
+ /**
89
+ * Parse any error into a DataError
90
+ */
91
+ export declare function parseGraphQLError(error: unknown): DataError;
92
+ /**
93
+ * Check if value is a DataError
94
+ */
95
+ export declare function isDataError(error: unknown): error is DataError;