postgresdk 0.18.10 → 0.18.12
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/cli.js +69 -47
- package/dist/emit-sdk-bundle.d.ts +1 -1
- package/dist/index.js +59 -37
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -5,25 +5,43 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
function __accessProp(key) {
|
|
9
|
+
return this[key];
|
|
10
|
+
}
|
|
11
|
+
var __toESMCache_node;
|
|
12
|
+
var __toESMCache_esm;
|
|
8
13
|
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
+
var canCache = mod != null && typeof mod === "object";
|
|
15
|
+
if (canCache) {
|
|
16
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
+
var cached = cache.get(mod);
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
9
21
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
22
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
23
|
for (let key of __getOwnPropNames(mod))
|
|
12
24
|
if (!__hasOwnProp.call(to, key))
|
|
13
25
|
__defProp(to, key, {
|
|
14
|
-
get: (
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
15
27
|
enumerable: true
|
|
16
28
|
});
|
|
29
|
+
if (canCache)
|
|
30
|
+
cache.set(mod, to);
|
|
17
31
|
return to;
|
|
18
32
|
};
|
|
19
33
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
|
+
var __returnValue = (v) => v;
|
|
35
|
+
function __exportSetter(name, newValue) {
|
|
36
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
37
|
+
}
|
|
20
38
|
var __export = (target, all) => {
|
|
21
39
|
for (var name in all)
|
|
22
40
|
__defProp(target, name, {
|
|
23
41
|
get: all[name],
|
|
24
42
|
enumerable: true,
|
|
25
43
|
configurable: true,
|
|
26
|
-
set: (
|
|
44
|
+
set: __exportSetter.bind(all, name)
|
|
27
45
|
});
|
|
28
46
|
};
|
|
29
47
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -2119,7 +2137,7 @@ var exports_cli_init = {};
|
|
|
2119
2137
|
__export(exports_cli_init, {
|
|
2120
2138
|
initCommand: () => initCommand
|
|
2121
2139
|
});
|
|
2122
|
-
import { existsSync as existsSync3, writeFileSync, readFileSync, copyFileSync } from "fs";
|
|
2140
|
+
import { existsSync as existsSync3, writeFileSync, readFileSync as readFileSync2, copyFileSync } from "fs";
|
|
2123
2141
|
import { resolve } from "path";
|
|
2124
2142
|
import prompts from "prompts";
|
|
2125
2143
|
async function initCommand(args) {
|
|
@@ -2137,7 +2155,7 @@ async function initCommand(args) {
|
|
|
2137
2155
|
}
|
|
2138
2156
|
console.log(`⚠️ Found existing postgresdk.config.ts
|
|
2139
2157
|
`);
|
|
2140
|
-
const existingContent =
|
|
2158
|
+
const existingContent = readFileSync2(configPath, "utf-8");
|
|
2141
2159
|
const existingFields = extractConfigFields(existingContent);
|
|
2142
2160
|
console.log("\uD83D\uDCCB Existing configuration detected:");
|
|
2143
2161
|
existingFields.forEach((field) => {
|
|
@@ -2529,7 +2547,7 @@ __export(exports_cli_pull, {
|
|
|
2529
2547
|
pullCommand: () => pullCommand
|
|
2530
2548
|
});
|
|
2531
2549
|
import { writeFile as writeFile2, mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
|
|
2532
|
-
import { join as join2, dirname as
|
|
2550
|
+
import { join as join2, dirname as dirname3, resolve as resolve2 } from "path";
|
|
2533
2551
|
import { existsSync as existsSync4 } from "fs";
|
|
2534
2552
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
2535
2553
|
async function pullCommand(args) {
|
|
@@ -2616,7 +2634,7 @@ Options:`);
|
|
|
2616
2634
|
const changedFiles = [];
|
|
2617
2635
|
for (const [path, content] of Object.entries(sdk.files)) {
|
|
2618
2636
|
const fullPath = join2(config.output, path);
|
|
2619
|
-
await mkdir2(
|
|
2637
|
+
await mkdir2(dirname3(fullPath), { recursive: true });
|
|
2620
2638
|
let shouldWrite = true;
|
|
2621
2639
|
if (existsSync4(fullPath)) {
|
|
2622
2640
|
const existing = await readFile2(fullPath, "utf-8");
|
|
@@ -2662,9 +2680,9 @@ var init_cli_pull = () => {};
|
|
|
2662
2680
|
|
|
2663
2681
|
// src/index.ts
|
|
2664
2682
|
var import_config = __toESM(require_config(), 1);
|
|
2665
|
-
import { join, relative } from "node:path";
|
|
2666
|
-
import { pathToFileURL } from "node:url";
|
|
2667
|
-
import { existsSync as existsSync2 } from "node:fs";
|
|
2683
|
+
import { join, relative, dirname as dirname2 } from "node:path";
|
|
2684
|
+
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
2685
|
+
import { existsSync as existsSync2, readFileSync } from "node:fs";
|
|
2668
2686
|
|
|
2669
2687
|
// src/introspect.ts
|
|
2670
2688
|
import { Client } from "pg";
|
|
@@ -2935,9 +2953,10 @@ function emitIncludeResolver(graph, useJsExtensions) {
|
|
|
2935
2953
|
Select${Type} & {
|
|
2936
2954
|
[K in keyof TInclude as TInclude[K] extends false | undefined ? never : K]:`;
|
|
2937
2955
|
for (let i = 0;i < edgeEntries.length; i++) {
|
|
2938
|
-
const
|
|
2939
|
-
if (!
|
|
2956
|
+
const entry = edgeEntries[i];
|
|
2957
|
+
if (!entry)
|
|
2940
2958
|
continue;
|
|
2959
|
+
const [relKey, edge] = entry;
|
|
2941
2960
|
const targetType = pascal(edge.target);
|
|
2942
2961
|
const isLast = i === edgeEntries.length - 1;
|
|
2943
2962
|
out += `
|
|
@@ -3167,11 +3186,15 @@ function isVectorType(pgType) {
|
|
|
3167
3186
|
const t = pgType.toLowerCase();
|
|
3168
3187
|
return t === "vector" || t === "halfvec" || t === "sparsevec" || t === "bit";
|
|
3169
3188
|
}
|
|
3189
|
+
function isJsonbType(pgType) {
|
|
3190
|
+
return pgType.toLowerCase() === "json" || pgType.toLowerCase() === "jsonb";
|
|
3191
|
+
}
|
|
3170
3192
|
function emitHonoRoutes(table, _graph, opts) {
|
|
3171
3193
|
const fileTableName = table.name;
|
|
3172
3194
|
const Type = pascal(table.name);
|
|
3173
3195
|
const hasVectorColumns = table.columns.some((c) => isVectorType(c.pgType));
|
|
3174
3196
|
const vectorColumns = table.columns.filter((c) => isVectorType(c.pgType)).map((c) => c.name);
|
|
3197
|
+
const jsonbColumns = table.columns.filter((c) => isJsonbType(c.pgType)).map((c) => c.name);
|
|
3175
3198
|
const rawPk = table.pk;
|
|
3176
3199
|
const pkCols = Array.isArray(rawPk) ? rawPk : rawPk ? [rawPk] : [];
|
|
3177
3200
|
const safePkCols = pkCols.length ? pkCols : ["id"];
|
|
@@ -3258,7 +3281,8 @@ export function register${Type}Routes(app: Hono, deps: { pg: { query: (text: str
|
|
|
3258
3281
|
softDeleteColumn: ${softDel ? `"${softDel}"` : "null"},
|
|
3259
3282
|
includeMethodsDepth: ${opts.includeMethodsDepth},
|
|
3260
3283
|
allColumnNames: [${table.columns.map((c) => `"${c.name}"`).join(", ")}]${vectorColumns.length > 0 ? `,
|
|
3261
|
-
vectorColumns: ${JSON.stringify(vectorColumns)}` : ""}
|
|
3284
|
+
vectorColumns: ${JSON.stringify(vectorColumns)}` : ""}${jsonbColumns.length > 0 ? `,
|
|
3285
|
+
jsonbColumns: ${JSON.stringify(jsonbColumns)}` : ""}
|
|
3262
3286
|
};
|
|
3263
3287
|
${hasAuth ? `
|
|
3264
3288
|
// \uD83D\uDD10 Auth: protect all routes for this table
|
|
@@ -3476,7 +3500,7 @@ function isVectorType2(pgType) {
|
|
|
3476
3500
|
const t = pgType.toLowerCase();
|
|
3477
3501
|
return t === "vector" || t === "halfvec" || t === "sparsevec" || t === "bit";
|
|
3478
3502
|
}
|
|
3479
|
-
function
|
|
3503
|
+
function isJsonbType2(pgType) {
|
|
3480
3504
|
const t = pgType.toLowerCase();
|
|
3481
3505
|
return t === "json" || t === "jsonb";
|
|
3482
3506
|
}
|
|
@@ -3502,7 +3526,7 @@ function emitClient(table, graph, opts, model) {
|
|
|
3502
3526
|
const Type = pascal(table.name);
|
|
3503
3527
|
const ext = opts.useJsExtensions ? ".js" : "";
|
|
3504
3528
|
const hasVectorColumns = table.columns.some((c) => isVectorType2(c.pgType));
|
|
3505
|
-
const hasJsonbColumns = table.columns.some((c) =>
|
|
3529
|
+
const hasJsonbColumns = table.columns.some((c) => isJsonbType2(c.pgType));
|
|
3506
3530
|
if (process.env.SDK_DEBUG) {
|
|
3507
3531
|
const vectorCols = table.columns.filter((c) => isVectorType2(c.pgType));
|
|
3508
3532
|
if (vectorCols.length > 0) {
|
|
@@ -4972,14 +4996,14 @@ function tsTypeFor(pgType, opts, enums) {
|
|
|
4972
4996
|
return "number[]";
|
|
4973
4997
|
return "string";
|
|
4974
4998
|
}
|
|
4975
|
-
function
|
|
4999
|
+
function isJsonbType3(pgType) {
|
|
4976
5000
|
const t = pgType.toLowerCase();
|
|
4977
5001
|
return t === "json" || t === "jsonb";
|
|
4978
5002
|
}
|
|
4979
5003
|
var pascal2 = (s) => s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
|
|
4980
5004
|
function emitTypes(table, opts, enums) {
|
|
4981
5005
|
const Type = pascal2(table.name);
|
|
4982
|
-
const hasJsonbColumns = table.columns.some((col) =>
|
|
5006
|
+
const hasJsonbColumns = table.columns.some((col) => isJsonbType3(col.pgType));
|
|
4983
5007
|
const insertFields = table.columns.map((col) => {
|
|
4984
5008
|
const base = tsTypeFor(col.pgType, opts, enums);
|
|
4985
5009
|
const optional = col.hasDefault || col.nullable ? "?" : "";
|
|
@@ -5647,7 +5671,7 @@ export * from "./include-spec${ext}";
|
|
|
5647
5671
|
}
|
|
5648
5672
|
|
|
5649
5673
|
// src/emit-sdk-bundle.ts
|
|
5650
|
-
function emitSdkBundle(clientFiles, clientDir) {
|
|
5674
|
+
function emitSdkBundle(clientFiles, clientDir, version) {
|
|
5651
5675
|
const files = {};
|
|
5652
5676
|
for (const file of clientFiles) {
|
|
5653
5677
|
const normalizedClientDir = clientDir.replace(/\\/g, "/");
|
|
@@ -5657,7 +5681,6 @@ function emitSdkBundle(clientFiles, clientDir) {
|
|
|
5657
5681
|
files[relativePath] = file.content;
|
|
5658
5682
|
}
|
|
5659
5683
|
}
|
|
5660
|
-
const version = `1.0.0`;
|
|
5661
5684
|
return `/**
|
|
5662
5685
|
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
5663
5686
|
*
|
|
@@ -5694,6 +5717,7 @@ export interface OperationContext {
|
|
|
5694
5717
|
softDeleteColumn?: string | null;
|
|
5695
5718
|
includeMethodsDepth: number;
|
|
5696
5719
|
vectorColumns?: string[];
|
|
5720
|
+
jsonbColumns?: string[];
|
|
5697
5721
|
allColumnNames?: string[];
|
|
5698
5722
|
select?: string[];
|
|
5699
5723
|
exclude?: string[];
|
|
@@ -5748,22 +5772,6 @@ function buildColumnList(
|
|
|
5748
5772
|
return finalColumns.map(col => \`"\${col}"\`).join(", ");
|
|
5749
5773
|
}
|
|
5750
5774
|
|
|
5751
|
-
/**
|
|
5752
|
-
* Prepare query parameters for PostgreSQL.
|
|
5753
|
-
* The pg library should handle JSONB automatically, but there are edge cases
|
|
5754
|
-
* where explicit stringification is needed (e.g., certain pg versions or when
|
|
5755
|
-
* objects have been through serialization/deserialization).
|
|
5756
|
-
*/
|
|
5757
|
-
function prepareParams(params: any[]): any[] {
|
|
5758
|
-
return params.map(p => {
|
|
5759
|
-
if (p === null || p === undefined) return p;
|
|
5760
|
-
// Stringify objects/arrays for JSONB - while pg should handle this automatically,
|
|
5761
|
-
// we've observed cases where it fails without explicit stringification
|
|
5762
|
-
if (typeof p === 'object') return JSON.stringify(p);
|
|
5763
|
-
return p;
|
|
5764
|
-
});
|
|
5765
|
-
}
|
|
5766
|
-
|
|
5767
5775
|
/**
|
|
5768
5776
|
* Parse vector columns in retrieved rows.
|
|
5769
5777
|
* pgvector returns vectors as strings (e.g., "[1.5,2.5,3.5]") which need to be
|
|
@@ -5809,8 +5817,13 @@ export async function createRecord(
|
|
|
5809
5817
|
VALUES (\${placeholders})
|
|
5810
5818
|
RETURNING \${returningClause}\`;
|
|
5811
5819
|
|
|
5812
|
-
|
|
5813
|
-
|
|
5820
|
+
const preparedVals = vals.map((v, i) =>
|
|
5821
|
+
v !== null && v !== undefined && typeof v === 'object' && ctx.jsonbColumns?.includes(cols[i])
|
|
5822
|
+
? JSON.stringify(v)
|
|
5823
|
+
: v
|
|
5824
|
+
);
|
|
5825
|
+
log.debug("SQL:", text, "vals:", preparedVals);
|
|
5826
|
+
const { rows } = await ctx.pg.query(text, preparedVals);
|
|
5814
5827
|
const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
|
|
5815
5828
|
|
|
5816
5829
|
return { data: parsedRows[0] ?? null, status: parsedRows[0] ? 201 : 500 };
|
|
@@ -5852,7 +5865,7 @@ export async function getByPk(
|
|
|
5852
5865
|
const text = \`SELECT \${columns} FROM "\${ctx.table}" WHERE \${wherePkSql} LIMIT 1\`;
|
|
5853
5866
|
log.debug(\`GET \${ctx.table} by PK:\`, pkValues, "SQL:", text);
|
|
5854
5867
|
|
|
5855
|
-
const { rows } = await ctx.pg.query(text,
|
|
5868
|
+
const { rows } = await ctx.pg.query(text, pkValues);
|
|
5856
5869
|
const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
|
|
5857
5870
|
|
|
5858
5871
|
if (!parsedRows[0]) {
|
|
@@ -6324,10 +6337,15 @@ export async function updateRecord(
|
|
|
6324
6337
|
|
|
6325
6338
|
const returningClause = buildColumnList(ctx.select, ctx.exclude, ctx.allColumnNames);
|
|
6326
6339
|
const text = \`UPDATE "\${ctx.table}" SET \${setSql} WHERE \${wherePkSql} RETURNING \${returningClause}\`;
|
|
6327
|
-
const
|
|
6340
|
+
const updateVals = Object.entries(filteredData).map(([col, v]) =>
|
|
6341
|
+
v !== null && v !== undefined && typeof v === 'object' && ctx.jsonbColumns?.includes(col)
|
|
6342
|
+
? JSON.stringify(v)
|
|
6343
|
+
: v
|
|
6344
|
+
);
|
|
6345
|
+
const params = [...pkValues, ...updateVals];
|
|
6328
6346
|
|
|
6329
6347
|
log.debug(\`PATCH \${ctx.table} SQL:\`, text, "params:", params);
|
|
6330
|
-
const { rows } = await ctx.pg.query(text,
|
|
6348
|
+
const { rows } = await ctx.pg.query(text, params);
|
|
6331
6349
|
const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
|
|
6332
6350
|
|
|
6333
6351
|
if (!parsedRows[0]) {
|
|
@@ -6378,7 +6396,7 @@ export async function deleteRecord(
|
|
|
6378
6396
|
: \`DELETE FROM "\${ctx.table}" WHERE \${wherePkSql} RETURNING \${returningClause}\`;
|
|
6379
6397
|
|
|
6380
6398
|
log.debug(\`DELETE \${ctx.softDeleteColumn ? '(soft)' : ''} \${ctx.table} SQL:\`, text, "pk:", pkValues);
|
|
6381
|
-
const { rows } = await ctx.pg.query(text,
|
|
6399
|
+
const { rows } = await ctx.pg.query(text, pkValues);
|
|
6382
6400
|
const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
|
|
6383
6401
|
|
|
6384
6402
|
if (!parsedRows[0]) {
|
|
@@ -7131,6 +7149,9 @@ function generateTestCases(table, sampleData, updateData, hasForeignKeys = false
|
|
|
7131
7149
|
// src/index.ts
|
|
7132
7150
|
init_emit_sdk_contract();
|
|
7133
7151
|
init_utils();
|
|
7152
|
+
var __filename2 = fileURLToPath(import.meta.url);
|
|
7153
|
+
var __dirname2 = dirname2(__filename2);
|
|
7154
|
+
var { version: CLI_VERSION } = JSON.parse(readFileSync(join(__dirname2, "../package.json"), "utf-8"));
|
|
7134
7155
|
async function generate(configPath) {
|
|
7135
7156
|
if (!existsSync2(configPath)) {
|
|
7136
7157
|
throw new Error(`Config file not found: ${configPath}
|
|
@@ -7143,6 +7164,7 @@ async function generate(configPath) {
|
|
|
7143
7164
|
const rawCfg = module.default || module;
|
|
7144
7165
|
const normalizedAuth = normalizeAuthConfig(rawCfg.auth);
|
|
7145
7166
|
const cfg = { ...rawCfg, auth: normalizedAuth };
|
|
7167
|
+
console.log(`\uD83D\uDCE6 postgresdk v${CLI_VERSION}`);
|
|
7146
7168
|
console.log("\uD83D\uDD0D Introspecting database...");
|
|
7147
7169
|
const model = await introspect(cfg.connectionString, cfg.schema || "public");
|
|
7148
7170
|
console.log("\uD83D\uDD17 Building relationship graph...");
|
|
@@ -7284,7 +7306,7 @@ async function generate(configPath) {
|
|
|
7284
7306
|
});
|
|
7285
7307
|
files.push({
|
|
7286
7308
|
path: join(serverDir, "sdk-bundle.ts"),
|
|
7287
|
-
content: emitSdkBundle(clientFiles, clientDir)
|
|
7309
|
+
content: emitSdkBundle(clientFiles, clientDir, CLI_VERSION)
|
|
7288
7310
|
});
|
|
7289
7311
|
if (generateTests) {
|
|
7290
7312
|
console.log("\uD83E\uDDEA Generating tests...");
|
|
@@ -7360,12 +7382,12 @@ async function generate(configPath) {
|
|
|
7360
7382
|
// src/cli.ts
|
|
7361
7383
|
var import_config2 = __toESM(require_config(), 1);
|
|
7362
7384
|
import { resolve as resolve3 } from "node:path";
|
|
7363
|
-
import { readFileSync as
|
|
7364
|
-
import { fileURLToPath } from "node:url";
|
|
7365
|
-
import { dirname as
|
|
7366
|
-
var
|
|
7367
|
-
var
|
|
7368
|
-
var packageJson = JSON.parse(
|
|
7385
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
7386
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7387
|
+
import { dirname as dirname4, join as join3 } from "node:path";
|
|
7388
|
+
var __filename3 = fileURLToPath2(import.meta.url);
|
|
7389
|
+
var __dirname3 = dirname4(__filename3);
|
|
7390
|
+
var packageJson = JSON.parse(readFileSync3(join3(__dirname3, "../package.json"), "utf-8"));
|
|
7369
7391
|
var VERSION = packageJson.version;
|
|
7370
7392
|
var args = process.argv.slice(2);
|
|
7371
7393
|
var command = args[0];
|
package/dist/index.js
CHANGED
|
@@ -4,25 +4,43 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
7
12
|
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
8
20
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
21
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
22
|
for (let key of __getOwnPropNames(mod))
|
|
11
23
|
if (!__hasOwnProp.call(to, key))
|
|
12
24
|
__defProp(to, key, {
|
|
13
|
-
get: (
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
14
26
|
enumerable: true
|
|
15
27
|
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
16
30
|
return to;
|
|
17
31
|
};
|
|
18
32
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __returnValue = (v) => v;
|
|
34
|
+
function __exportSetter(name, newValue) {
|
|
35
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
36
|
+
}
|
|
19
37
|
var __export = (target, all) => {
|
|
20
38
|
for (var name in all)
|
|
21
39
|
__defProp(target, name, {
|
|
22
40
|
get: all[name],
|
|
23
41
|
enumerable: true,
|
|
24
42
|
configurable: true,
|
|
25
|
-
set: (
|
|
43
|
+
set: __exportSetter.bind(all, name)
|
|
26
44
|
});
|
|
27
45
|
};
|
|
28
46
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -1701,9 +1719,9 @@ var init_emit_sdk_contract = __esm(() => {
|
|
|
1701
1719
|
|
|
1702
1720
|
// src/index.ts
|
|
1703
1721
|
var import_config = __toESM(require_config(), 1);
|
|
1704
|
-
import { join, relative } from "node:path";
|
|
1705
|
-
import { pathToFileURL } from "node:url";
|
|
1706
|
-
import { existsSync as existsSync2 } from "node:fs";
|
|
1722
|
+
import { join, relative, dirname as dirname2 } from "node:path";
|
|
1723
|
+
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
1724
|
+
import { existsSync as existsSync2, readFileSync } from "node:fs";
|
|
1707
1725
|
|
|
1708
1726
|
// src/introspect.ts
|
|
1709
1727
|
import { Client } from "pg";
|
|
@@ -1974,9 +1992,10 @@ function emitIncludeResolver(graph, useJsExtensions) {
|
|
|
1974
1992
|
Select${Type} & {
|
|
1975
1993
|
[K in keyof TInclude as TInclude[K] extends false | undefined ? never : K]:`;
|
|
1976
1994
|
for (let i = 0;i < edgeEntries.length; i++) {
|
|
1977
|
-
const
|
|
1978
|
-
if (!
|
|
1995
|
+
const entry = edgeEntries[i];
|
|
1996
|
+
if (!entry)
|
|
1979
1997
|
continue;
|
|
1998
|
+
const [relKey, edge] = entry;
|
|
1980
1999
|
const targetType = pascal(edge.target);
|
|
1981
2000
|
const isLast = i === edgeEntries.length - 1;
|
|
1982
2001
|
out += `
|
|
@@ -2206,11 +2225,15 @@ function isVectorType(pgType) {
|
|
|
2206
2225
|
const t = pgType.toLowerCase();
|
|
2207
2226
|
return t === "vector" || t === "halfvec" || t === "sparsevec" || t === "bit";
|
|
2208
2227
|
}
|
|
2228
|
+
function isJsonbType(pgType) {
|
|
2229
|
+
return pgType.toLowerCase() === "json" || pgType.toLowerCase() === "jsonb";
|
|
2230
|
+
}
|
|
2209
2231
|
function emitHonoRoutes(table, _graph, opts) {
|
|
2210
2232
|
const fileTableName = table.name;
|
|
2211
2233
|
const Type = pascal(table.name);
|
|
2212
2234
|
const hasVectorColumns = table.columns.some((c) => isVectorType(c.pgType));
|
|
2213
2235
|
const vectorColumns = table.columns.filter((c) => isVectorType(c.pgType)).map((c) => c.name);
|
|
2236
|
+
const jsonbColumns = table.columns.filter((c) => isJsonbType(c.pgType)).map((c) => c.name);
|
|
2214
2237
|
const rawPk = table.pk;
|
|
2215
2238
|
const pkCols = Array.isArray(rawPk) ? rawPk : rawPk ? [rawPk] : [];
|
|
2216
2239
|
const safePkCols = pkCols.length ? pkCols : ["id"];
|
|
@@ -2297,7 +2320,8 @@ export function register${Type}Routes(app: Hono, deps: { pg: { query: (text: str
|
|
|
2297
2320
|
softDeleteColumn: ${softDel ? `"${softDel}"` : "null"},
|
|
2298
2321
|
includeMethodsDepth: ${opts.includeMethodsDepth},
|
|
2299
2322
|
allColumnNames: [${table.columns.map((c) => `"${c.name}"`).join(", ")}]${vectorColumns.length > 0 ? `,
|
|
2300
|
-
vectorColumns: ${JSON.stringify(vectorColumns)}` : ""}
|
|
2323
|
+
vectorColumns: ${JSON.stringify(vectorColumns)}` : ""}${jsonbColumns.length > 0 ? `,
|
|
2324
|
+
jsonbColumns: ${JSON.stringify(jsonbColumns)}` : ""}
|
|
2301
2325
|
};
|
|
2302
2326
|
${hasAuth ? `
|
|
2303
2327
|
// \uD83D\uDD10 Auth: protect all routes for this table
|
|
@@ -2515,7 +2539,7 @@ function isVectorType2(pgType) {
|
|
|
2515
2539
|
const t = pgType.toLowerCase();
|
|
2516
2540
|
return t === "vector" || t === "halfvec" || t === "sparsevec" || t === "bit";
|
|
2517
2541
|
}
|
|
2518
|
-
function
|
|
2542
|
+
function isJsonbType2(pgType) {
|
|
2519
2543
|
const t = pgType.toLowerCase();
|
|
2520
2544
|
return t === "json" || t === "jsonb";
|
|
2521
2545
|
}
|
|
@@ -2541,7 +2565,7 @@ function emitClient(table, graph, opts, model) {
|
|
|
2541
2565
|
const Type = pascal(table.name);
|
|
2542
2566
|
const ext = opts.useJsExtensions ? ".js" : "";
|
|
2543
2567
|
const hasVectorColumns = table.columns.some((c) => isVectorType2(c.pgType));
|
|
2544
|
-
const hasJsonbColumns = table.columns.some((c) =>
|
|
2568
|
+
const hasJsonbColumns = table.columns.some((c) => isJsonbType2(c.pgType));
|
|
2545
2569
|
if (process.env.SDK_DEBUG) {
|
|
2546
2570
|
const vectorCols = table.columns.filter((c) => isVectorType2(c.pgType));
|
|
2547
2571
|
if (vectorCols.length > 0) {
|
|
@@ -4011,14 +4035,14 @@ function tsTypeFor(pgType, opts, enums) {
|
|
|
4011
4035
|
return "number[]";
|
|
4012
4036
|
return "string";
|
|
4013
4037
|
}
|
|
4014
|
-
function
|
|
4038
|
+
function isJsonbType3(pgType) {
|
|
4015
4039
|
const t = pgType.toLowerCase();
|
|
4016
4040
|
return t === "json" || t === "jsonb";
|
|
4017
4041
|
}
|
|
4018
4042
|
var pascal2 = (s) => s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
|
|
4019
4043
|
function emitTypes(table, opts, enums) {
|
|
4020
4044
|
const Type = pascal2(table.name);
|
|
4021
|
-
const hasJsonbColumns = table.columns.some((col) =>
|
|
4045
|
+
const hasJsonbColumns = table.columns.some((col) => isJsonbType3(col.pgType));
|
|
4022
4046
|
const insertFields = table.columns.map((col) => {
|
|
4023
4047
|
const base = tsTypeFor(col.pgType, opts, enums);
|
|
4024
4048
|
const optional = col.hasDefault || col.nullable ? "?" : "";
|
|
@@ -4686,7 +4710,7 @@ export * from "./include-spec${ext}";
|
|
|
4686
4710
|
}
|
|
4687
4711
|
|
|
4688
4712
|
// src/emit-sdk-bundle.ts
|
|
4689
|
-
function emitSdkBundle(clientFiles, clientDir) {
|
|
4713
|
+
function emitSdkBundle(clientFiles, clientDir, version) {
|
|
4690
4714
|
const files = {};
|
|
4691
4715
|
for (const file of clientFiles) {
|
|
4692
4716
|
const normalizedClientDir = clientDir.replace(/\\/g, "/");
|
|
@@ -4696,7 +4720,6 @@ function emitSdkBundle(clientFiles, clientDir) {
|
|
|
4696
4720
|
files[relativePath] = file.content;
|
|
4697
4721
|
}
|
|
4698
4722
|
}
|
|
4699
|
-
const version = `1.0.0`;
|
|
4700
4723
|
return `/**
|
|
4701
4724
|
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
4702
4725
|
*
|
|
@@ -4733,6 +4756,7 @@ export interface OperationContext {
|
|
|
4733
4756
|
softDeleteColumn?: string | null;
|
|
4734
4757
|
includeMethodsDepth: number;
|
|
4735
4758
|
vectorColumns?: string[];
|
|
4759
|
+
jsonbColumns?: string[];
|
|
4736
4760
|
allColumnNames?: string[];
|
|
4737
4761
|
select?: string[];
|
|
4738
4762
|
exclude?: string[];
|
|
@@ -4787,22 +4811,6 @@ function buildColumnList(
|
|
|
4787
4811
|
return finalColumns.map(col => \`"\${col}"\`).join(", ");
|
|
4788
4812
|
}
|
|
4789
4813
|
|
|
4790
|
-
/**
|
|
4791
|
-
* Prepare query parameters for PostgreSQL.
|
|
4792
|
-
* The pg library should handle JSONB automatically, but there are edge cases
|
|
4793
|
-
* where explicit stringification is needed (e.g., certain pg versions or when
|
|
4794
|
-
* objects have been through serialization/deserialization).
|
|
4795
|
-
*/
|
|
4796
|
-
function prepareParams(params: any[]): any[] {
|
|
4797
|
-
return params.map(p => {
|
|
4798
|
-
if (p === null || p === undefined) return p;
|
|
4799
|
-
// Stringify objects/arrays for JSONB - while pg should handle this automatically,
|
|
4800
|
-
// we've observed cases where it fails without explicit stringification
|
|
4801
|
-
if (typeof p === 'object') return JSON.stringify(p);
|
|
4802
|
-
return p;
|
|
4803
|
-
});
|
|
4804
|
-
}
|
|
4805
|
-
|
|
4806
4814
|
/**
|
|
4807
4815
|
* Parse vector columns in retrieved rows.
|
|
4808
4816
|
* pgvector returns vectors as strings (e.g., "[1.5,2.5,3.5]") which need to be
|
|
@@ -4848,8 +4856,13 @@ export async function createRecord(
|
|
|
4848
4856
|
VALUES (\${placeholders})
|
|
4849
4857
|
RETURNING \${returningClause}\`;
|
|
4850
4858
|
|
|
4851
|
-
|
|
4852
|
-
|
|
4859
|
+
const preparedVals = vals.map((v, i) =>
|
|
4860
|
+
v !== null && v !== undefined && typeof v === 'object' && ctx.jsonbColumns?.includes(cols[i])
|
|
4861
|
+
? JSON.stringify(v)
|
|
4862
|
+
: v
|
|
4863
|
+
);
|
|
4864
|
+
log.debug("SQL:", text, "vals:", preparedVals);
|
|
4865
|
+
const { rows } = await ctx.pg.query(text, preparedVals);
|
|
4853
4866
|
const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
|
|
4854
4867
|
|
|
4855
4868
|
return { data: parsedRows[0] ?? null, status: parsedRows[0] ? 201 : 500 };
|
|
@@ -4891,7 +4904,7 @@ export async function getByPk(
|
|
|
4891
4904
|
const text = \`SELECT \${columns} FROM "\${ctx.table}" WHERE \${wherePkSql} LIMIT 1\`;
|
|
4892
4905
|
log.debug(\`GET \${ctx.table} by PK:\`, pkValues, "SQL:", text);
|
|
4893
4906
|
|
|
4894
|
-
const { rows } = await ctx.pg.query(text,
|
|
4907
|
+
const { rows } = await ctx.pg.query(text, pkValues);
|
|
4895
4908
|
const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
|
|
4896
4909
|
|
|
4897
4910
|
if (!parsedRows[0]) {
|
|
@@ -5363,10 +5376,15 @@ export async function updateRecord(
|
|
|
5363
5376
|
|
|
5364
5377
|
const returningClause = buildColumnList(ctx.select, ctx.exclude, ctx.allColumnNames);
|
|
5365
5378
|
const text = \`UPDATE "\${ctx.table}" SET \${setSql} WHERE \${wherePkSql} RETURNING \${returningClause}\`;
|
|
5366
|
-
const
|
|
5379
|
+
const updateVals = Object.entries(filteredData).map(([col, v]) =>
|
|
5380
|
+
v !== null && v !== undefined && typeof v === 'object' && ctx.jsonbColumns?.includes(col)
|
|
5381
|
+
? JSON.stringify(v)
|
|
5382
|
+
: v
|
|
5383
|
+
);
|
|
5384
|
+
const params = [...pkValues, ...updateVals];
|
|
5367
5385
|
|
|
5368
5386
|
log.debug(\`PATCH \${ctx.table} SQL:\`, text, "params:", params);
|
|
5369
|
-
const { rows } = await ctx.pg.query(text,
|
|
5387
|
+
const { rows } = await ctx.pg.query(text, params);
|
|
5370
5388
|
const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
|
|
5371
5389
|
|
|
5372
5390
|
if (!parsedRows[0]) {
|
|
@@ -5417,7 +5435,7 @@ export async function deleteRecord(
|
|
|
5417
5435
|
: \`DELETE FROM "\${ctx.table}" WHERE \${wherePkSql} RETURNING \${returningClause}\`;
|
|
5418
5436
|
|
|
5419
5437
|
log.debug(\`DELETE \${ctx.softDeleteColumn ? '(soft)' : ''} \${ctx.table} SQL:\`, text, "pk:", pkValues);
|
|
5420
|
-
const { rows } = await ctx.pg.query(text,
|
|
5438
|
+
const { rows } = await ctx.pg.query(text, pkValues);
|
|
5421
5439
|
const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
|
|
5422
5440
|
|
|
5423
5441
|
if (!parsedRows[0]) {
|
|
@@ -6170,6 +6188,9 @@ function generateTestCases(table, sampleData, updateData, hasForeignKeys = false
|
|
|
6170
6188
|
// src/index.ts
|
|
6171
6189
|
init_emit_sdk_contract();
|
|
6172
6190
|
init_utils();
|
|
6191
|
+
var __filename2 = fileURLToPath(import.meta.url);
|
|
6192
|
+
var __dirname2 = dirname2(__filename2);
|
|
6193
|
+
var { version: CLI_VERSION } = JSON.parse(readFileSync(join(__dirname2, "../package.json"), "utf-8"));
|
|
6173
6194
|
async function generate(configPath) {
|
|
6174
6195
|
if (!existsSync2(configPath)) {
|
|
6175
6196
|
throw new Error(`Config file not found: ${configPath}
|
|
@@ -6182,6 +6203,7 @@ async function generate(configPath) {
|
|
|
6182
6203
|
const rawCfg = module.default || module;
|
|
6183
6204
|
const normalizedAuth = normalizeAuthConfig(rawCfg.auth);
|
|
6184
6205
|
const cfg = { ...rawCfg, auth: normalizedAuth };
|
|
6206
|
+
console.log(`\uD83D\uDCE6 postgresdk v${CLI_VERSION}`);
|
|
6185
6207
|
console.log("\uD83D\uDD0D Introspecting database...");
|
|
6186
6208
|
const model = await introspect(cfg.connectionString, cfg.schema || "public");
|
|
6187
6209
|
console.log("\uD83D\uDD17 Building relationship graph...");
|
|
@@ -6323,7 +6345,7 @@ async function generate(configPath) {
|
|
|
6323
6345
|
});
|
|
6324
6346
|
files.push({
|
|
6325
6347
|
path: join(serverDir, "sdk-bundle.ts"),
|
|
6326
|
-
content: emitSdkBundle(clientFiles, clientDir)
|
|
6348
|
+
content: emitSdkBundle(clientFiles, clientDir, CLI_VERSION)
|
|
6327
6349
|
});
|
|
6328
6350
|
if (generateTests) {
|
|
6329
6351
|
console.log("\uD83E\uDDEA Generating tests...");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postgresdk",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.12",
|
|
4
4
|
"description": "Generate a typed server/client SDK from a Postgres schema (includes, Zod, Hono).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "bun build src/cli.ts src/index.ts --outdir dist --target node --format esm --external=pg --external=zod --external=hono --external=prompts --external=node:* && tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
25
|
-
"test": "bun test:write-files && bun test:init && bun test:gen && bun test test/test-where-clause.test.ts && bun test test/test-where-or-and.test.ts && bun test test/test-nested-include-options.test.ts && bun test test/test-include-methods-with-options.test.ts && bun test:gen-with-tests && bun test:pull && bun test:enums && bun test:typecheck && bun test:drizzle-e2e && bun test test/test-numeric-mode-integration.test.ts",
|
|
25
|
+
"test": "bun test:write-files && bun test:init && bun test:gen && bun test test/test-where-clause.test.ts && bun test test/test-where-or-and.test.ts && bun test test/test-nested-include-options.test.ts && bun test test/test-include-methods-with-options.test.ts && bun test:gen-with-tests && bun test:pull && bun test:enums && bun test:typecheck && bun test:drizzle-e2e && bun test test/test-numeric-mode-integration.test.ts && bun test test/test-jsonb-array-serialization.test.ts",
|
|
26
26
|
"test:write-files": "bun test/test-write-files-if-changed.ts",
|
|
27
27
|
"test:init": "bun test/test-init.ts",
|
|
28
28
|
"test:gen": "bun test/test-gen.ts",
|