postgresdk 0.9.7 → 0.9.9
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 +158 -49
- package/dist/core/operations.d.ts +1 -0
- package/dist/index.js +39 -9
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1391,35 +1391,90 @@ function extractConfigFields(configContent) {
|
|
|
1391
1391
|
isCommented: !!jsExtClientMatch[1]
|
|
1392
1392
|
});
|
|
1393
1393
|
}
|
|
1394
|
-
const
|
|
1395
|
-
if (
|
|
1394
|
+
const testsBlock = extractComplexBlock(configContent, "tests");
|
|
1395
|
+
if (testsBlock) {
|
|
1396
1396
|
fields.push({
|
|
1397
1397
|
key: "tests",
|
|
1398
|
-
value:
|
|
1398
|
+
value: testsBlock.content,
|
|
1399
1399
|
description: "Test generation configuration",
|
|
1400
|
-
isCommented:
|
|
1400
|
+
isCommented: testsBlock.isCommented
|
|
1401
1401
|
});
|
|
1402
1402
|
}
|
|
1403
|
-
const
|
|
1404
|
-
if (
|
|
1403
|
+
const authBlock = extractComplexBlock(configContent, "auth");
|
|
1404
|
+
if (authBlock) {
|
|
1405
1405
|
fields.push({
|
|
1406
1406
|
key: "auth",
|
|
1407
|
-
value:
|
|
1407
|
+
value: authBlock.content,
|
|
1408
1408
|
description: "Authentication configuration",
|
|
1409
|
-
isCommented:
|
|
1409
|
+
isCommented: authBlock.isCommented
|
|
1410
1410
|
});
|
|
1411
1411
|
}
|
|
1412
|
-
const
|
|
1413
|
-
if (
|
|
1412
|
+
const pullBlock = extractComplexBlock(configContent, "pull");
|
|
1413
|
+
if (pullBlock) {
|
|
1414
1414
|
fields.push({
|
|
1415
1415
|
key: "pull",
|
|
1416
|
-
value:
|
|
1416
|
+
value: pullBlock.content,
|
|
1417
1417
|
description: "SDK distribution configuration",
|
|
1418
|
-
isCommented:
|
|
1418
|
+
isCommented: pullBlock.isCommented
|
|
1419
1419
|
});
|
|
1420
1420
|
}
|
|
1421
1421
|
return fields;
|
|
1422
1422
|
}
|
|
1423
|
+
function extractComplexBlock(configContent, blockName) {
|
|
1424
|
+
const blockStartRegex = new RegExp(`^\\s*(//)?\\s*${blockName}:\\s*\\{`, "m");
|
|
1425
|
+
const match = configContent.match(blockStartRegex);
|
|
1426
|
+
if (!match)
|
|
1427
|
+
return null;
|
|
1428
|
+
const isCommented = !!match[1];
|
|
1429
|
+
const startIndex = match.index;
|
|
1430
|
+
let braceCount = 0;
|
|
1431
|
+
let inString = false;
|
|
1432
|
+
let inComment = false;
|
|
1433
|
+
let stringChar = "";
|
|
1434
|
+
let i = startIndex;
|
|
1435
|
+
while (i < configContent.length && configContent[i] !== "{") {
|
|
1436
|
+
i++;
|
|
1437
|
+
}
|
|
1438
|
+
const blockStart = i;
|
|
1439
|
+
braceCount = 1;
|
|
1440
|
+
i++;
|
|
1441
|
+
while (i < configContent.length && braceCount > 0) {
|
|
1442
|
+
const char = configContent[i];
|
|
1443
|
+
const prevChar = i > 0 ? configContent[i - 1] : "";
|
|
1444
|
+
if (!inComment && (char === '"' || char === "'" || char === "`")) {
|
|
1445
|
+
if (!inString) {
|
|
1446
|
+
inString = true;
|
|
1447
|
+
stringChar = char;
|
|
1448
|
+
} else if (char === stringChar && prevChar !== "\\") {
|
|
1449
|
+
inString = false;
|
|
1450
|
+
stringChar = "";
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
if (!inString && char === "/" && configContent[i + 1] === "/") {
|
|
1454
|
+
inComment = true;
|
|
1455
|
+
}
|
|
1456
|
+
if (inComment && char === `
|
|
1457
|
+
`) {
|
|
1458
|
+
inComment = false;
|
|
1459
|
+
}
|
|
1460
|
+
if (!inString && !inComment) {
|
|
1461
|
+
if (char === "{") {
|
|
1462
|
+
braceCount++;
|
|
1463
|
+
} else if (char === "}") {
|
|
1464
|
+
braceCount--;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
i++;
|
|
1468
|
+
}
|
|
1469
|
+
if (braceCount === 0) {
|
|
1470
|
+
const blockContent = configContent.slice(blockStart + 1, i - 1);
|
|
1471
|
+
return {
|
|
1472
|
+
content: `{${blockContent}}`,
|
|
1473
|
+
isCommented
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
return null;
|
|
1477
|
+
}
|
|
1423
1478
|
function generateMergedConfig(existingFields, mergeStrategy, userChoices = undefined) {
|
|
1424
1479
|
const template = `/**
|
|
1425
1480
|
* PostgreSDK Configuration
|
|
@@ -1501,11 +1556,7 @@ export default {
|
|
|
1501
1556
|
* Generate basic SDK tests
|
|
1502
1557
|
* Uncomment to enable test generation with Docker setup
|
|
1503
1558
|
*/
|
|
1504
|
-
|
|
1505
|
-
// generate: true,
|
|
1506
|
-
// output: "./api/tests",
|
|
1507
|
-
// framework: "vitest" // or "jest" or "bun"
|
|
1508
|
-
// },
|
|
1559
|
+
${getComplexBlockLine("tests", existingFields, mergeStrategy, userChoices)}
|
|
1509
1560
|
|
|
1510
1561
|
// ========== AUTHENTICATION ==========
|
|
1511
1562
|
|
|
@@ -1521,24 +1572,7 @@ export default {
|
|
|
1521
1572
|
*
|
|
1522
1573
|
* Full syntax for advanced options:
|
|
1523
1574
|
*/
|
|
1524
|
-
|
|
1525
|
-
// // Strategy: "none" | "api-key" | "jwt-hs256"
|
|
1526
|
-
// strategy: "none",
|
|
1527
|
-
//
|
|
1528
|
-
// // For API Key authentication
|
|
1529
|
-
// apiKeyHeader: "x-api-key", // Header name for API key
|
|
1530
|
-
// apiKeys: [ // List of valid API keys
|
|
1531
|
-
// process.env.API_KEY_1,
|
|
1532
|
-
// process.env.API_KEY_2,
|
|
1533
|
-
// ],
|
|
1534
|
-
//
|
|
1535
|
-
// // For JWT (HS256) authentication
|
|
1536
|
-
// jwt: {
|
|
1537
|
-
// sharedSecret: process.env.JWT_SECRET, // Secret for signing/verifying
|
|
1538
|
-
// issuer: "my-app", // Optional: validate 'iss' claim
|
|
1539
|
-
// audience: "my-users", // Optional: validate 'aud' claim
|
|
1540
|
-
// }
|
|
1541
|
-
// },
|
|
1575
|
+
${getComplexBlockLine("auth", existingFields, mergeStrategy, userChoices)}
|
|
1542
1576
|
|
|
1543
1577
|
// ========== SDK DISTRIBUTION (Pull Configuration) ==========
|
|
1544
1578
|
|
|
@@ -1546,11 +1580,7 @@ export default {
|
|
|
1546
1580
|
* Configuration for pulling SDK from a remote API
|
|
1547
1581
|
* Used when running 'postgresdk pull' command
|
|
1548
1582
|
*/
|
|
1549
|
-
|
|
1550
|
-
// from: "https://api.myapp.com", // API URL to pull SDK from
|
|
1551
|
-
// output: "./src/sdk", // Local directory for pulled SDK
|
|
1552
|
-
// token: process.env.API_TOKEN, // Optional authentication token
|
|
1553
|
-
// },
|
|
1583
|
+
${getComplexBlockLine("pull", existingFields, mergeStrategy, userChoices)}
|
|
1554
1584
|
};
|
|
1555
1585
|
`;
|
|
1556
1586
|
return template;
|
|
@@ -1589,6 +1619,55 @@ function getFieldLine(key, existingFields, mergeStrategy, defaultValue, userChoi
|
|
|
1589
1619
|
}
|
|
1590
1620
|
return `// ${key}: ${defaultValue},`;
|
|
1591
1621
|
}
|
|
1622
|
+
function getComplexBlockLine(key, existingFields, mergeStrategy, userChoices) {
|
|
1623
|
+
const existing = existingFields.find((f) => f.key === key);
|
|
1624
|
+
const shouldUseExisting = mergeStrategy === "keep-existing" && existing && !existing.isCommented || mergeStrategy === "interactive" && userChoices?.get(key) === "keep" && existing && !existing.isCommented;
|
|
1625
|
+
const shouldUseNew = mergeStrategy === "use-defaults" || mergeStrategy === "interactive" && userChoices?.get(key) === "new";
|
|
1626
|
+
if (shouldUseExisting && existing) {
|
|
1627
|
+
return `${key}: ${existing.value},`;
|
|
1628
|
+
}
|
|
1629
|
+
if (shouldUseNew) {
|
|
1630
|
+
return getDefaultComplexBlock(key);
|
|
1631
|
+
}
|
|
1632
|
+
return getDefaultComplexBlock(key);
|
|
1633
|
+
}
|
|
1634
|
+
function getDefaultComplexBlock(key) {
|
|
1635
|
+
switch (key) {
|
|
1636
|
+
case "tests":
|
|
1637
|
+
return `// tests: {
|
|
1638
|
+
// generate: true,
|
|
1639
|
+
// output: "./api/tests",
|
|
1640
|
+
// framework: "vitest" // or "jest" or "bun"
|
|
1641
|
+
// },`;
|
|
1642
|
+
case "auth":
|
|
1643
|
+
return `// auth: {
|
|
1644
|
+
// // Strategy: "none" | "api-key" | "jwt-hs256"
|
|
1645
|
+
// strategy: "none",
|
|
1646
|
+
//
|
|
1647
|
+
// // For API Key authentication
|
|
1648
|
+
// apiKeyHeader: "x-api-key", // Header name for API key
|
|
1649
|
+
// apiKeys: [ // List of valid API keys
|
|
1650
|
+
// process.env.API_KEY_1,
|
|
1651
|
+
// process.env.API_KEY_2,
|
|
1652
|
+
// ],
|
|
1653
|
+
//
|
|
1654
|
+
// // For JWT (HS256) authentication
|
|
1655
|
+
// jwt: {
|
|
1656
|
+
// sharedSecret: process.env.JWT_SECRET, // Secret for signing/verifying
|
|
1657
|
+
// issuer: "my-app", // Optional: validate 'iss' claim
|
|
1658
|
+
// audience: "my-users", // Optional: validate 'aud' claim
|
|
1659
|
+
// }
|
|
1660
|
+
// },`;
|
|
1661
|
+
case "pull":
|
|
1662
|
+
return `// pull: {
|
|
1663
|
+
// from: "https://api.myapp.com", // API URL to pull SDK from
|
|
1664
|
+
// output: "./src/sdk", // Local directory for pulled SDK
|
|
1665
|
+
// token: process.env.API_TOKEN, // Optional authentication token
|
|
1666
|
+
// },`;
|
|
1667
|
+
default:
|
|
1668
|
+
return `// ${key}: {},`;
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1592
1671
|
var init_cli_config_utils = () => {};
|
|
1593
1672
|
|
|
1594
1673
|
// src/cli-init.ts
|
|
@@ -2344,6 +2423,7 @@ import * as coreOps from "../core/operations${ext}";
|
|
|
2344
2423
|
${authImport}
|
|
2345
2424
|
|
|
2346
2425
|
const listSchema = z.object({
|
|
2426
|
+
where: z.any().optional(),
|
|
2347
2427
|
include: z.any().optional(),
|
|
2348
2428
|
limit: z.number().int().positive().max(100).optional(),
|
|
2349
2429
|
offset: z.number().int().min(0).optional(),
|
|
@@ -3573,19 +3653,48 @@ export async function getByPk(
|
|
|
3573
3653
|
*/
|
|
3574
3654
|
export async function listRecords(
|
|
3575
3655
|
ctx: OperationContext,
|
|
3576
|
-
params: { limit?: number; offset?: number; include?: any }
|
|
3577
|
-
): Promise<{ data?: any; error?: string; issues?: any;
|
|
3656
|
+
params: { where?: any; limit?: number; offset?: number; include?: any }
|
|
3657
|
+
): Promise<{ data?: any; error?: string; issues?: any; needsIncludes?: boolean; includeSpec?: any; status: number }> {
|
|
3578
3658
|
try {
|
|
3579
|
-
const { limit = 50, offset = 0, include } = params;
|
|
3659
|
+
const { where: whereClause, limit = 50, offset = 0, include } = params;
|
|
3660
|
+
|
|
3661
|
+
// Build WHERE clause
|
|
3662
|
+
const whereParts: string[] = [];
|
|
3663
|
+
const whereParams: any[] = [];
|
|
3664
|
+
let paramIndex = 1;
|
|
3665
|
+
|
|
3666
|
+
// Add soft delete filter if applicable
|
|
3667
|
+
if (ctx.softDeleteColumn) {
|
|
3668
|
+
whereParts.push(\`"\${ctx.softDeleteColumn}" IS NULL\`);
|
|
3669
|
+
}
|
|
3670
|
+
|
|
3671
|
+
// Add user-provided where conditions
|
|
3672
|
+
if (whereClause && typeof whereClause === 'object') {
|
|
3673
|
+
for (const [key, value] of Object.entries(whereClause)) {
|
|
3674
|
+
if (value === null) {
|
|
3675
|
+
whereParts.push(\`"\${key}" IS NULL\`);
|
|
3676
|
+
} else if (value === undefined) {
|
|
3677
|
+
// Skip undefined values
|
|
3678
|
+
continue;
|
|
3679
|
+
} else {
|
|
3680
|
+
whereParts.push(\`"\${key}" = $\${paramIndex}\`);
|
|
3681
|
+
whereParams.push(value);
|
|
3682
|
+
paramIndex++;
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
|
|
3687
|
+
const whereSQL = whereParts.length > 0 ? \`WHERE \${whereParts.join(" AND ")}\` : "";
|
|
3580
3688
|
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3689
|
+
// Add limit and offset params
|
|
3690
|
+
const limitParam = \`$\${paramIndex}\`;
|
|
3691
|
+
const offsetParam = \`$\${paramIndex + 1}\`;
|
|
3692
|
+
const allParams = [...whereParams, limit, offset];
|
|
3584
3693
|
|
|
3585
|
-
const text = \`SELECT * FROM "\${ctx.table}" \${
|
|
3586
|
-
log.debug(\`LIST \${ctx.table} SQL:\`, text, "params:",
|
|
3694
|
+
const text = \`SELECT * FROM "\${ctx.table}" \${whereSQL} LIMIT \${limitParam} OFFSET \${offsetParam}\`;
|
|
3695
|
+
log.debug(\`LIST \${ctx.table} SQL:\`, text, "params:", allParams);
|
|
3587
3696
|
|
|
3588
|
-
const { rows } = await ctx.pg.query(text,
|
|
3697
|
+
const { rows } = await ctx.pg.query(text, allParams);
|
|
3589
3698
|
|
|
3590
3699
|
if (!include) {
|
|
3591
3700
|
log.debug(\`LIST \${ctx.table} rows:\`, rows.length);
|
|
@@ -35,6 +35,7 @@ export declare function getByPk(ctx: OperationContext, pkValues: any[]): Promise
|
|
|
35
35
|
* LIST operation - Get multiple records with optional filters
|
|
36
36
|
*/
|
|
37
37
|
export declare function listRecords(ctx: OperationContext, params: {
|
|
38
|
+
where?: any;
|
|
38
39
|
limit?: number;
|
|
39
40
|
offset?: number;
|
|
40
41
|
include?: any;
|
package/dist/index.js
CHANGED
|
@@ -1679,6 +1679,7 @@ import * as coreOps from "../core/operations${ext}";
|
|
|
1679
1679
|
${authImport}
|
|
1680
1680
|
|
|
1681
1681
|
const listSchema = z.object({
|
|
1682
|
+
where: z.any().optional(),
|
|
1682
1683
|
include: z.any().optional(),
|
|
1683
1684
|
limit: z.number().int().positive().max(100).optional(),
|
|
1684
1685
|
offset: z.number().int().min(0).optional(),
|
|
@@ -2908,19 +2909,48 @@ export async function getByPk(
|
|
|
2908
2909
|
*/
|
|
2909
2910
|
export async function listRecords(
|
|
2910
2911
|
ctx: OperationContext,
|
|
2911
|
-
params: { limit?: number; offset?: number; include?: any }
|
|
2912
|
-
): Promise<{ data?: any; error?: string; issues?: any;
|
|
2912
|
+
params: { where?: any; limit?: number; offset?: number; include?: any }
|
|
2913
|
+
): Promise<{ data?: any; error?: string; issues?: any; needsIncludes?: boolean; includeSpec?: any; status: number }> {
|
|
2913
2914
|
try {
|
|
2914
|
-
const { limit = 50, offset = 0, include } = params;
|
|
2915
|
+
const { where: whereClause, limit = 50, offset = 0, include } = params;
|
|
2916
|
+
|
|
2917
|
+
// Build WHERE clause
|
|
2918
|
+
const whereParts: string[] = [];
|
|
2919
|
+
const whereParams: any[] = [];
|
|
2920
|
+
let paramIndex = 1;
|
|
2921
|
+
|
|
2922
|
+
// Add soft delete filter if applicable
|
|
2923
|
+
if (ctx.softDeleteColumn) {
|
|
2924
|
+
whereParts.push(\`"\${ctx.softDeleteColumn}" IS NULL\`);
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
// Add user-provided where conditions
|
|
2928
|
+
if (whereClause && typeof whereClause === 'object') {
|
|
2929
|
+
for (const [key, value] of Object.entries(whereClause)) {
|
|
2930
|
+
if (value === null) {
|
|
2931
|
+
whereParts.push(\`"\${key}" IS NULL\`);
|
|
2932
|
+
} else if (value === undefined) {
|
|
2933
|
+
// Skip undefined values
|
|
2934
|
+
continue;
|
|
2935
|
+
} else {
|
|
2936
|
+
whereParts.push(\`"\${key}" = $\${paramIndex}\`);
|
|
2937
|
+
whereParams.push(value);
|
|
2938
|
+
paramIndex++;
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
|
|
2943
|
+
const whereSQL = whereParts.length > 0 ? \`WHERE \${whereParts.join(" AND ")}\` : "";
|
|
2915
2944
|
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2945
|
+
// Add limit and offset params
|
|
2946
|
+
const limitParam = \`$\${paramIndex}\`;
|
|
2947
|
+
const offsetParam = \`$\${paramIndex + 1}\`;
|
|
2948
|
+
const allParams = [...whereParams, limit, offset];
|
|
2919
2949
|
|
|
2920
|
-
const text = \`SELECT * FROM "\${ctx.table}" \${
|
|
2921
|
-
log.debug(\`LIST \${ctx.table} SQL:\`, text, "params:",
|
|
2950
|
+
const text = \`SELECT * FROM "\${ctx.table}" \${whereSQL} LIMIT \${limitParam} OFFSET \${offsetParam}\`;
|
|
2951
|
+
log.debug(\`LIST \${ctx.table} SQL:\`, text, "params:", allParams);
|
|
2922
2952
|
|
|
2923
|
-
const { rows } = await ctx.pg.query(text,
|
|
2953
|
+
const { rows } = await ctx.pg.query(text, allParams);
|
|
2924
2954
|
|
|
2925
2955
|
if (!include) {
|
|
2926
2956
|
log.debug(\`LIST \${ctx.table} rows:\`, rows.length);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postgresdk",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.9",
|
|
4
4
|
"description": "Generate a typed server/client SDK from a Postgres schema (includes, Zod, Hono).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "bun build src/cli.ts src/index.ts --outdir dist --target node --format esm --external=pg --external=zod --external=hono --external=prompts --external=node:* && tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
25
|
-
"test": "bun test:init && bun test:gen && bun test:gen-with-tests && bun test:pull && bun test:typecheck && bun test:drizzle-e2e",
|
|
25
|
+
"test": "bun test:init && bun test:gen && bun test test/test-where-clause.test.ts && bun test:gen-with-tests && bun test:pull && bun test:typecheck && bun test:drizzle-e2e",
|
|
26
26
|
"test:init": "bun test/test-init.ts",
|
|
27
27
|
"test:gen": "bun test/test-gen.ts",
|
|
28
28
|
"test:gen-with-tests": "bun test/test-gen-with-tests.ts",
|