postgresdk 0.16.7 → 0.16.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 +74 -21
- package/dist/index.js +25 -11
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1805,6 +1805,15 @@ function extractConfigFields(configContent) {
|
|
|
1805
1805
|
isCommented: pullBlock.isCommented
|
|
1806
1806
|
});
|
|
1807
1807
|
}
|
|
1808
|
+
const pullTokenMatch = configContent.match(/^\s*(\/\/)?\s*pullToken:\s*(.+),?$/m);
|
|
1809
|
+
if (pullTokenMatch) {
|
|
1810
|
+
fields.push({
|
|
1811
|
+
key: "pullToken",
|
|
1812
|
+
value: pullTokenMatch[2]?.trim().replace(/,$/, ""),
|
|
1813
|
+
description: "Token for protecting /_psdk/* endpoints",
|
|
1814
|
+
isCommented: !!pullTokenMatch[1]
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1808
1817
|
return fields;
|
|
1809
1818
|
}
|
|
1810
1819
|
function extractComplexBlock(configContent, blockName) {
|
|
@@ -1947,21 +1956,36 @@ export default {
|
|
|
1947
1956
|
${getComplexBlockLine("tests", existingFields, mergeStrategy, userChoices)}
|
|
1948
1957
|
|
|
1949
1958
|
// ========== AUTHENTICATION ==========
|
|
1950
|
-
|
|
1959
|
+
|
|
1951
1960
|
/**
|
|
1952
1961
|
* Authentication configuration for your API
|
|
1953
|
-
*
|
|
1962
|
+
*
|
|
1954
1963
|
* Simple syntax examples:
|
|
1955
1964
|
* auth: { apiKey: process.env.API_KEY }
|
|
1956
1965
|
* auth: { jwt: process.env.JWT_SECRET }
|
|
1957
|
-
*
|
|
1966
|
+
*
|
|
1958
1967
|
* Multiple API keys:
|
|
1959
1968
|
* auth: { apiKeys: [process.env.KEY1, process.env.KEY2] }
|
|
1960
|
-
*
|
|
1969
|
+
*
|
|
1961
1970
|
* Full syntax for advanced options:
|
|
1962
1971
|
*/
|
|
1963
1972
|
${getComplexBlockLine("auth", existingFields, mergeStrategy, userChoices)}
|
|
1964
|
-
|
|
1973
|
+
|
|
1974
|
+
// ========== SDK ENDPOINT PROTECTION ==========
|
|
1975
|
+
|
|
1976
|
+
/**
|
|
1977
|
+
* Token for protecting /_psdk/* endpoints (SDK distribution and contract endpoints)
|
|
1978
|
+
*
|
|
1979
|
+
* When set, clients must provide this token via Authorization header when pulling SDK.
|
|
1980
|
+
* If not set, /_psdk/* endpoints are publicly accessible.
|
|
1981
|
+
*
|
|
1982
|
+
* This is separate from the main auth strategy (JWT/API key) used for CRUD operations.
|
|
1983
|
+
*
|
|
1984
|
+
* Use "env:" prefix to read from environment variables:
|
|
1985
|
+
* pullToken: "env:POSTGRESDK_PULL_TOKEN"
|
|
1986
|
+
*/
|
|
1987
|
+
${getFieldLine("pullToken", existingFields, mergeStrategy, '"env:POSTGRESDK_PULL_TOKEN"', userChoices)}
|
|
1988
|
+
|
|
1965
1989
|
// ========== SDK DISTRIBUTION (Pull Configuration) ==========
|
|
1966
1990
|
|
|
1967
1991
|
/**
|
|
@@ -2050,9 +2074,9 @@ function getDefaultComplexBlock(key) {
|
|
|
2050
2074
|
// },`;
|
|
2051
2075
|
case "pull":
|
|
2052
2076
|
return `// pull: {
|
|
2053
|
-
// from: "https://api.myapp.com",
|
|
2054
|
-
// output: "./src/sdk",
|
|
2055
|
-
//
|
|
2077
|
+
// from: "https://api.myapp.com", // API URL to pull SDK from
|
|
2078
|
+
// output: "./src/sdk", // Local directory for pulled SDK
|
|
2079
|
+
// pullToken: "env:POSTGRESDK_PULL_TOKEN", // Optional: if server has pullToken set
|
|
2056
2080
|
// },`;
|
|
2057
2081
|
default:
|
|
2058
2082
|
return `// ${key}: {},`;
|
|
@@ -2154,6 +2178,7 @@ async function initCommand(args) {
|
|
|
2154
2178
|
const newOptions = [
|
|
2155
2179
|
{ key: "tests", description: "Enable test generation" },
|
|
2156
2180
|
{ key: "auth", description: "Add authentication" },
|
|
2181
|
+
{ key: "pullToken", description: "Add SDK endpoint protection" },
|
|
2157
2182
|
{ key: "pull", description: "Configure SDK distribution" }
|
|
2158
2183
|
];
|
|
2159
2184
|
const existingKeys = new Set(existingFields.map((f) => f.key));
|
|
@@ -2518,7 +2543,14 @@ Options:`);
|
|
|
2518
2543
|
const headers = resolvedToken ? { Authorization: `Bearer ${resolvedToken}` } : {};
|
|
2519
2544
|
const manifestRes = await fetch(`${config.from}/_psdk/sdk/manifest`, { headers });
|
|
2520
2545
|
if (!manifestRes.ok) {
|
|
2521
|
-
|
|
2546
|
+
let errorMsg = `${manifestRes.status} ${manifestRes.statusText}`;
|
|
2547
|
+
try {
|
|
2548
|
+
const errorBody = await manifestRes.json();
|
|
2549
|
+
if (errorBody.error) {
|
|
2550
|
+
errorMsg = errorBody.error;
|
|
2551
|
+
}
|
|
2552
|
+
} catch {}
|
|
2553
|
+
throw new Error(`Failed to fetch SDK manifest: ${errorMsg}`);
|
|
2522
2554
|
}
|
|
2523
2555
|
const manifest = await manifestRes.json();
|
|
2524
2556
|
console.log(`\uD83D\uDCE6 SDK version: ${manifest.version}`);
|
|
@@ -2526,7 +2558,14 @@ Options:`);
|
|
|
2526
2558
|
console.log(`\uD83D\uDCC4 Files: ${manifest.files.length}`);
|
|
2527
2559
|
const sdkRes = await fetch(`${config.from}/_psdk/sdk/download`, { headers });
|
|
2528
2560
|
if (!sdkRes.ok) {
|
|
2529
|
-
|
|
2561
|
+
let errorMsg = `${sdkRes.status} ${sdkRes.statusText}`;
|
|
2562
|
+
try {
|
|
2563
|
+
const errorBody = await sdkRes.json();
|
|
2564
|
+
if (errorBody.error) {
|
|
2565
|
+
errorMsg = errorBody.error;
|
|
2566
|
+
}
|
|
2567
|
+
} catch {}
|
|
2568
|
+
throw new Error(`Failed to download SDK: ${errorMsg}`);
|
|
2530
2569
|
}
|
|
2531
2570
|
const sdk = await sdkRes.json();
|
|
2532
2571
|
for (const [path, content] of Object.entries(sdk.files)) {
|
|
@@ -4481,10 +4520,12 @@ function emitHonoRouter(tables, hasAuth, useJsExtensions, pullToken) {
|
|
|
4481
4520
|
const tableNames = tables.map((t) => t.name).sort();
|
|
4482
4521
|
const ext = useJsExtensions ? ".js" : "";
|
|
4483
4522
|
let resolvedPullToken;
|
|
4523
|
+
let pullTokenEnvVar;
|
|
4484
4524
|
if (pullToken) {
|
|
4485
4525
|
if (pullToken.startsWith("env:")) {
|
|
4486
4526
|
const envVarName = pullToken.slice(4);
|
|
4487
4527
|
resolvedPullToken = `process.env.${envVarName}`;
|
|
4528
|
+
pullTokenEnvVar = envVarName;
|
|
4488
4529
|
} else {
|
|
4489
4530
|
resolvedPullToken = JSON.stringify(pullToken);
|
|
4490
4531
|
}
|
|
@@ -4586,7 +4627,9 @@ ${pullToken ? `
|
|
|
4586
4627
|
|
|
4587
4628
|
if (!expectedToken) {
|
|
4588
4629
|
// Token not configured in environment - reject request
|
|
4589
|
-
return c.json({
|
|
4630
|
+
return c.json({
|
|
4631
|
+
error: "SDK endpoints are protected but pullToken environment variable not set. ${pullTokenEnvVar ? `Set ${pullTokenEnvVar} in your environment or remove pullToken from config.` : "Set the pullToken environment variable or remove pullToken from config."}"
|
|
4632
|
+
}, 500);
|
|
4590
4633
|
}
|
|
4591
4634
|
|
|
4592
4635
|
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
@@ -4936,17 +4979,27 @@ function buildWhereClause(
|
|
|
4936
4979
|
paramIndex++;
|
|
4937
4980
|
break;
|
|
4938
4981
|
case '$in':
|
|
4939
|
-
if (Array.isArray(opValue)
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4982
|
+
if (Array.isArray(opValue)) {
|
|
4983
|
+
if (opValue.length > 0) {
|
|
4984
|
+
whereParts.push(\`"\${key}" = ANY($\${paramIndex})\`);
|
|
4985
|
+
whereParams.push(opValue);
|
|
4986
|
+
paramIndex++;
|
|
4987
|
+
} else {
|
|
4988
|
+
// Empty $in is logically FALSE - matches nothing
|
|
4989
|
+
whereParts.push('FALSE');
|
|
4990
|
+
}
|
|
4943
4991
|
}
|
|
4944
4992
|
break;
|
|
4945
4993
|
case '$nin':
|
|
4946
|
-
if (Array.isArray(opValue)
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4994
|
+
if (Array.isArray(opValue)) {
|
|
4995
|
+
if (opValue.length > 0) {
|
|
4996
|
+
whereParts.push(\`"\${key}" != ALL($\${paramIndex})\`);
|
|
4997
|
+
whereParams.push(opValue);
|
|
4998
|
+
paramIndex++;
|
|
4999
|
+
} else {
|
|
5000
|
+
// Empty $nin is logically TRUE - matches everything (but we still need a condition)
|
|
5001
|
+
// This is handled by simply not adding a condition
|
|
5002
|
+
}
|
|
4950
5003
|
}
|
|
4951
5004
|
break;
|
|
4952
5005
|
case '$like':
|
|
@@ -5241,14 +5294,14 @@ export async function listRecords(
|
|
|
5241
5294
|
// Get total count for pagination
|
|
5242
5295
|
const countText = \`SELECT COUNT(*) FROM "\${ctx.table}" \${countWhereSQL}\`;
|
|
5243
5296
|
log.debug(\`LIST \${ctx.table} COUNT SQL:\`, countText, "params:", countParams);
|
|
5244
|
-
const countResult = await ctx.pg.query(countText,
|
|
5297
|
+
const countResult = await ctx.pg.query(countText, countParams);
|
|
5245
5298
|
const total = parseInt(countResult.rows[0].count, 10);
|
|
5246
5299
|
|
|
5247
5300
|
// Get paginated data
|
|
5248
5301
|
const text = \`SELECT \${selectClause} FROM "\${ctx.table}" \${whereSQL} \${orderBySQL} LIMIT \${limitParam} OFFSET \${offsetParam}\`;
|
|
5249
5302
|
log.debug(\`LIST \${ctx.table} SQL:\`, text, "params:", allParams);
|
|
5250
5303
|
|
|
5251
|
-
const { rows } = await ctx.pg.query(text,
|
|
5304
|
+
const { rows } = await ctx.pg.query(text, allParams);
|
|
5252
5305
|
const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
|
|
5253
5306
|
|
|
5254
5307
|
// Calculate hasMore
|
package/dist/index.js
CHANGED
|
@@ -3619,10 +3619,12 @@ function emitHonoRouter(tables, hasAuth, useJsExtensions, pullToken) {
|
|
|
3619
3619
|
const tableNames = tables.map((t) => t.name).sort();
|
|
3620
3620
|
const ext = useJsExtensions ? ".js" : "";
|
|
3621
3621
|
let resolvedPullToken;
|
|
3622
|
+
let pullTokenEnvVar;
|
|
3622
3623
|
if (pullToken) {
|
|
3623
3624
|
if (pullToken.startsWith("env:")) {
|
|
3624
3625
|
const envVarName = pullToken.slice(4);
|
|
3625
3626
|
resolvedPullToken = `process.env.${envVarName}`;
|
|
3627
|
+
pullTokenEnvVar = envVarName;
|
|
3626
3628
|
} else {
|
|
3627
3629
|
resolvedPullToken = JSON.stringify(pullToken);
|
|
3628
3630
|
}
|
|
@@ -3724,7 +3726,9 @@ ${pullToken ? `
|
|
|
3724
3726
|
|
|
3725
3727
|
if (!expectedToken) {
|
|
3726
3728
|
// Token not configured in environment - reject request
|
|
3727
|
-
return c.json({
|
|
3729
|
+
return c.json({
|
|
3730
|
+
error: "SDK endpoints are protected but pullToken environment variable not set. ${pullTokenEnvVar ? `Set ${pullTokenEnvVar} in your environment or remove pullToken from config.` : "Set the pullToken environment variable or remove pullToken from config."}"
|
|
3731
|
+
}, 500);
|
|
3728
3732
|
}
|
|
3729
3733
|
|
|
3730
3734
|
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
@@ -4074,17 +4078,27 @@ function buildWhereClause(
|
|
|
4074
4078
|
paramIndex++;
|
|
4075
4079
|
break;
|
|
4076
4080
|
case '$in':
|
|
4077
|
-
if (Array.isArray(opValue)
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
+
if (Array.isArray(opValue)) {
|
|
4082
|
+
if (opValue.length > 0) {
|
|
4083
|
+
whereParts.push(\`"\${key}" = ANY($\${paramIndex})\`);
|
|
4084
|
+
whereParams.push(opValue);
|
|
4085
|
+
paramIndex++;
|
|
4086
|
+
} else {
|
|
4087
|
+
// Empty $in is logically FALSE - matches nothing
|
|
4088
|
+
whereParts.push('FALSE');
|
|
4089
|
+
}
|
|
4081
4090
|
}
|
|
4082
4091
|
break;
|
|
4083
4092
|
case '$nin':
|
|
4084
|
-
if (Array.isArray(opValue)
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4093
|
+
if (Array.isArray(opValue)) {
|
|
4094
|
+
if (opValue.length > 0) {
|
|
4095
|
+
whereParts.push(\`"\${key}" != ALL($\${paramIndex})\`);
|
|
4096
|
+
whereParams.push(opValue);
|
|
4097
|
+
paramIndex++;
|
|
4098
|
+
} else {
|
|
4099
|
+
// Empty $nin is logically TRUE - matches everything (but we still need a condition)
|
|
4100
|
+
// This is handled by simply not adding a condition
|
|
4101
|
+
}
|
|
4088
4102
|
}
|
|
4089
4103
|
break;
|
|
4090
4104
|
case '$like':
|
|
@@ -4379,14 +4393,14 @@ export async function listRecords(
|
|
|
4379
4393
|
// Get total count for pagination
|
|
4380
4394
|
const countText = \`SELECT COUNT(*) FROM "\${ctx.table}" \${countWhereSQL}\`;
|
|
4381
4395
|
log.debug(\`LIST \${ctx.table} COUNT SQL:\`, countText, "params:", countParams);
|
|
4382
|
-
const countResult = await ctx.pg.query(countText,
|
|
4396
|
+
const countResult = await ctx.pg.query(countText, countParams);
|
|
4383
4397
|
const total = parseInt(countResult.rows[0].count, 10);
|
|
4384
4398
|
|
|
4385
4399
|
// Get paginated data
|
|
4386
4400
|
const text = \`SELECT \${selectClause} FROM "\${ctx.table}" \${whereSQL} \${orderBySQL} LIMIT \${limitParam} OFFSET \${offsetParam}\`;
|
|
4387
4401
|
log.debug(\`LIST \${ctx.table} SQL:\`, text, "params:", allParams);
|
|
4388
4402
|
|
|
4389
|
-
const { rows } = await ctx.pg.query(text,
|
|
4403
|
+
const { rows } = await ctx.pg.query(text, allParams);
|
|
4390
4404
|
const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
|
|
4391
4405
|
|
|
4392
4406
|
// Calculate hasMore
|