@supacloud/cli 0.7.0 → 0.9.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/index.js +49 -11
- package/package.json +2 -3
package/dist/index.js
CHANGED
|
@@ -14,9 +14,6 @@ var __export = (target, all) => {
|
|
|
14
14
|
});
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
// src/index.ts
|
|
18
|
-
import path from "node:path";
|
|
19
|
-
|
|
20
17
|
// node_modules/zod/v4/classic/external.js
|
|
21
18
|
var exports_external = {};
|
|
22
19
|
__export(exports_external, {
|
|
@@ -14780,7 +14777,9 @@ Actions: ${allActions.join(", ")}${readOnly ? " (read-only mode)" : ""}`, {
|
|
|
14780
14777
|
user_id: exports_external.string().optional().describe("[get_auth_user] User UUID"),
|
|
14781
14778
|
limit: exports_external.number().optional().describe("[list_auth_users] Max users (default: 20)"),
|
|
14782
14779
|
name: exports_external.string().optional().describe("[apply_migration] Migration name"),
|
|
14783
|
-
columns: exports_external.string().optional().describe("[create_table_rls] Column definitions")
|
|
14780
|
+
columns: exports_external.string().optional().describe("[create_table_rls] Column definitions"),
|
|
14781
|
+
policy_mode: exports_external.enum(["deny_all", "owner"]).optional().describe("[create_table_rls] RLS policy mode (default: deny_all)"),
|
|
14782
|
+
owner_column: exports_external.string().optional().describe("[create_table_rls owner] UUID owner column matched to auth.uid()")
|
|
14784
14783
|
}, async (args) => {
|
|
14785
14784
|
const { action } = args;
|
|
14786
14785
|
const ref = projectRef || args.ref;
|
|
@@ -15056,9 +15055,15 @@ Actions: ${allActions.join(", ")}${readOnly ? " (read-only mode)" : ""}`, {
|
|
|
15056
15055
|
case "create_table_rls": {
|
|
15057
15056
|
if (!args.table || !args.columns)
|
|
15058
15057
|
throw new Error("'table' and 'columns' required");
|
|
15059
|
-
const
|
|
15058
|
+
const qualifiedTable = `${quoteIdentifier(schema, "schema")}.${quoteIdentifier(args.table, "table")}`;
|
|
15059
|
+
const columns = validateColumnDefinitions(args.columns);
|
|
15060
|
+
const policyMode = args.policy_mode || "deny_all";
|
|
15061
|
+
if (policyMode !== "deny_all" && policyMode !== "owner")
|
|
15062
|
+
throw new Error("Invalid RLS policy mode");
|
|
15063
|
+
const policySql = buildRlsPolicySql(qualifiedTable, policyMode, args.owner_column);
|
|
15064
|
+
const sql = `BEGIN; CREATE TABLE IF NOT EXISTS ${qualifiedTable} (${columns}); ALTER TABLE ${qualifiedTable} ENABLE ROW LEVEL SECURITY; ${policySql} COMMIT;`;
|
|
15060
15065
|
const r = await execSql(sql);
|
|
15061
|
-
text = r.ok ? `✅ Table '${schema}.${args.table}' created with RLS` : `❌ Failed (${r.status}): ${JSON.stringify(r.data)}`;
|
|
15066
|
+
text = r.ok ? `✅ Table '${schema}.${args.table}' created with RLS (${policyMode === "owner" ? "auth.uid() owner policy" : "deny-all by default"})` : `❌ Failed (${r.status}): ${JSON.stringify(r.data)}`;
|
|
15062
15067
|
break;
|
|
15063
15068
|
}
|
|
15064
15069
|
default:
|
|
@@ -15067,6 +15072,42 @@ Actions: ${allActions.join(", ")}${readOnly ? " (read-only mode)" : ""}`, {
|
|
|
15067
15072
|
return { content: [{ type: "text", text }] };
|
|
15068
15073
|
});
|
|
15069
15074
|
}
|
|
15075
|
+
function quoteIdentifier(value, label) {
|
|
15076
|
+
if (typeof value !== "string" || !/^[A-Za-z_][A-Za-z0-9_]{0,62}$/.test(value)) {
|
|
15077
|
+
throw new Error(`Invalid ${label} identifier`);
|
|
15078
|
+
}
|
|
15079
|
+
return `"${value}"`;
|
|
15080
|
+
}
|
|
15081
|
+
function validateColumnDefinitions(value) {
|
|
15082
|
+
if (typeof value !== "string")
|
|
15083
|
+
throw new Error("Invalid column definitions");
|
|
15084
|
+
const columns = value.trim();
|
|
15085
|
+
if (!columns || columns.length > 16384)
|
|
15086
|
+
throw new Error("Invalid column definitions");
|
|
15087
|
+
if (/[;\0]/.test(columns) || /--|\/\*|\*\//.test(columns) || /\b(?:ALTER|CREATE|DROP|GRANT|REVOKE|TRUNCATE|COPY|CALL|DO)\b/i.test(columns)) {
|
|
15088
|
+
throw new Error("Unsafe column definitions");
|
|
15089
|
+
}
|
|
15090
|
+
return columns;
|
|
15091
|
+
}
|
|
15092
|
+
function buildRlsPolicySql(qualifiedTable, policyMode, ownerColumnValue) {
|
|
15093
|
+
const policyNames = [
|
|
15094
|
+
"Enable ALL for authenticated",
|
|
15095
|
+
"SupaCloud owner select",
|
|
15096
|
+
"SupaCloud owner insert",
|
|
15097
|
+
"SupaCloud owner update",
|
|
15098
|
+
"SupaCloud owner delete"
|
|
15099
|
+
];
|
|
15100
|
+
const dropPolicies = policyNames.map((name) => `DROP POLICY IF EXISTS "${name}" ON ${qualifiedTable};`).join(" ");
|
|
15101
|
+
if (policyMode === "deny_all")
|
|
15102
|
+
return dropPolicies;
|
|
15103
|
+
const ownerColumn = quoteIdentifier(ownerColumnValue, "owner column");
|
|
15104
|
+
const predicate = `auth.uid() IS NOT NULL AND auth.uid() = ${ownerColumn}`;
|
|
15105
|
+
return `${dropPolicies}
|
|
15106
|
+
CREATE POLICY "SupaCloud owner select" ON ${qualifiedTable} FOR SELECT TO authenticated USING (${predicate});
|
|
15107
|
+
CREATE POLICY "SupaCloud owner insert" ON ${qualifiedTable} FOR INSERT TO authenticated WITH CHECK (${predicate});
|
|
15108
|
+
CREATE POLICY "SupaCloud owner update" ON ${qualifiedTable} FOR UPDATE TO authenticated USING (${predicate}) WITH CHECK (${predicate});
|
|
15109
|
+
CREATE POLICY "SupaCloud owner delete" ON ${qualifiedTable} FOR DELETE TO authenticated USING (${predicate});`;
|
|
15110
|
+
}
|
|
15070
15111
|
function sqlString(value) {
|
|
15071
15112
|
return `'${value.replace(/'/g, "''")}'`;
|
|
15072
15113
|
}
|
|
@@ -16908,9 +16949,8 @@ Actions: routes, upsert_route, update_route, delete_route, config, get_certifica
|
|
|
16908
16949
|
}
|
|
16909
16950
|
|
|
16910
16951
|
// src/index.ts
|
|
16911
|
-
var
|
|
16912
|
-
var
|
|
16913
|
-
var preferredCommand = "supacloud-cli";
|
|
16952
|
+
var commandName = "supacloud-cli";
|
|
16953
|
+
var preferredCommand = commandName;
|
|
16914
16954
|
var projectActionSchema = exports_external.enum([
|
|
16915
16955
|
"get",
|
|
16916
16956
|
"health",
|
|
@@ -16957,8 +16997,6 @@ function printHelp(context = resolveSupaCloudContext()) {
|
|
|
16957
16997
|
║ Project CLI for SupaCloud users ║
|
|
16958
16998
|
╚═══════════════════════════════════════════════════════════╝
|
|
16959
16999
|
|
|
16960
|
-
${commandName === "supacloud" ? "NOTE\n\n `supacloud` is kept as a compatibility alias. Prefer `supacloud-cli`\n to avoid confusion with the server binary at /usr/local/bin/supacloud.\n" : ""}
|
|
16961
|
-
|
|
16962
17000
|
USAGE
|
|
16963
17001
|
|
|
16964
17002
|
${preferredCommand} <module> <action> [--flags]
|
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supacloud/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Project-scoped CLI for SupaCloud users",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"supacloud-cli": "dist/index.js"
|
|
9
|
-
"supacloud": "dist/index.js"
|
|
8
|
+
"supacloud-cli": "dist/index.js"
|
|
10
9
|
},
|
|
11
10
|
"files": [
|
|
12
11
|
"dist",
|