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 CHANGED
@@ -1391,35 +1391,90 @@ function extractConfigFields(configContent) {
1391
1391
  isCommented: !!jsExtClientMatch[1]
1392
1392
  });
1393
1393
  }
1394
- const testsMatch = configContent.match(/^\s*(\/\/)?\s*tests:\s*\{/m);
1395
- if (testsMatch) {
1394
+ const testsBlock = extractComplexBlock(configContent, "tests");
1395
+ if (testsBlock) {
1396
1396
  fields.push({
1397
1397
  key: "tests",
1398
- value: "configured",
1398
+ value: testsBlock.content,
1399
1399
  description: "Test generation configuration",
1400
- isCommented: !!testsMatch[1]
1400
+ isCommented: testsBlock.isCommented
1401
1401
  });
1402
1402
  }
1403
- const authMatch = configContent.match(/^\s*(\/\/)?\s*auth:\s*\{/m);
1404
- if (authMatch) {
1403
+ const authBlock = extractComplexBlock(configContent, "auth");
1404
+ if (authBlock) {
1405
1405
  fields.push({
1406
1406
  key: "auth",
1407
- value: "configured",
1407
+ value: authBlock.content,
1408
1408
  description: "Authentication configuration",
1409
- isCommented: !!authMatch[1]
1409
+ isCommented: authBlock.isCommented
1410
1410
  });
1411
1411
  }
1412
- const pullMatch = configContent.match(/^\s*(\/\/)?\s*pull:\s*\{/m);
1413
- if (pullMatch) {
1412
+ const pullBlock = extractComplexBlock(configContent, "pull");
1413
+ if (pullBlock) {
1414
1414
  fields.push({
1415
1415
  key: "pull",
1416
- value: "configured",
1416
+ value: pullBlock.content,
1417
1417
  description: "SDK distribution configuration",
1418
- isCommented: !!pullMatch[1]
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
- // tests: {
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
- // auth: {
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
- // pull: {
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; status: number; needsIncludes?: boolean; includeSpec?: 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
- const where = ctx.softDeleteColumn
3582
- ? \`WHERE "\${ctx.softDeleteColumn}" IS NULL\`
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}" \${where} LIMIT $1 OFFSET $2\`;
3586
- log.debug(\`LIST \${ctx.table} SQL:\`, text, "params:", [limit, offset]);
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, [limit, offset]);
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; status: number; needsIncludes?: boolean; includeSpec?: 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
- const where = ctx.softDeleteColumn
2917
- ? \`WHERE "\${ctx.softDeleteColumn}" IS NULL\`
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}" \${where} LIMIT $1 OFFSET $2\`;
2921
- log.debug(\`LIST \${ctx.table} SQL:\`, text, "params:", [limit, offset]);
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, [limit, offset]);
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.7",
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",