postgresdk 0.16.14 → 0.17.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/cache.d.ts +34 -0
- package/dist/cli.js +239 -73
- package/dist/emit-sdk-contract.d.ts +0 -1
- package/dist/index.js +171 -51
- package/dist/utils.d.ts +16 -0
- package/package.json +1 -1
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
*/
|
|
18
|
+
export declare function computeSchemaHash(model: Model, config: Config): string;
|
|
19
|
+
/**
|
|
20
|
+
* Get the cache directory path
|
|
21
|
+
*/
|
|
22
|
+
export declare function getCacheDir(baseDir?: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Read cache data if it exists
|
|
25
|
+
*/
|
|
26
|
+
export declare function readCache(baseDir?: string): Promise<CacheData | null>;
|
|
27
|
+
/**
|
|
28
|
+
* Write cache data
|
|
29
|
+
*/
|
|
30
|
+
export declare function writeCache(data: CacheData, baseDir?: string): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Append an entry to the history log
|
|
33
|
+
*/
|
|
34
|
+
export declare function appendToHistory(entry: string, baseDir?: string): Promise<void>;
|
package/dist/cli.js
CHANGED
|
@@ -471,7 +471,7 @@ var require_config = __commonJS(() => {
|
|
|
471
471
|
});
|
|
472
472
|
|
|
473
473
|
// src/utils.ts
|
|
474
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
474
|
+
import { mkdir, writeFile, readFile } from "fs/promises";
|
|
475
475
|
import { dirname } from "path";
|
|
476
476
|
async function writeFiles(files) {
|
|
477
477
|
for (const f of files) {
|
|
@@ -701,7 +701,6 @@ function generateUnifiedContract(model, config, graph) {
|
|
|
701
701
|
}
|
|
702
702
|
const contract = {
|
|
703
703
|
version: "2.0.0",
|
|
704
|
-
generatedAt: new Date().toISOString(),
|
|
705
704
|
description: "Unified API and SDK contract - your one-stop reference for all operations",
|
|
706
705
|
sdk: {
|
|
707
706
|
initialization: generateSDKInitExamples(),
|
|
@@ -1237,7 +1236,6 @@ function generateUnifiedContractMarkdown(contract) {
|
|
|
1237
1236
|
lines.push(contract.description);
|
|
1238
1237
|
lines.push("");
|
|
1239
1238
|
lines.push(`**Version:** ${contract.version}`);
|
|
1240
|
-
lines.push(`**Generated:** ${new Date(contract.generatedAt).toLocaleString()}`);
|
|
1241
1239
|
lines.push("");
|
|
1242
1240
|
lines.push("## SDK Setup");
|
|
1243
1241
|
lines.push("");
|
|
@@ -1688,6 +1686,102 @@ var init_emit_sdk_contract = __esm(() => {
|
|
|
1688
1686
|
init_emit_include_methods();
|
|
1689
1687
|
});
|
|
1690
1688
|
|
|
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 } from "fs";
|
|
1693
|
+
import { join } from "path";
|
|
1694
|
+
function computeSchemaHash(model, config) {
|
|
1695
|
+
const payload = {
|
|
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 init_cache = () => {};
|
|
1784
|
+
|
|
1691
1785
|
// src/cli-config-utils.ts
|
|
1692
1786
|
function extractConfigFields(configContent) {
|
|
1693
1787
|
const fields = [];
|
|
@@ -2107,7 +2201,7 @@ var exports_cli_init = {};
|
|
|
2107
2201
|
__export(exports_cli_init, {
|
|
2108
2202
|
initCommand: () => initCommand
|
|
2109
2203
|
});
|
|
2110
|
-
import { existsSync as
|
|
2204
|
+
import { existsSync as existsSync3, writeFileSync, readFileSync, copyFileSync } from "fs";
|
|
2111
2205
|
import { resolve } from "path";
|
|
2112
2206
|
import prompts from "prompts";
|
|
2113
2207
|
async function initCommand(args) {
|
|
@@ -2117,7 +2211,7 @@ async function initCommand(args) {
|
|
|
2117
2211
|
const isApiSide = args.includes("--api");
|
|
2118
2212
|
const isSdkSide = args.includes("--sdk") || args[0] === "pull";
|
|
2119
2213
|
const configPath = resolve(process.cwd(), "postgresdk.config.ts");
|
|
2120
|
-
if (
|
|
2214
|
+
if (existsSync3(configPath)) {
|
|
2121
2215
|
if (forceError) {
|
|
2122
2216
|
console.error("❌ Error: postgresdk.config.ts already exists");
|
|
2123
2217
|
console.log(" To reinitialize, please remove or rename the existing file first.");
|
|
@@ -2272,7 +2366,7 @@ async function initCommand(args) {
|
|
|
2272
2366
|
}
|
|
2273
2367
|
const template = projectType === "api" ? CONFIG_TEMPLATE_API : CONFIG_TEMPLATE_SDK;
|
|
2274
2368
|
const envPath = resolve(process.cwd(), ".env");
|
|
2275
|
-
const hasEnv =
|
|
2369
|
+
const hasEnv = existsSync3(envPath);
|
|
2276
2370
|
try {
|
|
2277
2371
|
writeFileSync(configPath, template, "utf-8");
|
|
2278
2372
|
console.log("✅ Created postgresdk.config.ts");
|
|
@@ -2516,9 +2610,9 @@ var exports_cli_pull = {};
|
|
|
2516
2610
|
__export(exports_cli_pull, {
|
|
2517
2611
|
pullCommand: () => pullCommand
|
|
2518
2612
|
});
|
|
2519
|
-
import { writeFile as
|
|
2520
|
-
import { join as
|
|
2521
|
-
import { existsSync as
|
|
2613
|
+
import { writeFile as writeFile3, mkdir as mkdir3, readFile as readFile3 } from "fs/promises";
|
|
2614
|
+
import { join as join3, dirname as dirname3, resolve as resolve2 } from "path";
|
|
2615
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2522
2616
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
2523
2617
|
async function pullCommand(args) {
|
|
2524
2618
|
let configPath = "postgresdk.config.ts";
|
|
@@ -2528,7 +2622,7 @@ async function pullCommand(args) {
|
|
|
2528
2622
|
}
|
|
2529
2623
|
let fileConfig = {};
|
|
2530
2624
|
const fullConfigPath = resolve2(process.cwd(), configPath);
|
|
2531
|
-
if (
|
|
2625
|
+
if (existsSync4(fullConfigPath)) {
|
|
2532
2626
|
console.log(`\uD83D\uDCCB Loading ${configPath}`);
|
|
2533
2627
|
try {
|
|
2534
2628
|
const configUrl = pathToFileURL2(fullConfigPath).href;
|
|
@@ -2586,7 +2680,6 @@ Options:`);
|
|
|
2586
2680
|
}
|
|
2587
2681
|
const manifest = await manifestRes.json();
|
|
2588
2682
|
console.log(`\uD83D\uDCE6 SDK version: ${manifest.version}`);
|
|
2589
|
-
console.log(`\uD83D\uDCC5 Generated: ${manifest.generated}`);
|
|
2590
2683
|
console.log(`\uD83D\uDCC4 Files: ${manifest.files.length}`);
|
|
2591
2684
|
const sdkRes = await fetch(`${config.from}/_psdk/sdk/download`, { headers });
|
|
2592
2685
|
if (!sdkRes.ok) {
|
|
@@ -2600,31 +2693,78 @@ Options:`);
|
|
|
2600
2693
|
throw new Error(`Failed to download SDK: ${errorMsg}`);
|
|
2601
2694
|
}
|
|
2602
2695
|
const sdk = await sdkRes.json();
|
|
2696
|
+
let filesWritten = 0;
|
|
2697
|
+
let filesUnchanged = 0;
|
|
2698
|
+
const changedFiles = [];
|
|
2603
2699
|
for (const [path, content] of Object.entries(sdk.files)) {
|
|
2604
|
-
const fullPath =
|
|
2605
|
-
await
|
|
2606
|
-
|
|
2607
|
-
|
|
2700
|
+
const fullPath = join3(config.output, path);
|
|
2701
|
+
await mkdir3(dirname3(fullPath), { recursive: true });
|
|
2702
|
+
let shouldWrite = true;
|
|
2703
|
+
if (existsSync4(fullPath)) {
|
|
2704
|
+
const existing = await readFile3(fullPath, "utf-8");
|
|
2705
|
+
if (existing === content) {
|
|
2706
|
+
shouldWrite = false;
|
|
2707
|
+
filesUnchanged++;
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
if (shouldWrite) {
|
|
2711
|
+
await writeFile3(fullPath, content, "utf-8");
|
|
2712
|
+
filesWritten++;
|
|
2713
|
+
changedFiles.push(path);
|
|
2714
|
+
console.log(` ✓ ${path}`);
|
|
2715
|
+
}
|
|
2608
2716
|
}
|
|
2609
|
-
|
|
2717
|
+
const metadataPath = join3(config.output, ".postgresdk.json");
|
|
2718
|
+
const metadata = {
|
|
2610
2719
|
version: sdk.version,
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2720
|
+
pulledFrom: config.from
|
|
2721
|
+
};
|
|
2722
|
+
let metadataChanged = true;
|
|
2723
|
+
if (existsSync4(metadataPath)) {
|
|
2724
|
+
const existing = await readFile3(metadataPath, "utf-8");
|
|
2725
|
+
if (existing === JSON.stringify(metadata, null, 2)) {
|
|
2726
|
+
metadataChanged = false;
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
if (metadataChanged) {
|
|
2730
|
+
await writeFile3(metadataPath, JSON.stringify(metadata, null, 2));
|
|
2731
|
+
}
|
|
2732
|
+
if (filesWritten === 0 && !metadataChanged) {
|
|
2733
|
+
console.log(`✅ SDK up-to-date (${filesUnchanged} files unchanged)`);
|
|
2734
|
+
await appendToHistory(`Pull
|
|
2735
|
+
✅ SDK up-to-date
|
|
2736
|
+
- Pulled from: ${config.from}
|
|
2737
|
+
- Files checked: ${filesUnchanged}`);
|
|
2738
|
+
} else {
|
|
2739
|
+
console.log(`✅ SDK pulled successfully to ${config.output}`);
|
|
2740
|
+
console.log(` Updated: ${filesWritten} files, Unchanged: ${filesUnchanged} files`);
|
|
2741
|
+
let logEntry = `Pull
|
|
2742
|
+
✅ Updated ${filesWritten} files from ${config.from}
|
|
2743
|
+
- SDK version: ${sdk.version}
|
|
2744
|
+
- Files unchanged: ${filesUnchanged}`;
|
|
2745
|
+
if (changedFiles.length > 0 && changedFiles.length <= 10) {
|
|
2746
|
+
logEntry += `
|
|
2747
|
+
- Modified: ${changedFiles.join(", ")}`;
|
|
2748
|
+
} else if (changedFiles.length > 10) {
|
|
2749
|
+
logEntry += `
|
|
2750
|
+
- Modified: ${changedFiles.slice(0, 10).join(", ")} and ${changedFiles.length - 10} more...`;
|
|
2751
|
+
}
|
|
2752
|
+
await appendToHistory(logEntry);
|
|
2753
|
+
}
|
|
2616
2754
|
} catch (err) {
|
|
2617
2755
|
console.error(`❌ Pull failed:`, err);
|
|
2618
2756
|
process.exit(1);
|
|
2619
2757
|
}
|
|
2620
2758
|
}
|
|
2621
|
-
var init_cli_pull = () => {
|
|
2759
|
+
var init_cli_pull = __esm(() => {
|
|
2760
|
+
init_cache();
|
|
2761
|
+
});
|
|
2622
2762
|
|
|
2623
2763
|
// src/index.ts
|
|
2624
2764
|
var import_config = __toESM(require_config(), 1);
|
|
2625
|
-
import { join, relative } from "node:path";
|
|
2765
|
+
import { join as join2, relative } from "node:path";
|
|
2626
2766
|
import { pathToFileURL } from "node:url";
|
|
2627
|
-
import { existsSync } from "node:fs";
|
|
2767
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
2628
2768
|
|
|
2629
2769
|
// src/introspect.ts
|
|
2630
2770
|
import { Client } from "pg";
|
|
@@ -4987,7 +5127,6 @@ ${pullToken ? `
|
|
|
4987
5127
|
router.get("/_psdk/sdk/manifest", (c) => {
|
|
4988
5128
|
return c.json({
|
|
4989
5129
|
version: SDK_MANIFEST.version,
|
|
4990
|
-
generated: SDK_MANIFEST.generated,
|
|
4991
5130
|
files: Object.keys(SDK_MANIFEST.files)
|
|
4992
5131
|
});
|
|
4993
5132
|
});
|
|
@@ -5089,7 +5228,6 @@ function emitSdkBundle(clientFiles, clientDir) {
|
|
|
5089
5228
|
}
|
|
5090
5229
|
}
|
|
5091
5230
|
const version = `1.0.0`;
|
|
5092
|
-
const generated = new Date().toISOString();
|
|
5093
5231
|
return `/**
|
|
5094
5232
|
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
5095
5233
|
*
|
|
@@ -5101,7 +5239,6 @@ function emitSdkBundle(clientFiles, clientDir) {
|
|
|
5101
5239
|
|
|
5102
5240
|
export const SDK_MANIFEST = {
|
|
5103
5241
|
version: "${version}",
|
|
5104
|
-
generated: "${generated}",
|
|
5105
5242
|
files: ${JSON.stringify(files, null, 2)}
|
|
5106
5243
|
};
|
|
5107
5244
|
`;
|
|
@@ -6513,8 +6650,9 @@ function generateTestCases(table, sampleData, updateData, hasForeignKeys = false
|
|
|
6513
6650
|
// src/index.ts
|
|
6514
6651
|
init_emit_sdk_contract();
|
|
6515
6652
|
init_utils();
|
|
6653
|
+
init_cache();
|
|
6516
6654
|
async function generate(configPath) {
|
|
6517
|
-
if (!
|
|
6655
|
+
if (!existsSync2(configPath)) {
|
|
6518
6656
|
throw new Error(`Config file not found: ${configPath}
|
|
6519
6657
|
|
|
6520
6658
|
` + `Run 'postgresdk init' to create a config file, or specify a custom path with:
|
|
@@ -6527,43 +6665,58 @@ async function generate(configPath) {
|
|
|
6527
6665
|
const cfg = { ...rawCfg, auth: normalizedAuth };
|
|
6528
6666
|
console.log("\uD83D\uDD0D Introspecting database...");
|
|
6529
6667
|
const model = await introspect(cfg.connectionString, cfg.schema || "public");
|
|
6668
|
+
const schemaHash = computeSchemaHash(model, cfg);
|
|
6669
|
+
const cache = await readCache();
|
|
6670
|
+
let serverDir;
|
|
6671
|
+
if (typeof cfg.outDir === "string") {
|
|
6672
|
+
serverDir = cfg.outDir;
|
|
6673
|
+
} else if (cfg.outDir && typeof cfg.outDir === "object") {
|
|
6674
|
+
serverDir = cfg.outDir.server;
|
|
6675
|
+
} else {
|
|
6676
|
+
serverDir = "./api/server";
|
|
6677
|
+
}
|
|
6678
|
+
const currentOutDir = typeof cfg.outDir === "string" ? cfg.outDir : `${serverDir}`;
|
|
6679
|
+
const cachedOutDir = typeof cache?.config.outDir === "string" ? cache.config.outDir : cache?.config.outDir?.server;
|
|
6680
|
+
if (cache && cache.schemaHash === schemaHash && existsSync2(serverDir) && currentOutDir === cachedOutDir) {
|
|
6681
|
+
console.log("✅ Schema unchanged, skipping generation");
|
|
6682
|
+
await appendToHistory(`Generate
|
|
6683
|
+
✅ Schema unchanged, skipped generation
|
|
6684
|
+
- Schema hash: ${schemaHash.substring(0, 12)}...`);
|
|
6685
|
+
return;
|
|
6686
|
+
}
|
|
6530
6687
|
console.log("\uD83D\uDD17 Building relationship graph...");
|
|
6531
6688
|
const graph = buildGraph(model);
|
|
6532
|
-
let serverDir;
|
|
6533
6689
|
let originalClientDir;
|
|
6534
6690
|
if (typeof cfg.outDir === "string") {
|
|
6535
|
-
serverDir = cfg.outDir;
|
|
6536
6691
|
originalClientDir = cfg.outDir;
|
|
6537
6692
|
} else if (cfg.outDir && typeof cfg.outDir === "object") {
|
|
6538
|
-
serverDir = cfg.outDir.server;
|
|
6539
6693
|
originalClientDir = cfg.outDir.client;
|
|
6540
6694
|
} else {
|
|
6541
|
-
serverDir = "./api/server";
|
|
6542
6695
|
originalClientDir = "./api/client";
|
|
6543
6696
|
}
|
|
6544
6697
|
const sameDirectory = serverDir === originalClientDir;
|
|
6545
6698
|
let clientDir = originalClientDir;
|
|
6546
6699
|
if (sameDirectory) {
|
|
6547
|
-
clientDir =
|
|
6700
|
+
clientDir = join2(originalClientDir, "sdk");
|
|
6548
6701
|
}
|
|
6549
6702
|
const serverFramework = cfg.serverFramework || "hono";
|
|
6550
6703
|
const generateTests = cfg.tests?.generate ?? false;
|
|
6551
6704
|
const originalTestDir = cfg.tests?.output || "./api/tests";
|
|
6552
6705
|
let testDir = originalTestDir;
|
|
6553
6706
|
if (generateTests && (originalTestDir === serverDir || originalTestDir === originalClientDir)) {
|
|
6554
|
-
testDir =
|
|
6707
|
+
testDir = join2(originalTestDir, "tests");
|
|
6555
6708
|
}
|
|
6556
6709
|
const testFramework = cfg.tests?.framework || "vitest";
|
|
6557
6710
|
console.log("\uD83D\uDCC1 Creating directories...");
|
|
6558
6711
|
const dirs = [
|
|
6559
6712
|
serverDir,
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6713
|
+
join2(serverDir, "types"),
|
|
6714
|
+
join2(serverDir, "zod"),
|
|
6715
|
+
join2(serverDir, "routes"),
|
|
6563
6716
|
clientDir,
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6717
|
+
join2(clientDir, "types"),
|
|
6718
|
+
join2(clientDir, "zod"),
|
|
6719
|
+
join2(clientDir, "params")
|
|
6567
6720
|
];
|
|
6568
6721
|
if (generateTests) {
|
|
6569
6722
|
dirs.push(testDir);
|
|
@@ -6571,26 +6724,26 @@ async function generate(configPath) {
|
|
|
6571
6724
|
await ensureDirs(dirs);
|
|
6572
6725
|
const files = [];
|
|
6573
6726
|
const includeSpec = emitIncludeSpec(graph);
|
|
6574
|
-
files.push({ path:
|
|
6575
|
-
files.push({ path:
|
|
6576
|
-
files.push({ path:
|
|
6577
|
-
files.push({ path:
|
|
6578
|
-
files.push({ path:
|
|
6579
|
-
files.push({ path:
|
|
6727
|
+
files.push({ path: join2(serverDir, "include-spec.ts"), content: includeSpec });
|
|
6728
|
+
files.push({ path: join2(clientDir, "include-spec.ts"), content: includeSpec });
|
|
6729
|
+
files.push({ path: join2(clientDir, "params", "shared.ts"), content: emitSharedParamsZod() });
|
|
6730
|
+
files.push({ path: join2(clientDir, "types", "shared.ts"), content: emitSharedTypes() });
|
|
6731
|
+
files.push({ path: join2(clientDir, "base-client.ts"), content: emitBaseClient() });
|
|
6732
|
+
files.push({ path: join2(clientDir, "where-types.ts"), content: emitWhereTypes() });
|
|
6580
6733
|
files.push({
|
|
6581
|
-
path:
|
|
6734
|
+
path: join2(serverDir, "include-builder.ts"),
|
|
6582
6735
|
content: emitIncludeBuilder(graph, cfg.includeMethodsDepth || 2)
|
|
6583
6736
|
});
|
|
6584
6737
|
files.push({
|
|
6585
|
-
path:
|
|
6738
|
+
path: join2(serverDir, "include-loader.ts"),
|
|
6586
6739
|
content: emitIncludeLoader(graph, model, cfg.includeMethodsDepth || 2, cfg.useJsExtensions)
|
|
6587
6740
|
});
|
|
6588
|
-
files.push({ path:
|
|
6741
|
+
files.push({ path: join2(serverDir, "logger.ts"), content: emitLogger() });
|
|
6589
6742
|
if (getAuthStrategy(normalizedAuth) !== "none") {
|
|
6590
|
-
files.push({ path:
|
|
6743
|
+
files.push({ path: join2(serverDir, "auth.ts"), content: emitAuth(normalizedAuth) });
|
|
6591
6744
|
}
|
|
6592
6745
|
files.push({
|
|
6593
|
-
path:
|
|
6746
|
+
path: join2(serverDir, "core", "operations.ts"),
|
|
6594
6747
|
content: emitCoreOperations()
|
|
6595
6748
|
});
|
|
6596
6749
|
if (process.env.SDK_DEBUG) {
|
|
@@ -6599,13 +6752,13 @@ async function generate(configPath) {
|
|
|
6599
6752
|
for (const table of Object.values(model.tables)) {
|
|
6600
6753
|
const numericMode = cfg.numericMode ?? "auto";
|
|
6601
6754
|
const typesSrc = emitTypes(table, { numericMode }, model.enums);
|
|
6602
|
-
files.push({ path:
|
|
6603
|
-
files.push({ path:
|
|
6755
|
+
files.push({ path: join2(serverDir, "types", `${table.name}.ts`), content: typesSrc });
|
|
6756
|
+
files.push({ path: join2(clientDir, "types", `${table.name}.ts`), content: typesSrc });
|
|
6604
6757
|
const zodSrc = emitZod(table, { numericMode }, model.enums);
|
|
6605
|
-
files.push({ path:
|
|
6606
|
-
files.push({ path:
|
|
6758
|
+
files.push({ path: join2(serverDir, "zod", `${table.name}.ts`), content: zodSrc });
|
|
6759
|
+
files.push({ path: join2(clientDir, "zod", `${table.name}.ts`), content: zodSrc });
|
|
6607
6760
|
const paramsZodSrc = emitParamsZod(table, graph);
|
|
6608
|
-
files.push({ path:
|
|
6761
|
+
files.push({ path: join2(clientDir, "params", `${table.name}.ts`), content: paramsZodSrc });
|
|
6609
6762
|
let routeContent;
|
|
6610
6763
|
if (serverFramework === "hono") {
|
|
6611
6764
|
routeContent = emitHonoRoutes(table, graph, {
|
|
@@ -6619,11 +6772,11 @@ async function generate(configPath) {
|
|
|
6619
6772
|
throw new Error(`Framework "${serverFramework}" is not yet supported. Currently only "hono" is available.`);
|
|
6620
6773
|
}
|
|
6621
6774
|
files.push({
|
|
6622
|
-
path:
|
|
6775
|
+
path: join2(serverDir, "routes", `${table.name}.ts`),
|
|
6623
6776
|
content: routeContent
|
|
6624
6777
|
});
|
|
6625
6778
|
files.push({
|
|
6626
|
-
path:
|
|
6779
|
+
path: join2(clientDir, `${table.name}.ts`),
|
|
6627
6780
|
content: emitClient(table, graph, {
|
|
6628
6781
|
useJsExtensions: cfg.useJsExtensionsClient,
|
|
6629
6782
|
includeMethodsDepth: cfg.includeMethodsDepth ?? 2,
|
|
@@ -6632,12 +6785,12 @@ async function generate(configPath) {
|
|
|
6632
6785
|
});
|
|
6633
6786
|
}
|
|
6634
6787
|
files.push({
|
|
6635
|
-
path:
|
|
6788
|
+
path: join2(clientDir, "index.ts"),
|
|
6636
6789
|
content: emitClientIndex(Object.values(model.tables), cfg.useJsExtensionsClient)
|
|
6637
6790
|
});
|
|
6638
6791
|
if (serverFramework === "hono") {
|
|
6639
6792
|
files.push({
|
|
6640
|
-
path:
|
|
6793
|
+
path: join2(serverDir, "router.ts"),
|
|
6641
6794
|
content: emitHonoRouter(Object.values(model.tables), getAuthStrategy(normalizedAuth) !== "none", cfg.useJsExtensions, cfg.pullToken)
|
|
6642
6795
|
});
|
|
6643
6796
|
}
|
|
@@ -6647,59 +6800,72 @@ async function generate(configPath) {
|
|
|
6647
6800
|
}
|
|
6648
6801
|
const contract = generateUnifiedContract2(model, cfg, graph);
|
|
6649
6802
|
files.push({
|
|
6650
|
-
path:
|
|
6803
|
+
path: join2(serverDir, "CONTRACT.md"),
|
|
6651
6804
|
content: generateUnifiedContractMarkdown2(contract)
|
|
6652
6805
|
});
|
|
6653
6806
|
files.push({
|
|
6654
|
-
path:
|
|
6807
|
+
path: join2(clientDir, "CONTRACT.md"),
|
|
6655
6808
|
content: generateUnifiedContractMarkdown2(contract)
|
|
6656
6809
|
});
|
|
6657
6810
|
const contractCode = emitUnifiedContract(model, cfg, graph);
|
|
6658
6811
|
files.push({
|
|
6659
|
-
path:
|
|
6812
|
+
path: join2(serverDir, "contract.ts"),
|
|
6660
6813
|
content: contractCode
|
|
6661
6814
|
});
|
|
6662
6815
|
const clientFiles = files.filter((f) => {
|
|
6663
6816
|
return f.path.includes(clientDir);
|
|
6664
6817
|
});
|
|
6665
6818
|
files.push({
|
|
6666
|
-
path:
|
|
6819
|
+
path: join2(serverDir, "sdk-bundle.ts"),
|
|
6667
6820
|
content: emitSdkBundle(clientFiles, clientDir)
|
|
6668
6821
|
});
|
|
6669
6822
|
if (generateTests) {
|
|
6670
6823
|
console.log("\uD83E\uDDEA Generating tests...");
|
|
6671
6824
|
const relativeClientPath = relative(testDir, clientDir);
|
|
6672
6825
|
files.push({
|
|
6673
|
-
path:
|
|
6826
|
+
path: join2(testDir, "setup.ts"),
|
|
6674
6827
|
content: emitTestSetup(relativeClientPath, testFramework)
|
|
6675
6828
|
});
|
|
6676
6829
|
files.push({
|
|
6677
|
-
path:
|
|
6830
|
+
path: join2(testDir, "docker-compose.yml"),
|
|
6678
6831
|
content: emitDockerCompose()
|
|
6679
6832
|
});
|
|
6680
6833
|
files.push({
|
|
6681
|
-
path:
|
|
6834
|
+
path: join2(testDir, "run-tests.sh"),
|
|
6682
6835
|
content: emitTestScript(testFramework, testDir)
|
|
6683
6836
|
});
|
|
6684
6837
|
files.push({
|
|
6685
|
-
path:
|
|
6838
|
+
path: join2(testDir, ".gitignore"),
|
|
6686
6839
|
content: emitTestGitignore()
|
|
6687
6840
|
});
|
|
6688
6841
|
if (testFramework === "vitest") {
|
|
6689
6842
|
files.push({
|
|
6690
|
-
path:
|
|
6843
|
+
path: join2(testDir, "vitest.config.ts"),
|
|
6691
6844
|
content: emitVitestConfig()
|
|
6692
6845
|
});
|
|
6693
6846
|
}
|
|
6694
6847
|
for (const table of Object.values(model.tables)) {
|
|
6695
6848
|
files.push({
|
|
6696
|
-
path:
|
|
6849
|
+
path: join2(testDir, `${table.name}.test.ts`),
|
|
6697
6850
|
content: emitTableTest(table, model, relativeClientPath, testFramework)
|
|
6698
6851
|
});
|
|
6699
6852
|
}
|
|
6700
6853
|
}
|
|
6701
6854
|
console.log("✍️ Writing files...");
|
|
6702
6855
|
await writeFiles(files);
|
|
6856
|
+
const outDirStr = typeof cfg.outDir === "string" ? cfg.outDir : `${cfg.outDir?.server || "./api/server"}`;
|
|
6857
|
+
await writeCache({
|
|
6858
|
+
schemaHash,
|
|
6859
|
+
lastRun: new Date().toISOString(),
|
|
6860
|
+
filesGenerated: files.length,
|
|
6861
|
+
config: {
|
|
6862
|
+
outDir: cfg.outDir || "./api",
|
|
6863
|
+
schema: cfg.schema || "public"
|
|
6864
|
+
}
|
|
6865
|
+
});
|
|
6866
|
+
await appendToHistory(`Generate
|
|
6867
|
+
✅ Generated ${files.length} files
|
|
6868
|
+
- Schema hash: ${schemaHash.substring(0, 12)}...`);
|
|
6703
6869
|
console.log(`✅ Generated ${files.length} files`);
|
|
6704
6870
|
console.log(` Server: ${serverDir}`);
|
|
6705
6871
|
console.log(` Client: ${sameDirectory ? clientDir + " (in sdk subdir due to same output dir)" : clientDir}`);
|
|
@@ -6738,10 +6904,10 @@ var import_config2 = __toESM(require_config(), 1);
|
|
|
6738
6904
|
import { resolve as resolve3 } from "node:path";
|
|
6739
6905
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
6740
6906
|
import { fileURLToPath } from "node:url";
|
|
6741
|
-
import { dirname as
|
|
6907
|
+
import { dirname as dirname4, join as join4 } from "node:path";
|
|
6742
6908
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
6743
|
-
var __dirname2 =
|
|
6744
|
-
var packageJson = JSON.parse(readFileSync2(
|
|
6909
|
+
var __dirname2 = dirname4(__filename2);
|
|
6910
|
+
var packageJson = JSON.parse(readFileSync2(join4(__dirname2, "../package.json"), "utf-8"));
|
|
6745
6911
|
var VERSION = packageJson.version;
|
|
6746
6912
|
var args = process.argv.slice(2);
|
|
6747
6913
|
var command = args[0];
|
package/dist/index.js
CHANGED
|
@@ -470,7 +470,7 @@ var require_config = __commonJS(() => {
|
|
|
470
470
|
});
|
|
471
471
|
|
|
472
472
|
// src/utils.ts
|
|
473
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
473
|
+
import { mkdir, writeFile, readFile } from "fs/promises";
|
|
474
474
|
import { dirname } from "path";
|
|
475
475
|
async function writeFiles(files) {
|
|
476
476
|
for (const f of files) {
|
|
@@ -700,7 +700,6 @@ function generateUnifiedContract(model, config, graph) {
|
|
|
700
700
|
}
|
|
701
701
|
const contract = {
|
|
702
702
|
version: "2.0.0",
|
|
703
|
-
generatedAt: new Date().toISOString(),
|
|
704
703
|
description: "Unified API and SDK contract - your one-stop reference for all operations",
|
|
705
704
|
sdk: {
|
|
706
705
|
initialization: generateSDKInitExamples(),
|
|
@@ -1236,7 +1235,6 @@ function generateUnifiedContractMarkdown(contract) {
|
|
|
1236
1235
|
lines.push(contract.description);
|
|
1237
1236
|
lines.push("");
|
|
1238
1237
|
lines.push(`**Version:** ${contract.version}`);
|
|
1239
|
-
lines.push(`**Generated:** ${new Date(contract.generatedAt).toLocaleString()}`);
|
|
1240
1238
|
lines.push("");
|
|
1241
1239
|
lines.push("## SDK Setup");
|
|
1242
1240
|
lines.push("");
|
|
@@ -1687,11 +1685,107 @@ var init_emit_sdk_contract = __esm(() => {
|
|
|
1687
1685
|
init_emit_include_methods();
|
|
1688
1686
|
});
|
|
1689
1687
|
|
|
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 } from "fs";
|
|
1692
|
+
import { join } from "path";
|
|
1693
|
+
function computeSchemaHash(model, config) {
|
|
1694
|
+
const payload = {
|
|
1695
|
+
schema: model.schema,
|
|
1696
|
+
tables: model.tables,
|
|
1697
|
+
enums: model.enums,
|
|
1698
|
+
config: {
|
|
1699
|
+
outDir: config.outDir,
|
|
1700
|
+
schema: config.schema,
|
|
1701
|
+
softDeleteColumn: config.softDeleteColumn,
|
|
1702
|
+
includeMethodsDepth: config.includeMethodsDepth,
|
|
1703
|
+
serverFramework: config.serverFramework,
|
|
1704
|
+
useJsExtensions: config.useJsExtensions,
|
|
1705
|
+
useJsExtensionsClient: config.useJsExtensionsClient,
|
|
1706
|
+
numericMode: config.numericMode,
|
|
1707
|
+
skipJunctionTables: config.skipJunctionTables,
|
|
1708
|
+
apiPathPrefix: config.apiPathPrefix,
|
|
1709
|
+
auth: config.auth,
|
|
1710
|
+
tests: config.tests
|
|
1711
|
+
}
|
|
1712
|
+
};
|
|
1713
|
+
const json = JSON.stringify(payload, Object.keys(payload).sort());
|
|
1714
|
+
return createHash("sha256").update(json).digest("hex");
|
|
1715
|
+
}
|
|
1716
|
+
function getCacheDir(baseDir = process.cwd()) {
|
|
1717
|
+
return join(baseDir, ".postgresdk");
|
|
1718
|
+
}
|
|
1719
|
+
async function ensureGitignore(baseDir = process.cwd()) {
|
|
1720
|
+
const gitignorePath = join(baseDir, ".gitignore");
|
|
1721
|
+
if (!existsSync(gitignorePath)) {
|
|
1722
|
+
return;
|
|
1723
|
+
}
|
|
1724
|
+
try {
|
|
1725
|
+
const content = await readFile2(gitignorePath, "utf-8");
|
|
1726
|
+
if (content.includes(".postgresdk")) {
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
const entry = `
|
|
1730
|
+
# PostgreSDK cache and history
|
|
1731
|
+
.postgresdk/
|
|
1732
|
+
`;
|
|
1733
|
+
await appendFile(gitignorePath, entry);
|
|
1734
|
+
console.log("✓ Added .postgresdk/ to .gitignore");
|
|
1735
|
+
} catch {}
|
|
1736
|
+
}
|
|
1737
|
+
async function readCache(baseDir) {
|
|
1738
|
+
const cachePath = join(getCacheDir(baseDir), "cache.json");
|
|
1739
|
+
if (!existsSync(cachePath)) {
|
|
1740
|
+
return null;
|
|
1741
|
+
}
|
|
1742
|
+
try {
|
|
1743
|
+
const content = await readFile2(cachePath, "utf-8");
|
|
1744
|
+
return JSON.parse(content);
|
|
1745
|
+
} catch {
|
|
1746
|
+
return null;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
async function writeCache(data, baseDir) {
|
|
1750
|
+
const cacheDir = getCacheDir(baseDir);
|
|
1751
|
+
const isNewCache = !existsSync(cacheDir);
|
|
1752
|
+
await mkdir2(cacheDir, { recursive: true });
|
|
1753
|
+
if (isNewCache) {
|
|
1754
|
+
await ensureGitignore(baseDir);
|
|
1755
|
+
}
|
|
1756
|
+
const cachePath = join(cacheDir, "cache.json");
|
|
1757
|
+
await writeFile2(cachePath, JSON.stringify(data, null, 2), "utf-8");
|
|
1758
|
+
}
|
|
1759
|
+
async function appendToHistory(entry, baseDir) {
|
|
1760
|
+
const cacheDir = getCacheDir(baseDir);
|
|
1761
|
+
const isNewCache = !existsSync(cacheDir);
|
|
1762
|
+
await mkdir2(cacheDir, { recursive: true });
|
|
1763
|
+
if (isNewCache) {
|
|
1764
|
+
await ensureGitignore(baseDir);
|
|
1765
|
+
}
|
|
1766
|
+
const historyPath = join(cacheDir, "history.md");
|
|
1767
|
+
const timestamp = new Date().toISOString().replace("T", " ").substring(0, 19);
|
|
1768
|
+
const formattedEntry = `## ${timestamp} - ${entry}
|
|
1769
|
+
|
|
1770
|
+
`;
|
|
1771
|
+
try {
|
|
1772
|
+
const existing = existsSync(historyPath) ? await readFile2(historyPath, "utf-8") : `# PostgreSDK Generation History
|
|
1773
|
+
|
|
1774
|
+
`;
|
|
1775
|
+
await writeFile2(historyPath, existing + formattedEntry, "utf-8");
|
|
1776
|
+
} catch {
|
|
1777
|
+
await writeFile2(historyPath, `# PostgreSDK Generation History
|
|
1778
|
+
|
|
1779
|
+
${formattedEntry}`, "utf-8");
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
var init_cache = () => {};
|
|
1783
|
+
|
|
1690
1784
|
// src/index.ts
|
|
1691
1785
|
var import_config = __toESM(require_config(), 1);
|
|
1692
|
-
import { join, relative } from "node:path";
|
|
1786
|
+
import { join as join2, relative } from "node:path";
|
|
1693
1787
|
import { pathToFileURL } from "node:url";
|
|
1694
|
-
import { existsSync } from "node:fs";
|
|
1788
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
1695
1789
|
|
|
1696
1790
|
// src/introspect.ts
|
|
1697
1791
|
import { Client } from "pg";
|
|
@@ -4054,7 +4148,6 @@ ${pullToken ? `
|
|
|
4054
4148
|
router.get("/_psdk/sdk/manifest", (c) => {
|
|
4055
4149
|
return c.json({
|
|
4056
4150
|
version: SDK_MANIFEST.version,
|
|
4057
|
-
generated: SDK_MANIFEST.generated,
|
|
4058
4151
|
files: Object.keys(SDK_MANIFEST.files)
|
|
4059
4152
|
});
|
|
4060
4153
|
});
|
|
@@ -4156,7 +4249,6 @@ function emitSdkBundle(clientFiles, clientDir) {
|
|
|
4156
4249
|
}
|
|
4157
4250
|
}
|
|
4158
4251
|
const version = `1.0.0`;
|
|
4159
|
-
const generated = new Date().toISOString();
|
|
4160
4252
|
return `/**
|
|
4161
4253
|
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
4162
4254
|
*
|
|
@@ -4168,7 +4260,6 @@ function emitSdkBundle(clientFiles, clientDir) {
|
|
|
4168
4260
|
|
|
4169
4261
|
export const SDK_MANIFEST = {
|
|
4170
4262
|
version: "${version}",
|
|
4171
|
-
generated: "${generated}",
|
|
4172
4263
|
files: ${JSON.stringify(files, null, 2)}
|
|
4173
4264
|
};
|
|
4174
4265
|
`;
|
|
@@ -5580,8 +5671,9 @@ function generateTestCases(table, sampleData, updateData, hasForeignKeys = false
|
|
|
5580
5671
|
// src/index.ts
|
|
5581
5672
|
init_emit_sdk_contract();
|
|
5582
5673
|
init_utils();
|
|
5674
|
+
init_cache();
|
|
5583
5675
|
async function generate(configPath) {
|
|
5584
|
-
if (!
|
|
5676
|
+
if (!existsSync2(configPath)) {
|
|
5585
5677
|
throw new Error(`Config file not found: ${configPath}
|
|
5586
5678
|
|
|
5587
5679
|
` + `Run 'postgresdk init' to create a config file, or specify a custom path with:
|
|
@@ -5594,43 +5686,58 @@ async function generate(configPath) {
|
|
|
5594
5686
|
const cfg = { ...rawCfg, auth: normalizedAuth };
|
|
5595
5687
|
console.log("\uD83D\uDD0D Introspecting database...");
|
|
5596
5688
|
const model = await introspect(cfg.connectionString, cfg.schema || "public");
|
|
5689
|
+
const schemaHash = computeSchemaHash(model, cfg);
|
|
5690
|
+
const cache = await readCache();
|
|
5691
|
+
let serverDir;
|
|
5692
|
+
if (typeof cfg.outDir === "string") {
|
|
5693
|
+
serverDir = cfg.outDir;
|
|
5694
|
+
} else if (cfg.outDir && typeof cfg.outDir === "object") {
|
|
5695
|
+
serverDir = cfg.outDir.server;
|
|
5696
|
+
} else {
|
|
5697
|
+
serverDir = "./api/server";
|
|
5698
|
+
}
|
|
5699
|
+
const currentOutDir = typeof cfg.outDir === "string" ? cfg.outDir : `${serverDir}`;
|
|
5700
|
+
const cachedOutDir = typeof cache?.config.outDir === "string" ? cache.config.outDir : cache?.config.outDir?.server;
|
|
5701
|
+
if (cache && cache.schemaHash === schemaHash && existsSync2(serverDir) && currentOutDir === cachedOutDir) {
|
|
5702
|
+
console.log("✅ Schema unchanged, skipping generation");
|
|
5703
|
+
await appendToHistory(`Generate
|
|
5704
|
+
✅ Schema unchanged, skipped generation
|
|
5705
|
+
- Schema hash: ${schemaHash.substring(0, 12)}...`);
|
|
5706
|
+
return;
|
|
5707
|
+
}
|
|
5597
5708
|
console.log("\uD83D\uDD17 Building relationship graph...");
|
|
5598
5709
|
const graph = buildGraph(model);
|
|
5599
|
-
let serverDir;
|
|
5600
5710
|
let originalClientDir;
|
|
5601
5711
|
if (typeof cfg.outDir === "string") {
|
|
5602
|
-
serverDir = cfg.outDir;
|
|
5603
5712
|
originalClientDir = cfg.outDir;
|
|
5604
5713
|
} else if (cfg.outDir && typeof cfg.outDir === "object") {
|
|
5605
|
-
serverDir = cfg.outDir.server;
|
|
5606
5714
|
originalClientDir = cfg.outDir.client;
|
|
5607
5715
|
} else {
|
|
5608
|
-
serverDir = "./api/server";
|
|
5609
5716
|
originalClientDir = "./api/client";
|
|
5610
5717
|
}
|
|
5611
5718
|
const sameDirectory = serverDir === originalClientDir;
|
|
5612
5719
|
let clientDir = originalClientDir;
|
|
5613
5720
|
if (sameDirectory) {
|
|
5614
|
-
clientDir =
|
|
5721
|
+
clientDir = join2(originalClientDir, "sdk");
|
|
5615
5722
|
}
|
|
5616
5723
|
const serverFramework = cfg.serverFramework || "hono";
|
|
5617
5724
|
const generateTests = cfg.tests?.generate ?? false;
|
|
5618
5725
|
const originalTestDir = cfg.tests?.output || "./api/tests";
|
|
5619
5726
|
let testDir = originalTestDir;
|
|
5620
5727
|
if (generateTests && (originalTestDir === serverDir || originalTestDir === originalClientDir)) {
|
|
5621
|
-
testDir =
|
|
5728
|
+
testDir = join2(originalTestDir, "tests");
|
|
5622
5729
|
}
|
|
5623
5730
|
const testFramework = cfg.tests?.framework || "vitest";
|
|
5624
5731
|
console.log("\uD83D\uDCC1 Creating directories...");
|
|
5625
5732
|
const dirs = [
|
|
5626
5733
|
serverDir,
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5734
|
+
join2(serverDir, "types"),
|
|
5735
|
+
join2(serverDir, "zod"),
|
|
5736
|
+
join2(serverDir, "routes"),
|
|
5630
5737
|
clientDir,
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5738
|
+
join2(clientDir, "types"),
|
|
5739
|
+
join2(clientDir, "zod"),
|
|
5740
|
+
join2(clientDir, "params")
|
|
5634
5741
|
];
|
|
5635
5742
|
if (generateTests) {
|
|
5636
5743
|
dirs.push(testDir);
|
|
@@ -5638,26 +5745,26 @@ async function generate(configPath) {
|
|
|
5638
5745
|
await ensureDirs(dirs);
|
|
5639
5746
|
const files = [];
|
|
5640
5747
|
const includeSpec = emitIncludeSpec(graph);
|
|
5641
|
-
files.push({ path:
|
|
5642
|
-
files.push({ path:
|
|
5643
|
-
files.push({ path:
|
|
5644
|
-
files.push({ path:
|
|
5645
|
-
files.push({ path:
|
|
5646
|
-
files.push({ path:
|
|
5748
|
+
files.push({ path: join2(serverDir, "include-spec.ts"), content: includeSpec });
|
|
5749
|
+
files.push({ path: join2(clientDir, "include-spec.ts"), content: includeSpec });
|
|
5750
|
+
files.push({ path: join2(clientDir, "params", "shared.ts"), content: emitSharedParamsZod() });
|
|
5751
|
+
files.push({ path: join2(clientDir, "types", "shared.ts"), content: emitSharedTypes() });
|
|
5752
|
+
files.push({ path: join2(clientDir, "base-client.ts"), content: emitBaseClient() });
|
|
5753
|
+
files.push({ path: join2(clientDir, "where-types.ts"), content: emitWhereTypes() });
|
|
5647
5754
|
files.push({
|
|
5648
|
-
path:
|
|
5755
|
+
path: join2(serverDir, "include-builder.ts"),
|
|
5649
5756
|
content: emitIncludeBuilder(graph, cfg.includeMethodsDepth || 2)
|
|
5650
5757
|
});
|
|
5651
5758
|
files.push({
|
|
5652
|
-
path:
|
|
5759
|
+
path: join2(serverDir, "include-loader.ts"),
|
|
5653
5760
|
content: emitIncludeLoader(graph, model, cfg.includeMethodsDepth || 2, cfg.useJsExtensions)
|
|
5654
5761
|
});
|
|
5655
|
-
files.push({ path:
|
|
5762
|
+
files.push({ path: join2(serverDir, "logger.ts"), content: emitLogger() });
|
|
5656
5763
|
if (getAuthStrategy(normalizedAuth) !== "none") {
|
|
5657
|
-
files.push({ path:
|
|
5764
|
+
files.push({ path: join2(serverDir, "auth.ts"), content: emitAuth(normalizedAuth) });
|
|
5658
5765
|
}
|
|
5659
5766
|
files.push({
|
|
5660
|
-
path:
|
|
5767
|
+
path: join2(serverDir, "core", "operations.ts"),
|
|
5661
5768
|
content: emitCoreOperations()
|
|
5662
5769
|
});
|
|
5663
5770
|
if (process.env.SDK_DEBUG) {
|
|
@@ -5666,13 +5773,13 @@ async function generate(configPath) {
|
|
|
5666
5773
|
for (const table of Object.values(model.tables)) {
|
|
5667
5774
|
const numericMode = cfg.numericMode ?? "auto";
|
|
5668
5775
|
const typesSrc = emitTypes(table, { numericMode }, model.enums);
|
|
5669
|
-
files.push({ path:
|
|
5670
|
-
files.push({ path:
|
|
5776
|
+
files.push({ path: join2(serverDir, "types", `${table.name}.ts`), content: typesSrc });
|
|
5777
|
+
files.push({ path: join2(clientDir, "types", `${table.name}.ts`), content: typesSrc });
|
|
5671
5778
|
const zodSrc = emitZod(table, { numericMode }, model.enums);
|
|
5672
|
-
files.push({ path:
|
|
5673
|
-
files.push({ path:
|
|
5779
|
+
files.push({ path: join2(serverDir, "zod", `${table.name}.ts`), content: zodSrc });
|
|
5780
|
+
files.push({ path: join2(clientDir, "zod", `${table.name}.ts`), content: zodSrc });
|
|
5674
5781
|
const paramsZodSrc = emitParamsZod(table, graph);
|
|
5675
|
-
files.push({ path:
|
|
5782
|
+
files.push({ path: join2(clientDir, "params", `${table.name}.ts`), content: paramsZodSrc });
|
|
5676
5783
|
let routeContent;
|
|
5677
5784
|
if (serverFramework === "hono") {
|
|
5678
5785
|
routeContent = emitHonoRoutes(table, graph, {
|
|
@@ -5686,11 +5793,11 @@ async function generate(configPath) {
|
|
|
5686
5793
|
throw new Error(`Framework "${serverFramework}" is not yet supported. Currently only "hono" is available.`);
|
|
5687
5794
|
}
|
|
5688
5795
|
files.push({
|
|
5689
|
-
path:
|
|
5796
|
+
path: join2(serverDir, "routes", `${table.name}.ts`),
|
|
5690
5797
|
content: routeContent
|
|
5691
5798
|
});
|
|
5692
5799
|
files.push({
|
|
5693
|
-
path:
|
|
5800
|
+
path: join2(clientDir, `${table.name}.ts`),
|
|
5694
5801
|
content: emitClient(table, graph, {
|
|
5695
5802
|
useJsExtensions: cfg.useJsExtensionsClient,
|
|
5696
5803
|
includeMethodsDepth: cfg.includeMethodsDepth ?? 2,
|
|
@@ -5699,12 +5806,12 @@ async function generate(configPath) {
|
|
|
5699
5806
|
});
|
|
5700
5807
|
}
|
|
5701
5808
|
files.push({
|
|
5702
|
-
path:
|
|
5809
|
+
path: join2(clientDir, "index.ts"),
|
|
5703
5810
|
content: emitClientIndex(Object.values(model.tables), cfg.useJsExtensionsClient)
|
|
5704
5811
|
});
|
|
5705
5812
|
if (serverFramework === "hono") {
|
|
5706
5813
|
files.push({
|
|
5707
|
-
path:
|
|
5814
|
+
path: join2(serverDir, "router.ts"),
|
|
5708
5815
|
content: emitHonoRouter(Object.values(model.tables), getAuthStrategy(normalizedAuth) !== "none", cfg.useJsExtensions, cfg.pullToken)
|
|
5709
5816
|
});
|
|
5710
5817
|
}
|
|
@@ -5714,59 +5821,72 @@ async function generate(configPath) {
|
|
|
5714
5821
|
}
|
|
5715
5822
|
const contract = generateUnifiedContract2(model, cfg, graph);
|
|
5716
5823
|
files.push({
|
|
5717
|
-
path:
|
|
5824
|
+
path: join2(serverDir, "CONTRACT.md"),
|
|
5718
5825
|
content: generateUnifiedContractMarkdown2(contract)
|
|
5719
5826
|
});
|
|
5720
5827
|
files.push({
|
|
5721
|
-
path:
|
|
5828
|
+
path: join2(clientDir, "CONTRACT.md"),
|
|
5722
5829
|
content: generateUnifiedContractMarkdown2(contract)
|
|
5723
5830
|
});
|
|
5724
5831
|
const contractCode = emitUnifiedContract(model, cfg, graph);
|
|
5725
5832
|
files.push({
|
|
5726
|
-
path:
|
|
5833
|
+
path: join2(serverDir, "contract.ts"),
|
|
5727
5834
|
content: contractCode
|
|
5728
5835
|
});
|
|
5729
5836
|
const clientFiles = files.filter((f) => {
|
|
5730
5837
|
return f.path.includes(clientDir);
|
|
5731
5838
|
});
|
|
5732
5839
|
files.push({
|
|
5733
|
-
path:
|
|
5840
|
+
path: join2(serverDir, "sdk-bundle.ts"),
|
|
5734
5841
|
content: emitSdkBundle(clientFiles, clientDir)
|
|
5735
5842
|
});
|
|
5736
5843
|
if (generateTests) {
|
|
5737
5844
|
console.log("\uD83E\uDDEA Generating tests...");
|
|
5738
5845
|
const relativeClientPath = relative(testDir, clientDir);
|
|
5739
5846
|
files.push({
|
|
5740
|
-
path:
|
|
5847
|
+
path: join2(testDir, "setup.ts"),
|
|
5741
5848
|
content: emitTestSetup(relativeClientPath, testFramework)
|
|
5742
5849
|
});
|
|
5743
5850
|
files.push({
|
|
5744
|
-
path:
|
|
5851
|
+
path: join2(testDir, "docker-compose.yml"),
|
|
5745
5852
|
content: emitDockerCompose()
|
|
5746
5853
|
});
|
|
5747
5854
|
files.push({
|
|
5748
|
-
path:
|
|
5855
|
+
path: join2(testDir, "run-tests.sh"),
|
|
5749
5856
|
content: emitTestScript(testFramework, testDir)
|
|
5750
5857
|
});
|
|
5751
5858
|
files.push({
|
|
5752
|
-
path:
|
|
5859
|
+
path: join2(testDir, ".gitignore"),
|
|
5753
5860
|
content: emitTestGitignore()
|
|
5754
5861
|
});
|
|
5755
5862
|
if (testFramework === "vitest") {
|
|
5756
5863
|
files.push({
|
|
5757
|
-
path:
|
|
5864
|
+
path: join2(testDir, "vitest.config.ts"),
|
|
5758
5865
|
content: emitVitestConfig()
|
|
5759
5866
|
});
|
|
5760
5867
|
}
|
|
5761
5868
|
for (const table of Object.values(model.tables)) {
|
|
5762
5869
|
files.push({
|
|
5763
|
-
path:
|
|
5870
|
+
path: join2(testDir, `${table.name}.test.ts`),
|
|
5764
5871
|
content: emitTableTest(table, model, relativeClientPath, testFramework)
|
|
5765
5872
|
});
|
|
5766
5873
|
}
|
|
5767
5874
|
}
|
|
5768
5875
|
console.log("✍️ Writing files...");
|
|
5769
5876
|
await writeFiles(files);
|
|
5877
|
+
const outDirStr = typeof cfg.outDir === "string" ? cfg.outDir : `${cfg.outDir?.server || "./api/server"}`;
|
|
5878
|
+
await writeCache({
|
|
5879
|
+
schemaHash,
|
|
5880
|
+
lastRun: new Date().toISOString(),
|
|
5881
|
+
filesGenerated: files.length,
|
|
5882
|
+
config: {
|
|
5883
|
+
outDir: cfg.outDir || "./api",
|
|
5884
|
+
schema: cfg.schema || "public"
|
|
5885
|
+
}
|
|
5886
|
+
});
|
|
5887
|
+
await appendToHistory(`Generate
|
|
5888
|
+
✅ Generated ${files.length} files
|
|
5889
|
+
- Schema hash: ${schemaHash.substring(0, 12)}...`);
|
|
5770
5890
|
console.log(`✅ Generated ${files.length} files`);
|
|
5771
5891
|
console.log(` Server: ${serverDir}`);
|
|
5772
5892
|
console.log(` Client: ${sameDirectory ? clientDir + " (in sdk subdir due to same output dir)" : clientDir}`);
|
package/dist/utils.d.ts
CHANGED
|
@@ -3,4 +3,20 @@ export declare function writeFiles(files: Array<{
|
|
|
3
3
|
path: string;
|
|
4
4
|
content: string;
|
|
5
5
|
}>): Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* Write files only if content has changed (idempotent)
|
|
8
|
+
* Returns the count of files actually written
|
|
9
|
+
*/
|
|
10
|
+
export declare function writeFilesIfChanged(files: Array<{
|
|
11
|
+
path: string;
|
|
12
|
+
content: string;
|
|
13
|
+
}>): Promise<{
|
|
14
|
+
written: number;
|
|
15
|
+
unchanged: number;
|
|
16
|
+
filesWritten: string[];
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Compute hash of a string
|
|
20
|
+
*/
|
|
21
|
+
export declare function hashContent(content: string): string;
|
|
6
22
|
export declare function ensureDirs(dirs: string[]): Promise<void>;
|