@constructive-io/graphql-codegen 4.25.0 → 4.27.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.
- package/core/codegen/cli/command-map-generator.js +9 -9
- package/core/codegen/cli/custom-command-generator.js +2 -2
- package/core/codegen/cli/docs-generator.js +23 -31
- package/core/codegen/cli/index.d.ts +3 -1
- package/core/codegen/cli/index.js +16 -1
- package/core/codegen/cli/table-command-generator.d.ts +2 -0
- package/core/codegen/cli/table-command-generator.js +251 -37
- package/core/codegen/cli/utils-generator.d.ts +8 -0
- package/core/codegen/cli/utils-generator.js +14 -0
- package/core/codegen/docs-utils.d.ts +17 -0
- package/core/codegen/docs-utils.js +137 -3
- package/core/codegen/hooks-docs-generator.js +3 -3
- package/core/codegen/index.js +1 -1
- package/core/codegen/orm/docs-generator.js +3 -3
- package/core/codegen/orm/index.js +1 -1
- package/core/codegen/orm/input-types-generator.js +1 -1
- package/core/codegen/orm/model-generator.js +1 -1
- package/core/codegen/queries.js +1 -1
- package/core/codegen/templates/cli-utils.ts +11 -11
- package/core/codegen/templates/embedder.ts +175 -0
- package/core/generate.js +2 -0
- package/esm/core/codegen/cli/command-map-generator.js +1 -1
- package/esm/core/codegen/cli/custom-command-generator.js +1 -1
- package/esm/core/codegen/cli/docs-generator.js +7 -15
- package/esm/core/codegen/cli/index.d.ts +3 -1
- package/esm/core/codegen/cli/index.js +16 -2
- package/esm/core/codegen/cli/table-command-generator.d.ts +2 -0
- package/esm/core/codegen/cli/table-command-generator.js +251 -37
- package/esm/core/codegen/cli/utils-generator.d.ts +8 -0
- package/esm/core/codegen/cli/utils-generator.js +13 -0
- package/esm/core/codegen/docs-utils.d.ts +17 -0
- package/esm/core/codegen/docs-utils.js +135 -3
- package/esm/core/codegen/hooks-docs-generator.js +1 -1
- package/esm/core/codegen/index.js +1 -1
- package/esm/core/codegen/orm/docs-generator.js +1 -1
- package/esm/core/codegen/orm/index.js +1 -1
- package/esm/core/codegen/orm/input-types-generator.js +1 -1
- package/esm/core/codegen/orm/model-generator.js +1 -1
- package/esm/core/codegen/queries.js +1 -1
- package/esm/core/generate.js +2 -0
- package/package.json +15 -16
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as t from '@babel/types';
|
|
2
|
-
import { toKebabCase } from '
|
|
2
|
+
import { toKebabCase } from 'inflekt';
|
|
3
3
|
import { generateCode } from '../babel-ast';
|
|
4
|
-
import { getGeneratedFileHeader, getPrimaryKeyInfo, getScalarFields, getSelectableScalarFields, getTableNames, getWritableFieldNames, resolveInnerInputType, ucFirst, lcFirst, toPascalCase, getCreateInputTypeName, getPatchTypeName, } from '../utils';
|
|
4
|
+
import { getGeneratedFileHeader, getPrimaryKeyInfo, getScalarFields, getSelectableScalarFields, getTableNames, getWritableFieldNames, resolveInnerInputType, ucFirst, lcFirst, toPascalCase, getCreateInputTypeName, getPatchTypeName, getFilterTypeName, getOrderByTypeName, getConditionTypeName, } from '../utils';
|
|
5
5
|
import { categorizeSpecialFields } from '../docs-utils';
|
|
6
6
|
function createImportDeclaration(moduleSpecifier, namedImports, typeOnly = false) {
|
|
7
7
|
const specifiers = namedImports.map((name) => t.importSpecifier(t.identifier(name), t.identifier(name)));
|
|
@@ -165,7 +165,146 @@ function buildSubcommandSwitch(subcommands, handlerPrefix, usageVarName) {
|
|
|
165
165
|
]));
|
|
166
166
|
return t.switchStatement(t.identifier('subcommand'), cases);
|
|
167
167
|
}
|
|
168
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Build an auto-embed block that resolves the embedder and converts text
|
|
170
|
+
* values in the where clause to vector embeddings.
|
|
171
|
+
*
|
|
172
|
+
* Generates code equivalent to:
|
|
173
|
+
* if (argv['auto-embed']) {
|
|
174
|
+
* const embedder = resolveEmbedder();
|
|
175
|
+
* if (!embedder) {
|
|
176
|
+
* console.error('--auto-embed requires an embedder. Set EMBEDDER_PROVIDER=ollama');
|
|
177
|
+
* process.exit(1);
|
|
178
|
+
* }
|
|
179
|
+
* await autoEmbedWhere(findManyArgs.where ?? {}, ['fieldA', 'fieldB'], embedder);
|
|
180
|
+
* }
|
|
181
|
+
*
|
|
182
|
+
* @param whereExpr - The expression to access the where clause (e.g. findManyArgs.where, searchWhere)
|
|
183
|
+
* @param vectorFieldNames - Names of vector embedding fields detected at codegen time
|
|
184
|
+
* @param assignBack - If true, assigns the result back to findManyArgs.where
|
|
185
|
+
*/
|
|
186
|
+
function buildAutoEmbedBlock(whereExpr, vectorFieldNames, assignBack = false) {
|
|
187
|
+
const fieldNamesArray = t.arrayExpression(vectorFieldNames.map((n) => t.stringLiteral(n)));
|
|
188
|
+
const embedderDecl = t.variableDeclaration('const', [
|
|
189
|
+
t.variableDeclarator(t.identifier('embedder'), t.callExpression(t.identifier('resolveEmbedder'), [])),
|
|
190
|
+
]);
|
|
191
|
+
const noEmbedderCheck = t.ifStatement(t.unaryExpression('!', t.identifier('embedder')), t.blockStatement([
|
|
192
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('error')), [
|
|
193
|
+
t.stringLiteral('--auto-embed requires an embedder. Set EMBEDDER_PROVIDER=ollama (and optionally EMBEDDER_MODEL, EMBEDDER_BASE_URL).'),
|
|
194
|
+
])),
|
|
195
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('process'), t.identifier('exit')), [t.numericLiteral(1)])),
|
|
196
|
+
]));
|
|
197
|
+
const autoEmbedCall = t.awaitExpression(t.callExpression(t.identifier('autoEmbedWhere'), [
|
|
198
|
+
t.logicalExpression('??', whereExpr, t.objectExpression([])),
|
|
199
|
+
fieldNamesArray,
|
|
200
|
+
t.identifier('embedder'),
|
|
201
|
+
]));
|
|
202
|
+
const bodyStatements = [embedderDecl, noEmbedderCheck];
|
|
203
|
+
if (assignBack) {
|
|
204
|
+
// findManyArgs.where = await autoEmbedWhere(findManyArgs.where ?? {}, [...], embedder);
|
|
205
|
+
bodyStatements.push(t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('findManyArgs'), t.identifier('where')), autoEmbedCall)));
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
// await autoEmbedWhere(searchWhere, [...], embedder);
|
|
209
|
+
bodyStatements.push(t.expressionStatement(autoEmbedCall));
|
|
210
|
+
}
|
|
211
|
+
return t.ifStatement(t.memberExpression(t.identifier('argv'), t.stringLiteral('auto-embed'), true), t.blockStatement(bodyStatements));
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Build an auto-embed block for mutation handlers (create/update).
|
|
215
|
+
*
|
|
216
|
+
* Generates code equivalent to:
|
|
217
|
+
* if (argv['auto-embed']) {
|
|
218
|
+
* const embedder = resolveEmbedder();
|
|
219
|
+
* if (!embedder) {
|
|
220
|
+
* console.error('--auto-embed requires an embedder. Set EMBEDDER_PROVIDER=ollama');
|
|
221
|
+
* process.exit(1);
|
|
222
|
+
* }
|
|
223
|
+
* await autoEmbedInput(cleanedData, ['embedding'], embedder);
|
|
224
|
+
* }
|
|
225
|
+
*
|
|
226
|
+
* @param vectorFieldNames - Names of vector embedding fields detected at codegen time
|
|
227
|
+
*/
|
|
228
|
+
function buildAutoEmbedInputBlock(vectorFieldNames) {
|
|
229
|
+
const fieldNamesArray = t.arrayExpression(vectorFieldNames.map((n) => t.stringLiteral(n)));
|
|
230
|
+
const embedderDecl = t.variableDeclaration('const', [
|
|
231
|
+
t.variableDeclarator(t.identifier('embedder'), t.callExpression(t.identifier('resolveEmbedder'), [])),
|
|
232
|
+
]);
|
|
233
|
+
const noEmbedderCheck = t.ifStatement(t.unaryExpression('!', t.identifier('embedder')), t.blockStatement([
|
|
234
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('error')), [
|
|
235
|
+
t.stringLiteral('--auto-embed requires an embedder. Set EMBEDDER_PROVIDER=ollama (and optionally EMBEDDER_MODEL, EMBEDDER_BASE_URL).'),
|
|
236
|
+
])),
|
|
237
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('process'), t.identifier('exit')), [t.numericLiteral(1)])),
|
|
238
|
+
]));
|
|
239
|
+
const autoEmbedCall = t.awaitExpression(t.callExpression(t.identifier('autoEmbedInput'), [
|
|
240
|
+
t.identifier('cleanedData'),
|
|
241
|
+
fieldNamesArray,
|
|
242
|
+
t.identifier('embedder'),
|
|
243
|
+
]));
|
|
244
|
+
return t.ifStatement(t.memberExpression(t.identifier('argv'), t.stringLiteral('auto-embed'), true), t.blockStatement([
|
|
245
|
+
embedderDecl,
|
|
246
|
+
noEmbedderCheck,
|
|
247
|
+
t.expressionStatement(autoEmbedCall),
|
|
248
|
+
]));
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Build the FindManyArgs type instantiation for a table:
|
|
252
|
+
* FindManyArgs<SelectType, FilterType, ConditionType, OrderByType> & { select: SelectType }
|
|
253
|
+
*
|
|
254
|
+
* The intersection with { select: SelectType } makes select required,
|
|
255
|
+
* matching what the ORM's findMany method expects. parseFindManyArgs
|
|
256
|
+
* always sets select at runtime (from defaultSelect or --select flag).
|
|
257
|
+
*/
|
|
258
|
+
function buildFindManyArgsType(table, conditionEnabled) {
|
|
259
|
+
const { typeName } = getTableNames(table);
|
|
260
|
+
const selectTypeName = `${typeName}Select`;
|
|
261
|
+
const whereTypeName = getFilterTypeName(table);
|
|
262
|
+
const conditionTypeName = conditionEnabled ? getConditionTypeName(table) : undefined;
|
|
263
|
+
const orderByTypeName = getOrderByTypeName(table);
|
|
264
|
+
const findManyType = t.tsTypeReference(t.identifier('FindManyArgs'), t.tsTypeParameterInstantiation([
|
|
265
|
+
t.tsTypeReference(t.identifier(selectTypeName)),
|
|
266
|
+
t.tsTypeReference(t.identifier(whereTypeName)),
|
|
267
|
+
conditionTypeName
|
|
268
|
+
? t.tsTypeReference(t.identifier(conditionTypeName))
|
|
269
|
+
: t.tsNeverKeyword(),
|
|
270
|
+
t.tsTypeReference(t.identifier(orderByTypeName)),
|
|
271
|
+
]));
|
|
272
|
+
// Intersect with { select: SelectType } to make select required
|
|
273
|
+
return t.tsIntersectionType([
|
|
274
|
+
findManyType,
|
|
275
|
+
t.tsTypeLiteral([
|
|
276
|
+
Object.assign(t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(selectTypeName)))), { optional: false }),
|
|
277
|
+
]),
|
|
278
|
+
]);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Build the FindFirstArgs type instantiation for a table:
|
|
282
|
+
* FindFirstArgs<SelectType, FilterType, ConditionType> & { select: SelectType }
|
|
283
|
+
*
|
|
284
|
+
* The intersection with { select: SelectType } makes select required,
|
|
285
|
+
* matching what the ORM's findFirst method expects.
|
|
286
|
+
*/
|
|
287
|
+
function buildFindFirstArgsType(table, conditionEnabled) {
|
|
288
|
+
const { typeName } = getTableNames(table);
|
|
289
|
+
const selectTypeName = `${typeName}Select`;
|
|
290
|
+
const whereTypeName = getFilterTypeName(table);
|
|
291
|
+
const conditionTypeName = conditionEnabled ? getConditionTypeName(table) : undefined;
|
|
292
|
+
const findFirstType = t.tsTypeReference(t.identifier('FindFirstArgs'), t.tsTypeParameterInstantiation([
|
|
293
|
+
t.tsTypeReference(t.identifier(selectTypeName)),
|
|
294
|
+
t.tsTypeReference(t.identifier(whereTypeName)),
|
|
295
|
+
conditionTypeName
|
|
296
|
+
? t.tsTypeReference(t.identifier(conditionTypeName))
|
|
297
|
+
: t.tsNeverKeyword(),
|
|
298
|
+
]));
|
|
299
|
+
// Intersect with { select: SelectType } to make select required
|
|
300
|
+
return t.tsIntersectionType([
|
|
301
|
+
findFirstType,
|
|
302
|
+
t.tsTypeLiteral([
|
|
303
|
+
Object.assign(t.tsPropertySignature(t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(selectTypeName)))), { optional: false }),
|
|
304
|
+
]),
|
|
305
|
+
]);
|
|
306
|
+
}
|
|
307
|
+
function buildListHandler(table, vectorFieldNames, targetName, typeRegistry, conditionEnabled = false) {
|
|
169
308
|
const { singularName } = getTableNames(table);
|
|
170
309
|
const defaultSelectObj = buildSelectObject(table, typeRegistry);
|
|
171
310
|
// --- Build the try body ---
|
|
@@ -174,13 +313,23 @@ function buildListHandler(table, targetName, typeRegistry) {
|
|
|
174
313
|
tryBody.push(t.variableDeclaration('const', [
|
|
175
314
|
t.variableDeclarator(t.identifier('defaultSelect'), defaultSelectObj),
|
|
176
315
|
]));
|
|
177
|
-
// const findManyArgs = parseFindManyArgs(argv, defaultSelect);
|
|
178
|
-
|
|
179
|
-
|
|
316
|
+
// const findManyArgs = parseFindManyArgs<FindManyArgs<...>>(argv, defaultSelect);
|
|
317
|
+
{
|
|
318
|
+
const callExpr = t.callExpression(t.identifier('parseFindManyArgs'), [
|
|
180
319
|
t.identifier('argv'),
|
|
181
320
|
t.identifier('defaultSelect'),
|
|
182
|
-
])
|
|
183
|
-
|
|
321
|
+
]);
|
|
322
|
+
callExpr.typeParameters = t.tsTypeParameterInstantiation([
|
|
323
|
+
buildFindManyArgsType(table, conditionEnabled),
|
|
324
|
+
]);
|
|
325
|
+
tryBody.push(t.variableDeclaration('const', [
|
|
326
|
+
t.variableDeclarator(t.identifier('findManyArgs'), callExpr),
|
|
327
|
+
]));
|
|
328
|
+
}
|
|
329
|
+
// Auto-embed vector fields in the where clause when --auto-embed is passed
|
|
330
|
+
if (vectorFieldNames.length > 0) {
|
|
331
|
+
tryBody.push(buildAutoEmbedBlock(t.memberExpression(t.identifier('findManyArgs'), t.identifier('where')), vectorFieldNames, true));
|
|
332
|
+
}
|
|
184
333
|
tryBody.push(buildGetClientStatement(targetName));
|
|
185
334
|
// const result = await client.<singular>.findMany(findManyArgs).execute();
|
|
186
335
|
tryBody.push(t.variableDeclaration('const', [
|
|
@@ -197,10 +346,10 @@ function buildListHandler(table, targetName, typeRegistry) {
|
|
|
197
346
|
}
|
|
198
347
|
/**
|
|
199
348
|
* Build a `handleFindFirst` function — CLI equivalent of the TS SDK's findFirst().
|
|
200
|
-
* Accepts --
|
|
349
|
+
* Accepts --select, --where.<field>.<op>, --condition.<field>.<op> flags.
|
|
201
350
|
* Internally calls findMany with first:1 and returns a single record (or null).
|
|
202
351
|
*/
|
|
203
|
-
function buildFindFirstHandler(table, targetName, typeRegistry) {
|
|
352
|
+
function buildFindFirstHandler(table, targetName, typeRegistry, conditionEnabled = false) {
|
|
204
353
|
const { singularName } = getTableNames(table);
|
|
205
354
|
const defaultSelectObj = buildSelectObject(table, typeRegistry);
|
|
206
355
|
const tryBody = [];
|
|
@@ -208,13 +357,19 @@ function buildFindFirstHandler(table, targetName, typeRegistry) {
|
|
|
208
357
|
tryBody.push(t.variableDeclaration('const', [
|
|
209
358
|
t.variableDeclarator(t.identifier('defaultSelect'), defaultSelectObj),
|
|
210
359
|
]));
|
|
211
|
-
// const findFirstArgs = parseFindFirstArgs(argv, defaultSelect);
|
|
212
|
-
|
|
213
|
-
|
|
360
|
+
// const findFirstArgs = parseFindFirstArgs<FindFirstArgs<...>>(argv, defaultSelect);
|
|
361
|
+
{
|
|
362
|
+
const callExpr = t.callExpression(t.identifier('parseFindFirstArgs'), [
|
|
214
363
|
t.identifier('argv'),
|
|
215
364
|
t.identifier('defaultSelect'),
|
|
216
|
-
])
|
|
217
|
-
|
|
365
|
+
]);
|
|
366
|
+
callExpr.typeParameters = t.tsTypeParameterInstantiation([
|
|
367
|
+
buildFindFirstArgsType(table, conditionEnabled),
|
|
368
|
+
]);
|
|
369
|
+
tryBody.push(t.variableDeclaration('const', [
|
|
370
|
+
t.variableDeclarator(t.identifier('findFirstArgs'), callExpr),
|
|
371
|
+
]));
|
|
372
|
+
}
|
|
218
373
|
tryBody.push(buildGetClientStatement(targetName));
|
|
219
374
|
// const result = await client.<singular>.findFirst(findFirstArgs).execute();
|
|
220
375
|
tryBody.push(t.variableDeclaration('const', [
|
|
@@ -233,9 +388,9 @@ function buildFindFirstHandler(table, targetName, typeRegistry) {
|
|
|
233
388
|
* Build a `handleSearch` function for tables with search-capable fields.
|
|
234
389
|
* Extracts the first positional arg as the query string and auto-builds a
|
|
235
390
|
* `where` clause that targets all detected search fields (tsvector, BM25,
|
|
236
|
-
* trigram, vector embedding). Supports --limit, --offset, --
|
|
391
|
+
* trigram, vector embedding). Supports --limit, --offset, --select, --orderBy.
|
|
237
392
|
*/
|
|
238
|
-
function buildSearchHandler(table, specialGroups, targetName, typeRegistry) {
|
|
393
|
+
function buildSearchHandler(table, specialGroups, vectorFieldNames, targetName, typeRegistry, conditionEnabled = false) {
|
|
239
394
|
const { singularName } = getTableNames(table);
|
|
240
395
|
const defaultSelectObj = buildSelectObject(table, typeRegistry);
|
|
241
396
|
const tryBody = [];
|
|
@@ -251,11 +406,11 @@ function buildSearchHandler(table, specialGroups, targetName, typeRegistry) {
|
|
|
251
406
|
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('process'), t.identifier('exit')), [t.numericLiteral(1)])),
|
|
252
407
|
])));
|
|
253
408
|
// Build the where clause properties from detected search fields
|
|
254
|
-
// e.g. { tsvContent: { query }, bm25Body: { query }, trgmTitle: { value: query, threshold: 0.3 }, vectorEmbedding: {
|
|
409
|
+
// e.g. { tsvContent: { query }, bm25Body: { query }, trgmTitle: { value: query, threshold: 0.3 }, vectorEmbedding: { vector: query } }
|
|
255
410
|
const whereProps = [];
|
|
256
411
|
for (const group of specialGroups) {
|
|
257
412
|
for (const field of group.fields) {
|
|
258
|
-
if (field.type.
|
|
413
|
+
if (field.type.gqlType === 'FullText' && !field.type.isArray) {
|
|
259
414
|
// tsvector field: { query }
|
|
260
415
|
whereProps.push(t.objectProperty(t.identifier(field.name), t.objectExpression([
|
|
261
416
|
t.objectProperty(t.identifier('query'), t.identifier('query'), false, true),
|
|
@@ -281,9 +436,10 @@ function buildSearchHandler(table, specialGroups, targetName, typeRegistry) {
|
|
|
281
436
|
])));
|
|
282
437
|
}
|
|
283
438
|
else if (group.category === 'embedding') {
|
|
284
|
-
// Vector embedding field: {
|
|
439
|
+
// Vector embedding field: { vector: query }
|
|
440
|
+
// When --auto-embed is used, autoEmbedWhere will convert the text to a vector
|
|
285
441
|
whereProps.push(t.objectProperty(t.identifier(field.name), t.objectExpression([
|
|
286
|
-
t.objectProperty(t.identifier('
|
|
442
|
+
t.objectProperty(t.identifier('vector'), t.identifier('query')),
|
|
287
443
|
])));
|
|
288
444
|
}
|
|
289
445
|
}
|
|
@@ -292,18 +448,28 @@ function buildSearchHandler(table, specialGroups, targetName, typeRegistry) {
|
|
|
292
448
|
tryBody.push(t.variableDeclaration('const', [
|
|
293
449
|
t.variableDeclarator(t.identifier('searchWhere'), t.objectExpression(whereProps)),
|
|
294
450
|
]));
|
|
451
|
+
// Auto-embed vector fields in the where clause when --auto-embed is passed
|
|
452
|
+
if (vectorFieldNames.length > 0) {
|
|
453
|
+
tryBody.push(buildAutoEmbedBlock(t.identifier('searchWhere'), vectorFieldNames, false));
|
|
454
|
+
}
|
|
295
455
|
// const defaultSelect = { ... };
|
|
296
456
|
tryBody.push(t.variableDeclaration('const', [
|
|
297
457
|
t.variableDeclarator(t.identifier('defaultSelect'), defaultSelectObj),
|
|
298
458
|
]));
|
|
299
|
-
// const findManyArgs = parseFindManyArgs(argv, defaultSelect, searchWhere);
|
|
300
|
-
|
|
301
|
-
|
|
459
|
+
// const findManyArgs = parseFindManyArgs<FindManyArgs<...>>(argv, defaultSelect, searchWhere);
|
|
460
|
+
{
|
|
461
|
+
const callExpr = t.callExpression(t.identifier('parseFindManyArgs'), [
|
|
302
462
|
t.identifier('argv'),
|
|
303
463
|
t.identifier('defaultSelect'),
|
|
304
464
|
t.identifier('searchWhere'),
|
|
305
|
-
])
|
|
306
|
-
|
|
465
|
+
]);
|
|
466
|
+
callExpr.typeParameters = t.tsTypeParameterInstantiation([
|
|
467
|
+
buildFindManyArgsType(table, conditionEnabled),
|
|
468
|
+
]);
|
|
469
|
+
tryBody.push(t.variableDeclaration('const', [
|
|
470
|
+
t.variableDeclarator(t.identifier('findManyArgs'), callExpr),
|
|
471
|
+
]));
|
|
472
|
+
}
|
|
307
473
|
tryBody.push(buildGetClientStatement(targetName));
|
|
308
474
|
// const result = await client.<singular>.findMany(findManyArgs).execute();
|
|
309
475
|
tryBody.push(t.variableDeclaration('const', [
|
|
@@ -375,7 +541,7 @@ export function getFieldsWithDefaults(table, typeRegistry) {
|
|
|
375
541
|
}
|
|
376
542
|
return fieldsWithDefaults;
|
|
377
543
|
}
|
|
378
|
-
function buildMutationHandler(table, operation, targetName, typeRegistry, ormTypes) {
|
|
544
|
+
function buildMutationHandler(table, operation, vectorFieldNames, targetName, typeRegistry, ormTypes) {
|
|
379
545
|
const { singularName } = getTableNames(table);
|
|
380
546
|
const pkFields = getPrimaryKeyInfo(table);
|
|
381
547
|
const pk = pkFields[0];
|
|
@@ -503,6 +669,11 @@ function buildMutationHandler(table, operation, targetName, typeRegistry, ormTyp
|
|
|
503
669
|
tryBody.push(t.variableDeclaration('const', [
|
|
504
670
|
t.variableDeclarator(t.identifier('cleanedData'), cleanedDataExpr),
|
|
505
671
|
]));
|
|
672
|
+
// Inject --auto-embed block for create/update when table has vector fields.
|
|
673
|
+
// Converts text strings in vector fields to embeddings before the ORM call.
|
|
674
|
+
if (vectorFieldNames.length > 0) {
|
|
675
|
+
tryBody.push(buildAutoEmbedInputBlock(vectorFieldNames));
|
|
676
|
+
}
|
|
506
677
|
}
|
|
507
678
|
tryBody.push(buildGetClientStatement(targetName), t.variableDeclaration('const', [
|
|
508
679
|
t.variableDeclarator(t.identifier('result'), t.awaitExpression(buildOrmCall(singularName, operation, ormArgs))),
|
|
@@ -517,7 +688,7 @@ function buildMutationHandler(table, operation, targetName, typeRegistry, ormTyp
|
|
|
517
688
|
]), false, true);
|
|
518
689
|
}
|
|
519
690
|
export function generateTableCommand(table, options) {
|
|
520
|
-
const { singularName } = getTableNames(table);
|
|
691
|
+
const { singularName, typeName } = getTableNames(table);
|
|
521
692
|
const commandName = toKebabCase(singularName);
|
|
522
693
|
const statements = [];
|
|
523
694
|
const executorPath = options?.executorImportPath ?? '../executor';
|
|
@@ -542,7 +713,25 @@ export function generateTableCommand(table, options) {
|
|
|
542
713
|
const inputTypesPath = options?.targetName
|
|
543
714
|
? `../../../orm/input-types`
|
|
544
715
|
: `../../orm/input-types`;
|
|
545
|
-
|
|
716
|
+
// Import table-specific ORM types for generic type parameters on parseFindManyArgs/parseFindFirstArgs
|
|
717
|
+
const selectTypeName = `${typeName}Select`;
|
|
718
|
+
const whereTypeName = getFilterTypeName(table);
|
|
719
|
+
const orderByTypeName = getOrderByTypeName(table);
|
|
720
|
+
const conditionEnabled = options?.condition === true;
|
|
721
|
+
const conditionTypeName = conditionEnabled ? getConditionTypeName(table) : undefined;
|
|
722
|
+
statements.push(createImportDeclaration(inputTypesPath, [
|
|
723
|
+
createInputTypeName,
|
|
724
|
+
patchTypeName,
|
|
725
|
+
selectTypeName,
|
|
726
|
+
whereTypeName,
|
|
727
|
+
...(conditionTypeName ? [conditionTypeName] : []),
|
|
728
|
+
orderByTypeName,
|
|
729
|
+
], true));
|
|
730
|
+
// Import FindManyArgs/FindFirstArgs from select-types for proper generic typing
|
|
731
|
+
const selectTypesPath = options?.targetName
|
|
732
|
+
? `../../../orm/select-types`
|
|
733
|
+
: `../../orm/select-types`;
|
|
734
|
+
statements.push(createImportDeclaration(selectTypesPath, ['FindManyArgs', 'FindFirstArgs'], true));
|
|
546
735
|
// Generate field schema for type coercion
|
|
547
736
|
// Use explicit FieldSchema type annotation so TS narrows string literals to FieldType
|
|
548
737
|
const fieldSchemaId = t.identifier('fieldSchema');
|
|
@@ -563,6 +752,21 @@ export function generateTableCommand(table, options) {
|
|
|
563
752
|
// Detect whether this table has search-capable fields (tsvector, BM25, trgm, vector embedding)
|
|
564
753
|
const specialGroups = categorizeSpecialFields(table, options?.typeRegistry);
|
|
565
754
|
const hasSearchFields = specialGroups.some((g) => g.category === 'search' || g.category === 'embedding');
|
|
755
|
+
// Collect vector embedding field names for --auto-embed support
|
|
756
|
+
const vectorFieldNames = [];
|
|
757
|
+
for (const group of specialGroups) {
|
|
758
|
+
if (group.category === 'embedding') {
|
|
759
|
+
for (const field of group.fields) {
|
|
760
|
+
vectorFieldNames.push(field.name);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
const hasEmbeddings = vectorFieldNames.length > 0;
|
|
765
|
+
// Import embedder functions when table has vector embedding fields
|
|
766
|
+
if (hasEmbeddings) {
|
|
767
|
+
const embedderPath = options?.targetName ? '../../embedder' : '../embedder';
|
|
768
|
+
statements.push(createImportDeclaration(embedderPath, ['resolveEmbedder', 'autoEmbedWhere', 'autoEmbedInput']));
|
|
769
|
+
}
|
|
566
770
|
const subcommands = ['list', 'find-first'];
|
|
567
771
|
if (hasSearchFields)
|
|
568
772
|
subcommands.push('search');
|
|
@@ -588,11 +792,21 @@ export function generateTableCommand(table, options) {
|
|
|
588
792
|
usageLines.push(` create Create a new ${singularName}`);
|
|
589
793
|
if (hasUpdate)
|
|
590
794
|
usageLines.push(` update Update an existing ${singularName}`);
|
|
795
|
+
if (hasEmbeddings) {
|
|
796
|
+
usageLines.push('', 'Create/Update Options:', ' --auto-embed Convert text values in vector fields to embeddings before saving');
|
|
797
|
+
}
|
|
591
798
|
if (hasDelete)
|
|
592
799
|
usageLines.push(` delete Delete a ${singularName}`);
|
|
593
|
-
usageLines.push('', 'List Options:', ' --limit <n> Max number of records to return (forward pagination)', ' --last <n> Number of records from the end (backward pagination)', ' --after <cursor> Cursor for forward pagination', ' --before <cursor> Cursor for backward pagination', ' --offset <n> Number of records to skip', ' --
|
|
800
|
+
usageLines.push('', 'List Options:', ' --limit <n> Max number of records to return (forward pagination)', ' --last <n> Number of records from the end (backward pagination)', ' --after <cursor> Cursor for forward pagination', ' --before <cursor> Cursor for backward pagination', ' --offset <n> Number of records to skip', ' --select <fields> Comma-separated list of fields to return', ' --where.<field>.<op> Filter (dot-notation, e.g. --where.name.equalTo foo)', ' --condition.<f>.<op> Condition filter (dot-notation)', ' --orderBy <values> Comma-separated ordering values (e.g. NAME_ASC,CREATED_AT_DESC)', '', 'Find-First Options:', ' --select <fields> Comma-separated list of fields to return', ' --where.<field>.<op> Filter (dot-notation, e.g. --where.status.equalTo active)', ' --condition.<f>.<op> Condition filter (dot-notation)', '');
|
|
594
801
|
if (hasSearchFields) {
|
|
595
|
-
usageLines.push('Search Options:', ' <query> Search query string (required)', ' --limit <n> Max number of records to return', ' --offset <n> Number of records to skip', ' --
|
|
802
|
+
usageLines.push('Search Options:', ' <query> Search query string (required)', ' --limit <n> Max number of records to return', ' --offset <n> Number of records to skip', ' --select <fields> Comma-separated list of fields to return', ' --orderBy <values> Comma-separated list of ordering values');
|
|
803
|
+
if (hasEmbeddings) {
|
|
804
|
+
usageLines.push(' --auto-embed Convert text queries to vectors via configured embedder');
|
|
805
|
+
}
|
|
806
|
+
usageLines.push('');
|
|
807
|
+
}
|
|
808
|
+
if (hasEmbeddings) {
|
|
809
|
+
usageLines.push('Embedding Options (for --auto-embed):', ' Set EMBEDDER_PROVIDER=ollama to enable text-to-vector embedding.', ' Optional: EMBEDDER_MODEL (default: nomic-embed-text)', ' Optional: EMBEDDER_BASE_URL (default: http://localhost:11434)', '');
|
|
596
810
|
}
|
|
597
811
|
usageLines.push(' --help, -h Show this help message', '');
|
|
598
812
|
statements.push(t.variableDeclaration('const', [
|
|
@@ -655,17 +869,17 @@ export function generateTableCommand(table, options) {
|
|
|
655
869
|
]), false, true));
|
|
656
870
|
const tn = options?.targetName;
|
|
657
871
|
const ormTypes = { createInputTypeName, patchTypeName, innerFieldName };
|
|
658
|
-
statements.push(buildListHandler(table, tn, options?.typeRegistry));
|
|
659
|
-
statements.push(buildFindFirstHandler(table, tn, options?.typeRegistry));
|
|
872
|
+
statements.push(buildListHandler(table, vectorFieldNames, tn, options?.typeRegistry, conditionEnabled));
|
|
873
|
+
statements.push(buildFindFirstHandler(table, tn, options?.typeRegistry, conditionEnabled));
|
|
660
874
|
if (hasSearchFields)
|
|
661
|
-
statements.push(buildSearchHandler(table, specialGroups, tn, options?.typeRegistry));
|
|
875
|
+
statements.push(buildSearchHandler(table, specialGroups, vectorFieldNames, tn, options?.typeRegistry, conditionEnabled));
|
|
662
876
|
if (hasGet)
|
|
663
877
|
statements.push(buildGetHandler(table, tn, options?.typeRegistry));
|
|
664
|
-
statements.push(buildMutationHandler(table, 'create', tn, options?.typeRegistry, ormTypes));
|
|
878
|
+
statements.push(buildMutationHandler(table, 'create', vectorFieldNames, tn, options?.typeRegistry, ormTypes));
|
|
665
879
|
if (hasUpdate)
|
|
666
|
-
statements.push(buildMutationHandler(table, 'update', tn, options?.typeRegistry, ormTypes));
|
|
880
|
+
statements.push(buildMutationHandler(table, 'update', vectorFieldNames, tn, options?.typeRegistry, ormTypes));
|
|
667
881
|
if (hasDelete)
|
|
668
|
-
statements.push(buildMutationHandler(table, 'delete', tn, options?.typeRegistry, ormTypes));
|
|
882
|
+
statements.push(buildMutationHandler(table, 'delete', vectorFieldNames, tn, options?.typeRegistry, ormTypes));
|
|
669
883
|
const header = getGeneratedFileHeader(`CLI commands for ${table.name}`);
|
|
670
884
|
const code = generateCode(statements);
|
|
671
885
|
return {
|
|
@@ -25,3 +25,11 @@ export declare function generateNodeFetchFile(): GeneratedFile;
|
|
|
25
25
|
* provide their own entry point with custom configuration.
|
|
26
26
|
*/
|
|
27
27
|
export declare function generateEntryPointFile(): GeneratedFile;
|
|
28
|
+
/**
|
|
29
|
+
* Generate an embedder.ts file with pluggable text-to-vector embedding.
|
|
30
|
+
*
|
|
31
|
+
* Provides a runtime embedder registry using @agentic-kit/ollama so that
|
|
32
|
+
* CLI search and list commands can convert text queries into vector arrays
|
|
33
|
+
* for pgvector similarity search when --auto-embed is passed.
|
|
34
|
+
*/
|
|
35
|
+
export declare function generateEmbedderFile(): GeneratedFile;
|
|
@@ -67,3 +67,16 @@ export function generateEntryPointFile() {
|
|
|
67
67
|
content: readTemplateFile('cli-entry.ts', 'CLI entry point'),
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Generate an embedder.ts file with pluggable text-to-vector embedding.
|
|
72
|
+
*
|
|
73
|
+
* Provides a runtime embedder registry using @agentic-kit/ollama so that
|
|
74
|
+
* CLI search and list commands can convert text queries into vector arrays
|
|
75
|
+
* for pgvector similarity search when --auto-embed is passed.
|
|
76
|
+
*/
|
|
77
|
+
export function generateEmbedderFile() {
|
|
78
|
+
return {
|
|
79
|
+
fileName: 'embedder.ts',
|
|
80
|
+
content: readTemplateFile('embedder.ts', 'CLI embedder — pluggable text-to-vector embedding for search commands'),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -65,6 +65,23 @@ export declare function buildSpecialFieldsMarkdown(groups: SpecialFieldGroup[]):
|
|
|
65
65
|
* Returns empty array when there are no special fields.
|
|
66
66
|
*/
|
|
67
67
|
export declare function buildSpecialFieldsPlain(groups: SpecialFieldGroup[]): string[];
|
|
68
|
+
export interface SearchExample {
|
|
69
|
+
description: string;
|
|
70
|
+
code: string[];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Build concrete, field-specific CLI examples for tables with search fields.
|
|
74
|
+
* Uses the same field-name derivation logic as buildSearchHandler in
|
|
75
|
+
* table-command-generator.ts so the examples match the actual generated code.
|
|
76
|
+
*
|
|
77
|
+
* Returns an empty array when the table has no search/embedding fields.
|
|
78
|
+
*/
|
|
79
|
+
export declare function buildSearchExamples(specialGroups: SpecialFieldGroup[], toolName: string, cmd: string): SearchExample[];
|
|
80
|
+
/**
|
|
81
|
+
* Build markdown lines for search-specific examples in README-style docs.
|
|
82
|
+
* Returns empty array when there are no search examples.
|
|
83
|
+
*/
|
|
84
|
+
export declare function buildSearchExamplesMarkdown(specialGroups: SpecialFieldGroup[], toolName: string, cmd: string): string[];
|
|
68
85
|
/**
|
|
69
86
|
* Represents a flattened argument for docs/skills generation.
|
|
70
87
|
* INPUT_OBJECT args are expanded to dot-notation fields.
|
|
@@ -96,16 +96,23 @@ function isPostGISField(f) {
|
|
|
96
96
|
return false;
|
|
97
97
|
}
|
|
98
98
|
function isEmbeddingField(f) {
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
// VectorCodecPlugin maps pgvector `vector` columns to the `Vector` GQL scalar.
|
|
100
|
+
// This is the primary detection path — no _meta or pgType enrichment needed.
|
|
101
|
+
if (f.type.gqlType === 'Vector')
|
|
101
102
|
return true;
|
|
103
|
+
// Legacy fallback: name-based heuristic for schemas without VectorCodecPlugin
|
|
102
104
|
if (/embedding$/i.test(f.name) && f.type.isArray && f.type.gqlType === 'Float')
|
|
103
105
|
return true;
|
|
104
106
|
return false;
|
|
105
107
|
}
|
|
106
108
|
function isTsvectorField(f) {
|
|
107
109
|
const pgType = f.type.pgType?.toLowerCase();
|
|
108
|
-
|
|
110
|
+
if (pgType === 'tsvector')
|
|
111
|
+
return true;
|
|
112
|
+
// Fallback: PostGraphile maps tsvector columns to the FullText GQL scalar
|
|
113
|
+
if (f.type.gqlType === 'FullText' && !f.type.isArray)
|
|
114
|
+
return true;
|
|
115
|
+
return false;
|
|
109
116
|
}
|
|
110
117
|
function isSearchComputedField(f) {
|
|
111
118
|
if (f.name === 'searchScore')
|
|
@@ -227,6 +234,131 @@ export function buildSpecialFieldsPlain(groups) {
|
|
|
227
234
|
}
|
|
228
235
|
return lines;
|
|
229
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Build concrete, field-specific CLI examples for tables with search fields.
|
|
239
|
+
* Uses the same field-name derivation logic as buildSearchHandler in
|
|
240
|
+
* table-command-generator.ts so the examples match the actual generated code.
|
|
241
|
+
*
|
|
242
|
+
* Returns an empty array when the table has no search/embedding fields.
|
|
243
|
+
*/
|
|
244
|
+
export function buildSearchExamples(specialGroups, toolName, cmd) {
|
|
245
|
+
const examples = [];
|
|
246
|
+
const scoreFields = [];
|
|
247
|
+
for (const group of specialGroups) {
|
|
248
|
+
for (const field of group.fields) {
|
|
249
|
+
// tsvector (FullText scalar) — where input uses the column name directly
|
|
250
|
+
if (field.type.gqlType === 'FullText' && !field.type.isArray) {
|
|
251
|
+
examples.push({
|
|
252
|
+
description: `Full-text search via tsvector (\`${field.name}\`)`,
|
|
253
|
+
code: [
|
|
254
|
+
`${toolName} ${cmd} list --where.${field.name} "search query" --select title,tsvRank`,
|
|
255
|
+
],
|
|
256
|
+
});
|
|
257
|
+
scoreFields.push('tsvRank');
|
|
258
|
+
}
|
|
259
|
+
// BM25 computed score — bodyBm25Score → bm25Body
|
|
260
|
+
if (/Bm25Score$/.test(field.name)) {
|
|
261
|
+
const baseName = field.name.replace(/Bm25Score$/, '');
|
|
262
|
+
const inputName = `bm25${baseName.charAt(0).toUpperCase()}${baseName.slice(1)}`;
|
|
263
|
+
examples.push({
|
|
264
|
+
description: `BM25 keyword search via \`${inputName}\``,
|
|
265
|
+
code: [
|
|
266
|
+
`${toolName} ${cmd} list --where.${inputName}.query "search query" --select title,${field.name}`,
|
|
267
|
+
],
|
|
268
|
+
});
|
|
269
|
+
scoreFields.push(field.name);
|
|
270
|
+
}
|
|
271
|
+
// Trigram similarity — titleTrgmSimilarity → trgmTitle
|
|
272
|
+
if (/TrgmSimilarity$/.test(field.name)) {
|
|
273
|
+
const baseName = field.name.replace(/TrgmSimilarity$/, '');
|
|
274
|
+
const inputName = `trgm${baseName.charAt(0).toUpperCase()}${baseName.slice(1)}`;
|
|
275
|
+
examples.push({
|
|
276
|
+
description: `Fuzzy search via trigram similarity (\`${inputName}\`)`,
|
|
277
|
+
code: [
|
|
278
|
+
`${toolName} ${cmd} list --where.${inputName}.value "approximate query" --where.${inputName}.threshold 0.3 --select title,${field.name}`,
|
|
279
|
+
],
|
|
280
|
+
});
|
|
281
|
+
scoreFields.push(field.name);
|
|
282
|
+
}
|
|
283
|
+
// pgvector embedding — uses column name, with --auto-embed for text-to-vector
|
|
284
|
+
if (group.category === 'embedding') {
|
|
285
|
+
examples.push({
|
|
286
|
+
description: `Vector similarity search via \`${field.name}\` (manual vector)`,
|
|
287
|
+
code: [
|
|
288
|
+
`# Pass a pre-computed vector array via dot-notation`,
|
|
289
|
+
`${toolName} ${cmd} list --where.${field.name}.vector '[0.1,0.2,0.3]' --where.${field.name}.distance 1.0 --select title,${field.name}VectorDistance`,
|
|
290
|
+
],
|
|
291
|
+
});
|
|
292
|
+
examples.push({
|
|
293
|
+
description: `Vector semantic search via \`${field.name}\` with --auto-embed`,
|
|
294
|
+
code: [
|
|
295
|
+
`# --auto-embed converts text to vectors using the configured embedder (e.g. Ollama nomic-embed-text)`,
|
|
296
|
+
`EMBEDDER_PROVIDER=ollama ${toolName} ${cmd} search "semantic query" --auto-embed --select title,${field.name}VectorDistance`,
|
|
297
|
+
`EMBEDDER_PROVIDER=ollama ${toolName} ${cmd} list --where.${field.name}.vector "semantic query" --auto-embed --select title,${field.name}VectorDistance`,
|
|
298
|
+
],
|
|
299
|
+
});
|
|
300
|
+
examples.push({
|
|
301
|
+
description: `Create/update with auto-embedded \`${field.name}\` via --auto-embed`,
|
|
302
|
+
code: [
|
|
303
|
+
`# --auto-embed on create/update converts text strings in vector fields to embeddings before saving`,
|
|
304
|
+
`EMBEDDER_PROVIDER=ollama ${toolName} ${cmd} create --${field.name} "text to embed" --auto-embed`,
|
|
305
|
+
`EMBEDDER_PROVIDER=ollama ${toolName} ${cmd} update --${field.name} "new text to embed" --auto-embed`,
|
|
306
|
+
],
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
// searchScore — composite blend field, useful for ordering
|
|
310
|
+
if (field.name === 'searchScore') {
|
|
311
|
+
scoreFields.push('searchScore');
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// Composite fullTextSearch example (dispatches to all text adapters)
|
|
316
|
+
const hasTextSearch = specialGroups.some((g) => g.category === 'search' && g.fields.some((f) => f.type.gqlType === 'FullText' || /TrgmSimilarity$/.test(f.name) || /Bm25Score$/.test(f.name)));
|
|
317
|
+
if (hasTextSearch) {
|
|
318
|
+
const fieldsArg = scoreFields.length > 0
|
|
319
|
+
? `title,${[...new Set(scoreFields)].join(',')}`
|
|
320
|
+
: 'title';
|
|
321
|
+
examples.push({
|
|
322
|
+
description: 'Composite search (fullTextSearch dispatches to all text adapters)',
|
|
323
|
+
code: [
|
|
324
|
+
`${toolName} ${cmd} list --where.fullTextSearch "search query" --select ${fieldsArg}`,
|
|
325
|
+
],
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
// Combined search + pagination + ordering example
|
|
329
|
+
if (examples.length > 0) {
|
|
330
|
+
examples.push({
|
|
331
|
+
description: 'Search with pagination and field projection',
|
|
332
|
+
code: [
|
|
333
|
+
`${toolName} ${cmd} list --where.fullTextSearch "query" --limit 10 --select id,title,searchScore`,
|
|
334
|
+
`${toolName} ${cmd} search "query" --limit 10 --select id,title,searchScore`,
|
|
335
|
+
],
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
return examples;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Build markdown lines for search-specific examples in README-style docs.
|
|
342
|
+
* Returns empty array when there are no search examples.
|
|
343
|
+
*/
|
|
344
|
+
export function buildSearchExamplesMarkdown(specialGroups, toolName, cmd) {
|
|
345
|
+
const examples = buildSearchExamples(specialGroups, toolName, cmd);
|
|
346
|
+
if (examples.length === 0)
|
|
347
|
+
return [];
|
|
348
|
+
const lines = [];
|
|
349
|
+
lines.push('**Search Examples:**');
|
|
350
|
+
lines.push('');
|
|
351
|
+
for (const ex of examples) {
|
|
352
|
+
lines.push(`*${ex.description}:*`);
|
|
353
|
+
lines.push('```bash');
|
|
354
|
+
for (const c of ex.code) {
|
|
355
|
+
lines.push(c);
|
|
356
|
+
}
|
|
357
|
+
lines.push('```');
|
|
358
|
+
lines.push('');
|
|
359
|
+
}
|
|
360
|
+
return lines;
|
|
361
|
+
}
|
|
230
362
|
function unwrapNonNull(typeRef) {
|
|
231
363
|
if (typeRef.kind === 'NON_NULL' && typeRef.ofType) {
|
|
232
364
|
return { inner: typeRef.ofType, required: true };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { toKebabCase } from '
|
|
1
|
+
import { toKebabCase } from 'inflekt';
|
|
2
2
|
import { buildSkillFile, buildSkillReference, formatArgType, fieldPlaceholder, pkPlaceholder, argPlaceholder, getReadmeHeader, getReadmeFooter, } from './docs-utils';
|
|
3
3
|
import { getTableNames, getScalarFields, getPrimaryKeyInfo, getListQueryHookName, getSingleQueryHookName, getCreateMutationHookName, getUpdateMutationHookName, getDeleteMutationHookName, hasValidPrimaryKey, ucFirst, } from './utils';
|
|
4
4
|
function getCustomHookName(op) {
|
|
@@ -92,7 +92,7 @@ export function generate(options) {
|
|
|
92
92
|
hasInvalidation = true;
|
|
93
93
|
}
|
|
94
94
|
// Condition types (PostGraphile simple equality filters)
|
|
95
|
-
const conditionEnabled = config.codegen?.condition
|
|
95
|
+
const conditionEnabled = config.codegen?.condition === true;
|
|
96
96
|
// 4. Generate table-based query hooks (queries/*.ts)
|
|
97
97
|
const queryHooks = generateAllQueryHooks(tables, {
|
|
98
98
|
reactQueryEnabled,
|