@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
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateCustomMutationsBarrel = exports.generateCustomQueriesBarrel = exports.generateMainBarrel = exports.generateMutationsBarrel = exports.generateQueriesBarrel = exports.generateCustomMutationHook = exports.generateAllCustomMutationHooks = exports.generateCustomQueryHook = exports.generateAllCustomQueryHooks = exports.generateDeleteMutationHook = exports.generateUpdateMutationHook = exports.generateCreateMutationHook = exports.generateAllMutationHooks = exports.generateSingleQueryHook = exports.generateListQueryHook = exports.generateAllQueryHooks = exports.generateTypesFile = exports.generateClientFile = void 0;
3
+ exports.generateInvalidationFile = exports.generateMutationKeysFile = exports.generateQueryKeysFile = exports.generateCustomMutationsBarrel = exports.generateCustomQueriesBarrel = exports.generateMainBarrel = exports.generateMutationsBarrel = exports.generateQueriesBarrel = exports.generateCustomMutationHook = exports.generateAllCustomMutationHooks = exports.generateCustomQueryHook = exports.generateAllCustomQueryHooks = exports.generateDeleteMutationHook = exports.generateUpdateMutationHook = exports.generateCreateMutationHook = exports.generateAllMutationHooks = exports.generateSingleQueryHook = exports.generateListQueryHook = exports.generateAllQueryHooks = exports.generateTypesFile = exports.generateClientFile = void 0;
4
4
  exports.generateAllFiles = generateAllFiles;
5
5
  exports.generate = generate;
6
+ const config_1 = require("../../types/config");
6
7
  const client_1 = require("./client");
7
8
  const types_1 = require("./types");
8
9
  const schema_types_generator_1 = require("./schema-types-generator");
@@ -10,6 +11,9 @@ const queries_1 = require("./queries");
10
11
  const mutations_1 = require("./mutations");
11
12
  const custom_queries_1 = require("./custom-queries");
12
13
  const custom_mutations_1 = require("./custom-mutations");
14
+ const query_keys_1 = require("./query-keys");
15
+ const mutation_keys_1 = require("./mutation-keys");
16
+ const invalidation_1 = require("./invalidation");
13
17
  const barrel_1 = require("./barrel");
14
18
  const utils_1 = require("./utils");
15
19
  // ============================================================================
@@ -31,6 +35,10 @@ function generate(options) {
31
35
  const maxDepth = config.codegen.maxFieldDepth;
32
36
  const skipQueryField = config.codegen.skipQueryField;
33
37
  const reactQueryEnabled = config.reactQuery.enabled;
38
+ // Query key configuration (use defaults if not provided)
39
+ const queryKeyConfig = config.queryKeys ?? config_1.DEFAULT_QUERY_KEY_CONFIG;
40
+ const useCentralizedKeys = queryKeyConfig.generateScopedKeys;
41
+ const hasRelationships = Object.keys(queryKeyConfig.relationships).length > 0;
34
42
  // 1. Generate client.ts
35
43
  files.push({
36
44
  path: 'client.ts',
@@ -64,8 +72,53 @@ function generate(options) {
64
72
  enumsFromSchemaTypes: generatedEnumNames,
65
73
  }),
66
74
  });
75
+ // 3b. Generate centralized query keys (query-keys.ts)
76
+ let hasQueryKeys = false;
77
+ if (useCentralizedKeys) {
78
+ const queryKeysResult = (0, query_keys_1.generateQueryKeysFile)({
79
+ tables,
80
+ customQueries: customOperations?.queries ?? [],
81
+ config: queryKeyConfig,
82
+ });
83
+ files.push({
84
+ path: queryKeysResult.fileName,
85
+ content: queryKeysResult.content,
86
+ });
87
+ hasQueryKeys = true;
88
+ }
89
+ // 3c. Generate centralized mutation keys (mutation-keys.ts)
90
+ let hasMutationKeys = false;
91
+ if (useCentralizedKeys && queryKeyConfig.generateMutationKeys) {
92
+ const mutationKeysResult = (0, mutation_keys_1.generateMutationKeysFile)({
93
+ tables,
94
+ customMutations: customOperations?.mutations ?? [],
95
+ config: queryKeyConfig,
96
+ });
97
+ files.push({
98
+ path: mutationKeysResult.fileName,
99
+ content: mutationKeysResult.content,
100
+ });
101
+ hasMutationKeys = true;
102
+ }
103
+ // 3d. Generate cache invalidation helpers (invalidation.ts)
104
+ let hasInvalidation = false;
105
+ if (useCentralizedKeys && queryKeyConfig.generateCascadeHelpers) {
106
+ const invalidationResult = (0, invalidation_1.generateInvalidationFile)({
107
+ tables,
108
+ config: queryKeyConfig,
109
+ });
110
+ files.push({
111
+ path: invalidationResult.fileName,
112
+ content: invalidationResult.content,
113
+ });
114
+ hasInvalidation = true;
115
+ }
67
116
  // 4. Generate table-based query hooks (queries/*.ts)
68
- const queryHooks = (0, queries_1.generateAllQueryHooks)(tables, { reactQueryEnabled });
117
+ const queryHooks = (0, queries_1.generateAllQueryHooks)(tables, {
118
+ reactQueryEnabled,
119
+ useCentralizedKeys,
120
+ hasRelationships,
121
+ });
69
122
  for (const hook of queryHooks) {
70
123
  files.push({
71
124
  path: `queries/${hook.fileName}`,
@@ -82,6 +135,7 @@ function generate(options) {
82
135
  skipQueryField,
83
136
  reactQueryEnabled,
84
137
  tableTypeNames,
138
+ useCentralizedKeys,
85
139
  });
86
140
  for (const hook of customQueryHooks) {
87
141
  files.push({
@@ -101,6 +155,8 @@ function generate(options) {
101
155
  const mutationHooks = (0, mutations_1.generateAllMutationHooks)(tables, {
102
156
  reactQueryEnabled,
103
157
  enumsFromSchemaTypes: generatedEnumNames,
158
+ useCentralizedKeys,
159
+ hasRelationships,
104
160
  });
105
161
  for (const hook of mutationHooks) {
106
162
  files.push({
@@ -118,6 +174,7 @@ function generate(options) {
118
174
  skipQueryField,
119
175
  reactQueryEnabled,
120
176
  tableTypeNames,
177
+ useCentralizedKeys,
121
178
  });
122
179
  for (const hook of customMutationHooks) {
123
180
  files.push({
@@ -140,7 +197,13 @@ function generate(options) {
140
197
  // 9. Generate main index.ts barrel (with schema-types if present)
141
198
  files.push({
142
199
  path: 'index.ts',
143
- content: (0, barrel_1.generateMainBarrel)(tables, { hasSchemaTypes, hasMutations }),
200
+ content: (0, barrel_1.generateMainBarrel)(tables, {
201
+ hasSchemaTypes,
202
+ hasMutations,
203
+ hasQueryKeys,
204
+ hasMutationKeys,
205
+ hasInvalidation,
206
+ }),
144
207
  });
145
208
  return {
146
209
  files,
@@ -182,3 +245,9 @@ Object.defineProperty(exports, "generateMutationsBarrel", { enumerable: true, ge
182
245
  Object.defineProperty(exports, "generateMainBarrel", { enumerable: true, get: function () { return barrel_2.generateMainBarrel; } });
183
246
  Object.defineProperty(exports, "generateCustomQueriesBarrel", { enumerable: true, get: function () { return barrel_2.generateCustomQueriesBarrel; } });
184
247
  Object.defineProperty(exports, "generateCustomMutationsBarrel", { enumerable: true, get: function () { return barrel_2.generateCustomMutationsBarrel; } });
248
+ var query_keys_2 = require("./query-keys");
249
+ Object.defineProperty(exports, "generateQueryKeysFile", { enumerable: true, get: function () { return query_keys_2.generateQueryKeysFile; } });
250
+ var mutation_keys_2 = require("./mutation-keys");
251
+ Object.defineProperty(exports, "generateMutationKeysFile", { enumerable: true, get: function () { return mutation_keys_2.generateMutationKeysFile; } });
252
+ var invalidation_2 = require("./invalidation");
253
+ Object.defineProperty(exports, "generateInvalidationFile", { enumerable: true, get: function () { return invalidation_2.generateInvalidationFile; } });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Cache invalidation helpers generator
3
+ *
4
+ * Generates type-safe cache invalidation utilities with cascade support
5
+ * for parent-child entity relationships.
6
+ */
7
+ import type { CleanTable } from '../../types/schema';
8
+ import type { ResolvedQueryKeyConfig } from '../../types/config';
9
+ export interface InvalidationGeneratorOptions {
10
+ tables: CleanTable[];
11
+ config: ResolvedQueryKeyConfig;
12
+ }
13
+ export interface GeneratedInvalidationFile {
14
+ fileName: string;
15
+ content: string;
16
+ }
17
+ /**
18
+ * Generate the complete invalidation.ts file
19
+ */
20
+ export declare function generateInvalidationFile(options: InvalidationGeneratorOptions): GeneratedInvalidationFile;
@@ -0,0 +1,327 @@
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.generateInvalidationFile = generateInvalidationFile;
37
+ const utils_1 = require("./utils");
38
+ const t = __importStar(require("@babel/types"));
39
+ const babel_ast_1 = require("./babel-ast");
40
+ /**
41
+ * Build a map of parent -> children for cascade invalidation
42
+ */
43
+ function buildChildrenMap(relationships) {
44
+ const childrenMap = new Map();
45
+ for (const [child, rel] of Object.entries(relationships)) {
46
+ const parent = rel.parent.toLowerCase();
47
+ if (!childrenMap.has(parent)) {
48
+ childrenMap.set(parent, []);
49
+ }
50
+ childrenMap.get(parent).push(child);
51
+ }
52
+ return childrenMap;
53
+ }
54
+ /**
55
+ * Get all descendants (children, grandchildren, etc.) of an entity
56
+ */
57
+ function getAllDescendants(entity, childrenMap) {
58
+ const descendants = [];
59
+ const queue = [entity.toLowerCase()];
60
+ while (queue.length > 0) {
61
+ const current = queue.shift();
62
+ const children = childrenMap.get(current) ?? [];
63
+ for (const child of children) {
64
+ descendants.push(child);
65
+ queue.push(child);
66
+ }
67
+ }
68
+ return descendants;
69
+ }
70
+ /**
71
+ * Build the invalidate object property for a single entity
72
+ */
73
+ function buildEntityInvalidateProperty(table, relationships, childrenMap, allTables) {
74
+ const { typeName, singularName } = (0, utils_1.getTableNames)(table);
75
+ const entityKey = typeName.toLowerCase();
76
+ const keysName = `${(0, utils_1.lcFirst)(typeName)}Keys`;
77
+ const descendants = getAllDescendants(entityKey, childrenMap);
78
+ const hasDescendants = descendants.length > 0;
79
+ const relationship = relationships[entityKey];
80
+ const hasParent = !!relationship;
81
+ const innerProperties = [];
82
+ // Helper to create QueryClient type reference
83
+ const queryClientTypeRef = () => t.tsTypeReference(t.identifier('QueryClient'));
84
+ const stringOrNumberType = () => t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()]);
85
+ // Helper to create queryClient.invalidateQueries({ queryKey: ... })
86
+ const invalidateCall = (queryKeyExpr) => t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), [t.objectExpression([t.objectProperty(t.identifier('queryKey'), queryKeyExpr)])]);
87
+ // all property
88
+ const allArrowFn = t.arrowFunctionExpression([(0, babel_ast_1.typedParam)('queryClient', queryClientTypeRef())], invalidateCall(t.memberExpression(t.identifier(keysName), t.identifier('all'))));
89
+ const allProp = t.objectProperty(t.identifier('all'), allArrowFn);
90
+ (0, babel_ast_1.addJSDocComment)(allProp, [`Invalidate all ${singularName} queries`]);
91
+ innerProperties.push(allProp);
92
+ // lists property
93
+ let listsProp;
94
+ if (hasParent) {
95
+ const scopeTypeName = `${typeName}Scope`;
96
+ const scopeParam = (0, babel_ast_1.typedParam)('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true);
97
+ const listsArrowFn = t.arrowFunctionExpression([(0, babel_ast_1.typedParam)('queryClient', queryClientTypeRef()), scopeParam], invalidateCall(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('lists')), [t.identifier('scope')])));
98
+ listsProp = t.objectProperty(t.identifier('lists'), listsArrowFn);
99
+ }
100
+ else {
101
+ const listsArrowFn = t.arrowFunctionExpression([(0, babel_ast_1.typedParam)('queryClient', queryClientTypeRef())], invalidateCall(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('lists')), [])));
102
+ listsProp = t.objectProperty(t.identifier('lists'), listsArrowFn);
103
+ }
104
+ (0, babel_ast_1.addJSDocComment)(listsProp, [`Invalidate ${singularName} list queries`]);
105
+ innerProperties.push(listsProp);
106
+ // detail property
107
+ let detailProp;
108
+ if (hasParent) {
109
+ const scopeTypeName = `${typeName}Scope`;
110
+ const scopeParam = (0, babel_ast_1.typedParam)('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true);
111
+ const detailArrowFn = t.arrowFunctionExpression([(0, babel_ast_1.typedParam)('queryClient', queryClientTypeRef()), (0, babel_ast_1.typedParam)('id', stringOrNumberType()), scopeParam], invalidateCall(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [t.identifier('id'), t.identifier('scope')])));
112
+ detailProp = t.objectProperty(t.identifier('detail'), detailArrowFn);
113
+ }
114
+ else {
115
+ const detailArrowFn = t.arrowFunctionExpression([(0, babel_ast_1.typedParam)('queryClient', queryClientTypeRef()), (0, babel_ast_1.typedParam)('id', stringOrNumberType())], invalidateCall(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [t.identifier('id')])));
116
+ detailProp = t.objectProperty(t.identifier('detail'), detailArrowFn);
117
+ }
118
+ (0, babel_ast_1.addJSDocComment)(detailProp, [`Invalidate a specific ${singularName}`]);
119
+ innerProperties.push(detailProp);
120
+ // withChildren property (cascade)
121
+ if (hasDescendants) {
122
+ const cascadeStatements = [];
123
+ // Comment: Invalidate this entity
124
+ const selfDetailStmt = t.expressionStatement(invalidateCall(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [t.identifier('id')])));
125
+ (0, babel_ast_1.addLineComment)(selfDetailStmt, `Invalidate this ${singularName}`);
126
+ cascadeStatements.push(selfDetailStmt);
127
+ cascadeStatements.push(t.expressionStatement(invalidateCall(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('lists')), []))));
128
+ // Comment: Cascade to child entities
129
+ let firstCascade = true;
130
+ for (const descendant of descendants) {
131
+ const descendantTable = allTables.find((tbl) => (0, utils_1.getTableNames)(tbl).typeName.toLowerCase() === descendant);
132
+ if (descendantTable) {
133
+ const { typeName: descTypeName } = (0, utils_1.getTableNames)(descendantTable);
134
+ const descRel = relationships[descendant];
135
+ if (descRel) {
136
+ let fkField = null;
137
+ if (descRel.parent.toLowerCase() === entityKey) {
138
+ fkField = descRel.foreignKey;
139
+ }
140
+ else if (descRel.ancestors?.includes(typeName.toLowerCase())) {
141
+ fkField = `${(0, utils_1.lcFirst)(typeName)}Id`;
142
+ }
143
+ const descKeysName = `${(0, utils_1.lcFirst)(descTypeName)}Keys`;
144
+ let cascadeStmt;
145
+ if (fkField) {
146
+ cascadeStmt = t.expressionStatement(invalidateCall(t.callExpression(t.memberExpression(t.identifier(descKeysName), t.identifier(`by${(0, utils_1.ucFirst)(typeName)}`)), [t.identifier('id')])));
147
+ }
148
+ else {
149
+ cascadeStmt = t.expressionStatement(invalidateCall(t.memberExpression(t.identifier(descKeysName), t.identifier('all'))));
150
+ }
151
+ if (firstCascade) {
152
+ (0, babel_ast_1.addLineComment)(cascadeStmt, 'Cascade to child entities');
153
+ firstCascade = false;
154
+ }
155
+ cascadeStatements.push(cascadeStmt);
156
+ }
157
+ }
158
+ }
159
+ const withChildrenArrowFn = t.arrowFunctionExpression([(0, babel_ast_1.typedParam)('queryClient', queryClientTypeRef()), (0, babel_ast_1.typedParam)('id', stringOrNumberType())], t.blockStatement(cascadeStatements));
160
+ const withChildrenProp = t.objectProperty(t.identifier('withChildren'), withChildrenArrowFn);
161
+ (0, babel_ast_1.addJSDocComment)(withChildrenProp, [
162
+ `Invalidate ${singularName} and all child entities`,
163
+ `Cascades to: ${descendants.join(', ')}`,
164
+ ]);
165
+ innerProperties.push(withChildrenProp);
166
+ }
167
+ const entityProp = t.objectProperty(t.identifier(singularName), t.objectExpression(innerProperties));
168
+ (0, babel_ast_1.addJSDocComment)(entityProp, [`Invalidate ${singularName} queries`]);
169
+ return entityProp;
170
+ }
171
+ /**
172
+ * Build the remove object property for a single entity
173
+ */
174
+ function buildEntityRemoveProperty(table, relationships) {
175
+ const { typeName, singularName } = (0, utils_1.getTableNames)(table);
176
+ const keysName = `${(0, utils_1.lcFirst)(typeName)}Keys`;
177
+ const relationship = relationships[typeName.toLowerCase()];
178
+ // Helper types
179
+ const queryClientTypeRef = () => t.tsTypeReference(t.identifier('QueryClient'));
180
+ const stringOrNumberType = () => t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()]);
181
+ // Helper to create queryClient.removeQueries({ queryKey: ... })
182
+ const removeCall = (queryKeyExpr) => t.callExpression(t.memberExpression(t.identifier('queryClient'), t.identifier('removeQueries')), [t.objectExpression([t.objectProperty(t.identifier('queryKey'), queryKeyExpr)])]);
183
+ let removeProp;
184
+ if (relationship) {
185
+ const scopeTypeName = `${typeName}Scope`;
186
+ const scopeParam = (0, babel_ast_1.typedParam)('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true);
187
+ const removeArrowFn = t.arrowFunctionExpression([(0, babel_ast_1.typedParam)('queryClient', queryClientTypeRef()), (0, babel_ast_1.typedParam)('id', stringOrNumberType()), scopeParam], t.blockStatement([
188
+ t.expressionStatement(removeCall(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [t.identifier('id'), t.identifier('scope')])))
189
+ ]));
190
+ removeProp = t.objectProperty(t.identifier(singularName), removeArrowFn);
191
+ }
192
+ else {
193
+ const removeArrowFn = t.arrowFunctionExpression([(0, babel_ast_1.typedParam)('queryClient', queryClientTypeRef()), (0, babel_ast_1.typedParam)('id', stringOrNumberType())], t.blockStatement([
194
+ t.expressionStatement(removeCall(t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [t.identifier('id')])))
195
+ ]));
196
+ removeProp = t.objectProperty(t.identifier(singularName), removeArrowFn);
197
+ }
198
+ (0, babel_ast_1.addJSDocComment)(removeProp, [`Remove ${singularName} from cache`]);
199
+ return removeProp;
200
+ }
201
+ /**
202
+ * Generate the complete invalidation.ts file
203
+ */
204
+ function generateInvalidationFile(options) {
205
+ const { tables, config } = options;
206
+ const { relationships, generateCascadeHelpers } = config;
207
+ const childrenMap = buildChildrenMap(relationships);
208
+ const statements = [];
209
+ // Import QueryClient type
210
+ const queryClientImport = t.importDeclaration([t.importSpecifier(t.identifier('QueryClient'), t.identifier('QueryClient'))], t.stringLiteral('@tanstack/react-query'));
211
+ queryClientImport.importKind = 'type';
212
+ statements.push(queryClientImport);
213
+ // Import query keys
214
+ const keyImports = [];
215
+ for (const table of tables) {
216
+ const { typeName } = (0, utils_1.getTableNames)(table);
217
+ keyImports.push(`${(0, utils_1.lcFirst)(typeName)}Keys`);
218
+ }
219
+ statements.push(t.importDeclaration(keyImports.map(name => t.importSpecifier(t.identifier(name), t.identifier(name))), t.stringLiteral('./query-keys')));
220
+ // Import scope types if needed
221
+ const scopeTypes = [];
222
+ for (const table of tables) {
223
+ const { typeName } = (0, utils_1.getTableNames)(table);
224
+ if (relationships[typeName.toLowerCase()]) {
225
+ scopeTypes.push(`${typeName}Scope`);
226
+ }
227
+ }
228
+ if (scopeTypes.length > 0) {
229
+ const scopeImport = t.importDeclaration(scopeTypes.map(name => t.importSpecifier(t.identifier(name), t.identifier(name))), t.stringLiteral('./query-keys'));
230
+ scopeImport.importKind = 'type';
231
+ statements.push(scopeImport);
232
+ }
233
+ // Generate invalidate object
234
+ const invalidateProperties = [];
235
+ for (const table of tables) {
236
+ invalidateProperties.push(buildEntityInvalidateProperty(table, relationships, childrenMap, tables));
237
+ }
238
+ const invalidateDecl = t.exportNamedDeclaration(t.variableDeclaration('const', [
239
+ t.variableDeclarator(t.identifier('invalidate'), (0, babel_ast_1.asConst)(t.objectExpression(invalidateProperties)))
240
+ ]));
241
+ // Build JSDoc for invalidate
242
+ const invalidateDocLines = [
243
+ 'Type-safe query invalidation helpers',
244
+ '',
245
+ '@example',
246
+ '```ts',
247
+ '// Invalidate all user queries',
248
+ 'invalidate.user.all(queryClient);',
249
+ '',
250
+ '// Invalidate user lists',
251
+ 'invalidate.user.lists(queryClient);',
252
+ '',
253
+ '// Invalidate specific user',
254
+ 'invalidate.user.detail(queryClient, userId);',
255
+ ];
256
+ if (generateCascadeHelpers && Object.keys(relationships).length > 0) {
257
+ invalidateDocLines.push('');
258
+ invalidateDocLines.push('// Cascade invalidate (entity + all children)');
259
+ invalidateDocLines.push('invalidate.database.withChildren(queryClient, databaseId);');
260
+ }
261
+ invalidateDocLines.push('```');
262
+ (0, babel_ast_1.addJSDocComment)(invalidateDecl, invalidateDocLines);
263
+ statements.push(invalidateDecl);
264
+ // Generate remove object
265
+ const removeProperties = [];
266
+ for (const table of tables) {
267
+ removeProperties.push(buildEntityRemoveProperty(table, relationships));
268
+ }
269
+ const removeDecl = t.exportNamedDeclaration(t.variableDeclaration('const', [
270
+ t.variableDeclarator(t.identifier('remove'), (0, babel_ast_1.asConst)(t.objectExpression(removeProperties)))
271
+ ]));
272
+ (0, babel_ast_1.addJSDocComment)(removeDecl, [
273
+ 'Remove queries from cache (for delete operations)',
274
+ '',
275
+ 'Use these when an entity is deleted to remove it from cache',
276
+ 'instead of just invalidating (which would trigger a refetch).',
277
+ ]);
278
+ statements.push(removeDecl);
279
+ // Generate code from AST
280
+ const code = (0, babel_ast_1.generateCode)(statements);
281
+ // Build final content with header and section comments
282
+ const header = (0, utils_1.getGeneratedFileHeader)('Cache invalidation helpers');
283
+ const description = `// ============================================================================
284
+ // Type-safe cache invalidation utilities
285
+ //
286
+ // Features:
287
+ // - Simple invalidation helpers per entity
288
+ // - Cascade invalidation for parent-child relationships
289
+ // - Remove helpers for delete operations
290
+ // ============================================================================`;
291
+ let content = `${header}
292
+
293
+ ${description}
294
+
295
+ `;
296
+ // Insert section comments into the generated code
297
+ const codeLines = code.split('\n');
298
+ let addedInvalidationSection = false;
299
+ let addedRemoveSection = false;
300
+ for (let i = 0; i < codeLines.length; i++) {
301
+ const line = codeLines[i];
302
+ // Detect invalidation section (after imports)
303
+ if (!addedInvalidationSection && line.includes('* Type-safe query invalidation helpers')) {
304
+ content += `// ============================================================================
305
+ // Invalidation Helpers
306
+ // ============================================================================
307
+
308
+ `;
309
+ addedInvalidationSection = true;
310
+ }
311
+ // Detect remove section
312
+ if (!addedRemoveSection && line.includes('* Remove queries from cache')) {
313
+ content += `
314
+ // ============================================================================
315
+ // Remove Helpers (for delete operations)
316
+ // ============================================================================
317
+
318
+ `;
319
+ addedRemoveSection = true;
320
+ }
321
+ content += line + '\n';
322
+ }
323
+ return {
324
+ fileName: 'invalidation.ts',
325
+ content,
326
+ };
327
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Mutation key factory generator
3
+ *
4
+ * Generates centralized mutation keys for tracking in-flight mutations.
5
+ * Useful for:
6
+ * - Optimistic updates with rollback
7
+ * - Mutation deduplication
8
+ * - Tracking mutation state with useIsMutating
9
+ */
10
+ import type { CleanTable, CleanOperation } from '../../types/schema';
11
+ import type { ResolvedQueryKeyConfig } from '../../types/config';
12
+ export interface MutationKeyGeneratorOptions {
13
+ tables: CleanTable[];
14
+ customMutations: CleanOperation[];
15
+ config: ResolvedQueryKeyConfig;
16
+ }
17
+ export interface GeneratedMutationKeysFile {
18
+ fileName: string;
19
+ content: string;
20
+ }
21
+ /**
22
+ * Generate the complete mutation-keys.ts file
23
+ */
24
+ export declare function generateMutationKeysFile(options: MutationKeyGeneratorOptions): GeneratedMutationKeysFile;