@constructive-io/graphql-codegen 2.23.3 → 2.24.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 (86) hide show
  1. package/README.md +147 -2
  2. package/cli/codegen/babel-ast.d.ts +46 -0
  3. package/cli/codegen/babel-ast.js +145 -0
  4. package/cli/codegen/barrel.d.ts +7 -2
  5. package/cli/codegen/barrel.js +159 -97
  6. package/cli/codegen/client.js +61 -0
  7. package/cli/codegen/custom-mutations.d.ts +2 -12
  8. package/cli/codegen/custom-mutations.js +116 -124
  9. package/cli/codegen/custom-queries.d.ts +2 -10
  10. package/cli/codegen/custom-queries.js +246 -335
  11. package/cli/codegen/index.d.ts +3 -0
  12. package/cli/codegen/index.js +72 -3
  13. package/cli/codegen/invalidation.d.ts +20 -0
  14. package/cli/codegen/invalidation.js +327 -0
  15. package/cli/codegen/mutation-keys.d.ts +24 -0
  16. package/cli/codegen/mutation-keys.js +247 -0
  17. package/cli/codegen/mutations.d.ts +3 -19
  18. package/cli/codegen/mutations.js +372 -383
  19. package/cli/codegen/orm/barrel.d.ts +1 -1
  20. package/cli/codegen/orm/barrel.js +42 -10
  21. package/cli/codegen/orm/client-generator.d.ts +1 -19
  22. package/cli/codegen/orm/client-generator.js +108 -77
  23. package/cli/codegen/orm/custom-ops-generator.d.ts +1 -12
  24. package/cli/codegen/orm/custom-ops-generator.js +192 -235
  25. package/cli/codegen/orm/input-types-generator.d.ts +13 -1
  26. package/cli/codegen/orm/input-types-generator.js +403 -147
  27. package/cli/codegen/orm/model-generator.d.ts +1 -19
  28. package/cli/codegen/orm/model-generator.js +229 -234
  29. package/cli/codegen/queries.d.ts +3 -11
  30. package/cli/codegen/queries.js +582 -389
  31. package/cli/codegen/query-keys.d.ts +15 -0
  32. package/cli/codegen/query-keys.js +477 -0
  33. package/cli/codegen/scalars.js +1 -0
  34. package/cli/codegen/schema-types-generator.d.ts +15 -10
  35. package/cli/codegen/schema-types-generator.js +87 -175
  36. package/cli/codegen/type-resolver.d.ts +1 -30
  37. package/cli/codegen/type-resolver.js +0 -53
  38. package/cli/codegen/types.d.ts +1 -1
  39. package/cli/codegen/types.js +76 -21
  40. package/esm/cli/codegen/babel-ast.d.ts +46 -0
  41. package/esm/cli/codegen/babel-ast.js +97 -0
  42. package/esm/cli/codegen/barrel.d.ts +7 -2
  43. package/esm/cli/codegen/barrel.js +126 -97
  44. package/esm/cli/codegen/client.js +61 -0
  45. package/esm/cli/codegen/custom-mutations.d.ts +2 -12
  46. package/esm/cli/codegen/custom-mutations.js +83 -124
  47. package/esm/cli/codegen/custom-queries.d.ts +2 -10
  48. package/esm/cli/codegen/custom-queries.js +214 -336
  49. package/esm/cli/codegen/index.d.ts +3 -0
  50. package/esm/cli/codegen/index.js +68 -2
  51. package/esm/cli/codegen/invalidation.d.ts +20 -0
  52. package/esm/cli/codegen/invalidation.js +291 -0
  53. package/esm/cli/codegen/mutation-keys.d.ts +24 -0
  54. package/esm/cli/codegen/mutation-keys.js +211 -0
  55. package/esm/cli/codegen/mutations.d.ts +3 -19
  56. package/esm/cli/codegen/mutations.js +340 -384
  57. package/esm/cli/codegen/orm/barrel.d.ts +1 -1
  58. package/esm/cli/codegen/orm/barrel.js +10 -11
  59. package/esm/cli/codegen/orm/client-generator.d.ts +1 -19
  60. package/esm/cli/codegen/orm/client-generator.js +76 -78
  61. package/esm/cli/codegen/orm/custom-ops-generator.d.ts +1 -12
  62. package/esm/cli/codegen/orm/custom-ops-generator.js +160 -236
  63. package/esm/cli/codegen/orm/input-types-generator.d.ts +13 -1
  64. package/esm/cli/codegen/orm/input-types-generator.js +371 -148
  65. package/esm/cli/codegen/orm/model-generator.d.ts +1 -19
  66. package/esm/cli/codegen/orm/model-generator.js +197 -235
  67. package/esm/cli/codegen/queries.d.ts +3 -11
  68. package/esm/cli/codegen/queries.js +550 -390
  69. package/esm/cli/codegen/query-keys.d.ts +15 -0
  70. package/esm/cli/codegen/query-keys.js +441 -0
  71. package/esm/cli/codegen/scalars.js +1 -0
  72. package/esm/cli/codegen/schema-types-generator.d.ts +15 -10
  73. package/esm/cli/codegen/schema-types-generator.js +54 -175
  74. package/esm/cli/codegen/type-resolver.d.ts +1 -30
  75. package/esm/cli/codegen/type-resolver.js +0 -49
  76. package/esm/cli/codegen/types.d.ts +1 -1
  77. package/esm/cli/codegen/types.js +44 -22
  78. package/esm/types/config.d.ts +75 -0
  79. package/esm/types/config.js +18 -0
  80. package/package.json +6 -4
  81. package/types/config.d.ts +75 -0
  82. package/types/config.js +19 -1
  83. package/cli/codegen/ts-ast.d.ts +0 -124
  84. package/cli/codegen/ts-ast.js +0 -280
  85. package/esm/cli/codegen/ts-ast.d.ts +0 -124
  86. package/esm/cli/codegen/ts-ast.js +0 -260
@@ -0,0 +1,15 @@
1
+ import type { CleanTable, CleanOperation } from '../../types/schema';
2
+ import type { ResolvedQueryKeyConfig } from '../../types/config';
3
+ export interface QueryKeyGeneratorOptions {
4
+ tables: CleanTable[];
5
+ customQueries: CleanOperation[];
6
+ config: ResolvedQueryKeyConfig;
7
+ }
8
+ export interface GeneratedQueryKeysFile {
9
+ fileName: string;
10
+ content: string;
11
+ }
12
+ /**
13
+ * Generate the complete query-keys.ts file
14
+ */
15
+ export declare function generateQueryKeysFile(options: QueryKeyGeneratorOptions): GeneratedQueryKeysFile;
@@ -0,0 +1,441 @@
1
+ /**
2
+ * Query key factory generator
3
+ *
4
+ * Generates centralized query keys following the lukemorales query-key-factory pattern.
5
+ * Supports hierarchical scoped keys for parent-child entity relationships.
6
+ *
7
+ * Uses Babel AST for code generation - no string concatenation.
8
+ *
9
+ * @see https://tanstack.com/query/docs/framework/react/community/lukemorales-query-key-factory
10
+ */
11
+ import * as t from '@babel/types';
12
+ import { getTableNames, getGeneratedFileHeader, ucFirst, lcFirst } from './utils';
13
+ import { generateCode, addJSDocComment, asConst, constArray, typedParam, keyofTypeof, } from './babel-ast';
14
+ /**
15
+ * Get all ancestor entities for a given entity based on relationships
16
+ */
17
+ function getAncestors(entityName, relationships) {
18
+ const relationship = relationships[entityName.toLowerCase()];
19
+ if (!relationship)
20
+ return [];
21
+ if (relationship.ancestors && relationship.ancestors.length > 0) {
22
+ return relationship.ancestors;
23
+ }
24
+ const ancestors = [];
25
+ let current = relationship.parent;
26
+ while (current) {
27
+ ancestors.push(current);
28
+ const parentRel = relationships[current.toLowerCase()];
29
+ current = parentRel?.parent ?? null;
30
+ }
31
+ return ancestors;
32
+ }
33
+ /**
34
+ * Generate scope type declaration for an entity
35
+ */
36
+ function generateScopeTypeDeclaration(entityName, relationships) {
37
+ const relationship = relationships[entityName.toLowerCase()];
38
+ if (!relationship)
39
+ return null;
40
+ const ancestors = getAncestors(entityName, relationships);
41
+ const allParents = [relationship.parent, ...ancestors];
42
+ const typeName = `${ucFirst(entityName)}Scope`;
43
+ const members = [];
44
+ for (const parent of allParents) {
45
+ const rel = relationships[entityName.toLowerCase()];
46
+ let fkField = `${lcFirst(parent)}Id`;
47
+ if (rel && rel.parent === parent) {
48
+ fkField = rel.foreignKey;
49
+ }
50
+ else {
51
+ const directRel = Object.entries(relationships).find(([, r]) => r.parent === parent);
52
+ if (directRel) {
53
+ fkField = directRel[1].foreignKey;
54
+ }
55
+ }
56
+ const signature = t.tsPropertySignature(t.identifier(fkField), t.tsTypeAnnotation(t.tsStringKeyword()));
57
+ signature.optional = true;
58
+ members.push(signature);
59
+ }
60
+ return t.exportNamedDeclaration(t.tsTypeAliasDeclaration(t.identifier(typeName), null, t.tsTypeLiteral(members)));
61
+ }
62
+ /**
63
+ * Build the 'all' property: all: ['entityKey'] as const
64
+ */
65
+ function buildAllProperty(entityKey, singularName) {
66
+ const prop = t.objectProperty(t.identifier('all'), constArray([t.stringLiteral(entityKey)]));
67
+ addJSDocComment(prop, [`All ${singularName} queries`]);
68
+ return prop;
69
+ }
70
+ /**
71
+ * Build a byParent property for scoped keys
72
+ */
73
+ function buildByParentProperty(entityKey, typeName, parent, fkField) {
74
+ const parentUpper = ucFirst(parent);
75
+ const parentLower = lcFirst(parent);
76
+ const arrowFn = t.arrowFunctionExpression([typedParam(fkField, t.tsStringKeyword())], constArray([
77
+ t.stringLiteral(entityKey),
78
+ t.objectExpression([
79
+ t.objectProperty(t.identifier(fkField), t.identifier(fkField), false, true)
80
+ ])
81
+ ]));
82
+ const prop = t.objectProperty(t.identifier(`by${parentUpper}`), arrowFn);
83
+ addJSDocComment(prop, [`${typeName} queries scoped to a specific ${parentLower}`]);
84
+ return prop;
85
+ }
86
+ /**
87
+ * Build the scoped helper function property
88
+ */
89
+ function buildScopedProperty(keysName, typeName, relationship, ancestors) {
90
+ const scopeTypeName = `${typeName}Scope`;
91
+ const scopeParam = typedParam('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true);
92
+ const statements = [];
93
+ if (relationship.parent) {
94
+ statements.push(t.ifStatement(t.optionalMemberExpression(t.identifier('scope'), t.identifier(relationship.foreignKey), false, true), t.blockStatement([
95
+ t.returnStatement(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier(`by${ucFirst(relationship.parent)}`)), [t.memberExpression(t.identifier('scope'), t.identifier(relationship.foreignKey))]))
96
+ ])));
97
+ }
98
+ for (const ancestor of ancestors) {
99
+ const ancestorLower = lcFirst(ancestor);
100
+ const fkField = `${ancestorLower}Id`;
101
+ statements.push(t.ifStatement(t.optionalMemberExpression(t.identifier('scope'), t.identifier(fkField), false, true), t.blockStatement([
102
+ t.returnStatement(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier(`by${ucFirst(ancestor)}`)), [t.memberExpression(t.identifier('scope'), t.identifier(fkField))]))
103
+ ])));
104
+ }
105
+ statements.push(t.returnStatement(t.memberExpression(t.identifier(keysName), t.identifier('all'))));
106
+ const arrowFn = t.arrowFunctionExpression([scopeParam], t.blockStatement(statements));
107
+ const prop = t.objectProperty(t.identifier('scoped'), arrowFn);
108
+ addJSDocComment(prop, ['Get scope-aware base key']);
109
+ return prop;
110
+ }
111
+ /**
112
+ * Build lists property (scoped version)
113
+ */
114
+ function buildScopedListsProperty(keysName, scopeTypeName) {
115
+ const scopeParam = typedParam('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true);
116
+ const arrowFn = t.arrowFunctionExpression([scopeParam], constArray([
117
+ t.spreadElement(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('scoped')), [t.identifier('scope')])),
118
+ t.stringLiteral('list')
119
+ ]));
120
+ const prop = t.objectProperty(t.identifier('lists'), arrowFn);
121
+ addJSDocComment(prop, ['List query keys (optionally scoped)']);
122
+ return prop;
123
+ }
124
+ /**
125
+ * Build list property (scoped version)
126
+ */
127
+ function buildScopedListProperty(keysName, scopeTypeName) {
128
+ const variablesParam = typedParam('variables', t.tsTypeReference(t.identifier('object')), true);
129
+ const scopeParam = typedParam('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true);
130
+ const arrowFn = t.arrowFunctionExpression([variablesParam, scopeParam], constArray([
131
+ t.spreadElement(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('lists')), [t.identifier('scope')])),
132
+ t.identifier('variables')
133
+ ]));
134
+ const prop = t.objectProperty(t.identifier('list'), arrowFn);
135
+ addJSDocComment(prop, ['List query key with variables']);
136
+ return prop;
137
+ }
138
+ /**
139
+ * Build details property (scoped version)
140
+ */
141
+ function buildScopedDetailsProperty(keysName, scopeTypeName) {
142
+ const scopeParam = typedParam('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true);
143
+ const arrowFn = t.arrowFunctionExpression([scopeParam], constArray([
144
+ t.spreadElement(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('scoped')), [t.identifier('scope')])),
145
+ t.stringLiteral('detail')
146
+ ]));
147
+ const prop = t.objectProperty(t.identifier('details'), arrowFn);
148
+ addJSDocComment(prop, ['Detail query keys (optionally scoped)']);
149
+ return prop;
150
+ }
151
+ /**
152
+ * Build detail property (scoped version)
153
+ */
154
+ function buildScopedDetailProperty(keysName, scopeTypeName) {
155
+ const idParam = typedParam('id', t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()]));
156
+ const scopeParam = typedParam('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true);
157
+ const arrowFn = t.arrowFunctionExpression([idParam, scopeParam], constArray([
158
+ t.spreadElement(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('details')), [t.identifier('scope')])),
159
+ t.identifier('id')
160
+ ]));
161
+ const prop = t.objectProperty(t.identifier('detail'), arrowFn);
162
+ addJSDocComment(prop, ['Detail query key for specific item']);
163
+ return prop;
164
+ }
165
+ /**
166
+ * Build simple (non-scoped) lists property
167
+ */
168
+ function buildSimpleListsProperty(keysName) {
169
+ const arrowFn = t.arrowFunctionExpression([], constArray([
170
+ t.spreadElement(t.memberExpression(t.identifier(keysName), t.identifier('all'))),
171
+ t.stringLiteral('list')
172
+ ]));
173
+ const prop = t.objectProperty(t.identifier('lists'), arrowFn);
174
+ addJSDocComment(prop, ['List query keys']);
175
+ return prop;
176
+ }
177
+ /**
178
+ * Build simple (non-scoped) list property
179
+ */
180
+ function buildSimpleListProperty(keysName) {
181
+ const variablesParam = typedParam('variables', t.tsTypeReference(t.identifier('object')), true);
182
+ const arrowFn = t.arrowFunctionExpression([variablesParam], constArray([
183
+ t.spreadElement(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('lists')), [])),
184
+ t.identifier('variables')
185
+ ]));
186
+ const prop = t.objectProperty(t.identifier('list'), arrowFn);
187
+ addJSDocComment(prop, ['List query key with variables']);
188
+ return prop;
189
+ }
190
+ /**
191
+ * Build simple (non-scoped) details property
192
+ */
193
+ function buildSimpleDetailsProperty(keysName) {
194
+ const arrowFn = t.arrowFunctionExpression([], constArray([
195
+ t.spreadElement(t.memberExpression(t.identifier(keysName), t.identifier('all'))),
196
+ t.stringLiteral('detail')
197
+ ]));
198
+ const prop = t.objectProperty(t.identifier('details'), arrowFn);
199
+ addJSDocComment(prop, ['Detail query keys']);
200
+ return prop;
201
+ }
202
+ /**
203
+ * Build simple (non-scoped) detail property
204
+ */
205
+ function buildSimpleDetailProperty(keysName) {
206
+ const idParam = typedParam('id', t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()]));
207
+ const arrowFn = t.arrowFunctionExpression([idParam], constArray([
208
+ t.spreadElement(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('details')), [])),
209
+ t.identifier('id')
210
+ ]));
211
+ const prop = t.objectProperty(t.identifier('detail'), arrowFn);
212
+ addJSDocComment(prop, ['Detail query key for specific item']);
213
+ return prop;
214
+ }
215
+ /**
216
+ * Generate query keys declaration for a single table entity
217
+ */
218
+ function generateEntityKeysDeclaration(table, relationships, generateScopedKeys) {
219
+ const { typeName, singularName } = getTableNames(table);
220
+ const entityKey = typeName.toLowerCase();
221
+ const keysName = `${lcFirst(typeName)}Keys`;
222
+ const relationship = relationships[entityKey];
223
+ const hasRelationship = !!relationship && generateScopedKeys;
224
+ const properties = [];
225
+ properties.push(buildAllProperty(entityKey, singularName));
226
+ if (hasRelationship) {
227
+ const ancestors = getAncestors(typeName, relationships);
228
+ const allParents = [relationship.parent, ...ancestors];
229
+ for (const parent of allParents) {
230
+ let fkField = `${lcFirst(parent)}Id`;
231
+ if (relationship.parent === parent) {
232
+ fkField = relationship.foreignKey;
233
+ }
234
+ properties.push(buildByParentProperty(entityKey, typeName, parent, fkField));
235
+ }
236
+ properties.push(buildScopedProperty(keysName, typeName, relationship, ancestors));
237
+ const scopeTypeName = `${typeName}Scope`;
238
+ properties.push(buildScopedListsProperty(keysName, scopeTypeName));
239
+ properties.push(buildScopedListProperty(keysName, scopeTypeName));
240
+ properties.push(buildScopedDetailsProperty(keysName, scopeTypeName));
241
+ properties.push(buildScopedDetailProperty(keysName, scopeTypeName));
242
+ }
243
+ else {
244
+ properties.push(buildSimpleListsProperty(keysName));
245
+ properties.push(buildSimpleListProperty(keysName));
246
+ properties.push(buildSimpleDetailsProperty(keysName));
247
+ properties.push(buildSimpleDetailProperty(keysName));
248
+ }
249
+ return t.exportNamedDeclaration(t.variableDeclaration('const', [
250
+ t.variableDeclarator(t.identifier(keysName), asConst(t.objectExpression(properties)))
251
+ ]));
252
+ }
253
+ /**
254
+ * Generate query keys declaration for custom operations
255
+ */
256
+ function generateCustomQueryKeysDeclaration(operations) {
257
+ if (operations.length === 0)
258
+ return null;
259
+ const properties = [];
260
+ for (const op of operations) {
261
+ const hasArgs = op.args.length > 0;
262
+ const hasRequiredArgs = op.args.some((arg) => arg.type.kind === 'NON_NULL');
263
+ let prop;
264
+ if (hasArgs) {
265
+ const variablesParam = typedParam('variables', t.tsTypeReference(t.identifier('object')), !hasRequiredArgs);
266
+ const arrowFn = t.arrowFunctionExpression([variablesParam], constArray([t.stringLiteral(op.name), t.identifier('variables')]));
267
+ prop = t.objectProperty(t.identifier(op.name), arrowFn);
268
+ }
269
+ else {
270
+ const arrowFn = t.arrowFunctionExpression([], constArray([t.stringLiteral(op.name)]));
271
+ prop = t.objectProperty(t.identifier(op.name), arrowFn);
272
+ }
273
+ addJSDocComment(prop, [`Query key for ${op.name}`]);
274
+ properties.push(prop);
275
+ }
276
+ return t.exportNamedDeclaration(t.variableDeclaration('const', [
277
+ t.variableDeclarator(t.identifier('customQueryKeys'), asConst(t.objectExpression(properties)))
278
+ ]));
279
+ }
280
+ /**
281
+ * Generate the unified query keys store declaration
282
+ */
283
+ function generateUnifiedStoreDeclaration(tables, hasCustomQueries) {
284
+ const properties = [];
285
+ for (const table of tables) {
286
+ const { typeName } = getTableNames(table);
287
+ const keysName = `${lcFirst(typeName)}Keys`;
288
+ properties.push(t.objectProperty(t.identifier(lcFirst(typeName)), t.identifier(keysName)));
289
+ }
290
+ if (hasCustomQueries) {
291
+ properties.push(t.objectProperty(t.identifier('custom'), t.identifier('customQueryKeys')));
292
+ }
293
+ const decl = t.exportNamedDeclaration(t.variableDeclaration('const', [
294
+ t.variableDeclarator(t.identifier('queryKeys'), asConst(t.objectExpression(properties)))
295
+ ]));
296
+ addJSDocComment(decl, [
297
+ 'Unified query key store',
298
+ '',
299
+ 'Use this for type-safe query key access across your application.',
300
+ '',
301
+ '@example',
302
+ '```ts',
303
+ '// Invalidate all user queries',
304
+ 'queryClient.invalidateQueries({ queryKey: queryKeys.user.all });',
305
+ '',
306
+ '// Invalidate user list queries',
307
+ 'queryClient.invalidateQueries({ queryKey: queryKeys.user.lists() });',
308
+ '',
309
+ '// Invalidate specific user',
310
+ 'queryClient.invalidateQueries({ queryKey: queryKeys.user.detail(userId) });',
311
+ '```',
312
+ ]);
313
+ return decl;
314
+ }
315
+ /**
316
+ * Generate the complete query-keys.ts file
317
+ */
318
+ export function generateQueryKeysFile(options) {
319
+ const { tables, customQueries, config } = options;
320
+ const { relationships, generateScopedKeys } = config;
321
+ const statements = [];
322
+ // Generate scope types for entities with relationships
323
+ if (generateScopedKeys && Object.keys(relationships).length > 0) {
324
+ const generatedScopes = new Set();
325
+ for (const table of tables) {
326
+ const { typeName } = getTableNames(table);
327
+ const scopeTypeName = `${typeName}Scope`;
328
+ if (!generatedScopes.has(scopeTypeName)) {
329
+ const scopeType = generateScopeTypeDeclaration(typeName, relationships);
330
+ if (scopeType) {
331
+ statements.push(scopeType);
332
+ generatedScopes.add(scopeTypeName);
333
+ }
334
+ }
335
+ }
336
+ }
337
+ // Generate entity keys
338
+ for (const table of tables) {
339
+ statements.push(generateEntityKeysDeclaration(table, relationships, generateScopedKeys));
340
+ }
341
+ // Generate custom query keys
342
+ const queryOperations = customQueries.filter((op) => op.kind === 'query');
343
+ const customKeysDecl = generateCustomQueryKeysDeclaration(queryOperations);
344
+ if (customKeysDecl) {
345
+ statements.push(customKeysDecl);
346
+ }
347
+ // Generate unified store
348
+ statements.push(generateUnifiedStoreDeclaration(tables, queryOperations.length > 0));
349
+ // Generate QueryKeyScope type
350
+ const scopeTypeDecl = t.exportNamedDeclaration(t.tsTypeAliasDeclaration(t.identifier('QueryKeyScope'), null, keyofTypeof('queryKeys')));
351
+ addJSDocComment(scopeTypeDecl, ['Type representing all available query key scopes']);
352
+ statements.push(scopeTypeDecl);
353
+ // Generate code from AST
354
+ const code = generateCode(statements);
355
+ // Build final content with header and section comments
356
+ const header = getGeneratedFileHeader('Centralized query key factory');
357
+ const description = `// ============================================================================
358
+ // This file provides a centralized, type-safe query key factory following
359
+ // the lukemorales query-key-factory pattern for React Query.
360
+ //
361
+ // Benefits:
362
+ // - Single source of truth for all query keys
363
+ // - Type-safe key access with autocomplete
364
+ // - Hierarchical invalidation (invalidate all 'user.*' queries)
365
+ // - Scoped keys for parent-child relationships
366
+ // ============================================================================`;
367
+ let content = `${header}
368
+
369
+ ${description}
370
+
371
+ `;
372
+ // Add scope types section if present
373
+ if (generateScopedKeys && Object.keys(relationships).length > 0) {
374
+ const hasScopes = tables.some(table => {
375
+ const { typeName } = getTableNames(table);
376
+ return !!relationships[typeName.toLowerCase()];
377
+ });
378
+ if (hasScopes) {
379
+ content += `// ============================================================================
380
+ // Scope Types
381
+ // ============================================================================
382
+
383
+ `;
384
+ }
385
+ }
386
+ // Insert section comments into the generated code
387
+ const codeLines = code.split('\n');
388
+ let inScopeTypes = generateScopedKeys && Object.keys(relationships).length > 0;
389
+ let addedEntitySection = false;
390
+ let addedCustomSection = false;
391
+ let addedUnifiedSection = false;
392
+ for (let i = 0; i < codeLines.length; i++) {
393
+ const line = codeLines[i];
394
+ // Detect transition from scope types to entity keys
395
+ if (inScopeTypes && line.startsWith('export const') && line.includes('Keys =')) {
396
+ content += `// ============================================================================
397
+ // Entity Query Keys
398
+ // ============================================================================
399
+
400
+ `;
401
+ inScopeTypes = false;
402
+ addedEntitySection = true;
403
+ }
404
+ // Detect custom query keys section
405
+ if (!addedCustomSection && line.startsWith('export const customQueryKeys')) {
406
+ content += `
407
+ // ============================================================================
408
+ // Custom Query Keys
409
+ // ============================================================================
410
+
411
+ `;
412
+ addedCustomSection = true;
413
+ }
414
+ // Detect unified store section
415
+ if (!addedUnifiedSection && line.includes('* Unified query key store')) {
416
+ content += `
417
+ // ============================================================================
418
+ // Unified Query Key Store
419
+ // ============================================================================
420
+
421
+ `;
422
+ addedUnifiedSection = true;
423
+ }
424
+ content += line + '\n';
425
+ }
426
+ // If no scope types, add entity section at the beginning
427
+ if (!addedEntitySection && !inScopeTypes) {
428
+ const firstExportIndex = content.indexOf('\nexport const');
429
+ if (firstExportIndex !== -1) {
430
+ content = content.slice(0, firstExportIndex) + `
431
+ // ============================================================================
432
+ // Entity Query Keys
433
+ // ============================================================================
434
+ ` + content.slice(firstExportIndex);
435
+ }
436
+ }
437
+ return {
438
+ fileName: 'query-keys.ts',
439
+ content,
440
+ };
441
+ }
@@ -20,6 +20,7 @@ export const SCALAR_TS_MAP = {
20
20
  // Geometry types
21
21
  GeoJSON: 'unknown',
22
22
  Geometry: 'unknown',
23
+ GeometryPoint: 'unknown',
23
24
  Point: 'unknown',
24
25
  // Interval
25
26
  Interval: 'string',
@@ -1,26 +1,31 @@
1
+ /**
2
+ * Schema types generator for React Query hooks (non-ORM mode)
3
+ *
4
+ * Generates TypeScript interfaces for:
5
+ * 1. INPUT_OBJECT types (e.g., BootstrapUserInput, LoginInput)
6
+ * 2. Payload OBJECT types (e.g., BootstrapUserPayload, LoginPayload)
7
+ * 3. ENUM types (e.g., FieldCategory, TableCategory)
8
+ *
9
+ * These types are referenced by custom mutation/query hooks but not generated
10
+ * elsewhere in non-ORM mode.
11
+ *
12
+ * Uses Babel AST for robust code generation.
13
+ */
1
14
  import type { TypeRegistry } from '../../types/schema';
15
+ import * as t from '@babel/types';
2
16
  export interface GeneratedSchemaTypesFile {
3
17
  fileName: string;
4
18
  content: string;
5
- /** List of enum type names that were generated */
6
19
  generatedEnums: string[];
7
- /** List of table entity types that are referenced */
8
20
  referencedTableTypes: string[];
9
21
  }
10
22
  export interface GenerateSchemaTypesOptions {
11
- /** The TypeRegistry containing all GraphQL types */
12
23
  typeRegistry: TypeRegistry;
13
- /** Type names that already exist in types.ts (table entity types) */
14
24
  tableTypeNames: Set<string>;
15
25
  }
16
26
  export interface PayloadTypesResult {
27
+ statements: t.Statement[];
17
28
  generatedTypes: Set<string>;
18
29
  referencedTableTypes: Set<string>;
19
30
  }
20
- /**
21
- * Generate comprehensive schema-types.ts file using ts-morph AST
22
- *
23
- * This generates all Input/Payload/Enum types from the TypeRegistry
24
- * that are needed by custom mutation/query hooks.
25
- */
26
31
  export declare function generateSchemaTypesFile(options: GenerateSchemaTypesOptions): GeneratedSchemaTypesFile;