@polardbx/pxbaas 0.1.0-beta.1 → 0.1.0-beta.2
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/README.md +108 -0
- package/dist/commands/app/index.d.ts.map +1 -1
- package/dist/commands/app/index.js +219 -10
- package/dist/commands/app/index.js.map +1 -1
- package/dist/commands/db/index.d.ts +1 -0
- package/dist/commands/db/index.d.ts.map +1 -1
- package/dist/commands/db/index.js +49 -15
- package/dist/commands/db/index.js.map +1 -1
- package/dist/commands/login/index.d.ts.map +1 -1
- package/dist/commands/login/index.js +9 -34
- package/dist/commands/login/index.js.map +1 -1
- package/dist/commands/profile/index.d.ts.map +1 -1
- package/dist/commands/profile/index.js +3 -0
- package/dist/commands/profile/index.js.map +1 -1
- package/dist/credentials/index.d.ts +2 -0
- package/dist/credentials/index.d.ts.map +1 -1
- package/dist/credentials/index.js +2 -0
- package/dist/credentials/index.js.map +1 -1
- package/dist/db/apply-schema.d.ts +98 -11
- package/dist/db/apply-schema.d.ts.map +1 -1
- package/dist/db/apply-schema.js +247 -80
- package/dist/db/apply-schema.js.map +1 -1
- package/dist/db/sql-file.d.ts +55 -0
- package/dist/db/sql-file.d.ts.map +1 -1
- package/dist/db/sql-file.js +417 -0
- package/dist/db/sql-file.js.map +1 -1
- package/package.json +2 -2
- package/skills/pxbaas-app-spec/references/templates-examples.md +5 -2
- package/skills/pxbaas-cli/SKILL.md +7 -4
- package/skills/pxbaas-cli/references/app-deploy.md +29 -2
- package/skills/pxbaas-cli/references/auth-and-credentials.md +14 -2
- package/skills/pxbaas-cli/references/db-and-meta.md +12 -0
- package/skills/pxbaas-cli/references/multi-tenant-apps.md +35 -4
- package/templates/webapp/csr-bff/db/rls.json +13 -0
- package/templates/webapp/csr-bff/db/schema.sql +8 -28
- package/templates/webapp/manifest.json +6 -3
- package/templates/webapp/ssr-blog/db/rls.json +13 -0
- package/templates/webapp/ssr-blog/db/schema.sql +8 -28
- package/templates/webapp/ssr-shop/db/rls.json +16 -0
- package/templates/webapp/ssr-shop/db/schema.sql +12 -42
package/dist/db/sql-file.js
CHANGED
|
@@ -164,4 +164,421 @@ export function buildStatementPlan(rawSql, schema) {
|
|
|
164
164
|
const sql = substituted ? substituteSchema(rawSql, schema) : rawSql;
|
|
165
165
|
return { statements: splitSqlStatements(sql), substituted };
|
|
166
166
|
}
|
|
167
|
+
// ---------------------------------------------------------------------------
|
|
168
|
+
// Multi-tenant schema handling (tenant self-service DDL via /api/tenant-ddl)
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
/**
|
|
171
|
+
* Multi-tenant variant of {@link substituteSchema}: DROP the `__SCHEMA__`.
|
|
172
|
+
* qualifier entirely instead of filling it in. The tenant-ddl connection is
|
|
173
|
+
* already bound to the tenant's own database, and cross-schema fully-qualified
|
|
174
|
+
* names are denied by GRANT / the server pre-check — so `__SCHEMA__`.`t` must
|
|
175
|
+
* become just `t`. The CLI never knows the tenant's real db_name (the resolver
|
|
176
|
+
* does not expose it), so de-qualifying is the only safe substitution.
|
|
177
|
+
*/
|
|
178
|
+
export function substituteSchemaForTenant(sql) {
|
|
179
|
+
return sql.replace(/`?__SCHEMA__`?\s*\.\s*/g, "");
|
|
180
|
+
}
|
|
181
|
+
/** Central-registry RLS INSERT recognizers (allow optional backticks / IGNORE). */
|
|
182
|
+
const RLS_TABLES_RE = /^\s*INSERT\s+(?:IGNORE\s+)?INTO\s+`?supabase`?\s*\.\s*`?_mysqlrest_rls_tables`?/i;
|
|
183
|
+
const RLS_POLICIES_RE = /^\s*INSERT\s+(?:IGNORE\s+)?INTO\s+`?supabase`?\s*\.\s*`?_mysqlrest_rls_policies`?/i;
|
|
184
|
+
/**
|
|
185
|
+
* Split statements into central-registry RLS INSERTs vs. plain DDL/DML. In
|
|
186
|
+
* multi-tenant mode the RLS INSERTs MUST be diverted to the structured
|
|
187
|
+
* `/api/tenant-ddl/rls/*` endpoints (the tenant account has no GRANT on the
|
|
188
|
+
* shared `supabase` database, so a raw INSERT would be denied).
|
|
189
|
+
*/
|
|
190
|
+
export function partitionStatements(statements) {
|
|
191
|
+
const rlsTableInserts = [];
|
|
192
|
+
const rlsPolicyInserts = [];
|
|
193
|
+
const plain = [];
|
|
194
|
+
for (const s of statements) {
|
|
195
|
+
if (RLS_TABLES_RE.test(s))
|
|
196
|
+
rlsTableInserts.push(s);
|
|
197
|
+
else if (RLS_POLICIES_RE.test(s))
|
|
198
|
+
rlsPolicyInserts.push(s);
|
|
199
|
+
else
|
|
200
|
+
plain.push(s);
|
|
201
|
+
}
|
|
202
|
+
return { rlsTableInserts, rlsPolicyInserts, plain };
|
|
203
|
+
}
|
|
204
|
+
/** Thrown when an RLS INSERT does not match the constrained shape we can parse. */
|
|
205
|
+
export class SqlParseError extends Error {
|
|
206
|
+
constructor(message) {
|
|
207
|
+
super(message);
|
|
208
|
+
this.name = "SqlParseError";
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/** Extract a balanced `(...)` group starting at `s[open] === '('`. */
|
|
212
|
+
function extractParenGroup(s, open) {
|
|
213
|
+
let depth = 0;
|
|
214
|
+
let inSingle = false;
|
|
215
|
+
let inDouble = false;
|
|
216
|
+
let inBacktick = false;
|
|
217
|
+
for (let i = open; i < s.length; i++) {
|
|
218
|
+
const c = s[i];
|
|
219
|
+
const next = i + 1 < s.length ? s[i + 1] : "";
|
|
220
|
+
if (inSingle) {
|
|
221
|
+
if (c === "'" && next === "'") {
|
|
222
|
+
i++;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (c === "'")
|
|
226
|
+
inSingle = false;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (inDouble) {
|
|
230
|
+
if (c === '"' && next === '"') {
|
|
231
|
+
i++;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (c === '"')
|
|
235
|
+
inDouble = false;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (inBacktick) {
|
|
239
|
+
if (c === "`")
|
|
240
|
+
inBacktick = false;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (c === "'") {
|
|
244
|
+
inSingle = true;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (c === '"') {
|
|
248
|
+
inDouble = true;
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (c === "`") {
|
|
252
|
+
inBacktick = true;
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (c === "(")
|
|
256
|
+
depth++;
|
|
257
|
+
else if (c === ")") {
|
|
258
|
+
depth--;
|
|
259
|
+
if (depth === 0)
|
|
260
|
+
return { content: s.slice(open + 1, i), end: i + 1 };
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
throw new SqlParseError("unbalanced parentheses in statement");
|
|
264
|
+
}
|
|
265
|
+
/** Split on top-level commas, ignoring separators inside quotes/backticks/parens. */
|
|
266
|
+
function splitTopLevel(s) {
|
|
267
|
+
const out = [];
|
|
268
|
+
let depth = 0;
|
|
269
|
+
let inSingle = false;
|
|
270
|
+
let inDouble = false;
|
|
271
|
+
let inBacktick = false;
|
|
272
|
+
let cur = "";
|
|
273
|
+
for (let i = 0; i < s.length; i++) {
|
|
274
|
+
const c = s[i];
|
|
275
|
+
const next = i + 1 < s.length ? s[i + 1] : "";
|
|
276
|
+
if (inSingle) {
|
|
277
|
+
cur += c;
|
|
278
|
+
if (c === "'" && next === "'") {
|
|
279
|
+
cur += next;
|
|
280
|
+
i++;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (c === "'")
|
|
284
|
+
inSingle = false;
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
if (inDouble) {
|
|
288
|
+
cur += c;
|
|
289
|
+
if (c === '"' && next === '"') {
|
|
290
|
+
cur += next;
|
|
291
|
+
i++;
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (c === '"')
|
|
295
|
+
inDouble = false;
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (inBacktick) {
|
|
299
|
+
cur += c;
|
|
300
|
+
if (c === "`")
|
|
301
|
+
inBacktick = false;
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
if (c === "'") {
|
|
305
|
+
inSingle = true;
|
|
306
|
+
cur += c;
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
if (c === '"') {
|
|
310
|
+
inDouble = true;
|
|
311
|
+
cur += c;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (c === "`") {
|
|
315
|
+
inBacktick = true;
|
|
316
|
+
cur += c;
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
if (c === "(" || c === "[") {
|
|
320
|
+
depth++;
|
|
321
|
+
cur += c;
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
if (c === ")" || c === "]") {
|
|
325
|
+
depth--;
|
|
326
|
+
cur += c;
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
if (c === "," && depth === 0) {
|
|
330
|
+
out.push(cur);
|
|
331
|
+
cur = "";
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
cur += c;
|
|
335
|
+
}
|
|
336
|
+
if (cur.trim() !== "")
|
|
337
|
+
out.push(cur);
|
|
338
|
+
return out.map((x) => x.trim()).filter((x) => x !== "");
|
|
339
|
+
}
|
|
340
|
+
function parseLiteral(tok) {
|
|
341
|
+
const t = tok.trim();
|
|
342
|
+
if (/^NULL$/i.test(t))
|
|
343
|
+
return null;
|
|
344
|
+
if (/^TRUE$/i.test(t))
|
|
345
|
+
return true;
|
|
346
|
+
if (/^FALSE$/i.test(t))
|
|
347
|
+
return false;
|
|
348
|
+
if (t.length >= 2 && t.startsWith("'") && t.endsWith("'"))
|
|
349
|
+
return t.slice(1, -1).replace(/''/g, "'");
|
|
350
|
+
if (t.length >= 2 && t.startsWith('"') && t.endsWith('"'))
|
|
351
|
+
return t.slice(1, -1).replace(/""/g, '"');
|
|
352
|
+
if (/^-?\d+$/.test(t))
|
|
353
|
+
return parseInt(t, 10);
|
|
354
|
+
if (/^-?\d+\.\d+$/.test(t))
|
|
355
|
+
return parseFloat(t);
|
|
356
|
+
return t;
|
|
357
|
+
}
|
|
358
|
+
/** Parse `INSERT ... (<cols>) VALUES (<tuple>)[, (<tuple>)...]` into rows. */
|
|
359
|
+
function parseInsertColumnsAndTuples(stmt) {
|
|
360
|
+
const open = stmt.indexOf("(");
|
|
361
|
+
if (open === -1)
|
|
362
|
+
throw new SqlParseError("cannot locate the column list");
|
|
363
|
+
const colGroup = extractParenGroup(stmt, open);
|
|
364
|
+
const columns = splitTopLevel(colGroup.content).map((c) => c.replace(/`/g, "").trim().toLowerCase());
|
|
365
|
+
const rest = stmt.slice(colGroup.end);
|
|
366
|
+
const vm = /\bVALUES\b/i.exec(rest);
|
|
367
|
+
if (!vm)
|
|
368
|
+
throw new SqlParseError("cannot locate the VALUES keyword");
|
|
369
|
+
let idx = colGroup.end + vm.index + vm[0].length;
|
|
370
|
+
const tuples = [];
|
|
371
|
+
while (idx < stmt.length) {
|
|
372
|
+
while (idx < stmt.length && /[\s,]/.test(stmt[idx]))
|
|
373
|
+
idx++;
|
|
374
|
+
if (idx >= stmt.length || stmt[idx] !== "(")
|
|
375
|
+
break; // e.g. trailing ON DUPLICATE KEY
|
|
376
|
+
const g = extractParenGroup(stmt, idx);
|
|
377
|
+
tuples.push(splitTopLevel(g.content).map(parseLiteral));
|
|
378
|
+
idx = g.end;
|
|
379
|
+
}
|
|
380
|
+
if (tuples.length === 0)
|
|
381
|
+
throw new SqlParseError("no VALUES tuples found");
|
|
382
|
+
return { columns, tuples };
|
|
383
|
+
}
|
|
384
|
+
function zipRow(columns, tuple) {
|
|
385
|
+
if (columns.length !== tuple.length) {
|
|
386
|
+
throw new SqlParseError(`column/value count mismatch (${columns.length} columns vs ${tuple.length} values)`);
|
|
387
|
+
}
|
|
388
|
+
const row = {};
|
|
389
|
+
columns.forEach((c, i) => { row[c] = tuple[i]; });
|
|
390
|
+
return row;
|
|
391
|
+
}
|
|
392
|
+
function asString(v) {
|
|
393
|
+
return typeof v === "string" ? v : v === null ? null : String(v);
|
|
394
|
+
}
|
|
395
|
+
function asBool(v, def) {
|
|
396
|
+
if (v === null || v === undefined)
|
|
397
|
+
return def;
|
|
398
|
+
if (typeof v === "boolean")
|
|
399
|
+
return v;
|
|
400
|
+
if (typeof v === "number")
|
|
401
|
+
return v !== 0;
|
|
402
|
+
const s = String(v).toLowerCase();
|
|
403
|
+
if (s === "true" || s === "1")
|
|
404
|
+
return true;
|
|
405
|
+
if (s === "false" || s === "0")
|
|
406
|
+
return false;
|
|
407
|
+
return def;
|
|
408
|
+
}
|
|
409
|
+
const RLS_COMMANDS = ["ALL", "SELECT", "INSERT", "UPDATE", "DELETE"];
|
|
410
|
+
function guidance(table, e) {
|
|
411
|
+
const detail = e instanceof Error ? e.message : String(e);
|
|
412
|
+
throw new SqlParseError(`Cannot parse the RLS INSERT for supabase.${table}: ${detail}. ` +
|
|
413
|
+
`Use a declarative db/rls.json for this app instead of embedding RLS INSERTs in schema.sql.`);
|
|
414
|
+
}
|
|
415
|
+
/** Parse one `_mysqlrest_rls_tables` INSERT into structured inputs (schema dropped). */
|
|
416
|
+
export function parseRlsTableInsert(stmt) {
|
|
417
|
+
try {
|
|
418
|
+
const { columns, tuples } = parseInsertColumnsAndTuples(stmt);
|
|
419
|
+
return tuples.map((t) => {
|
|
420
|
+
const row = zipRow(columns, t);
|
|
421
|
+
const tableName = asString(row["table_name"]);
|
|
422
|
+
if (!tableName)
|
|
423
|
+
throw new SqlParseError("missing or invalid table_name");
|
|
424
|
+
return {
|
|
425
|
+
table_name: tableName,
|
|
426
|
+
// table_schema is intentionally dropped: the server binds it to the tenant db.
|
|
427
|
+
rls_enabled: asBool(row["rls_enabled"], true),
|
|
428
|
+
rls_forced: asBool(row["rls_forced"], false),
|
|
429
|
+
};
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
catch (e) {
|
|
433
|
+
return guidance("_mysqlrest_rls_tables", e);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
/** Parse one `_mysqlrest_rls_policies` INSERT into structured inputs (schema dropped). */
|
|
437
|
+
export function parseRlsPolicyInsert(stmt) {
|
|
438
|
+
try {
|
|
439
|
+
const { columns, tuples } = parseInsertColumnsAndTuples(stmt);
|
|
440
|
+
return tuples.map((t) => {
|
|
441
|
+
const row = zipRow(columns, t);
|
|
442
|
+
const tableName = asString(row["table_name"]);
|
|
443
|
+
const policyName = asString(row["policy_name"]);
|
|
444
|
+
if (!tableName)
|
|
445
|
+
throw new SqlParseError("missing or invalid table_name");
|
|
446
|
+
if (!policyName)
|
|
447
|
+
throw new SqlParseError("missing or invalid policy_name");
|
|
448
|
+
const command = (asString(row["command"]) ?? "ALL").toUpperCase();
|
|
449
|
+
if (!RLS_COMMANDS.includes(command)) {
|
|
450
|
+
throw new SqlParseError(`command must be one of ${RLS_COMMANDS.join(", ")} (got '${command}')`);
|
|
451
|
+
}
|
|
452
|
+
const role = asString(row["role_name"]);
|
|
453
|
+
return {
|
|
454
|
+
table_name: tableName,
|
|
455
|
+
policy_name: policyName,
|
|
456
|
+
role_name: role && role !== "" ? role : "*",
|
|
457
|
+
command,
|
|
458
|
+
using_expr: row["using_expr"] === undefined ? null : asString(row["using_expr"]),
|
|
459
|
+
check_expr: row["check_expr"] === undefined ? null : asString(row["check_expr"]),
|
|
460
|
+
permissive: asBool(row["permissive"], true),
|
|
461
|
+
};
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
catch (e) {
|
|
465
|
+
return guidance("_mysqlrest_rls_policies", e);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
// --- Render structured RLS back to central-registry SQL (single-tenant path) ---
|
|
469
|
+
function sqlLiteral(v) {
|
|
470
|
+
if (v === null || v === undefined)
|
|
471
|
+
return "NULL";
|
|
472
|
+
return `'${String(v).replace(/'/g, "''")}'`;
|
|
473
|
+
}
|
|
474
|
+
function sqlBoolLit(v, def) {
|
|
475
|
+
return (v ?? def) ? "1" : "0";
|
|
476
|
+
}
|
|
477
|
+
/** Render an idempotent central-registry INSERT for one RLS table toggle. */
|
|
478
|
+
export function renderRlsTableInsert(schema, input) {
|
|
479
|
+
return (`INSERT INTO supabase._mysqlrest_rls_tables (table_schema, table_name, rls_enabled, rls_forced) ` +
|
|
480
|
+
`VALUES (${sqlLiteral(schema)}, ${sqlLiteral(input.table_name)}, ` +
|
|
481
|
+
`${sqlBoolLit(input.rls_enabled, true)}, ${sqlBoolLit(input.rls_forced, false)}) ` +
|
|
482
|
+
`ON DUPLICATE KEY UPDATE rls_enabled = VALUES(rls_enabled), rls_forced = VALUES(rls_forced)`);
|
|
483
|
+
}
|
|
484
|
+
/** Render an idempotent central-registry INSERT for one RLS policy. */
|
|
485
|
+
export function renderRlsPolicyInsert(schema, input) {
|
|
486
|
+
const role = input.role_name ?? "*";
|
|
487
|
+
const command = (input.command ?? "ALL").toString().toUpperCase();
|
|
488
|
+
return (`INSERT INTO supabase._mysqlrest_rls_policies ` +
|
|
489
|
+
`(table_schema, table_name, policy_name, role_name, command, using_expr, check_expr, permissive) ` +
|
|
490
|
+
`VALUES (${sqlLiteral(schema)}, ${sqlLiteral(input.table_name)}, ${sqlLiteral(input.policy_name)}, ` +
|
|
491
|
+
`${sqlLiteral(role)}, ${sqlLiteral(command)}, ${sqlLiteral(input.using_expr ?? null)}, ` +
|
|
492
|
+
`${sqlLiteral(input.check_expr ?? null)}, ${sqlBoolLit(input.permissive, true)}) ` +
|
|
493
|
+
`ON DUPLICATE KEY UPDATE role_name = VALUES(role_name), command = VALUES(command), ` +
|
|
494
|
+
`using_expr = VALUES(using_expr), check_expr = VALUES(check_expr), permissive = VALUES(permissive)`);
|
|
495
|
+
}
|
|
496
|
+
// --- Declarative db/rls.json (preferred RLS source; see plan §5.4) ---
|
|
497
|
+
/**
|
|
498
|
+
* Extract the table names created by a schema.sql script (for `--purge-db`).
|
|
499
|
+
* Handles `CREATE TABLE [IF NOT EXISTS] [`__SCHEMA__`.]<name>` with optional
|
|
500
|
+
* backticks. Names are returned de-qualified and de-duplicated, in first-seen order.
|
|
501
|
+
*/
|
|
502
|
+
export function extractCreateTableNames(sql) {
|
|
503
|
+
const re = /CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:`?__SCHEMA__`?\s*\.\s*)?`?([A-Za-z0-9_]+)`?/gi;
|
|
504
|
+
const names = [];
|
|
505
|
+
let m;
|
|
506
|
+
while ((m = re.exec(sql)) !== null) {
|
|
507
|
+
const n = m[1];
|
|
508
|
+
if (n && !names.includes(n))
|
|
509
|
+
names.push(n);
|
|
510
|
+
}
|
|
511
|
+
return names;
|
|
512
|
+
}
|
|
513
|
+
const IDENT_RE = /^[a-zA-Z0-9_]+$/;
|
|
514
|
+
function requireIdent(value, field) {
|
|
515
|
+
if (typeof value !== "string" || !value || value.length > 64 || !IDENT_RE.test(value)) {
|
|
516
|
+
throw new SqlParseError(`db/rls.json: invalid ${field} (only [a-zA-Z0-9_], max 64 chars)`);
|
|
517
|
+
}
|
|
518
|
+
return value;
|
|
519
|
+
}
|
|
520
|
+
function pick(obj, ...keys) {
|
|
521
|
+
for (const k of keys)
|
|
522
|
+
if (obj[k] !== undefined)
|
|
523
|
+
return obj[k];
|
|
524
|
+
return undefined;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Parse + validate a declarative `db/rls.json`. Accepts the documented friendly
|
|
528
|
+
* keys (table/name/role/using/check/rlsEnabled/...) and their snake_case aliases,
|
|
529
|
+
* mapping them onto the driver's structured inputs. `table_schema` is NEVER read
|
|
530
|
+
* here — the execution layer decides it (single-tenant = --schema; multi-tenant =
|
|
531
|
+
* server-bound).
|
|
532
|
+
*/
|
|
533
|
+
export function parseRlsJson(text) {
|
|
534
|
+
let raw;
|
|
535
|
+
try {
|
|
536
|
+
raw = JSON.parse(text);
|
|
537
|
+
}
|
|
538
|
+
catch (e) {
|
|
539
|
+
throw new SqlParseError(`db/rls.json is not valid JSON: ${e instanceof Error ? e.message : String(e)}`);
|
|
540
|
+
}
|
|
541
|
+
const root = (raw ?? {});
|
|
542
|
+
const rawTables = Array.isArray(root.tables) ? root.tables : [];
|
|
543
|
+
const rawPolicies = Array.isArray(root.policies) ? root.policies : [];
|
|
544
|
+
const tables = rawTables.map((entry) => {
|
|
545
|
+
const o = (entry ?? {});
|
|
546
|
+
const table = requireIdent(pick(o, "table", "table_name"), "tables[].table");
|
|
547
|
+
const enabled = pick(o, "rlsEnabled", "rls_enabled");
|
|
548
|
+
const forced = pick(o, "rlsForced", "rls_forced");
|
|
549
|
+
return {
|
|
550
|
+
table_name: table,
|
|
551
|
+
rls_enabled: enabled === undefined ? true : Boolean(enabled),
|
|
552
|
+
rls_forced: forced === undefined ? false : Boolean(forced),
|
|
553
|
+
};
|
|
554
|
+
});
|
|
555
|
+
const policies = rawPolicies.map((entry) => {
|
|
556
|
+
const o = (entry ?? {});
|
|
557
|
+
const table = requireIdent(pick(o, "table", "table_name"), "policies[].table");
|
|
558
|
+
const name = requireIdent(pick(o, "name", "policy_name"), "policies[].name");
|
|
559
|
+
const roleRaw = pick(o, "role", "role_name");
|
|
560
|
+
const role = roleRaw === undefined || roleRaw === null || roleRaw === "" ? "*" : String(roleRaw);
|
|
561
|
+
if (role !== "*" && !IDENT_RE.test(role)) {
|
|
562
|
+
throw new SqlParseError(`db/rls.json: policy '${name}' role must be '*' or [a-zA-Z0-9_]`);
|
|
563
|
+
}
|
|
564
|
+
const commandRaw = pick(o, "command");
|
|
565
|
+
const command = (commandRaw === undefined || commandRaw === null ? "ALL" : String(commandRaw)).toUpperCase();
|
|
566
|
+
if (!RLS_COMMANDS.includes(command)) {
|
|
567
|
+
throw new SqlParseError(`db/rls.json: policy '${name}' command must be one of ${RLS_COMMANDS.join(", ")}`);
|
|
568
|
+
}
|
|
569
|
+
const using = pick(o, "using", "using_expr");
|
|
570
|
+
const check = pick(o, "check", "check_expr");
|
|
571
|
+
const permissive = pick(o, "permissive");
|
|
572
|
+
return {
|
|
573
|
+
table_name: table,
|
|
574
|
+
policy_name: name,
|
|
575
|
+
role_name: role,
|
|
576
|
+
command,
|
|
577
|
+
using_expr: using === undefined || using === null ? null : String(using),
|
|
578
|
+
check_expr: check === undefined || check === null ? null : String(check),
|
|
579
|
+
permissive: permissive === undefined ? true : Boolean(permissive),
|
|
580
|
+
};
|
|
581
|
+
});
|
|
582
|
+
return { tables, policies };
|
|
583
|
+
}
|
|
167
584
|
//# sourceMappingURL=sql-file.js.map
|
package/dist/db/sql-file.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-file.js","sourceRoot":"","sources":["../../src/db/sql-file.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sql-file.js","sourceRoot":"","sources":["../../src/db/sql-file.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polardbx/pxbaas",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.2",
|
|
4
4
|
"description": "PolarDB-X BaaS CLI application",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"polardbx",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"inquirer": "^9.2.0",
|
|
36
36
|
"jsonwebtoken": "^9.0.0",
|
|
37
37
|
"keytar": "^7.9.0",
|
|
38
|
-
"@polardbx/pxbaas-client": "0.1.0-beta.
|
|
38
|
+
"@polardbx/pxbaas-client": "0.1.0-beta.2"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/inquirer": "^9.0.0",
|
|
@@ -84,8 +84,11 @@ apps/<name>/
|
|
|
84
84
|
| `ssr-shop` | React 18 + Hono SSR shop; catalog/detail/search server-rendered, cart/checkout hydrate; `session: "browser-side"`, RLS-gated orders | framework SSR with browser-side auth |
|
|
85
85
|
| `ssr-frameworks` | same UI rendered by React / Vue / Preact / Solid on Hono, plus `/compare`; no data plane | framework SSR capability showcase (heaviest cold start) |
|
|
86
86
|
|
|
87
|
-
`ssr-blog`, `csr-bff` and `ssr-shop` each ship a `db/schema.sql` (idempotent DDL +
|
|
88
|
-
token
|
|
87
|
+
`ssr-blog`, `csr-bff` and `ssr-shop` each ship a `db/schema.sql` (idempotent DDL + seed, with a `__SCHEMA__`
|
|
88
|
+
token) and a declarative `db/rls.json` (the RLS registry). Run `pxbaas app db-init <name>` to apply both —
|
|
89
|
+
it is mode-aware, so you do **not** hand-edit `__SCHEMA__`: single-tenant resolves it to `public` via `/pg/query`,
|
|
90
|
+
multi-tenant de-qualifies it and runs DDL on the tenant's own db via `/api/tenant-ddl`, registering RLS through the
|
|
91
|
+
structured `/rls/*` endpoints (`table_schema` bound server-side). Their workers carry a
|
|
89
92
|
**DEPLOY CONFIG** block (`INTERNAL_BASE` / `ANON_KEY`) that is empty by design — an unconfigured worker returns an
|
|
90
93
|
explicit 503 instead of silently falling back to someone else's instance or tenant.
|
|
91
94
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pxbaas-cli
|
|
3
|
-
description: Use the PolarDB-X BaaS CLI (command `pxbaas`) to consume the data plane from the command line — run SQL, inspect tables/schema, query and edit table data (PostgREST), deploy/list/delete Edge Functions, manage storage objects and auth users, generate types, claim a free disposable instance (`pxbaas provision`), and scaffold/deploy/publish/rollback Webapps (`pxbaas app`). Use when the user works with a PolarDB-X BaaS / MySQL-Supabase instance through `pxbaas`, has no instance yet and wants to provision one, wants to deploy a demo webapp, or mentions the pxbaas CLI, querying BaaS tables/data, running SQL, edge function deploy, storage objects, auth users, gen types, or pxbaas login/doctor/401/403 errors. Data-plane consumption + self-service provisioning (not platform deployment or tenant lifecycle). Agent rules — never run `pxbaas login` (human-only TTY), but `pxbaas provision`
|
|
3
|
+
description: Use the PolarDB-X BaaS CLI (command `pxbaas`) to consume the data plane from the command line — run SQL, inspect tables/schema, query and edit table data (PostgREST), deploy/list/delete Edge Functions, manage storage objects and auth users, generate types, claim a free disposable instance (`pxbaas provision`), and scaffold/deploy/publish/rollback Webapps (`pxbaas app`). Use when the user works with a PolarDB-X BaaS / MySQL-Supabase instance through `pxbaas`, has no instance yet and wants to provision one, wants to deploy a demo webapp, or mentions the pxbaas CLI, querying BaaS tables/data, running SQL, edge function deploy, storage objects, auth users, gen types, or pxbaas login/doctor/401/403 errors. Data-plane consumption + self-service provisioning (not platform deployment or tenant lifecycle). Agent rules — never run `pxbaas login` (human-only TTY), but `pxbaas provision` and `pxbaas profile import <env-file>` (bind a single-tenant instance from a `.env` file) ARE agent-runnable; always pass `--json`; confirm destructive operations with the user before executing.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# pxbaas-cli
|
|
@@ -76,6 +76,7 @@ Legend — **R** = read/safe · **W** = write · **D** = destructive (consent re
|
|
|
76
76
|
| Command | Default role | R/W/D | Details |
|
|
77
77
|
|---|---|---|---|
|
|
78
78
|
| `login` (human TTY only) | — | special | auth-and-credentials.md |
|
|
79
|
+
| `profile import <env-file>` (agent-runnable, single-tenant) | — | W (local) | auth-and-credentials.md |
|
|
79
80
|
| `logout` | — | W (local) | auth-and-credentials.md |
|
|
80
81
|
| `whoami` | — | R | auth-and-credentials.md |
|
|
81
82
|
| `status` | — | R | auth-and-credentials.md |
|
|
@@ -118,7 +119,8 @@ Legend — **R** = read/safe · **W** = write · **D** = destructive (consent re
|
|
|
118
119
|
| `app publish <name>` / `app rollback <name>` / `app unpublish <name>` | service_role | **D/W** | app-deploy.md |
|
|
119
120
|
| `app verify <name>` / `app url <name>` | — | R | app-deploy.md |
|
|
120
121
|
| `app list` / `app templates` | service_role | R | app-deploy.md |
|
|
121
|
-
| `app remove <name>` | service_role | **D** (`--confirm`) | app-deploy.md |
|
|
122
|
+
| `app remove <name>` | service_role | **D** (`--confirm`; legacy `spa/api/mpa` only) | app-deploy.md |
|
|
123
|
+
| `app destroy <name>` | service_role | **D** (two-phase `--confirm`; webapp only; deletes edge funcs + mount, **keeps DB tables**) | app-deploy.md |
|
|
122
124
|
|
|
123
125
|
For provisioning / multi-instance selection see **provision.md**; for the `pxbaas app` commands see **app-deploy.md**. For how to **write** edge-function webapp code (Deno runtime contract, guardrails, templates, raw deploy paths), use the companion **`pxbaas-app-spec` skill**. For the live platform API beyond the CLI (all endpoints, multi-tenant rules, error codes, limitations), see **api-reference.md** (`GET ${endpoint}/docs`).
|
|
124
126
|
|
|
@@ -138,6 +140,7 @@ For provisioning / multi-instance selection see **provision.md**; for the `pxbaa
|
|
|
138
140
|
"which instance am I on" → pxbaas instance current --json (or pxbaas whoami --json)
|
|
139
141
|
"deploy this demo webapp" → confirm first → pxbaas app deploy <name> --json
|
|
140
142
|
"give me a shareable demo URL" → pxbaas app url <name> --json
|
|
143
|
+
"destroy/delete this webapp" → confirm first → pxbaas app destroy <name> --confirm --json (removes edge funcs + mount; DB tables kept)
|
|
141
144
|
"can't connect / error" → pxbaas doctor --json; if credentials missing, ask user to run pxbaas login
|
|
142
145
|
```
|
|
143
146
|
|
|
@@ -149,8 +152,8 @@ For provisioning / multi-instance selection see **provision.md**; for the `pxbaa
|
|
|
149
152
|
- **NEVER write PostgreSQL SQL in `db query`** — the backend is MySQL/PolarDB-X (use `CAST(x AS t)`, backticks, `JSON_EXTRACT`, `UUID()`, not `::`, `"col"`, `->>`, `gen_random_uuid()`).
|
|
150
153
|
- **NEVER assume an API listed in `/docs` is available via the CLI** — `/docs` covers the whole platform including admin APIs (PG-Meta, Management API) and tenant lifecycle; the CLI wraps only the customer data-plane subset in the command map above.
|
|
151
154
|
- **NEVER mix credentials across profiles** or target another tenant; the CLI enforces tenant isolation.
|
|
152
|
-
- **NEVER write multi-tenant RLS policies into a tenant DB's `_mysqlrest_rls_*` copies, or with `table_schema='public'`** — the authoritative store is the central `supabase` DB keyed by the tenant database name, and a wrong value fails **silently** (RLS stays off). See `references/multi-tenant-apps.md`.
|
|
153
|
-
- **
|
|
155
|
+
- **NEVER write multi-tenant RLS policies into a tenant DB's `_mysqlrest_rls_*` copies, or with `table_schema='public'`** — the authoritative store is the central `supabase` DB keyed by the tenant database name, and a wrong value fails **silently** (RLS stays off). Prefer declaring RLS in `db/rls.json` and letting `pxbaas app db-init` bind `table_schema` for you. See `references/multi-tenant-apps.md`.
|
|
156
|
+
- **On a multi-tenant profile, `db query` / `db push` / `app db-init` route through `/api/tenant-ddl` with the per-tenant service_role key** — DDL runs on the tenant's OWN db (GRANT-isolated) and RLS goes through the structured `/rls/*` endpoints (`table_schema` bound server-side). `/pg/query` stays platform-admin only and rejects per-tenant keys; do not hand-run central RLS INSERTs with a global key.
|
|
154
157
|
- **NEVER parse human-readable output** — always use `--json`.
|
|
155
158
|
- **NEVER loop-retry Zero capacity/rate errors** (`429 SUPABASE_LIMIT_REACHED` / `503 SUPABASE_POOL_EXHAUSTED`) — stop, inform the user, offer `pxbaas instance release`; retry at most once for pool exhaustion.
|
|
156
159
|
- **NEVER expect plaintext keys from `pxbaas provision`** — it stores them in the keyring and shows only fingerprints (this is why it is agent-runnable, unlike `pxbaas login`).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Webapp Demo Deploy — `pxbaas app` commands
|
|
2
2
|
|
|
3
|
-
Covers: `app new` · `app capabilities` · `app deploy` · `app publish` · `app rollback` · `app unpublish` · `app verify` · `app url` · `app list` · `app remove` · `app templates`.
|
|
3
|
+
Covers: `app new` · `app capabilities` · `app deploy` · `app publish` · `app rollback` · `app unpublish` · `app destroy` · `app verify` · `app url` · `app list` · `app remove` · `app templates`.
|
|
4
4
|
|
|
5
5
|
`pxbaas app` supports both the legacy `spa`/`api`/`mpa` Edge Function demos and schemaVersion 2 Webapps with a prebuilt static frontend plus an optional Deno or Hono backend. Webapp deploys create immutable function candidates and publish them to a stable App mount using generation compare-and-swap (CAS).
|
|
6
6
|
|
|
@@ -99,6 +99,33 @@ pxbaas app unpublish <name> [--dir ./apps] [--json]
|
|
|
99
99
|
|
|
100
100
|
These commands are schemaVersion 2 only. They read `deployment.mountId`, `deployment.candidateFunctionSlug`, and `deployment.generation` from local `app.json`, send the generation as a CAS precondition, and persist the returned generation. A stale generation returns a normalized conflict code/message instead of silently overwriting another publish.
|
|
101
101
|
|
|
102
|
+
## pxbaas app db-init — initialize the app's database (single- AND multi-tenant)
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pxbaas app db-init <name> [--dir ./apps] [--schema public] [--dry-run] [--yes] [--json]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
- Applies the app's bundled `db/schema.sql` (DDL + seed) plus the declarative `db/rls.json` (RLS registry). Shows the full plan and requires authorization (`--dry-run` previews; `--yes` for non-interactive); runs sequentially, **stops on the first error, never rolls back**. The CLI never auto-runs DDL during `deploy`.
|
|
109
|
+
- **Mode-aware:** single-tenant runs through `/pg/query` (`__SCHEMA__`→`--schema`, default `public`); multi-tenant runs through `/api/tenant-ddl` with the per-tenant service_role key (`__SCHEMA__` de-qualified, DDL on the tenant's own db, RLS via the structured `/rls/*` endpoints with `table_schema` bound server-side). See `references/multi-tenant-apps.md` §3b.
|
|
110
|
+
- **RLS source (hybrid):** `db/rls.json` is preferred; if absent, RLS INSERTs embedded in `schema.sql` are parsed (multi-tenant) or run verbatim (single-tenant). When both exist, `rls.json` wins and the embedded INSERTs are skipped with a warning.
|
|
111
|
+
|
|
112
|
+
## pxbaas app destroy — permanently tear down a webapp (destructive; two-phase)
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pxbaas app destroy <name> [--dir ./apps] [--confirm] [--json]
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- schemaVersion 2 webapp only. Legacy `spa/api/mpa` functions keep using `app remove` (which now rejects a webapp and points here).
|
|
119
|
+
- **Reclaims two things**: the mount's edge-func candidates (`active`/`previous` plus any local unpublished `candidateFunctionSlug`) and the `_platform.app_mounts` mount-id record. Each function delete also revokes its share links and notifies the proxy (cache invalidation + quota release).
|
|
120
|
+
- **Does NOT drop DB business tables by default** (scope decision). Seed data and the central RLS metadata rows are left untouched. Pass `--purge-db` to also drop the app's own tables and remove its RLS registry rows (policies + the central `_mysqlrest_rls_tables` toggle, via `DELETE /api/tenant-ddl/rls/tables`), derived from `db/schema.sql` CREATE names + `db/rls.json`. On instances predating that DELETE endpoint the toggle row is left (harmless; re-created on the next `db-init`).
|
|
121
|
+
- Without `--confirm` it only previews what will be reclaimed (`status: "dry-run"`). With `--confirm` it destroys, then clears only the local `app.json` `deployment` block (source tree is kept for re-deploy).
|
|
122
|
+
- Idempotent: re-destroying an already-gone mount succeeds (`alreadyGone: true`). The server disables the mount before teardown, so a mid-way failure is safe to re-run.
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{ "status": "destroyed", "app": "shop", "mountId": "m-abc...", "alreadyGone": false,
|
|
126
|
+
"deletedFunctions": [ { "slug": "app_..._r_...", "deleted": true } ], "tablesUntouched": true }
|
|
127
|
+
```
|
|
128
|
+
|
|
102
129
|
## pxbaas app verify — cold-start-aware check
|
|
103
130
|
|
|
104
131
|
```bash
|
|
@@ -114,7 +141,7 @@ pxbaas app verify <name> [--dir ./apps] [--slug <slug>] [--timeout 60] [--json]
|
|
|
114
141
|
```bash
|
|
115
142
|
pxbaas app url <name> [--json] # print Public or Preview mount URL (legacy: share URL)
|
|
116
143
|
pxbaas app list [--json] # list deployed functions
|
|
117
|
-
pxbaas app remove <name> [--confirm] [--json] # delete
|
|
144
|
+
pxbaas app remove <name> [--confirm] [--json] # delete a LEGACY spa/api/mpa function (two-phase; webapps use `app destroy`)
|
|
118
145
|
pxbaas app templates [--json] # list all bundled templates
|
|
119
146
|
```
|
|
120
147
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Credentials, Environment & User Management
|
|
2
2
|
|
|
3
|
-
Covers: `login` (human-only) · `logout` · `whoami` · `status` · `doctor` · `profile` · `auth users`.
|
|
3
|
+
Covers: `login` (human-only) · `profile import` (agent-runnable, env-file) · `logout` · `whoami` · `status` · `doctor` · `profile` · `auth users`.
|
|
4
4
|
|
|
5
5
|
## login — agents must NEVER run this
|
|
6
6
|
|
|
@@ -11,7 +11,18 @@ pxbaas login --endpoint <url> --tenant-id <id> --anon-key-stdin --service-key-st
|
|
|
11
11
|
- **Requires an interactive TTY** (it prompts via inquirer); agents cannot complete it. **Do not attempt.**
|
|
12
12
|
- Keys are read from stdin via `--anon-key-stdin` / `--service-key-stdin` so they never land in shell history.
|
|
13
13
|
- Credentials are stored in the OS keyring (keytar), falling back to a `0600` file when no keyring is available.
|
|
14
|
-
- If another command reports `No active profile. Use "pxbaas login" ...` → **ask the user to log in manually
|
|
14
|
+
- If another command reports `No active profile. Use "pxbaas login" ...` → **ask the user to log in manually**, OR — if a Supabase/BaaS `.env` file for the instance is available — bind it non-interactively with `pxbaas profile import <env-file>` (see below; that one IS agent-runnable).
|
|
15
|
+
|
|
16
|
+
## profile import — bind a single-tenant instance from a `.env` file (agents MAY run this)
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pxbaas profile import <env-file> --name <profile> [--intranet] --json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- Reads `SUPABASE_URL` (fallback `SUPABASE_PUBLIC_URL` / `SUPABASE_INTRANET_URL`), `SUPABASE_ANON_KEY` (fallback `ANON_KEY` / `GLOBAL_ANON_KEY`) and `SUPABASE_SERVICE_ROLE_KEY` (fallback `SERVICE_ROLE_KEY` / `GLOBAL_SERVICE_ROLE_KEY`) from the file, validates the JWTs (rejects expired keys), then writes the profile + credentials and switches current to it.
|
|
23
|
+
- **Agent-runnable**: keys come from a file, never from argv/shell history, so no TTY is needed. Prefer this over `login` when you already have the instance `.env`.
|
|
24
|
+
- `--intranet` prefers `SUPABASE_INTRANET_URL` (VPC-internal entrypoint) over the public URL. `--name` sets the profile name (default: global `--profile`, else `default`); an existing profile of the same name is **overwritten**.
|
|
25
|
+
- **Single-tenant only for now.** A `MULTI_TENANT_MODE=true` env file is rejected with guidance — its global keys cannot access tenant resources; use `pxbaas login` with per-tenant anon/service keys instead.
|
|
15
26
|
|
|
16
27
|
## Verify login state (run these first)
|
|
17
28
|
|
|
@@ -65,6 +76,7 @@ pxbaas logout # clears credentials for the current profile
|
|
|
65
76
|
pxbaas profile ls --json # list all profiles
|
|
66
77
|
pxbaas profile use <name> --json # switch current profile
|
|
67
78
|
pxbaas profile rm <name> --json # remove a profile (destructive to local config — confirm first)
|
|
79
|
+
pxbaas profile import <env-file> --name <profile> --json # bind a single-tenant instance from a .env file (agent-runnable; see above)
|
|
68
80
|
```
|
|
69
81
|
|
|
70
82
|
- You can also pass global `--profile <name>` to target one environment without changing the default.
|