postgresdk 0.18.5 → 0.18.7
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 +92 -220
- package/dist/index.js +75 -185
- package/package.json +3 -2
- package/dist/cache.d.ts +0 -35
package/dist/cli.js
CHANGED
|
@@ -473,11 +473,25 @@ var require_config = __commonJS(() => {
|
|
|
473
473
|
// src/utils.ts
|
|
474
474
|
import { mkdir, writeFile, readFile } from "fs/promises";
|
|
475
475
|
import { dirname } from "path";
|
|
476
|
-
|
|
476
|
+
import { existsSync } from "fs";
|
|
477
|
+
async function writeFilesIfChanged(files) {
|
|
478
|
+
let written = 0;
|
|
479
|
+
let unchanged = 0;
|
|
480
|
+
const filesWritten = [];
|
|
477
481
|
for (const f of files) {
|
|
478
482
|
await mkdir(dirname(f.path), { recursive: true });
|
|
483
|
+
if (existsSync(f.path)) {
|
|
484
|
+
const existing = await readFile(f.path, "utf-8");
|
|
485
|
+
if (existing === f.content) {
|
|
486
|
+
unchanged++;
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
479
490
|
await writeFile(f.path, f.content, "utf-8");
|
|
491
|
+
written++;
|
|
492
|
+
filesWritten.push(f.path);
|
|
480
493
|
}
|
|
494
|
+
return { written, unchanged, filesWritten };
|
|
481
495
|
}
|
|
482
496
|
async function ensureDirs(dirs) {
|
|
483
497
|
for (const d of dirs)
|
|
@@ -1686,107 +1700,6 @@ var init_emit_sdk_contract = __esm(() => {
|
|
|
1686
1700
|
init_emit_include_methods();
|
|
1687
1701
|
});
|
|
1688
1702
|
|
|
1689
|
-
// src/cache.ts
|
|
1690
|
-
import { createHash } from "crypto";
|
|
1691
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, appendFile } from "fs/promises";
|
|
1692
|
-
import { existsSync, readFileSync } from "fs";
|
|
1693
|
-
import { join } from "path";
|
|
1694
|
-
function computeSchemaHash(model, config) {
|
|
1695
|
-
const payload = {
|
|
1696
|
-
version: POSTGRESDK_VERSION,
|
|
1697
|
-
schema: model.schema,
|
|
1698
|
-
tables: model.tables,
|
|
1699
|
-
enums: model.enums,
|
|
1700
|
-
config: {
|
|
1701
|
-
outDir: config.outDir,
|
|
1702
|
-
schema: config.schema,
|
|
1703
|
-
softDeleteColumn: config.softDeleteColumn,
|
|
1704
|
-
includeMethodsDepth: config.includeMethodsDepth,
|
|
1705
|
-
serverFramework: config.serverFramework,
|
|
1706
|
-
useJsExtensions: config.useJsExtensions,
|
|
1707
|
-
useJsExtensionsClient: config.useJsExtensionsClient,
|
|
1708
|
-
numericMode: config.numericMode,
|
|
1709
|
-
skipJunctionTables: config.skipJunctionTables,
|
|
1710
|
-
apiPathPrefix: config.apiPathPrefix,
|
|
1711
|
-
auth: config.auth,
|
|
1712
|
-
tests: config.tests
|
|
1713
|
-
}
|
|
1714
|
-
};
|
|
1715
|
-
const json = JSON.stringify(payload, Object.keys(payload).sort());
|
|
1716
|
-
return createHash("sha256").update(json).digest("hex");
|
|
1717
|
-
}
|
|
1718
|
-
function getCacheDir(baseDir = process.cwd()) {
|
|
1719
|
-
return join(baseDir, ".postgresdk");
|
|
1720
|
-
}
|
|
1721
|
-
async function ensureGitignore(baseDir = process.cwd()) {
|
|
1722
|
-
const gitignorePath = join(baseDir, ".gitignore");
|
|
1723
|
-
if (!existsSync(gitignorePath)) {
|
|
1724
|
-
return;
|
|
1725
|
-
}
|
|
1726
|
-
try {
|
|
1727
|
-
const content = await readFile2(gitignorePath, "utf-8");
|
|
1728
|
-
if (content.includes(".postgresdk")) {
|
|
1729
|
-
return;
|
|
1730
|
-
}
|
|
1731
|
-
const entry = `
|
|
1732
|
-
# PostgreSDK cache and history
|
|
1733
|
-
.postgresdk/
|
|
1734
|
-
`;
|
|
1735
|
-
await appendFile(gitignorePath, entry);
|
|
1736
|
-
console.log("✓ Added .postgresdk/ to .gitignore");
|
|
1737
|
-
} catch {}
|
|
1738
|
-
}
|
|
1739
|
-
async function readCache(baseDir) {
|
|
1740
|
-
const cachePath = join(getCacheDir(baseDir), "cache.json");
|
|
1741
|
-
if (!existsSync(cachePath)) {
|
|
1742
|
-
return null;
|
|
1743
|
-
}
|
|
1744
|
-
try {
|
|
1745
|
-
const content = await readFile2(cachePath, "utf-8");
|
|
1746
|
-
return JSON.parse(content);
|
|
1747
|
-
} catch {
|
|
1748
|
-
return null;
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
|
-
async function writeCache(data, baseDir) {
|
|
1752
|
-
const cacheDir = getCacheDir(baseDir);
|
|
1753
|
-
const isNewCache = !existsSync(cacheDir);
|
|
1754
|
-
await mkdir2(cacheDir, { recursive: true });
|
|
1755
|
-
if (isNewCache) {
|
|
1756
|
-
await ensureGitignore(baseDir);
|
|
1757
|
-
}
|
|
1758
|
-
const cachePath = join(cacheDir, "cache.json");
|
|
1759
|
-
await writeFile2(cachePath, JSON.stringify(data, null, 2), "utf-8");
|
|
1760
|
-
}
|
|
1761
|
-
async function appendToHistory(entry, baseDir) {
|
|
1762
|
-
const cacheDir = getCacheDir(baseDir);
|
|
1763
|
-
const isNewCache = !existsSync(cacheDir);
|
|
1764
|
-
await mkdir2(cacheDir, { recursive: true });
|
|
1765
|
-
if (isNewCache) {
|
|
1766
|
-
await ensureGitignore(baseDir);
|
|
1767
|
-
}
|
|
1768
|
-
const historyPath = join(cacheDir, "history.md");
|
|
1769
|
-
const timestamp = new Date().toISOString().replace("T", " ").substring(0, 19);
|
|
1770
|
-
const formattedEntry = `## ${timestamp} - ${entry}
|
|
1771
|
-
|
|
1772
|
-
`;
|
|
1773
|
-
try {
|
|
1774
|
-
const existing = existsSync(historyPath) ? await readFile2(historyPath, "utf-8") : `# PostgreSDK Generation History
|
|
1775
|
-
|
|
1776
|
-
`;
|
|
1777
|
-
await writeFile2(historyPath, existing + formattedEntry, "utf-8");
|
|
1778
|
-
} catch {
|
|
1779
|
-
await writeFile2(historyPath, `# PostgreSDK Generation History
|
|
1780
|
-
|
|
1781
|
-
${formattedEntry}`, "utf-8");
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
var __dirname = "/workspace/src", packageJson, POSTGRESDK_VERSION;
|
|
1785
|
-
var init_cache = __esm(() => {
|
|
1786
|
-
packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
|
|
1787
|
-
POSTGRESDK_VERSION = packageJson.version;
|
|
1788
|
-
});
|
|
1789
|
-
|
|
1790
1703
|
// src/cli-config-utils.ts
|
|
1791
1704
|
function extractConfigFields(configContent) {
|
|
1792
1705
|
const fields = [];
|
|
@@ -2206,7 +2119,7 @@ var exports_cli_init = {};
|
|
|
2206
2119
|
__export(exports_cli_init, {
|
|
2207
2120
|
initCommand: () => initCommand
|
|
2208
2121
|
});
|
|
2209
|
-
import { existsSync as existsSync3, writeFileSync, readFileSync
|
|
2122
|
+
import { existsSync as existsSync3, writeFileSync, readFileSync, copyFileSync } from "fs";
|
|
2210
2123
|
import { resolve } from "path";
|
|
2211
2124
|
import prompts from "prompts";
|
|
2212
2125
|
async function initCommand(args) {
|
|
@@ -2224,7 +2137,7 @@ async function initCommand(args) {
|
|
|
2224
2137
|
}
|
|
2225
2138
|
console.log(`⚠️ Found existing postgresdk.config.ts
|
|
2226
2139
|
`);
|
|
2227
|
-
const existingContent =
|
|
2140
|
+
const existingContent = readFileSync(configPath, "utf-8");
|
|
2228
2141
|
const existingFields = extractConfigFields(existingContent);
|
|
2229
2142
|
console.log("\uD83D\uDCCB Existing configuration detected:");
|
|
2230
2143
|
existingFields.forEach((field) => {
|
|
@@ -2615,8 +2528,8 @@ var exports_cli_pull = {};
|
|
|
2615
2528
|
__export(exports_cli_pull, {
|
|
2616
2529
|
pullCommand: () => pullCommand
|
|
2617
2530
|
});
|
|
2618
|
-
import { writeFile as
|
|
2619
|
-
import { join as
|
|
2531
|
+
import { writeFile as writeFile2, mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
|
|
2532
|
+
import { join as join2, dirname as dirname2, resolve as resolve2 } from "path";
|
|
2620
2533
|
import { existsSync as existsSync4 } from "fs";
|
|
2621
2534
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
2622
2535
|
async function pullCommand(args) {
|
|
@@ -2702,72 +2615,54 @@ Options:`);
|
|
|
2702
2615
|
let filesUnchanged = 0;
|
|
2703
2616
|
const changedFiles = [];
|
|
2704
2617
|
for (const [path, content] of Object.entries(sdk.files)) {
|
|
2705
|
-
const fullPath =
|
|
2706
|
-
await
|
|
2618
|
+
const fullPath = join2(config.output, path);
|
|
2619
|
+
await mkdir2(dirname2(fullPath), { recursive: true });
|
|
2707
2620
|
let shouldWrite = true;
|
|
2708
2621
|
if (existsSync4(fullPath)) {
|
|
2709
|
-
const existing = await
|
|
2622
|
+
const existing = await readFile2(fullPath, "utf-8");
|
|
2710
2623
|
if (existing === content) {
|
|
2711
2624
|
shouldWrite = false;
|
|
2712
2625
|
filesUnchanged++;
|
|
2713
2626
|
}
|
|
2714
2627
|
}
|
|
2715
2628
|
if (shouldWrite) {
|
|
2716
|
-
await
|
|
2629
|
+
await writeFile2(fullPath, content, "utf-8");
|
|
2717
2630
|
filesWritten++;
|
|
2718
2631
|
changedFiles.push(path);
|
|
2719
2632
|
console.log(` ✓ ${path}`);
|
|
2720
2633
|
}
|
|
2721
2634
|
}
|
|
2722
|
-
const metadataPath =
|
|
2635
|
+
const metadataPath = join2(config.output, ".postgresdk.json");
|
|
2723
2636
|
const metadata = {
|
|
2724
2637
|
version: sdk.version,
|
|
2725
2638
|
pulledFrom: config.from
|
|
2726
2639
|
};
|
|
2727
2640
|
let metadataChanged = true;
|
|
2728
2641
|
if (existsSync4(metadataPath)) {
|
|
2729
|
-
const existing = await
|
|
2642
|
+
const existing = await readFile2(metadataPath, "utf-8");
|
|
2730
2643
|
if (existing === JSON.stringify(metadata, null, 2)) {
|
|
2731
2644
|
metadataChanged = false;
|
|
2732
2645
|
}
|
|
2733
2646
|
}
|
|
2734
2647
|
if (metadataChanged) {
|
|
2735
|
-
await
|
|
2648
|
+
await writeFile2(metadataPath, JSON.stringify(metadata, null, 2));
|
|
2736
2649
|
}
|
|
2737
2650
|
if (filesWritten === 0 && !metadataChanged) {
|
|
2738
2651
|
console.log(`✅ SDK up-to-date (${filesUnchanged} files unchanged)`);
|
|
2739
|
-
await appendToHistory(`Pull
|
|
2740
|
-
✅ SDK up-to-date
|
|
2741
|
-
- Pulled from: ${config.from}
|
|
2742
|
-
- Files checked: ${filesUnchanged}`);
|
|
2743
2652
|
} else {
|
|
2744
2653
|
console.log(`✅ SDK pulled successfully to ${config.output}`);
|
|
2745
2654
|
console.log(` Updated: ${filesWritten} files, Unchanged: ${filesUnchanged} files`);
|
|
2746
|
-
let logEntry = `Pull
|
|
2747
|
-
✅ Updated ${filesWritten} files from ${config.from}
|
|
2748
|
-
- SDK version: ${sdk.version}
|
|
2749
|
-
- Files unchanged: ${filesUnchanged}`;
|
|
2750
|
-
if (changedFiles.length > 0 && changedFiles.length <= 10) {
|
|
2751
|
-
logEntry += `
|
|
2752
|
-
- Modified: ${changedFiles.join(", ")}`;
|
|
2753
|
-
} else if (changedFiles.length > 10) {
|
|
2754
|
-
logEntry += `
|
|
2755
|
-
- Modified: ${changedFiles.slice(0, 10).join(", ")} and ${changedFiles.length - 10} more...`;
|
|
2756
|
-
}
|
|
2757
|
-
await appendToHistory(logEntry);
|
|
2758
2655
|
}
|
|
2759
2656
|
} catch (err) {
|
|
2760
2657
|
console.error(`❌ Pull failed:`, err);
|
|
2761
2658
|
process.exit(1);
|
|
2762
2659
|
}
|
|
2763
2660
|
}
|
|
2764
|
-
var init_cli_pull =
|
|
2765
|
-
init_cache();
|
|
2766
|
-
});
|
|
2661
|
+
var init_cli_pull = () => {};
|
|
2767
2662
|
|
|
2768
2663
|
// src/index.ts
|
|
2769
2664
|
var import_config = __toESM(require_config(), 1);
|
|
2770
|
-
import { join
|
|
2665
|
+
import { join, relative } from "node:path";
|
|
2771
2666
|
import { pathToFileURL } from "node:url";
|
|
2772
2667
|
import { existsSync as existsSync2 } from "node:fs";
|
|
2773
2668
|
|
|
@@ -3565,6 +3460,7 @@ function emitClient(table, graph, opts, model) {
|
|
|
3565
3460
|
}
|
|
3566
3461
|
}
|
|
3567
3462
|
const typeImports = `import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}${ext}";`;
|
|
3463
|
+
const includeSpecImport = `import type { ${Type}IncludeSpec } from "./include-spec${ext}";`;
|
|
3568
3464
|
const otherTableImports = [];
|
|
3569
3465
|
for (const target of Array.from(importedTypes)) {
|
|
3570
3466
|
if (target !== table.name) {
|
|
@@ -3647,7 +3543,7 @@ function emitClient(table, graph, opts, model) {
|
|
|
3647
3543
|
order?: "asc" | "desc";
|
|
3648
3544
|
limit?: number;
|
|
3649
3545
|
offset?: number;
|
|
3650
|
-
include?:
|
|
3546
|
+
include?: ${Type}IncludeSpec;
|
|
3651
3547
|
};
|
|
3652
3548
|
}`;
|
|
3653
3549
|
}
|
|
@@ -3755,6 +3651,7 @@ import { BaseClient } from "./base-client${ext}";
|
|
|
3755
3651
|
import type { Where } from "./where-types${ext}";
|
|
3756
3652
|
import type { PaginatedResponse } from "./types/shared${ext}";
|
|
3757
3653
|
${typeImports}
|
|
3654
|
+
${includeSpecImport}
|
|
3758
3655
|
${otherTableImports.join(`
|
|
3759
3656
|
`)}
|
|
3760
3657
|
|
|
@@ -3904,7 +3801,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3904
3801
|
*/
|
|
3905
3802
|
async list<TJsonb extends Partial<Select${Type}> = {}>(params: {
|
|
3906
3803
|
select: string[];
|
|
3907
|
-
include?:
|
|
3804
|
+
include?: ${Type}IncludeSpec;
|
|
3908
3805
|
limit?: number;
|
|
3909
3806
|
offset?: number;
|
|
3910
3807
|
where?: Where<Select${Type}<TJsonb>>;${hasVectorColumns ? `
|
|
@@ -3924,7 +3821,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3924
3821
|
*/
|
|
3925
3822
|
async list<TJsonb extends Partial<Select${Type}> = {}>(params: {
|
|
3926
3823
|
exclude: string[];
|
|
3927
|
-
include?:
|
|
3824
|
+
include?: ${Type}IncludeSpec;
|
|
3928
3825
|
limit?: number;
|
|
3929
3826
|
offset?: number;
|
|
3930
3827
|
where?: Where<Select${Type}<TJsonb>>;${hasVectorColumns ? `
|
|
@@ -3952,7 +3849,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3952
3849
|
* const users = await client.list<{ metadata: Metadata }>({ where: { status: 'active' } });
|
|
3953
3850
|
*/
|
|
3954
3851
|
async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
|
|
3955
|
-
include?:
|
|
3852
|
+
include?: ${Type}IncludeSpec;
|
|
3956
3853
|
limit?: number;
|
|
3957
3854
|
offset?: number;
|
|
3958
3855
|
where?: Where<Select${Type}<TJsonb>>;${hasVectorColumns ? `
|
|
@@ -3966,7 +3863,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3966
3863
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3967
3864
|
}): Promise<PaginatedResponse<Select${Type}<TJsonb>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3968
3865
|
async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
|
|
3969
|
-
include?:
|
|
3866
|
+
include?: ${Type}IncludeSpec;
|
|
3970
3867
|
select?: string[];
|
|
3971
3868
|
exclude?: string[];
|
|
3972
3869
|
limit?: number;
|
|
@@ -3989,7 +3886,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3989
3886
|
*/
|
|
3990
3887
|
async list(params: {
|
|
3991
3888
|
select: string[];
|
|
3992
|
-
include?:
|
|
3889
|
+
include?: ${Type}IncludeSpec;
|
|
3993
3890
|
limit?: number;
|
|
3994
3891
|
offset?: number;
|
|
3995
3892
|
where?: Where<Select${Type}>;${hasVectorColumns ? `
|
|
@@ -4009,7 +3906,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
4009
3906
|
*/
|
|
4010
3907
|
async list(params: {
|
|
4011
3908
|
exclude: string[];
|
|
4012
|
-
include?:
|
|
3909
|
+
include?: ${Type}IncludeSpec;
|
|
4013
3910
|
limit?: number;
|
|
4014
3911
|
offset?: number;
|
|
4015
3912
|
where?: Where<Select${Type}>;${hasVectorColumns ? `
|
|
@@ -4034,7 +3931,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
4034
3931
|
* @returns Paginated results with all fields
|
|
4035
3932
|
*/
|
|
4036
3933
|
async list(params?: {
|
|
4037
|
-
include?:
|
|
3934
|
+
include?: ${Type}IncludeSpec;
|
|
4038
3935
|
limit?: number;
|
|
4039
3936
|
offset?: number;
|
|
4040
3937
|
where?: Where<Select${Type}>;${hasVectorColumns ? `
|
|
@@ -4048,7 +3945,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
4048
3945
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
4049
3946
|
}): Promise<PaginatedResponse<Select${Type}${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
4050
3947
|
async list(params?: {
|
|
4051
|
-
include?:
|
|
3948
|
+
include?: ${Type}IncludeSpec;
|
|
4052
3949
|
select?: string[];
|
|
4053
3950
|
exclude?: string[];
|
|
4054
3951
|
limit?: number;
|
|
@@ -7159,7 +7056,6 @@ function generateTestCases(table, sampleData, updateData, hasForeignKeys = false
|
|
|
7159
7056
|
// src/index.ts
|
|
7160
7057
|
init_emit_sdk_contract();
|
|
7161
7058
|
init_utils();
|
|
7162
|
-
init_cache();
|
|
7163
7059
|
async function generate(configPath) {
|
|
7164
7060
|
if (!existsSync2(configPath)) {
|
|
7165
7061
|
throw new Error(`Config file not found: ${configPath}
|
|
@@ -7174,58 +7070,43 @@ async function generate(configPath) {
|
|
|
7174
7070
|
const cfg = { ...rawCfg, auth: normalizedAuth };
|
|
7175
7071
|
console.log("\uD83D\uDD0D Introspecting database...");
|
|
7176
7072
|
const model = await introspect(cfg.connectionString, cfg.schema || "public");
|
|
7177
|
-
const schemaHash = computeSchemaHash(model, cfg);
|
|
7178
|
-
const cache = await readCache();
|
|
7179
|
-
let serverDir;
|
|
7180
|
-
if (typeof cfg.outDir === "string") {
|
|
7181
|
-
serverDir = cfg.outDir;
|
|
7182
|
-
} else if (cfg.outDir && typeof cfg.outDir === "object") {
|
|
7183
|
-
serverDir = cfg.outDir.server;
|
|
7184
|
-
} else {
|
|
7185
|
-
serverDir = "./api/server";
|
|
7186
|
-
}
|
|
7187
|
-
const currentOutDir = typeof cfg.outDir === "string" ? cfg.outDir : `${serverDir}`;
|
|
7188
|
-
const cachedOutDir = typeof cache?.config.outDir === "string" ? cache.config.outDir : cache?.config.outDir?.server;
|
|
7189
|
-
if (cache && cache.schemaHash === schemaHash && existsSync2(serverDir) && currentOutDir === cachedOutDir) {
|
|
7190
|
-
console.log("✅ Schema unchanged, skipping generation");
|
|
7191
|
-
await appendToHistory(`Generate
|
|
7192
|
-
✅ Schema unchanged, skipped generation
|
|
7193
|
-
- Schema hash: ${schemaHash.substring(0, 12)}...`);
|
|
7194
|
-
return;
|
|
7195
|
-
}
|
|
7196
7073
|
console.log("\uD83D\uDD17 Building relationship graph...");
|
|
7197
7074
|
const graph = buildGraph(model);
|
|
7075
|
+
let serverDir;
|
|
7198
7076
|
let originalClientDir;
|
|
7199
7077
|
if (typeof cfg.outDir === "string") {
|
|
7078
|
+
serverDir = cfg.outDir;
|
|
7200
7079
|
originalClientDir = cfg.outDir;
|
|
7201
7080
|
} else if (cfg.outDir && typeof cfg.outDir === "object") {
|
|
7081
|
+
serverDir = cfg.outDir.server;
|
|
7202
7082
|
originalClientDir = cfg.outDir.client;
|
|
7203
7083
|
} else {
|
|
7084
|
+
serverDir = "./api/server";
|
|
7204
7085
|
originalClientDir = "./api/client";
|
|
7205
7086
|
}
|
|
7206
7087
|
const sameDirectory = serverDir === originalClientDir;
|
|
7207
7088
|
let clientDir = originalClientDir;
|
|
7208
7089
|
if (sameDirectory) {
|
|
7209
|
-
clientDir =
|
|
7090
|
+
clientDir = join(originalClientDir, "sdk");
|
|
7210
7091
|
}
|
|
7211
7092
|
const serverFramework = cfg.serverFramework || "hono";
|
|
7212
7093
|
const generateTests = cfg.tests?.generate ?? false;
|
|
7213
7094
|
const originalTestDir = cfg.tests?.output || "./api/tests";
|
|
7214
7095
|
let testDir = originalTestDir;
|
|
7215
7096
|
if (generateTests && (originalTestDir === serverDir || originalTestDir === originalClientDir)) {
|
|
7216
|
-
testDir =
|
|
7097
|
+
testDir = join(originalTestDir, "tests");
|
|
7217
7098
|
}
|
|
7218
7099
|
const testFramework = cfg.tests?.framework || "vitest";
|
|
7219
7100
|
console.log("\uD83D\uDCC1 Creating directories...");
|
|
7220
7101
|
const dirs = [
|
|
7221
7102
|
serverDir,
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7103
|
+
join(serverDir, "types"),
|
|
7104
|
+
join(serverDir, "zod"),
|
|
7105
|
+
join(serverDir, "routes"),
|
|
7225
7106
|
clientDir,
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7107
|
+
join(clientDir, "types"),
|
|
7108
|
+
join(clientDir, "zod"),
|
|
7109
|
+
join(clientDir, "params")
|
|
7229
7110
|
];
|
|
7230
7111
|
if (generateTests) {
|
|
7231
7112
|
dirs.push(testDir);
|
|
@@ -7233,26 +7114,26 @@ async function generate(configPath) {
|
|
|
7233
7114
|
await ensureDirs(dirs);
|
|
7234
7115
|
const files = [];
|
|
7235
7116
|
const includeSpec = emitIncludeSpec(graph);
|
|
7236
|
-
files.push({ path:
|
|
7237
|
-
files.push({ path:
|
|
7238
|
-
files.push({ path:
|
|
7239
|
-
files.push({ path:
|
|
7240
|
-
files.push({ path:
|
|
7241
|
-
files.push({ path:
|
|
7117
|
+
files.push({ path: join(serverDir, "include-spec.ts"), content: includeSpec });
|
|
7118
|
+
files.push({ path: join(clientDir, "include-spec.ts"), content: includeSpec });
|
|
7119
|
+
files.push({ path: join(clientDir, "params", "shared.ts"), content: emitSharedParamsZod() });
|
|
7120
|
+
files.push({ path: join(clientDir, "types", "shared.ts"), content: emitSharedTypes() });
|
|
7121
|
+
files.push({ path: join(clientDir, "base-client.ts"), content: emitBaseClient() });
|
|
7122
|
+
files.push({ path: join(clientDir, "where-types.ts"), content: emitWhereTypes() });
|
|
7242
7123
|
files.push({
|
|
7243
|
-
path:
|
|
7124
|
+
path: join(serverDir, "include-builder.ts"),
|
|
7244
7125
|
content: emitIncludeBuilder(graph, cfg.includeMethodsDepth || 2)
|
|
7245
7126
|
});
|
|
7246
7127
|
files.push({
|
|
7247
|
-
path:
|
|
7128
|
+
path: join(serverDir, "include-loader.ts"),
|
|
7248
7129
|
content: emitIncludeLoader(graph, model, cfg.includeMethodsDepth || 2, cfg.useJsExtensions)
|
|
7249
7130
|
});
|
|
7250
|
-
files.push({ path:
|
|
7131
|
+
files.push({ path: join(serverDir, "logger.ts"), content: emitLogger() });
|
|
7251
7132
|
if (getAuthStrategy(normalizedAuth) !== "none") {
|
|
7252
|
-
files.push({ path:
|
|
7133
|
+
files.push({ path: join(serverDir, "auth.ts"), content: emitAuth(normalizedAuth) });
|
|
7253
7134
|
}
|
|
7254
7135
|
files.push({
|
|
7255
|
-
path:
|
|
7136
|
+
path: join(serverDir, "core", "operations.ts"),
|
|
7256
7137
|
content: emitCoreOperations()
|
|
7257
7138
|
});
|
|
7258
7139
|
if (process.env.SDK_DEBUG) {
|
|
@@ -7261,13 +7142,13 @@ async function generate(configPath) {
|
|
|
7261
7142
|
for (const table of Object.values(model.tables)) {
|
|
7262
7143
|
const numericMode = cfg.numericMode ?? "auto";
|
|
7263
7144
|
const typesSrc = emitTypes(table, { numericMode }, model.enums);
|
|
7264
|
-
files.push({ path:
|
|
7265
|
-
files.push({ path:
|
|
7145
|
+
files.push({ path: join(serverDir, "types", `${table.name}.ts`), content: typesSrc });
|
|
7146
|
+
files.push({ path: join(clientDir, "types", `${table.name}.ts`), content: typesSrc });
|
|
7266
7147
|
const zodSrc = emitZod(table, { numericMode }, model.enums);
|
|
7267
|
-
files.push({ path:
|
|
7268
|
-
files.push({ path:
|
|
7148
|
+
files.push({ path: join(serverDir, "zod", `${table.name}.ts`), content: zodSrc });
|
|
7149
|
+
files.push({ path: join(clientDir, "zod", `${table.name}.ts`), content: zodSrc });
|
|
7269
7150
|
const paramsZodSrc = emitParamsZod(table, graph);
|
|
7270
|
-
files.push({ path:
|
|
7151
|
+
files.push({ path: join(clientDir, "params", `${table.name}.ts`), content: paramsZodSrc });
|
|
7271
7152
|
let routeContent;
|
|
7272
7153
|
if (serverFramework === "hono") {
|
|
7273
7154
|
routeContent = emitHonoRoutes(table, graph, {
|
|
@@ -7281,11 +7162,11 @@ async function generate(configPath) {
|
|
|
7281
7162
|
throw new Error(`Framework "${serverFramework}" is not yet supported. Currently only "hono" is available.`);
|
|
7282
7163
|
}
|
|
7283
7164
|
files.push({
|
|
7284
|
-
path:
|
|
7165
|
+
path: join(serverDir, "routes", `${table.name}.ts`),
|
|
7285
7166
|
content: routeContent
|
|
7286
7167
|
});
|
|
7287
7168
|
files.push({
|
|
7288
|
-
path:
|
|
7169
|
+
path: join(clientDir, `${table.name}.ts`),
|
|
7289
7170
|
content: emitClient(table, graph, {
|
|
7290
7171
|
useJsExtensions: cfg.useJsExtensionsClient,
|
|
7291
7172
|
includeMethodsDepth: cfg.includeMethodsDepth ?? 2,
|
|
@@ -7294,12 +7175,12 @@ async function generate(configPath) {
|
|
|
7294
7175
|
});
|
|
7295
7176
|
}
|
|
7296
7177
|
files.push({
|
|
7297
|
-
path:
|
|
7178
|
+
path: join(clientDir, "index.ts"),
|
|
7298
7179
|
content: emitClientIndex(Object.values(model.tables), cfg.useJsExtensionsClient)
|
|
7299
7180
|
});
|
|
7300
7181
|
if (serverFramework === "hono") {
|
|
7301
7182
|
files.push({
|
|
7302
|
-
path:
|
|
7183
|
+
path: join(serverDir, "router.ts"),
|
|
7303
7184
|
content: emitHonoRouter(Object.values(model.tables), getAuthStrategy(normalizedAuth) !== "none", cfg.useJsExtensions, cfg.pullToken)
|
|
7304
7185
|
});
|
|
7305
7186
|
}
|
|
@@ -7309,73 +7190,64 @@ async function generate(configPath) {
|
|
|
7309
7190
|
}
|
|
7310
7191
|
const contract = generateUnifiedContract2(model, cfg, graph);
|
|
7311
7192
|
files.push({
|
|
7312
|
-
path:
|
|
7193
|
+
path: join(serverDir, "CONTRACT.md"),
|
|
7313
7194
|
content: generateUnifiedContractMarkdown2(contract)
|
|
7314
7195
|
});
|
|
7315
7196
|
files.push({
|
|
7316
|
-
path:
|
|
7197
|
+
path: join(clientDir, "CONTRACT.md"),
|
|
7317
7198
|
content: generateUnifiedContractMarkdown2(contract)
|
|
7318
7199
|
});
|
|
7319
7200
|
const contractCode = emitUnifiedContract(model, cfg, graph);
|
|
7320
7201
|
files.push({
|
|
7321
|
-
path:
|
|
7202
|
+
path: join(serverDir, "contract.ts"),
|
|
7322
7203
|
content: contractCode
|
|
7323
7204
|
});
|
|
7324
7205
|
const clientFiles = files.filter((f) => {
|
|
7325
7206
|
return f.path.includes(clientDir);
|
|
7326
7207
|
});
|
|
7327
7208
|
files.push({
|
|
7328
|
-
path:
|
|
7209
|
+
path: join(serverDir, "sdk-bundle.ts"),
|
|
7329
7210
|
content: emitSdkBundle(clientFiles, clientDir)
|
|
7330
7211
|
});
|
|
7331
7212
|
if (generateTests) {
|
|
7332
7213
|
console.log("\uD83E\uDDEA Generating tests...");
|
|
7333
7214
|
const relativeClientPath = relative(testDir, clientDir);
|
|
7334
7215
|
files.push({
|
|
7335
|
-
path:
|
|
7216
|
+
path: join(testDir, "setup.ts"),
|
|
7336
7217
|
content: emitTestSetup(relativeClientPath, testFramework)
|
|
7337
7218
|
});
|
|
7338
7219
|
files.push({
|
|
7339
|
-
path:
|
|
7220
|
+
path: join(testDir, "docker-compose.yml"),
|
|
7340
7221
|
content: emitDockerCompose()
|
|
7341
7222
|
});
|
|
7342
7223
|
files.push({
|
|
7343
|
-
path:
|
|
7224
|
+
path: join(testDir, "run-tests.sh"),
|
|
7344
7225
|
content: emitTestScript(testFramework, testDir)
|
|
7345
7226
|
});
|
|
7346
7227
|
files.push({
|
|
7347
|
-
path:
|
|
7228
|
+
path: join(testDir, ".gitignore"),
|
|
7348
7229
|
content: emitTestGitignore()
|
|
7349
7230
|
});
|
|
7350
7231
|
if (testFramework === "vitest") {
|
|
7351
7232
|
files.push({
|
|
7352
|
-
path:
|
|
7233
|
+
path: join(testDir, "vitest.config.ts"),
|
|
7353
7234
|
content: emitVitestConfig()
|
|
7354
7235
|
});
|
|
7355
7236
|
}
|
|
7356
7237
|
for (const table of Object.values(model.tables)) {
|
|
7357
7238
|
files.push({
|
|
7358
|
-
path:
|
|
7239
|
+
path: join(testDir, `${table.name}.test.ts`),
|
|
7359
7240
|
content: emitTableTest(table, model, relativeClientPath, testFramework)
|
|
7360
7241
|
});
|
|
7361
7242
|
}
|
|
7362
7243
|
}
|
|
7363
7244
|
console.log("✍️ Writing files...");
|
|
7364
|
-
await
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
config: {
|
|
7371
|
-
outDir: cfg.outDir || "./api",
|
|
7372
|
-
schema: cfg.schema || "public"
|
|
7373
|
-
}
|
|
7374
|
-
});
|
|
7375
|
-
await appendToHistory(`Generate
|
|
7376
|
-
✅ Generated ${files.length} files
|
|
7377
|
-
- Schema hash: ${schemaHash.substring(0, 12)}...`);
|
|
7378
|
-
console.log(`✅ Generated ${files.length} files`);
|
|
7245
|
+
const writeResult = await writeFilesIfChanged(files);
|
|
7246
|
+
if (writeResult.written === 0) {
|
|
7247
|
+
console.log(`✅ All ${writeResult.unchanged} files up-to-date (no changes)`);
|
|
7248
|
+
} else {
|
|
7249
|
+
console.log(`✅ Updated ${writeResult.written} files, ${writeResult.unchanged} unchanged`);
|
|
7250
|
+
}
|
|
7379
7251
|
console.log(` Server: ${serverDir}`);
|
|
7380
7252
|
console.log(` Client: ${sameDirectory ? clientDir + " (in sdk subdir due to same output dir)" : clientDir}`);
|
|
7381
7253
|
if (generateTests) {
|
|
@@ -7411,13 +7283,13 @@ async function generate(configPath) {
|
|
|
7411
7283
|
// src/cli.ts
|
|
7412
7284
|
var import_config2 = __toESM(require_config(), 1);
|
|
7413
7285
|
import { resolve as resolve3 } from "node:path";
|
|
7414
|
-
import { readFileSync as
|
|
7286
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
7415
7287
|
import { fileURLToPath } from "node:url";
|
|
7416
|
-
import { dirname as
|
|
7288
|
+
import { dirname as dirname3, join as join3 } from "node:path";
|
|
7417
7289
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
7418
|
-
var __dirname2 =
|
|
7419
|
-
var
|
|
7420
|
-
var VERSION =
|
|
7290
|
+
var __dirname2 = dirname3(__filename2);
|
|
7291
|
+
var packageJson = JSON.parse(readFileSync2(join3(__dirname2, "../package.json"), "utf-8"));
|
|
7292
|
+
var VERSION = packageJson.version;
|
|
7421
7293
|
var args = process.argv.slice(2);
|
|
7422
7294
|
var command = args[0];
|
|
7423
7295
|
if (args.includes("--version") || args.includes("-v") || command === "version") {
|
package/dist/index.js
CHANGED
|
@@ -472,11 +472,25 @@ var require_config = __commonJS(() => {
|
|
|
472
472
|
// src/utils.ts
|
|
473
473
|
import { mkdir, writeFile, readFile } from "fs/promises";
|
|
474
474
|
import { dirname } from "path";
|
|
475
|
-
|
|
475
|
+
import { existsSync } from "fs";
|
|
476
|
+
async function writeFilesIfChanged(files) {
|
|
477
|
+
let written = 0;
|
|
478
|
+
let unchanged = 0;
|
|
479
|
+
const filesWritten = [];
|
|
476
480
|
for (const f of files) {
|
|
477
481
|
await mkdir(dirname(f.path), { recursive: true });
|
|
482
|
+
if (existsSync(f.path)) {
|
|
483
|
+
const existing = await readFile(f.path, "utf-8");
|
|
484
|
+
if (existing === f.content) {
|
|
485
|
+
unchanged++;
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
478
489
|
await writeFile(f.path, f.content, "utf-8");
|
|
490
|
+
written++;
|
|
491
|
+
filesWritten.push(f.path);
|
|
479
492
|
}
|
|
493
|
+
return { written, unchanged, filesWritten };
|
|
480
494
|
}
|
|
481
495
|
async function ensureDirs(dirs) {
|
|
482
496
|
for (const d of dirs)
|
|
@@ -1685,110 +1699,9 @@ var init_emit_sdk_contract = __esm(() => {
|
|
|
1685
1699
|
init_emit_include_methods();
|
|
1686
1700
|
});
|
|
1687
1701
|
|
|
1688
|
-
// src/cache.ts
|
|
1689
|
-
import { createHash } from "crypto";
|
|
1690
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, appendFile } from "fs/promises";
|
|
1691
|
-
import { existsSync, readFileSync } from "fs";
|
|
1692
|
-
import { join } from "path";
|
|
1693
|
-
function computeSchemaHash(model, config) {
|
|
1694
|
-
const payload = {
|
|
1695
|
-
version: POSTGRESDK_VERSION,
|
|
1696
|
-
schema: model.schema,
|
|
1697
|
-
tables: model.tables,
|
|
1698
|
-
enums: model.enums,
|
|
1699
|
-
config: {
|
|
1700
|
-
outDir: config.outDir,
|
|
1701
|
-
schema: config.schema,
|
|
1702
|
-
softDeleteColumn: config.softDeleteColumn,
|
|
1703
|
-
includeMethodsDepth: config.includeMethodsDepth,
|
|
1704
|
-
serverFramework: config.serverFramework,
|
|
1705
|
-
useJsExtensions: config.useJsExtensions,
|
|
1706
|
-
useJsExtensionsClient: config.useJsExtensionsClient,
|
|
1707
|
-
numericMode: config.numericMode,
|
|
1708
|
-
skipJunctionTables: config.skipJunctionTables,
|
|
1709
|
-
apiPathPrefix: config.apiPathPrefix,
|
|
1710
|
-
auth: config.auth,
|
|
1711
|
-
tests: config.tests
|
|
1712
|
-
}
|
|
1713
|
-
};
|
|
1714
|
-
const json = JSON.stringify(payload, Object.keys(payload).sort());
|
|
1715
|
-
return createHash("sha256").update(json).digest("hex");
|
|
1716
|
-
}
|
|
1717
|
-
function getCacheDir(baseDir = process.cwd()) {
|
|
1718
|
-
return join(baseDir, ".postgresdk");
|
|
1719
|
-
}
|
|
1720
|
-
async function ensureGitignore(baseDir = process.cwd()) {
|
|
1721
|
-
const gitignorePath = join(baseDir, ".gitignore");
|
|
1722
|
-
if (!existsSync(gitignorePath)) {
|
|
1723
|
-
return;
|
|
1724
|
-
}
|
|
1725
|
-
try {
|
|
1726
|
-
const content = await readFile2(gitignorePath, "utf-8");
|
|
1727
|
-
if (content.includes(".postgresdk")) {
|
|
1728
|
-
return;
|
|
1729
|
-
}
|
|
1730
|
-
const entry = `
|
|
1731
|
-
# PostgreSDK cache and history
|
|
1732
|
-
.postgresdk/
|
|
1733
|
-
`;
|
|
1734
|
-
await appendFile(gitignorePath, entry);
|
|
1735
|
-
console.log("✓ Added .postgresdk/ to .gitignore");
|
|
1736
|
-
} catch {}
|
|
1737
|
-
}
|
|
1738
|
-
async function readCache(baseDir) {
|
|
1739
|
-
const cachePath = join(getCacheDir(baseDir), "cache.json");
|
|
1740
|
-
if (!existsSync(cachePath)) {
|
|
1741
|
-
return null;
|
|
1742
|
-
}
|
|
1743
|
-
try {
|
|
1744
|
-
const content = await readFile2(cachePath, "utf-8");
|
|
1745
|
-
return JSON.parse(content);
|
|
1746
|
-
} catch {
|
|
1747
|
-
return null;
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
async function writeCache(data, baseDir) {
|
|
1751
|
-
const cacheDir = getCacheDir(baseDir);
|
|
1752
|
-
const isNewCache = !existsSync(cacheDir);
|
|
1753
|
-
await mkdir2(cacheDir, { recursive: true });
|
|
1754
|
-
if (isNewCache) {
|
|
1755
|
-
await ensureGitignore(baseDir);
|
|
1756
|
-
}
|
|
1757
|
-
const cachePath = join(cacheDir, "cache.json");
|
|
1758
|
-
await writeFile2(cachePath, JSON.stringify(data, null, 2), "utf-8");
|
|
1759
|
-
}
|
|
1760
|
-
async function appendToHistory(entry, baseDir) {
|
|
1761
|
-
const cacheDir = getCacheDir(baseDir);
|
|
1762
|
-
const isNewCache = !existsSync(cacheDir);
|
|
1763
|
-
await mkdir2(cacheDir, { recursive: true });
|
|
1764
|
-
if (isNewCache) {
|
|
1765
|
-
await ensureGitignore(baseDir);
|
|
1766
|
-
}
|
|
1767
|
-
const historyPath = join(cacheDir, "history.md");
|
|
1768
|
-
const timestamp = new Date().toISOString().replace("T", " ").substring(0, 19);
|
|
1769
|
-
const formattedEntry = `## ${timestamp} - ${entry}
|
|
1770
|
-
|
|
1771
|
-
`;
|
|
1772
|
-
try {
|
|
1773
|
-
const existing = existsSync(historyPath) ? await readFile2(historyPath, "utf-8") : `# PostgreSDK Generation History
|
|
1774
|
-
|
|
1775
|
-
`;
|
|
1776
|
-
await writeFile2(historyPath, existing + formattedEntry, "utf-8");
|
|
1777
|
-
} catch {
|
|
1778
|
-
await writeFile2(historyPath, `# PostgreSDK Generation History
|
|
1779
|
-
|
|
1780
|
-
${formattedEntry}`, "utf-8");
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
var __dirname = "/workspace/src", packageJson, POSTGRESDK_VERSION;
|
|
1784
|
-
var init_cache = __esm(() => {
|
|
1785
|
-
packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
|
|
1786
|
-
POSTGRESDK_VERSION = packageJson.version;
|
|
1787
|
-
});
|
|
1788
|
-
|
|
1789
1702
|
// src/index.ts
|
|
1790
1703
|
var import_config = __toESM(require_config(), 1);
|
|
1791
|
-
import { join
|
|
1704
|
+
import { join, relative } from "node:path";
|
|
1792
1705
|
import { pathToFileURL } from "node:url";
|
|
1793
1706
|
import { existsSync as existsSync2 } from "node:fs";
|
|
1794
1707
|
|
|
@@ -2586,6 +2499,7 @@ function emitClient(table, graph, opts, model) {
|
|
|
2586
2499
|
}
|
|
2587
2500
|
}
|
|
2588
2501
|
const typeImports = `import type { Insert${Type}, Update${Type}, Select${Type} } from "./types/${table.name}${ext}";`;
|
|
2502
|
+
const includeSpecImport = `import type { ${Type}IncludeSpec } from "./include-spec${ext}";`;
|
|
2589
2503
|
const otherTableImports = [];
|
|
2590
2504
|
for (const target of Array.from(importedTypes)) {
|
|
2591
2505
|
if (target !== table.name) {
|
|
@@ -2668,7 +2582,7 @@ function emitClient(table, graph, opts, model) {
|
|
|
2668
2582
|
order?: "asc" | "desc";
|
|
2669
2583
|
limit?: number;
|
|
2670
2584
|
offset?: number;
|
|
2671
|
-
include?:
|
|
2585
|
+
include?: ${Type}IncludeSpec;
|
|
2672
2586
|
};
|
|
2673
2587
|
}`;
|
|
2674
2588
|
}
|
|
@@ -2776,6 +2690,7 @@ import { BaseClient } from "./base-client${ext}";
|
|
|
2776
2690
|
import type { Where } from "./where-types${ext}";
|
|
2777
2691
|
import type { PaginatedResponse } from "./types/shared${ext}";
|
|
2778
2692
|
${typeImports}
|
|
2693
|
+
${includeSpecImport}
|
|
2779
2694
|
${otherTableImports.join(`
|
|
2780
2695
|
`)}
|
|
2781
2696
|
|
|
@@ -2925,7 +2840,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
2925
2840
|
*/
|
|
2926
2841
|
async list<TJsonb extends Partial<Select${Type}> = {}>(params: {
|
|
2927
2842
|
select: string[];
|
|
2928
|
-
include?:
|
|
2843
|
+
include?: ${Type}IncludeSpec;
|
|
2929
2844
|
limit?: number;
|
|
2930
2845
|
offset?: number;
|
|
2931
2846
|
where?: Where<Select${Type}<TJsonb>>;${hasVectorColumns ? `
|
|
@@ -2945,7 +2860,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
2945
2860
|
*/
|
|
2946
2861
|
async list<TJsonb extends Partial<Select${Type}> = {}>(params: {
|
|
2947
2862
|
exclude: string[];
|
|
2948
|
-
include?:
|
|
2863
|
+
include?: ${Type}IncludeSpec;
|
|
2949
2864
|
limit?: number;
|
|
2950
2865
|
offset?: number;
|
|
2951
2866
|
where?: Where<Select${Type}<TJsonb>>;${hasVectorColumns ? `
|
|
@@ -2973,7 +2888,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
2973
2888
|
* const users = await client.list<{ metadata: Metadata }>({ where: { status: 'active' } });
|
|
2974
2889
|
*/
|
|
2975
2890
|
async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
|
|
2976
|
-
include?:
|
|
2891
|
+
include?: ${Type}IncludeSpec;
|
|
2977
2892
|
limit?: number;
|
|
2978
2893
|
offset?: number;
|
|
2979
2894
|
where?: Where<Select${Type}<TJsonb>>;${hasVectorColumns ? `
|
|
@@ -2987,7 +2902,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
2987
2902
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
2988
2903
|
}): Promise<PaginatedResponse<Select${Type}<TJsonb>${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
2989
2904
|
async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
|
|
2990
|
-
include?:
|
|
2905
|
+
include?: ${Type}IncludeSpec;
|
|
2991
2906
|
select?: string[];
|
|
2992
2907
|
exclude?: string[];
|
|
2993
2908
|
limit?: number;
|
|
@@ -3010,7 +2925,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3010
2925
|
*/
|
|
3011
2926
|
async list(params: {
|
|
3012
2927
|
select: string[];
|
|
3013
|
-
include?:
|
|
2928
|
+
include?: ${Type}IncludeSpec;
|
|
3014
2929
|
limit?: number;
|
|
3015
2930
|
offset?: number;
|
|
3016
2931
|
where?: Where<Select${Type}>;${hasVectorColumns ? `
|
|
@@ -3030,7 +2945,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3030
2945
|
*/
|
|
3031
2946
|
async list(params: {
|
|
3032
2947
|
exclude: string[];
|
|
3033
|
-
include?:
|
|
2948
|
+
include?: ${Type}IncludeSpec;
|
|
3034
2949
|
limit?: number;
|
|
3035
2950
|
offset?: number;
|
|
3036
2951
|
where?: Where<Select${Type}>;${hasVectorColumns ? `
|
|
@@ -3055,7 +2970,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3055
2970
|
* @returns Paginated results with all fields
|
|
3056
2971
|
*/
|
|
3057
2972
|
async list(params?: {
|
|
3058
|
-
include?:
|
|
2973
|
+
include?: ${Type}IncludeSpec;
|
|
3059
2974
|
limit?: number;
|
|
3060
2975
|
offset?: number;
|
|
3061
2976
|
where?: Where<Select${Type}>;${hasVectorColumns ? `
|
|
@@ -3069,7 +2984,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3069
2984
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3070
2985
|
}): Promise<PaginatedResponse<Select${Type}${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3071
2986
|
async list(params?: {
|
|
3072
|
-
include?:
|
|
2987
|
+
include?: ${Type}IncludeSpec;
|
|
3073
2988
|
select?: string[];
|
|
3074
2989
|
exclude?: string[];
|
|
3075
2990
|
limit?: number;
|
|
@@ -6180,7 +6095,6 @@ function generateTestCases(table, sampleData, updateData, hasForeignKeys = false
|
|
|
6180
6095
|
// src/index.ts
|
|
6181
6096
|
init_emit_sdk_contract();
|
|
6182
6097
|
init_utils();
|
|
6183
|
-
init_cache();
|
|
6184
6098
|
async function generate(configPath) {
|
|
6185
6099
|
if (!existsSync2(configPath)) {
|
|
6186
6100
|
throw new Error(`Config file not found: ${configPath}
|
|
@@ -6195,58 +6109,43 @@ async function generate(configPath) {
|
|
|
6195
6109
|
const cfg = { ...rawCfg, auth: normalizedAuth };
|
|
6196
6110
|
console.log("\uD83D\uDD0D Introspecting database...");
|
|
6197
6111
|
const model = await introspect(cfg.connectionString, cfg.schema || "public");
|
|
6198
|
-
const schemaHash = computeSchemaHash(model, cfg);
|
|
6199
|
-
const cache = await readCache();
|
|
6200
|
-
let serverDir;
|
|
6201
|
-
if (typeof cfg.outDir === "string") {
|
|
6202
|
-
serverDir = cfg.outDir;
|
|
6203
|
-
} else if (cfg.outDir && typeof cfg.outDir === "object") {
|
|
6204
|
-
serverDir = cfg.outDir.server;
|
|
6205
|
-
} else {
|
|
6206
|
-
serverDir = "./api/server";
|
|
6207
|
-
}
|
|
6208
|
-
const currentOutDir = typeof cfg.outDir === "string" ? cfg.outDir : `${serverDir}`;
|
|
6209
|
-
const cachedOutDir = typeof cache?.config.outDir === "string" ? cache.config.outDir : cache?.config.outDir?.server;
|
|
6210
|
-
if (cache && cache.schemaHash === schemaHash && existsSync2(serverDir) && currentOutDir === cachedOutDir) {
|
|
6211
|
-
console.log("✅ Schema unchanged, skipping generation");
|
|
6212
|
-
await appendToHistory(`Generate
|
|
6213
|
-
✅ Schema unchanged, skipped generation
|
|
6214
|
-
- Schema hash: ${schemaHash.substring(0, 12)}...`);
|
|
6215
|
-
return;
|
|
6216
|
-
}
|
|
6217
6112
|
console.log("\uD83D\uDD17 Building relationship graph...");
|
|
6218
6113
|
const graph = buildGraph(model);
|
|
6114
|
+
let serverDir;
|
|
6219
6115
|
let originalClientDir;
|
|
6220
6116
|
if (typeof cfg.outDir === "string") {
|
|
6117
|
+
serverDir = cfg.outDir;
|
|
6221
6118
|
originalClientDir = cfg.outDir;
|
|
6222
6119
|
} else if (cfg.outDir && typeof cfg.outDir === "object") {
|
|
6120
|
+
serverDir = cfg.outDir.server;
|
|
6223
6121
|
originalClientDir = cfg.outDir.client;
|
|
6224
6122
|
} else {
|
|
6123
|
+
serverDir = "./api/server";
|
|
6225
6124
|
originalClientDir = "./api/client";
|
|
6226
6125
|
}
|
|
6227
6126
|
const sameDirectory = serverDir === originalClientDir;
|
|
6228
6127
|
let clientDir = originalClientDir;
|
|
6229
6128
|
if (sameDirectory) {
|
|
6230
|
-
clientDir =
|
|
6129
|
+
clientDir = join(originalClientDir, "sdk");
|
|
6231
6130
|
}
|
|
6232
6131
|
const serverFramework = cfg.serverFramework || "hono";
|
|
6233
6132
|
const generateTests = cfg.tests?.generate ?? false;
|
|
6234
6133
|
const originalTestDir = cfg.tests?.output || "./api/tests";
|
|
6235
6134
|
let testDir = originalTestDir;
|
|
6236
6135
|
if (generateTests && (originalTestDir === serverDir || originalTestDir === originalClientDir)) {
|
|
6237
|
-
testDir =
|
|
6136
|
+
testDir = join(originalTestDir, "tests");
|
|
6238
6137
|
}
|
|
6239
6138
|
const testFramework = cfg.tests?.framework || "vitest";
|
|
6240
6139
|
console.log("\uD83D\uDCC1 Creating directories...");
|
|
6241
6140
|
const dirs = [
|
|
6242
6141
|
serverDir,
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6142
|
+
join(serverDir, "types"),
|
|
6143
|
+
join(serverDir, "zod"),
|
|
6144
|
+
join(serverDir, "routes"),
|
|
6246
6145
|
clientDir,
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6146
|
+
join(clientDir, "types"),
|
|
6147
|
+
join(clientDir, "zod"),
|
|
6148
|
+
join(clientDir, "params")
|
|
6250
6149
|
];
|
|
6251
6150
|
if (generateTests) {
|
|
6252
6151
|
dirs.push(testDir);
|
|
@@ -6254,26 +6153,26 @@ async function generate(configPath) {
|
|
|
6254
6153
|
await ensureDirs(dirs);
|
|
6255
6154
|
const files = [];
|
|
6256
6155
|
const includeSpec = emitIncludeSpec(graph);
|
|
6257
|
-
files.push({ path:
|
|
6258
|
-
files.push({ path:
|
|
6259
|
-
files.push({ path:
|
|
6260
|
-
files.push({ path:
|
|
6261
|
-
files.push({ path:
|
|
6262
|
-
files.push({ path:
|
|
6156
|
+
files.push({ path: join(serverDir, "include-spec.ts"), content: includeSpec });
|
|
6157
|
+
files.push({ path: join(clientDir, "include-spec.ts"), content: includeSpec });
|
|
6158
|
+
files.push({ path: join(clientDir, "params", "shared.ts"), content: emitSharedParamsZod() });
|
|
6159
|
+
files.push({ path: join(clientDir, "types", "shared.ts"), content: emitSharedTypes() });
|
|
6160
|
+
files.push({ path: join(clientDir, "base-client.ts"), content: emitBaseClient() });
|
|
6161
|
+
files.push({ path: join(clientDir, "where-types.ts"), content: emitWhereTypes() });
|
|
6263
6162
|
files.push({
|
|
6264
|
-
path:
|
|
6163
|
+
path: join(serverDir, "include-builder.ts"),
|
|
6265
6164
|
content: emitIncludeBuilder(graph, cfg.includeMethodsDepth || 2)
|
|
6266
6165
|
});
|
|
6267
6166
|
files.push({
|
|
6268
|
-
path:
|
|
6167
|
+
path: join(serverDir, "include-loader.ts"),
|
|
6269
6168
|
content: emitIncludeLoader(graph, model, cfg.includeMethodsDepth || 2, cfg.useJsExtensions)
|
|
6270
6169
|
});
|
|
6271
|
-
files.push({ path:
|
|
6170
|
+
files.push({ path: join(serverDir, "logger.ts"), content: emitLogger() });
|
|
6272
6171
|
if (getAuthStrategy(normalizedAuth) !== "none") {
|
|
6273
|
-
files.push({ path:
|
|
6172
|
+
files.push({ path: join(serverDir, "auth.ts"), content: emitAuth(normalizedAuth) });
|
|
6274
6173
|
}
|
|
6275
6174
|
files.push({
|
|
6276
|
-
path:
|
|
6175
|
+
path: join(serverDir, "core", "operations.ts"),
|
|
6277
6176
|
content: emitCoreOperations()
|
|
6278
6177
|
});
|
|
6279
6178
|
if (process.env.SDK_DEBUG) {
|
|
@@ -6282,13 +6181,13 @@ async function generate(configPath) {
|
|
|
6282
6181
|
for (const table of Object.values(model.tables)) {
|
|
6283
6182
|
const numericMode = cfg.numericMode ?? "auto";
|
|
6284
6183
|
const typesSrc = emitTypes(table, { numericMode }, model.enums);
|
|
6285
|
-
files.push({ path:
|
|
6286
|
-
files.push({ path:
|
|
6184
|
+
files.push({ path: join(serverDir, "types", `${table.name}.ts`), content: typesSrc });
|
|
6185
|
+
files.push({ path: join(clientDir, "types", `${table.name}.ts`), content: typesSrc });
|
|
6287
6186
|
const zodSrc = emitZod(table, { numericMode }, model.enums);
|
|
6288
|
-
files.push({ path:
|
|
6289
|
-
files.push({ path:
|
|
6187
|
+
files.push({ path: join(serverDir, "zod", `${table.name}.ts`), content: zodSrc });
|
|
6188
|
+
files.push({ path: join(clientDir, "zod", `${table.name}.ts`), content: zodSrc });
|
|
6290
6189
|
const paramsZodSrc = emitParamsZod(table, graph);
|
|
6291
|
-
files.push({ path:
|
|
6190
|
+
files.push({ path: join(clientDir, "params", `${table.name}.ts`), content: paramsZodSrc });
|
|
6292
6191
|
let routeContent;
|
|
6293
6192
|
if (serverFramework === "hono") {
|
|
6294
6193
|
routeContent = emitHonoRoutes(table, graph, {
|
|
@@ -6302,11 +6201,11 @@ async function generate(configPath) {
|
|
|
6302
6201
|
throw new Error(`Framework "${serverFramework}" is not yet supported. Currently only "hono" is available.`);
|
|
6303
6202
|
}
|
|
6304
6203
|
files.push({
|
|
6305
|
-
path:
|
|
6204
|
+
path: join(serverDir, "routes", `${table.name}.ts`),
|
|
6306
6205
|
content: routeContent
|
|
6307
6206
|
});
|
|
6308
6207
|
files.push({
|
|
6309
|
-
path:
|
|
6208
|
+
path: join(clientDir, `${table.name}.ts`),
|
|
6310
6209
|
content: emitClient(table, graph, {
|
|
6311
6210
|
useJsExtensions: cfg.useJsExtensionsClient,
|
|
6312
6211
|
includeMethodsDepth: cfg.includeMethodsDepth ?? 2,
|
|
@@ -6315,12 +6214,12 @@ async function generate(configPath) {
|
|
|
6315
6214
|
});
|
|
6316
6215
|
}
|
|
6317
6216
|
files.push({
|
|
6318
|
-
path:
|
|
6217
|
+
path: join(clientDir, "index.ts"),
|
|
6319
6218
|
content: emitClientIndex(Object.values(model.tables), cfg.useJsExtensionsClient)
|
|
6320
6219
|
});
|
|
6321
6220
|
if (serverFramework === "hono") {
|
|
6322
6221
|
files.push({
|
|
6323
|
-
path:
|
|
6222
|
+
path: join(serverDir, "router.ts"),
|
|
6324
6223
|
content: emitHonoRouter(Object.values(model.tables), getAuthStrategy(normalizedAuth) !== "none", cfg.useJsExtensions, cfg.pullToken)
|
|
6325
6224
|
});
|
|
6326
6225
|
}
|
|
@@ -6330,73 +6229,64 @@ async function generate(configPath) {
|
|
|
6330
6229
|
}
|
|
6331
6230
|
const contract = generateUnifiedContract2(model, cfg, graph);
|
|
6332
6231
|
files.push({
|
|
6333
|
-
path:
|
|
6232
|
+
path: join(serverDir, "CONTRACT.md"),
|
|
6334
6233
|
content: generateUnifiedContractMarkdown2(contract)
|
|
6335
6234
|
});
|
|
6336
6235
|
files.push({
|
|
6337
|
-
path:
|
|
6236
|
+
path: join(clientDir, "CONTRACT.md"),
|
|
6338
6237
|
content: generateUnifiedContractMarkdown2(contract)
|
|
6339
6238
|
});
|
|
6340
6239
|
const contractCode = emitUnifiedContract(model, cfg, graph);
|
|
6341
6240
|
files.push({
|
|
6342
|
-
path:
|
|
6241
|
+
path: join(serverDir, "contract.ts"),
|
|
6343
6242
|
content: contractCode
|
|
6344
6243
|
});
|
|
6345
6244
|
const clientFiles = files.filter((f) => {
|
|
6346
6245
|
return f.path.includes(clientDir);
|
|
6347
6246
|
});
|
|
6348
6247
|
files.push({
|
|
6349
|
-
path:
|
|
6248
|
+
path: join(serverDir, "sdk-bundle.ts"),
|
|
6350
6249
|
content: emitSdkBundle(clientFiles, clientDir)
|
|
6351
6250
|
});
|
|
6352
6251
|
if (generateTests) {
|
|
6353
6252
|
console.log("\uD83E\uDDEA Generating tests...");
|
|
6354
6253
|
const relativeClientPath = relative(testDir, clientDir);
|
|
6355
6254
|
files.push({
|
|
6356
|
-
path:
|
|
6255
|
+
path: join(testDir, "setup.ts"),
|
|
6357
6256
|
content: emitTestSetup(relativeClientPath, testFramework)
|
|
6358
6257
|
});
|
|
6359
6258
|
files.push({
|
|
6360
|
-
path:
|
|
6259
|
+
path: join(testDir, "docker-compose.yml"),
|
|
6361
6260
|
content: emitDockerCompose()
|
|
6362
6261
|
});
|
|
6363
6262
|
files.push({
|
|
6364
|
-
path:
|
|
6263
|
+
path: join(testDir, "run-tests.sh"),
|
|
6365
6264
|
content: emitTestScript(testFramework, testDir)
|
|
6366
6265
|
});
|
|
6367
6266
|
files.push({
|
|
6368
|
-
path:
|
|
6267
|
+
path: join(testDir, ".gitignore"),
|
|
6369
6268
|
content: emitTestGitignore()
|
|
6370
6269
|
});
|
|
6371
6270
|
if (testFramework === "vitest") {
|
|
6372
6271
|
files.push({
|
|
6373
|
-
path:
|
|
6272
|
+
path: join(testDir, "vitest.config.ts"),
|
|
6374
6273
|
content: emitVitestConfig()
|
|
6375
6274
|
});
|
|
6376
6275
|
}
|
|
6377
6276
|
for (const table of Object.values(model.tables)) {
|
|
6378
6277
|
files.push({
|
|
6379
|
-
path:
|
|
6278
|
+
path: join(testDir, `${table.name}.test.ts`),
|
|
6380
6279
|
content: emitTableTest(table, model, relativeClientPath, testFramework)
|
|
6381
6280
|
});
|
|
6382
6281
|
}
|
|
6383
6282
|
}
|
|
6384
6283
|
console.log("✍️ Writing files...");
|
|
6385
|
-
await
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
config: {
|
|
6392
|
-
outDir: cfg.outDir || "./api",
|
|
6393
|
-
schema: cfg.schema || "public"
|
|
6394
|
-
}
|
|
6395
|
-
});
|
|
6396
|
-
await appendToHistory(`Generate
|
|
6397
|
-
✅ Generated ${files.length} files
|
|
6398
|
-
- Schema hash: ${schemaHash.substring(0, 12)}...`);
|
|
6399
|
-
console.log(`✅ Generated ${files.length} files`);
|
|
6284
|
+
const writeResult = await writeFilesIfChanged(files);
|
|
6285
|
+
if (writeResult.written === 0) {
|
|
6286
|
+
console.log(`✅ All ${writeResult.unchanged} files up-to-date (no changes)`);
|
|
6287
|
+
} else {
|
|
6288
|
+
console.log(`✅ Updated ${writeResult.written} files, ${writeResult.unchanged} unchanged`);
|
|
6289
|
+
}
|
|
6400
6290
|
console.log(` Server: ${serverDir}`);
|
|
6401
6291
|
console.log(` Client: ${sameDirectory ? clientDir + " (in sdk subdir due to same output dir)" : clientDir}`);
|
|
6402
6292
|
if (generateTests) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postgresdk",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.7",
|
|
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,8 @@
|
|
|
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: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",
|
|
26
|
+
"test:write-files": "bun test/test-write-files-if-changed.ts",
|
|
26
27
|
"test:init": "bun test/test-init.ts",
|
|
27
28
|
"test:gen": "bun test/test-gen.ts",
|
|
28
29
|
"test:gen-with-tests": "bun test/test-gen-with-tests.ts",
|
package/dist/cache.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { Model } from "./introspect";
|
|
2
|
-
import type { Config } from "./types";
|
|
3
|
-
export interface CacheData {
|
|
4
|
-
schemaHash: string;
|
|
5
|
-
lastRun: string;
|
|
6
|
-
filesGenerated: number;
|
|
7
|
-
config: {
|
|
8
|
-
outDir: string | {
|
|
9
|
-
server: string;
|
|
10
|
-
client: string;
|
|
11
|
-
};
|
|
12
|
-
schema: string;
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Compute a deterministic hash of the schema and config
|
|
17
|
-
* Includes postgresdk version to trigger regeneration on upgrades
|
|
18
|
-
*/
|
|
19
|
-
export declare function computeSchemaHash(model: Model, config: Config): string;
|
|
20
|
-
/**
|
|
21
|
-
* Get the cache directory path
|
|
22
|
-
*/
|
|
23
|
-
export declare function getCacheDir(baseDir?: string): string;
|
|
24
|
-
/**
|
|
25
|
-
* Read cache data if it exists
|
|
26
|
-
*/
|
|
27
|
-
export declare function readCache(baseDir?: string): Promise<CacheData | null>;
|
|
28
|
-
/**
|
|
29
|
-
* Write cache data
|
|
30
|
-
*/
|
|
31
|
-
export declare function writeCache(data: CacheData, baseDir?: string): Promise<void>;
|
|
32
|
-
/**
|
|
33
|
-
* Append an entry to the history log
|
|
34
|
-
*/
|
|
35
|
-
export declare function appendToHistory(entry: string, baseDir?: string): Promise<void>;
|