@danielfgray/pg-sourcerer 0.1.9 → 0.2.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/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/plugins/kysely-queries.d.ts +54 -2
- package/dist/plugins/kysely-queries.d.ts.map +1 -1
- package/dist/plugins/kysely-queries.js +541 -62
- package/dist/plugins/kysely-queries.js.map +1 -1
- package/dist/plugins/kysely-types.d.ts +35 -0
- package/dist/plugins/kysely-types.d.ts.map +1 -0
- package/dist/plugins/kysely-types.js +601 -0
- package/dist/plugins/kysely-types.js.map +1 -0
- package/dist/plugins/sql-queries.d.ts +0 -2
- package/dist/plugins/sql-queries.d.ts.map +1 -1
- package/dist/plugins/sql-queries.js +1 -4
- package/dist/plugins/sql-queries.js.map +1 -1
- package/dist/services/emissions.d.ts.map +1 -1
- package/dist/services/emissions.js +31 -23
- package/dist/services/emissions.js.map +1 -1
- package/dist/services/file-writer.d.ts +0 -4
- package/dist/services/file-writer.d.ts.map +1 -1
- package/dist/services/file-writer.js +3 -12
- package/dist/services/file-writer.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,24 +6,28 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Schema as S } from "effect";
|
|
8
8
|
import { definePlugin } from "../services/plugin.js";
|
|
9
|
-
import { getTableEntities, getEnumEntities } from "../ir/semantic-ir.js";
|
|
9
|
+
import { getTableEntities, getEnumEntities, getFunctionEntities, getCompositeEntities } from "../ir/semantic-ir.js";
|
|
10
10
|
import { conjure, cast } from "../lib/conjure.js";
|
|
11
11
|
import { resolveFieldType, tsTypeToAst } from "../lib/field-utils.js";
|
|
12
12
|
import { inflect } from "../services/inflection.js";
|
|
13
13
|
const { ts, b } = conjure;
|
|
14
14
|
const { toExpr } = cast;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
15
|
+
/** Default export name: entityName + methodName (e.g., "UserFindById") */
|
|
16
|
+
const defaultExportName = (entityName, methodName) => `${entityName}${methodName}`;
|
|
17
|
+
/** Default function export name: camelCase of pg function name */
|
|
18
|
+
const defaultFunctionExportName = (pgFunctionName) => inflect.camelCase(pgFunctionName);
|
|
19
|
+
/**
|
|
20
|
+
* Schema for serializable config options (JSON/YAML compatible).
|
|
21
|
+
* Function options are typed separately in KyselyQueriesConfigInput.
|
|
22
|
+
*/
|
|
23
|
+
const KyselyQueriesPluginConfigSchema = S.Struct({
|
|
19
24
|
outputDir: S.optionalWith(S.String, { default: () => "kysely-queries" }),
|
|
20
|
-
header: S.optional(S.String),
|
|
21
25
|
/**
|
|
22
26
|
* Path to import DB type from (relative to outputDir).
|
|
23
|
-
* Defaults to "../
|
|
24
|
-
* For node16/nodenext module resolution, use ".js" extension even for .
|
|
27
|
+
* Defaults to "../db.js" which works with kysely-types plugin output.
|
|
28
|
+
* For node16/nodenext module resolution, use ".js" extension even for .ts files.
|
|
25
29
|
*/
|
|
26
|
-
dbTypesPath: S.optionalWith(S.String, { default: () => "../
|
|
30
|
+
dbTypesPath: S.optionalWith(S.String, { default: () => "../db.js" }),
|
|
27
31
|
/**
|
|
28
32
|
* Whether to call .execute() / .executeTakeFirst() on queries.
|
|
29
33
|
* When true (default), methods return Promise<Row> or Promise<Row[]>.
|
|
@@ -36,15 +40,37 @@ const KyselyQueriesPluginConfig = S.Struct({
|
|
|
36
40
|
* When enabled, generates: listMany(db, limit = 50, offset = 0)
|
|
37
41
|
*/
|
|
38
42
|
generateListMany: S.optionalWith(S.Boolean, { default: () => false }),
|
|
43
|
+
/**
|
|
44
|
+
* Whether to generate function wrappers for stored functions.
|
|
45
|
+
* When true (default), generates queries/mutations namespaces in functions.ts.
|
|
46
|
+
*/
|
|
47
|
+
generateFunctions: S.optionalWith(S.Boolean, { default: () => true }),
|
|
48
|
+
/**
|
|
49
|
+
* Output file name for function wrappers (relative to outputDir).
|
|
50
|
+
*/
|
|
51
|
+
functionsFile: S.optionalWith(S.String, { default: () => "functions.ts" }),
|
|
52
|
+
/**
|
|
53
|
+
* Export name function (validated as Any, properly typed in KyselyQueriesConfigInput)
|
|
54
|
+
*/
|
|
55
|
+
exportName: S.optional(S.Any),
|
|
56
|
+
/**
|
|
57
|
+
* Function export name function (validated as Any, properly typed in KyselyQueriesConfigInput)
|
|
58
|
+
*/
|
|
59
|
+
functionExportName: S.optional(S.Any),
|
|
39
60
|
});
|
|
40
61
|
/**
|
|
41
62
|
* Get the Kysely table interface name from the entity.
|
|
42
|
-
*
|
|
43
|
-
* Uses the inflection utility to match kysely-codegen's naming convention.
|
|
63
|
+
* Uses entity.name which is already PascalCase from inflection (e.g., Users).
|
|
44
64
|
*/
|
|
45
|
-
const getTableTypeName = (entity) =>
|
|
46
|
-
/**
|
|
47
|
-
|
|
65
|
+
const getTableTypeName = (entity) => entity.name;
|
|
66
|
+
/**
|
|
67
|
+
* Get the table reference for Kysely queries.
|
|
68
|
+
* Uses schema-qualified name only if the schema is NOT in defaultSchemas.
|
|
69
|
+
* This matches the keys in the DB interface from kysely-types plugin.
|
|
70
|
+
*/
|
|
71
|
+
const getTableRef = (entity, defaultSchemas) => defaultSchemas.includes(entity.schemaName)
|
|
72
|
+
? entity.pgName
|
|
73
|
+
: `${entity.schemaName}.${entity.pgName}`;
|
|
48
74
|
/** Find a field in the row shape by column name */
|
|
49
75
|
const findRowField = (entity, columnName) => entity.shapes.row.fields.find(f => f.columnName === columnName);
|
|
50
76
|
/** Get the TypeScript type AST for a field */
|
|
@@ -132,6 +158,15 @@ const deleteFrom = (tableRef) => call(id("db"), "deleteFrom", [str(tableRef)]);
|
|
|
132
158
|
* Chain method call onto existing expression
|
|
133
159
|
*/
|
|
134
160
|
const chain = (expr, method, args = []) => call(expr, method, args);
|
|
161
|
+
/**
|
|
162
|
+
* Create an exported const declaration: export const name = value
|
|
163
|
+
*/
|
|
164
|
+
const exportConst = (name, value) => {
|
|
165
|
+
const constDecl = b.variableDeclaration("const", [
|
|
166
|
+
b.variableDeclarator(id(name), toExpr(value))
|
|
167
|
+
]);
|
|
168
|
+
return b.exportNamedDeclaration(constDecl, []);
|
|
169
|
+
};
|
|
135
170
|
/**
|
|
136
171
|
* Build arrow function expression: (params) => body
|
|
137
172
|
*/
|
|
@@ -147,21 +182,398 @@ const objProp = (key, value) => {
|
|
|
147
182
|
return prop;
|
|
148
183
|
};
|
|
149
184
|
// ============================================================================
|
|
185
|
+
// PostgreSQL Type Name to TypeScript Mapping
|
|
186
|
+
// ============================================================================
|
|
187
|
+
/**
|
|
188
|
+
* Map PostgreSQL type name to TypeScript type string.
|
|
189
|
+
* Used for function argument and return type resolution.
|
|
190
|
+
*/
|
|
191
|
+
const pgTypeNameToTs = (typeName) => {
|
|
192
|
+
// Normalize: strip schema prefix if present
|
|
193
|
+
const baseName = typeName.includes(".") ? typeName.split(".").pop() : typeName;
|
|
194
|
+
switch (baseName) {
|
|
195
|
+
// Boolean
|
|
196
|
+
case "bool":
|
|
197
|
+
case "boolean":
|
|
198
|
+
return "boolean";
|
|
199
|
+
// Integer types → number
|
|
200
|
+
case "int2":
|
|
201
|
+
case "smallint":
|
|
202
|
+
case "int4":
|
|
203
|
+
case "integer":
|
|
204
|
+
case "int":
|
|
205
|
+
case "oid":
|
|
206
|
+
case "float4":
|
|
207
|
+
case "real":
|
|
208
|
+
case "float8":
|
|
209
|
+
case "double precision":
|
|
210
|
+
return "number";
|
|
211
|
+
// Big integers/numeric → string (to avoid precision loss)
|
|
212
|
+
case "int8":
|
|
213
|
+
case "bigint":
|
|
214
|
+
case "numeric":
|
|
215
|
+
case "decimal":
|
|
216
|
+
case "money":
|
|
217
|
+
return "string";
|
|
218
|
+
// Text types → string
|
|
219
|
+
case "text":
|
|
220
|
+
case "varchar":
|
|
221
|
+
case "character varying":
|
|
222
|
+
case "char":
|
|
223
|
+
case "character":
|
|
224
|
+
case "bpchar":
|
|
225
|
+
case "name":
|
|
226
|
+
case "xml":
|
|
227
|
+
case "bit":
|
|
228
|
+
case "varbit":
|
|
229
|
+
case "bit varying":
|
|
230
|
+
case "uuid":
|
|
231
|
+
case "inet":
|
|
232
|
+
case "cidr":
|
|
233
|
+
case "macaddr":
|
|
234
|
+
case "macaddr8":
|
|
235
|
+
case "time":
|
|
236
|
+
case "timetz":
|
|
237
|
+
case "time with time zone":
|
|
238
|
+
case "time without time zone":
|
|
239
|
+
case "interval":
|
|
240
|
+
return "string";
|
|
241
|
+
// Date/Time with date component → Date
|
|
242
|
+
case "date":
|
|
243
|
+
case "timestamp":
|
|
244
|
+
case "timestamptz":
|
|
245
|
+
case "timestamp with time zone":
|
|
246
|
+
case "timestamp without time zone":
|
|
247
|
+
return "Date";
|
|
248
|
+
// JSON → unknown
|
|
249
|
+
case "json":
|
|
250
|
+
case "jsonb":
|
|
251
|
+
case "jsonpath":
|
|
252
|
+
return "unknown";
|
|
253
|
+
// Binary → Buffer
|
|
254
|
+
case "bytea":
|
|
255
|
+
return "Buffer";
|
|
256
|
+
// Void
|
|
257
|
+
case "void":
|
|
258
|
+
return "void";
|
|
259
|
+
// Default to unknown
|
|
260
|
+
default:
|
|
261
|
+
return "unknown";
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
/**
|
|
265
|
+
* Check if a function argument type matches a table/view entity (row type argument).
|
|
266
|
+
* Functions with row-type arguments are computed fields (e.g., posts_short_body(posts))
|
|
267
|
+
* and should be excluded from function wrapper generation.
|
|
268
|
+
*/
|
|
269
|
+
const hasRowTypeArg = (arg, ir) => {
|
|
270
|
+
const tableEntities = getTableEntities(ir);
|
|
271
|
+
// Check if arg.typeName matches a table entity's qualified name
|
|
272
|
+
// Format: "schema.tablename" or just "tablename" for public schema
|
|
273
|
+
return tableEntities.some(entity => {
|
|
274
|
+
const qualifiedName = `${entity.schemaName}.${entity.pgName}`;
|
|
275
|
+
return arg.typeName === qualifiedName || arg.typeName === entity.pgName;
|
|
276
|
+
});
|
|
277
|
+
};
|
|
278
|
+
/**
|
|
279
|
+
* Check if a function should be included in generated wrappers.
|
|
280
|
+
*
|
|
281
|
+
* Includes functions that:
|
|
282
|
+
* - Have canExecute permission
|
|
283
|
+
* - Are not trigger functions
|
|
284
|
+
* - Are not from extensions
|
|
285
|
+
* - Are not @omit tagged
|
|
286
|
+
* - Don't have row-type arguments (computed fields)
|
|
287
|
+
*/
|
|
288
|
+
const isGeneratableFunction = (fn, ir) => {
|
|
289
|
+
if (!fn.canExecute)
|
|
290
|
+
return false;
|
|
291
|
+
if (fn.returnTypeName === "trigger")
|
|
292
|
+
return false;
|
|
293
|
+
if (fn.isFromExtension)
|
|
294
|
+
return false;
|
|
295
|
+
if (fn.tags.omit === true)
|
|
296
|
+
return false;
|
|
297
|
+
// Check for row-type args (computed field pattern)
|
|
298
|
+
if (fn.args.some(arg => hasRowTypeArg(arg, ir)))
|
|
299
|
+
return false;
|
|
300
|
+
return true;
|
|
301
|
+
};
|
|
302
|
+
/**
|
|
303
|
+
* Categorize functions by volatility.
|
|
304
|
+
* Volatile functions go in mutations namespace, stable/immutable in queries.
|
|
305
|
+
*/
|
|
306
|
+
const categorizeFunction = (fn) => fn.volatility === "volatile" ? "mutations" : "queries";
|
|
307
|
+
/**
|
|
308
|
+
* Get all generatable functions from the IR, categorized by volatility.
|
|
309
|
+
*/
|
|
310
|
+
const getGeneratableFunctions = (ir) => {
|
|
311
|
+
const all = getFunctionEntities(ir).filter(fn => isGeneratableFunction(fn, ir));
|
|
312
|
+
return {
|
|
313
|
+
queries: all.filter(fn => categorizeFunction(fn) === "queries"),
|
|
314
|
+
mutations: all.filter(fn => categorizeFunction(fn) === "mutations"),
|
|
315
|
+
};
|
|
316
|
+
};
|
|
317
|
+
/**
|
|
318
|
+
* Resolve a function's return type to TypeScript type information.
|
|
319
|
+
*/
|
|
320
|
+
const resolveReturnType = (fn, ir) => {
|
|
321
|
+
const returnTypeName = fn.returnTypeName;
|
|
322
|
+
const isArray = fn.returnsSet;
|
|
323
|
+
// 1. Check if it's a table return type
|
|
324
|
+
const tableEntities = getTableEntities(ir);
|
|
325
|
+
const tableMatch = tableEntities.find(entity => {
|
|
326
|
+
const qualifiedName = `${entity.schemaName}.${entity.pgName}`;
|
|
327
|
+
return returnTypeName === qualifiedName || returnTypeName === entity.pgName;
|
|
328
|
+
});
|
|
329
|
+
if (tableMatch) {
|
|
330
|
+
return {
|
|
331
|
+
tsType: tableMatch.name,
|
|
332
|
+
isArray,
|
|
333
|
+
isScalar: false,
|
|
334
|
+
needsImport: tableMatch.name,
|
|
335
|
+
returnEntity: tableMatch,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
// 2. Check if it's a composite type return
|
|
339
|
+
const compositeEntities = getCompositeEntities(ir);
|
|
340
|
+
const compositeMatch = compositeEntities.find(entity => {
|
|
341
|
+
const qualifiedName = `${entity.schemaName}.${entity.pgName}`;
|
|
342
|
+
return returnTypeName === qualifiedName || returnTypeName === entity.pgName;
|
|
343
|
+
});
|
|
344
|
+
if (compositeMatch) {
|
|
345
|
+
return {
|
|
346
|
+
tsType: compositeMatch.name,
|
|
347
|
+
isArray,
|
|
348
|
+
isScalar: false,
|
|
349
|
+
needsImport: compositeMatch.name,
|
|
350
|
+
returnEntity: compositeMatch,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
// 3. It's a scalar type - map via type name
|
|
354
|
+
// Handle "schema.typename" format by extracting just the type name
|
|
355
|
+
const baseTypeName = returnTypeName.includes(".")
|
|
356
|
+
? returnTypeName.split(".").pop()
|
|
357
|
+
: returnTypeName;
|
|
358
|
+
const tsType = pgTypeNameToTs(baseTypeName);
|
|
359
|
+
return {
|
|
360
|
+
tsType,
|
|
361
|
+
isArray,
|
|
362
|
+
isScalar: true,
|
|
363
|
+
};
|
|
364
|
+
};
|
|
365
|
+
/**
|
|
366
|
+
* Resolve a function argument to TypeScript type information.
|
|
367
|
+
*/
|
|
368
|
+
const resolveArg = (arg, ir) => {
|
|
369
|
+
const typeName = arg.typeName;
|
|
370
|
+
// Check if it's an array type (ends with [])
|
|
371
|
+
const isArrayType = typeName.endsWith("[]");
|
|
372
|
+
const baseTypeName = isArrayType ? typeName.slice(0, -2) : typeName;
|
|
373
|
+
// Check enums
|
|
374
|
+
const enums = getEnumEntities(ir);
|
|
375
|
+
const enumMatch = enums.find(e => {
|
|
376
|
+
const qualifiedName = `${e.schemaName}.${e.pgName}`;
|
|
377
|
+
return baseTypeName === qualifiedName || baseTypeName === e.pgName;
|
|
378
|
+
});
|
|
379
|
+
if (enumMatch) {
|
|
380
|
+
const tsType = isArrayType ? `${enumMatch.name}[]` : enumMatch.name;
|
|
381
|
+
return {
|
|
382
|
+
name: arg.name || "arg",
|
|
383
|
+
tsType,
|
|
384
|
+
isOptional: arg.hasDefault,
|
|
385
|
+
needsImport: enumMatch.name,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
// Check composites
|
|
389
|
+
const composites = getCompositeEntities(ir);
|
|
390
|
+
const compositeMatch = composites.find(e => {
|
|
391
|
+
const qualifiedName = `${e.schemaName}.${e.pgName}`;
|
|
392
|
+
return baseTypeName === qualifiedName || baseTypeName === e.pgName;
|
|
393
|
+
});
|
|
394
|
+
if (compositeMatch) {
|
|
395
|
+
const tsType = isArrayType ? `${compositeMatch.name}[]` : compositeMatch.name;
|
|
396
|
+
return {
|
|
397
|
+
name: arg.name || "arg",
|
|
398
|
+
tsType,
|
|
399
|
+
isOptional: arg.hasDefault,
|
|
400
|
+
needsImport: compositeMatch.name,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
// Scalar type - map via type name
|
|
404
|
+
// Handle "schema.typename" format
|
|
405
|
+
const scalarBase = baseTypeName.includes(".")
|
|
406
|
+
? baseTypeName.split(".").pop()
|
|
407
|
+
: baseTypeName;
|
|
408
|
+
const scalarTs = pgTypeNameToTs(scalarBase);
|
|
409
|
+
const tsType = isArrayType ? `${scalarTs}[]` : scalarTs;
|
|
410
|
+
return {
|
|
411
|
+
name: arg.name || "arg",
|
|
412
|
+
tsType,
|
|
413
|
+
isOptional: arg.hasDefault,
|
|
414
|
+
};
|
|
415
|
+
};
|
|
416
|
+
/**
|
|
417
|
+
* Resolve all arguments for a function.
|
|
418
|
+
*/
|
|
419
|
+
const resolveArgs = (fn, ir) => fn.args.map(arg => resolveArg(arg, ir));
|
|
420
|
+
// ============================================================================
|
|
421
|
+
// Function Wrapper AST Generation
|
|
422
|
+
// ============================================================================
|
|
423
|
+
/**
|
|
424
|
+
* Generate a typed parameter with explicit type annotation from type string.
|
|
425
|
+
*/
|
|
426
|
+
const typedParamFromString = (name, typeStr) => {
|
|
427
|
+
const param = id(name);
|
|
428
|
+
// Map type string to AST
|
|
429
|
+
let typeAst;
|
|
430
|
+
switch (typeStr) {
|
|
431
|
+
case "string":
|
|
432
|
+
typeAst = ts.string();
|
|
433
|
+
break;
|
|
434
|
+
case "number":
|
|
435
|
+
typeAst = ts.number();
|
|
436
|
+
break;
|
|
437
|
+
case "boolean":
|
|
438
|
+
typeAst = ts.boolean();
|
|
439
|
+
break;
|
|
440
|
+
case "Date":
|
|
441
|
+
typeAst = ts.ref("Date");
|
|
442
|
+
break;
|
|
443
|
+
case "Buffer":
|
|
444
|
+
typeAst = ts.ref("Buffer");
|
|
445
|
+
break;
|
|
446
|
+
case "unknown":
|
|
447
|
+
typeAst = ts.unknown();
|
|
448
|
+
break;
|
|
449
|
+
case "void":
|
|
450
|
+
typeAst = ts.void();
|
|
451
|
+
break;
|
|
452
|
+
default:
|
|
453
|
+
// Handle array types like "string[]"
|
|
454
|
+
if (typeStr.endsWith("[]")) {
|
|
455
|
+
const elemType = typeStr.slice(0, -2);
|
|
456
|
+
const elemAst = elemType === "string" ? ts.string()
|
|
457
|
+
: elemType === "number" ? ts.number()
|
|
458
|
+
: elemType === "boolean" ? ts.boolean()
|
|
459
|
+
: ts.ref(elemType);
|
|
460
|
+
typeAst = ts.array(elemAst);
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
// Assume it's a type reference (composite, enum, etc.)
|
|
464
|
+
typeAst = ts.ref(typeStr);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
param.typeAnnotation = b.tsTypeAnnotation(cast.toTSType(typeAst));
|
|
468
|
+
return param;
|
|
469
|
+
};
|
|
470
|
+
/**
|
|
471
|
+
* Generate an optional typed parameter with explicit type annotation.
|
|
472
|
+
*/
|
|
473
|
+
const optionalTypedParamFromString = (name, typeStr) => {
|
|
474
|
+
const param = typedParamFromString(name, typeStr);
|
|
475
|
+
param.optional = true;
|
|
476
|
+
return param;
|
|
477
|
+
};
|
|
478
|
+
/**
|
|
479
|
+
* Get the fully qualified function name for use in eb.fn call.
|
|
480
|
+
*/
|
|
481
|
+
const getFunctionQualifiedName = (fn) => `${fn.schemaName}.${fn.pgName}`;
|
|
482
|
+
/**
|
|
483
|
+
* Generate a function wrapper method as an object property.
|
|
484
|
+
*
|
|
485
|
+
* Patterns:
|
|
486
|
+
* - SETOF/table return: db.selectFrom(eb => eb.fn<Type>(...).as('f')).selectAll().execute()
|
|
487
|
+
* - Single row return: db.selectFrom(eb => eb.fn<Type>(...).as('f')).selectAll().executeTakeFirst()
|
|
488
|
+
* - Scalar return: db.selectNoFrom(eb => eb.fn<Type>(...).as('result')).executeTakeFirst().then(r => r?.result)
|
|
489
|
+
*/
|
|
490
|
+
const generateFunctionWrapper = (fn, ir, executeQueries, functionExportName) => {
|
|
491
|
+
const resolvedReturn = resolveReturnType(fn, ir);
|
|
492
|
+
const resolvedArgs = resolveArgs(fn, ir);
|
|
493
|
+
const qualifiedName = getFunctionQualifiedName(fn);
|
|
494
|
+
// Build eb.val(arg) for each argument
|
|
495
|
+
const fnArgs = resolvedArgs.map(arg => call(id("eb"), "val", [id(arg.name)]));
|
|
496
|
+
// Build eb.fn<Type>('schema.fn_name', [args]).as('alias')
|
|
497
|
+
// The type parameter is the return type
|
|
498
|
+
const returnTypeAst = resolvedReturn.isScalar
|
|
499
|
+
? typedParamFromString("_", resolvedReturn.tsType).typeAnnotation.typeAnnotation
|
|
500
|
+
: ts.ref(resolvedReturn.tsType);
|
|
501
|
+
// Create eb.fn with type parameter: eb.fn<Type>
|
|
502
|
+
const fnMember = b.memberExpression(id("eb"), id("fn"));
|
|
503
|
+
const fnWithType = b.tsInstantiationExpression(fnMember, b.tsTypeParameterInstantiation([cast.toTSType(returnTypeAst)]));
|
|
504
|
+
// Call it: eb.fn<Type>(name, args)
|
|
505
|
+
const fnCallBase = b.callExpression(fnWithType, [str(qualifiedName), b.arrayExpression(fnArgs.map(toExpr))]);
|
|
506
|
+
// .as('f') or .as('result') for scalar
|
|
507
|
+
const alias = resolvedReturn.isScalar ? "result" : "f";
|
|
508
|
+
const fnCallWithAlias = call(fnCallBase, "as", [str(alias)]);
|
|
509
|
+
// Arrow function for selectFrom callback: eb => eb.fn<...>(...).as('f')
|
|
510
|
+
const selectCallback = arrowFn([id("eb")], fnCallWithAlias);
|
|
511
|
+
// Build the query chain
|
|
512
|
+
let query;
|
|
513
|
+
if (resolvedReturn.isScalar) {
|
|
514
|
+
// Scalar: db.selectNoFrom(eb => ...).executeTakeFirst()
|
|
515
|
+
// Returns { result: T } | undefined - caller accesses .result
|
|
516
|
+
query = call(id("db"), "selectNoFrom", [selectCallback]);
|
|
517
|
+
if (executeQueries) {
|
|
518
|
+
query = chain(query, "executeTakeFirst");
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
// Table/composite: db.selectFrom(eb => ...).selectAll()
|
|
523
|
+
query = chain(call(id("db"), "selectFrom", [selectCallback]), "selectAll");
|
|
524
|
+
if (executeQueries) {
|
|
525
|
+
// SETOF → .execute(), single row → .executeTakeFirst()
|
|
526
|
+
query = chain(query, resolvedReturn.isArray ? "execute" : "executeTakeFirst");
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
// Build the parameters: (db: Kysely<DB>, arg1: Type1, arg2?: Type2, ...)
|
|
530
|
+
const params = [
|
|
531
|
+
typedParam("db", ts.ref("Kysely", [ts.ref("DB")])),
|
|
532
|
+
...resolvedArgs.map(arg => arg.isOptional
|
|
533
|
+
? optionalTypedParamFromString(arg.name, arg.tsType)
|
|
534
|
+
: typedParamFromString(arg.name, arg.tsType))
|
|
535
|
+
];
|
|
536
|
+
const wrapperFn = arrowFn(params, query);
|
|
537
|
+
const exportName = functionExportName(fn.pgName);
|
|
538
|
+
const constDecl = b.variableDeclaration("const", [
|
|
539
|
+
b.variableDeclarator(id(exportName), wrapperFn)
|
|
540
|
+
]);
|
|
541
|
+
return b.exportNamedDeclaration(constDecl, []);
|
|
542
|
+
};
|
|
543
|
+
/**
|
|
544
|
+
* Collect all type imports needed for function wrappers.
|
|
545
|
+
*/
|
|
546
|
+
const collectFunctionTypeImports = (functions, ir) => {
|
|
547
|
+
const imports = new Set();
|
|
548
|
+
for (const fn of functions) {
|
|
549
|
+
const resolvedReturn = resolveReturnType(fn, ir);
|
|
550
|
+
if (resolvedReturn.needsImport) {
|
|
551
|
+
imports.add(resolvedReturn.needsImport);
|
|
552
|
+
}
|
|
553
|
+
for (const arg of resolveArgs(fn, ir)) {
|
|
554
|
+
if (arg.needsImport) {
|
|
555
|
+
imports.add(arg.needsImport);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return imports;
|
|
560
|
+
};
|
|
561
|
+
// ============================================================================
|
|
150
562
|
// CRUD Method Generators
|
|
151
563
|
// ============================================================================
|
|
152
564
|
/**
|
|
153
565
|
* Generate findById method:
|
|
154
|
-
*
|
|
566
|
+
* export const UserFindById = (db, id) => db.selectFrom('table').selectAll().where('id', '=', id).executeTakeFirst()
|
|
155
567
|
*/
|
|
156
568
|
const generateFindById = (ctx) => {
|
|
157
|
-
const { entity, executeQueries } = ctx;
|
|
569
|
+
const { entity, executeQueries, defaultSchemas, entityName, exportName } = ctx;
|
|
158
570
|
if (!entity.primaryKey || !entity.permissions.canSelect)
|
|
159
571
|
return undefined;
|
|
160
572
|
const pkColName = entity.primaryKey.columns[0];
|
|
161
573
|
const pkField = findRowField(entity, pkColName);
|
|
162
574
|
if (!pkField)
|
|
163
575
|
return undefined;
|
|
164
|
-
const tableRef = getTableRef(entity);
|
|
576
|
+
const tableRef = getTableRef(entity, defaultSchemas);
|
|
165
577
|
const fieldName = pkField.name;
|
|
166
578
|
const fieldType = getFieldTypeAst(pkField, ctx);
|
|
167
579
|
// db.selectFrom('table').selectAll().where('col', '=', id)
|
|
@@ -170,7 +582,7 @@ const generateFindById = (ctx) => {
|
|
|
170
582
|
query = chain(query, "executeTakeFirst");
|
|
171
583
|
}
|
|
172
584
|
const fn = arrowFn([typedParam("db", ts.ref("Kysely", [ts.ref("DB")])), typedParam(fieldName, fieldType)], query);
|
|
173
|
-
return
|
|
585
|
+
return exportConst(exportName(entityName, "FindById"), fn);
|
|
174
586
|
};
|
|
175
587
|
/** Default limit for findMany queries */
|
|
176
588
|
const DEFAULT_LIMIT = 50;
|
|
@@ -183,14 +595,14 @@ const DEFAULT_OFFSET = 0;
|
|
|
183
595
|
const paramWithDefault = (name, defaultValue) => b.assignmentPattern(id(name), toExpr(defaultValue));
|
|
184
596
|
/**
|
|
185
597
|
* Generate listMany method with pagination defaults:
|
|
186
|
-
*
|
|
598
|
+
* export const UserListMany = (db, limit = 50, offset = 0) => db.selectFrom('table').selectAll()
|
|
187
599
|
* .limit(limit).offset(offset).execute()
|
|
188
600
|
*/
|
|
189
601
|
const generateListMany = (ctx) => {
|
|
190
|
-
const { entity, executeQueries } = ctx;
|
|
602
|
+
const { entity, executeQueries, defaultSchemas, entityName, exportName } = ctx;
|
|
191
603
|
if (!entity.permissions.canSelect)
|
|
192
604
|
return undefined;
|
|
193
|
-
const tableRef = getTableRef(entity);
|
|
605
|
+
const tableRef = getTableRef(entity, defaultSchemas);
|
|
194
606
|
// Build query: db.selectFrom('table').selectAll().limit(limit).offset(offset)
|
|
195
607
|
let query = chain(chain(chain(selectFrom(tableRef), "selectAll"), "limit", [id("limit")]), "offset", [id("offset")]);
|
|
196
608
|
// Add .execute() if executeQueries is true
|
|
@@ -202,17 +614,17 @@ const generateListMany = (ctx) => {
|
|
|
202
614
|
paramWithDefault("limit", b.numericLiteral(DEFAULT_LIMIT)),
|
|
203
615
|
paramWithDefault("offset", b.numericLiteral(DEFAULT_OFFSET)),
|
|
204
616
|
], query);
|
|
205
|
-
return
|
|
617
|
+
return exportConst(exportName(entityName, "ListMany"), fn);
|
|
206
618
|
};
|
|
207
619
|
/**
|
|
208
620
|
* Generate create method:
|
|
209
|
-
*
|
|
621
|
+
* export const UserCreate = (db, data) => db.insertInto('table').values(data).returningAll().executeTakeFirstOrThrow()
|
|
210
622
|
*/
|
|
211
623
|
const generateCreate = (ctx) => {
|
|
212
|
-
const { entity, executeQueries } = ctx;
|
|
624
|
+
const { entity, executeQueries, defaultSchemas, entityName, exportName } = ctx;
|
|
213
625
|
if (!entity.permissions.canInsert)
|
|
214
626
|
return undefined;
|
|
215
|
-
const tableRef = getTableRef(entity);
|
|
627
|
+
const tableRef = getTableRef(entity, defaultSchemas);
|
|
216
628
|
const tableTypeName = getTableTypeName(entity);
|
|
217
629
|
// db.insertInto('table').values(data).returningAll()
|
|
218
630
|
let query = chain(chain(insertInto(tableRef), "values", [id("data")]), "returningAll");
|
|
@@ -224,21 +636,21 @@ const generateCreate = (ctx) => {
|
|
|
224
636
|
typedParam("db", ts.ref("Kysely", [ts.ref("DB")])),
|
|
225
637
|
typedParam("data", ts.ref("Insertable", [ts.ref(tableTypeName)])),
|
|
226
638
|
], query);
|
|
227
|
-
return
|
|
639
|
+
return exportConst(exportName(entityName, "Create"), fn);
|
|
228
640
|
};
|
|
229
641
|
/**
|
|
230
642
|
* Generate update method:
|
|
231
|
-
*
|
|
643
|
+
* export const UserUpdate = (db, id, data) => db.updateTable('table').set(data).where('id', '=', id).returningAll().executeTakeFirstOrThrow()
|
|
232
644
|
*/
|
|
233
645
|
const generateUpdate = (ctx) => {
|
|
234
|
-
const { entity, executeQueries } = ctx;
|
|
646
|
+
const { entity, executeQueries, defaultSchemas, entityName, exportName } = ctx;
|
|
235
647
|
if (!entity.primaryKey || !entity.permissions.canUpdate)
|
|
236
648
|
return undefined;
|
|
237
649
|
const pkColName = entity.primaryKey.columns[0];
|
|
238
650
|
const pkField = findRowField(entity, pkColName);
|
|
239
651
|
if (!pkField)
|
|
240
652
|
return undefined;
|
|
241
|
-
const tableRef = getTableRef(entity);
|
|
653
|
+
const tableRef = getTableRef(entity, defaultSchemas);
|
|
242
654
|
const fieldName = pkField.name;
|
|
243
655
|
const fieldType = getFieldTypeAst(pkField, ctx);
|
|
244
656
|
const tableTypeName = getTableTypeName(entity);
|
|
@@ -253,21 +665,21 @@ const generateUpdate = (ctx) => {
|
|
|
253
665
|
typedParam(fieldName, fieldType),
|
|
254
666
|
typedParam("data", ts.ref("Updateable", [ts.ref(tableTypeName)])),
|
|
255
667
|
], query);
|
|
256
|
-
return
|
|
668
|
+
return exportConst(exportName(entityName, "Update"), fn);
|
|
257
669
|
};
|
|
258
670
|
/**
|
|
259
671
|
* Generate delete method:
|
|
260
|
-
*
|
|
672
|
+
* export const UserRemove = (db, id) => db.deleteFrom('table').where('id', '=', id).execute()
|
|
261
673
|
*/
|
|
262
674
|
const generateDelete = (ctx) => {
|
|
263
|
-
const { entity, executeQueries } = ctx;
|
|
675
|
+
const { entity, executeQueries, defaultSchemas, entityName, exportName } = ctx;
|
|
264
676
|
if (!entity.primaryKey || !entity.permissions.canDelete)
|
|
265
677
|
return undefined;
|
|
266
678
|
const pkColName = entity.primaryKey.columns[0];
|
|
267
679
|
const pkField = findRowField(entity, pkColName);
|
|
268
680
|
if (!pkField)
|
|
269
681
|
return undefined;
|
|
270
|
-
const tableRef = getTableRef(entity);
|
|
682
|
+
const tableRef = getTableRef(entity, defaultSchemas);
|
|
271
683
|
const fieldName = pkField.name;
|
|
272
684
|
const fieldType = getFieldTypeAst(pkField, ctx);
|
|
273
685
|
// db.deleteFrom('table').where('id', '=', id)
|
|
@@ -276,8 +688,7 @@ const generateDelete = (ctx) => {
|
|
|
276
688
|
query = chain(query, "execute");
|
|
277
689
|
}
|
|
278
690
|
const fn = arrowFn([typedParam("db", ts.ref("Kysely", [ts.ref("DB")])), typedParam(fieldName, fieldType)], query);
|
|
279
|
-
|
|
280
|
-
return objProp("remove", fn);
|
|
691
|
+
return exportConst(exportName(entityName, "Remove"), fn);
|
|
281
692
|
};
|
|
282
693
|
/** Generate all CRUD methods for an entity */
|
|
283
694
|
const generateCrudMethods = (ctx) => [
|
|
@@ -297,27 +708,27 @@ const shouldGenerateLookup = (index) => !index.isPartial &&
|
|
|
297
708
|
index.method !== "gin" &&
|
|
298
709
|
index.method !== "gist";
|
|
299
710
|
/**
|
|
300
|
-
* Generate
|
|
711
|
+
* Generate the method name portion for an index-based lookup.
|
|
301
712
|
* Uses semantic naming when the column corresponds to an FK relation.
|
|
302
713
|
*/
|
|
303
|
-
const
|
|
714
|
+
const generateLookupMethodName = (entity, index, relation) => {
|
|
304
715
|
const isUnique = isUniqueLookup(entity, index);
|
|
305
|
-
//
|
|
306
|
-
const
|
|
716
|
+
// Uses "FindOneBy" or "FindManyBy" suffix
|
|
717
|
+
const suffix = isUnique ? "FindOneBy" : "FindManyBy";
|
|
307
718
|
// Use semantic name if FK relation exists, otherwise fall back to column name
|
|
308
719
|
const columnName = index.columnNames[0];
|
|
309
720
|
const byName = relation
|
|
310
721
|
? deriveSemanticName(relation, columnName)
|
|
311
722
|
: index.columns[0];
|
|
312
|
-
return `${
|
|
723
|
+
return `${suffix}${toPascalCase(byName)}`;
|
|
313
724
|
};
|
|
314
725
|
/**
|
|
315
726
|
* Generate a lookup method for a single-column index.
|
|
316
727
|
* Uses semantic parameter naming when the column corresponds to an FK relation.
|
|
317
728
|
*/
|
|
318
729
|
const generateLookupMethod = (index, ctx) => {
|
|
319
|
-
const { entity, executeQueries } = ctx;
|
|
320
|
-
const tableRef = getTableRef(entity);
|
|
730
|
+
const { entity, executeQueries, defaultSchemas, entityName, exportName } = ctx;
|
|
731
|
+
const tableRef = getTableRef(entity, defaultSchemas);
|
|
321
732
|
const columnName = index.columnNames[0];
|
|
322
733
|
const field = findRowField(entity, columnName);
|
|
323
734
|
const fieldName = field?.name ?? index.columns[0];
|
|
@@ -342,8 +753,8 @@ const generateLookupMethod = (index, ctx) => {
|
|
|
342
753
|
query = chain(query, isUnique ? "executeTakeFirst" : "execute");
|
|
343
754
|
}
|
|
344
755
|
const fn = arrowFn([typedParam("db", ts.ref("Kysely", [ts.ref("DB")])), typedParam(paramName, paramType)], query);
|
|
345
|
-
const methodName =
|
|
346
|
-
return
|
|
756
|
+
const methodName = generateLookupMethodName(entity, index, relation);
|
|
757
|
+
return exportConst(exportName(entityName, methodName), fn);
|
|
347
758
|
};
|
|
348
759
|
/**
|
|
349
760
|
* Check if a column is covered by a unique constraint (not just unique index).
|
|
@@ -406,22 +817,50 @@ export const kyselyQueriesPlugin = definePlugin({
|
|
|
406
817
|
name: "kysely-queries",
|
|
407
818
|
provides: ["queries", "queries:kysely"],
|
|
408
819
|
requires: [], // No dependency on types:kysely for now - uses external kysely-codegen types
|
|
409
|
-
configSchema:
|
|
820
|
+
configSchema: KyselyQueriesPluginConfigSchema,
|
|
410
821
|
inflection: {
|
|
411
822
|
outputFile: ctx => `${ctx.entityName}.ts`,
|
|
412
823
|
symbolName: (entityName, artifactKind) => `${entityName}${artifactKind}`,
|
|
413
824
|
},
|
|
414
|
-
run: (ctx,
|
|
825
|
+
run: (ctx, rawConfig) => {
|
|
826
|
+
// Resolve config with function defaults
|
|
827
|
+
const config = {
|
|
828
|
+
...rawConfig,
|
|
829
|
+
exportName: rawConfig.exportName ?? defaultExportName,
|
|
830
|
+
functionExportName: rawConfig.functionExportName ?? defaultFunctionExportName,
|
|
831
|
+
};
|
|
415
832
|
const enums = getEnumEntities(ctx.ir);
|
|
416
|
-
const
|
|
833
|
+
const defaultSchemas = ctx.ir.schemas;
|
|
834
|
+
const { dbTypesPath, executeQueries, generateListMany, exportName, functionExportName } = config;
|
|
835
|
+
// Pre-compute function groupings by return entity name
|
|
836
|
+
// Functions returning entities go in that entity's file; scalars go in functions.ts
|
|
837
|
+
const functionsByEntity = new Map();
|
|
838
|
+
const scalarFunctions = [];
|
|
839
|
+
if (config.generateFunctions) {
|
|
840
|
+
const { queries, mutations } = getGeneratableFunctions(ctx.ir);
|
|
841
|
+
const allFunctions = [...queries, ...mutations];
|
|
842
|
+
for (const fn of allFunctions) {
|
|
843
|
+
const resolved = resolveReturnType(fn, ctx.ir);
|
|
844
|
+
if (resolved.returnEntity) {
|
|
845
|
+
const entityName = resolved.returnEntity.name;
|
|
846
|
+
const existing = functionsByEntity.get(entityName) ?? [];
|
|
847
|
+
functionsByEntity.set(entityName, [...existing, fn]);
|
|
848
|
+
}
|
|
849
|
+
else {
|
|
850
|
+
scalarFunctions.push(fn);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
417
854
|
getTableEntities(ctx.ir)
|
|
418
855
|
.filter(entity => entity.tags.omit !== true)
|
|
419
856
|
.forEach(entity => {
|
|
420
|
-
const genCtx = { entity, enums, ir: ctx.ir, dbTypesPath, executeQueries, generateListMany };
|
|
421
|
-
const methods = [...generateCrudMethods(genCtx), ...generateLookupMethods(genCtx)];
|
|
422
|
-
if (methods.length === 0)
|
|
423
|
-
return;
|
|
424
857
|
const entityName = ctx.inflection.entityName(entity.pgClass, entity.tags);
|
|
858
|
+
const genCtx = { entity, enums, ir: ctx.ir, defaultSchemas, dbTypesPath, executeQueries, generateListMany, entityName, exportName };
|
|
859
|
+
const crudStatements = [...generateCrudMethods(genCtx), ...generateLookupMethods(genCtx)];
|
|
860
|
+
// Get functions that return this entity
|
|
861
|
+
const entityFunctions = functionsByEntity.get(entity.name) ?? [];
|
|
862
|
+
if (crudStatements.length === 0 && entityFunctions.length === 0)
|
|
863
|
+
return;
|
|
425
864
|
const fileNameCtx = {
|
|
426
865
|
entityName,
|
|
427
866
|
pgName: entity.pgName,
|
|
@@ -430,17 +869,14 @@ export const kyselyQueriesPlugin = definePlugin({
|
|
|
430
869
|
entity,
|
|
431
870
|
};
|
|
432
871
|
const filePath = `${config.outputDir}/${ctx.pluginInflection.outputFile(fileNameCtx)}`;
|
|
433
|
-
//
|
|
434
|
-
const
|
|
435
|
-
//
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
]);
|
|
440
|
-
const exportDecl = b.exportNamedDeclaration(constDecl, []);
|
|
872
|
+
// All statements for the file: CRUD methods + function wrappers
|
|
873
|
+
const statements = [...crudStatements];
|
|
874
|
+
// Add function wrappers as flat exports
|
|
875
|
+
for (const fn of entityFunctions) {
|
|
876
|
+
statements.push(generateFunctionWrapper(fn, ctx.ir, executeQueries, config.functionExportName));
|
|
877
|
+
}
|
|
441
878
|
const file = ctx
|
|
442
|
-
.file(filePath)
|
|
443
|
-
.header(config.header ? `${config.header}\n` : "// This file is auto-generated. Do not edit.\n");
|
|
879
|
+
.file(filePath);
|
|
444
880
|
// Import Kysely type and DB from kysely-codegen output
|
|
445
881
|
file.import({ kind: "package", types: ["Kysely"], from: "kysely" });
|
|
446
882
|
file.import({ kind: "relative", types: ["DB"], from: dbTypesPath });
|
|
@@ -474,8 +910,51 @@ export const kyselyQueriesPlugin = definePlugin({
|
|
|
474
910
|
if (entity.permissions.canUpdate) {
|
|
475
911
|
file.import({ kind: "package", types: ["Updateable"], from: "kysely" });
|
|
476
912
|
}
|
|
477
|
-
file
|
|
913
|
+
// Import types needed by function args (for functions grouped into this file)
|
|
914
|
+
if (entityFunctions.length > 0) {
|
|
915
|
+
const fnTypeImports = collectFunctionTypeImports(entityFunctions, ctx.ir);
|
|
916
|
+
// Remove the entity's own type (already in scope or self-referential)
|
|
917
|
+
fnTypeImports.delete(entity.name);
|
|
918
|
+
if (fnTypeImports.size > 0) {
|
|
919
|
+
file.import({ kind: "relative", types: [...fnTypeImports], from: dbTypesPath });
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
file.ast(conjure.program(...statements)).emit();
|
|
478
923
|
});
|
|
924
|
+
// Generate files for composite types that have functions returning them
|
|
925
|
+
if (config.generateFunctions) {
|
|
926
|
+
const composites = getCompositeEntities(ctx.ir);
|
|
927
|
+
for (const composite of composites) {
|
|
928
|
+
const compositeFunctions = functionsByEntity.get(composite.name) ?? [];
|
|
929
|
+
if (compositeFunctions.length === 0)
|
|
930
|
+
continue;
|
|
931
|
+
const filePath = `${config.outputDir}/${composite.name}.ts`;
|
|
932
|
+
const statements = compositeFunctions.map(fn => generateFunctionWrapper(fn, ctx.ir, executeQueries, config.functionExportName));
|
|
933
|
+
const file = ctx.file(filePath);
|
|
934
|
+
file.import({ kind: "package", types: ["Kysely"], from: "kysely" });
|
|
935
|
+
file.import({ kind: "relative", types: ["DB"], from: dbTypesPath });
|
|
936
|
+
// Import the composite type and any types needed by function args
|
|
937
|
+
const fnTypeImports = collectFunctionTypeImports(compositeFunctions, ctx.ir);
|
|
938
|
+
fnTypeImports.add(composite.name); // Always import the composite type
|
|
939
|
+
file.import({ kind: "relative", types: [...fnTypeImports], from: dbTypesPath });
|
|
940
|
+
file.ast(conjure.program(...statements)).emit();
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
// Generate functions.ts for scalar-returning functions only
|
|
944
|
+
if (config.generateFunctions && scalarFunctions.length > 0) {
|
|
945
|
+
const filePath = `${config.outputDir}/${config.functionsFile}`;
|
|
946
|
+
const statements = scalarFunctions.map(fn => generateFunctionWrapper(fn, ctx.ir, executeQueries, config.functionExportName));
|
|
947
|
+
const file = ctx.file(filePath);
|
|
948
|
+
// Import Kysely type and DB
|
|
949
|
+
file.import({ kind: "package", types: ["Kysely"], from: "kysely" });
|
|
950
|
+
file.import({ kind: "relative", types: ["DB"], from: dbTypesPath });
|
|
951
|
+
// Import any types needed for function args (scalars don't need return type imports)
|
|
952
|
+
const typeImports = collectFunctionTypeImports(scalarFunctions, ctx.ir);
|
|
953
|
+
if (typeImports.size > 0) {
|
|
954
|
+
file.import({ kind: "relative", types: [...typeImports], from: dbTypesPath });
|
|
955
|
+
}
|
|
956
|
+
file.ast(conjure.program(...statements)).emit();
|
|
957
|
+
}
|
|
479
958
|
},
|
|
480
959
|
});
|
|
481
960
|
//# sourceMappingURL=kysely-queries.js.map
|