@constructive-io/graphql-codegen 2.23.3 → 2.24.1

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