@supacloud/cli 0.2.0 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +20 -0
  2. package/dist/index.js +50 -20
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -14,9 +14,29 @@ npx @supacloud/cli status
14
14
  npx @supacloud/cli project get
15
15
  npx @supacloud/cli project logs --log_type database
16
16
  npx @supacloud/cli database query --sql "select now()"
17
+ npx @supacloud/cli database query --ref abc123 --file ./queries/vector-search.sql
18
+ npx @supacloud/cli database push_migrations --ref abc123 --dir supabase/migrations --dry_run
17
19
  npx @supacloud/cli frontend list --ref abc123
18
20
  ```
19
21
 
22
+ Use `database query --file` for complex SQL, pgvector queries, and single-request transaction blocks.
23
+
24
+ ```sql
25
+ CREATE EXTENSION IF NOT EXISTS vector;
26
+
27
+ CREATE TABLE documents (
28
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
29
+ content text NOT NULL,
30
+ embedding vector(1536)
31
+ );
32
+
33
+ CREATE INDEX documents_embedding_hnsw_idx
34
+ ON documents
35
+ USING hnsw (embedding vector_cosine_ops);
36
+ ```
37
+
38
+ Transaction boundary: SupaCloud supports transaction blocks inside one SQL request and transactional migrations. It does not expose long-lived HTTP transaction sessions; use a direct Postgres DSN for application-side long transactions.
39
+
20
40
  Project commands owned by this CLI:
21
41
 
22
42
  - `project get`
package/dist/index.js CHANGED
@@ -4347,21 +4347,6 @@ class HttpTransport {
4347
4347
  // src/shared/tools/database-tools.ts
4348
4348
  import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2, statSync } from "node:fs";
4349
4349
  import { basename, join } from "node:path";
4350
- function normalizeSqlResponse(result) {
4351
- if (!result.ok)
4352
- return result;
4353
- const data = result.data;
4354
- if (Array.isArray(data?.result) && !Array.isArray(data.rows)) {
4355
- return {
4356
- ...result,
4357
- data: {
4358
- rows: data.result,
4359
- rowCount: data.result.length
4360
- }
4361
- };
4362
- }
4363
- return result;
4364
- }
4365
4350
  function migrationVersionFromFilename(file, fallbackIndex = 0) {
4366
4351
  const match = basename(file).match(/^(\d{8,20})[_-]/);
4367
4352
  if (match)
@@ -4382,6 +4367,36 @@ function appliedMigrationKeys(data) {
4382
4367
  }
4383
4368
  return keys;
4384
4369
  }
4370
+ function sqlReferencesVector(sql) {
4371
+ return /\bvector\s*\(\s*\d+\s*\)/i.test(sql) || /::\s*vector\b/i.test(sql) || /\bvector_(cosine|l2|ip)_ops\b/i.test(sql) || /<=>|<#>|<->/.test(sql);
4372
+ }
4373
+ function sqlCreatesVectorExtension(sql) {
4374
+ return /\bCREATE\s+EXTENSION\s+(?:IF\s+NOT\s+EXISTS\s+)?["']?vector["']?/i.test(sql);
4375
+ }
4376
+ function extensionRows(data) {
4377
+ if (Array.isArray(data))
4378
+ return data;
4379
+ const shaped = data;
4380
+ const rows = shaped?.rows || [];
4381
+ return Array.isArray(rows) ? rows : [];
4382
+ }
4383
+ function vectorWarningsForPendingMigrations(migrations, vectorEnabled) {
4384
+ const warnings = [];
4385
+ const vectorUsers = migrations.filter(({ sql }) => sqlReferencesVector(sql));
4386
+ const vectorCreators = migrations.filter(({ sql }) => sqlCreatesVectorExtension(sql));
4387
+ if (!vectorUsers.length && !vectorCreators.length)
4388
+ return warnings;
4389
+ if (vectorEnabled === false && vectorUsers.length && !vectorCreators.length) {
4390
+ warnings.push(`vector extension is not enabled, but pending migrations use vector types/operators: ${vectorUsers.map(({ file }) => file).join(", ")}`);
4391
+ }
4392
+ if (vectorEnabled === false && vectorCreators.length) {
4393
+ warnings.push(`pending migrations will enable pgvector: ${vectorCreators.map(({ file }) => file).join(", ")}`);
4394
+ }
4395
+ if (vectorEnabled === null) {
4396
+ warnings.push("could not verify whether vector extension is enabled");
4397
+ }
4398
+ return warnings;
4399
+ }
4385
4400
  function registerDatabaseTools(server, http, config = {}) {
4386
4401
  const { readOnly = false, projectRef } = config;
4387
4402
  const actions = [
@@ -4431,7 +4446,7 @@ Actions: ${allActions.join(", ")}${readOnly ? " (read-only mode)" : ""}`, {
4431
4446
  return { content: [{ type: "text", text: `❌ Failed to read file ${args.file}: ${e.message}` }] };
4432
4447
  }
4433
4448
  }
4434
- const execSql = async (sql) => normalizeSqlResponse(await http.post(`/v1/projects/${ref}/database/sql`, { sql }));
4449
+ const execSql = async (sql) => http.post(`/v1/projects/${ref}/database/sql`, { sql });
4435
4450
  let text;
4436
4451
  switch (action) {
4437
4452
  case "query": {
@@ -4578,13 +4593,26 @@ Actions: ${allActions.join(", ")}${readOnly ? " (read-only mode)" : ""}`, {
4578
4593
  const appliedKeys = appliedMigrationKeys(r.data);
4579
4594
  const pending = migrationFiles.filter(({ name, version }) => !appliedKeys.has(name) && !appliedKeys.has(String(version)));
4580
4595
  const alreadyApplied = migrationFiles.filter(({ name, version }) => appliedKeys.has(name) || appliedKeys.has(String(version)));
4596
+ const pendingWithSql = pending.map((migration) => ({
4597
+ ...migration,
4598
+ sql: readFileSync2(join(dir, migration.file), "utf-8")
4599
+ }));
4600
+ let vectorEnabled = null;
4601
+ if (pendingWithSql.some(({ sql }) => sqlReferencesVector(sql) || sqlCreatesVectorExtension(sql))) {
4602
+ const extResult = await execSql("SELECT extname AS name FROM pg_extension WHERE extname = 'vector';");
4603
+ vectorEnabled = extResult.ok ? extensionRows(extResult.data).some((row) => row.name === "vector" || row.extname === "vector") : null;
4604
+ }
4605
+ const warnings = vectorWarningsForPendingMigrations(pendingWithSql, vectorEnabled);
4581
4606
  text = [
4582
4607
  `Migration dry run for ${dir}`,
4583
4608
  `Total: ${migrationFiles.length}`,
4584
- `Pending: ${pending.length}`,
4585
- `Already applied: ${alreadyApplied.length}`,
4586
- ...pending.length ? ["", "Would apply:", ...pending.map(({ file, version }) => ` - ${file} (${version})`)] : [],
4587
- ...alreadyApplied.length ? ["", "Already applied:", ...alreadyApplied.map(({ file, version }) => ` - ${file} (${version})`)] : []
4609
+ "",
4610
+ "Pending:",
4611
+ ...pending.length ? pending.map(({ file, version }) => ` - ${file} (${version})`) : [" - none"],
4612
+ "",
4613
+ "Already applied:",
4614
+ ...alreadyApplied.length ? alreadyApplied.map(({ file, version }) => ` - ${file} (${version})`) : [" - none"],
4615
+ ...warnings.length ? ["", "Warnings:", ...warnings.map((warning) => ` - ${warning}`)] : []
4588
4616
  ].join(`
4589
4617
  `);
4590
4618
  break;
@@ -5671,6 +5699,8 @@ EXAMPLES
5671
5699
  supacloud project logs --log_type database
5672
5700
  supacloud frontend list --ref abc123
5673
5701
  supacloud database query --sql "select now()"
5702
+ supacloud database query --ref abc123 --file ./queries/vector-search.sql
5703
+ supacloud database push_migrations --ref abc123 --dir supabase/migrations --dry_run
5674
5704
  supacloud edge_functions deploy --ref abc123 --slug hello --path ./supabase/functions/hello
5675
5705
  supacloud edge_functions config --ref abc123 --slug hello --verify_jwt false --background_routes "/queue/*,/render/*"
5676
5706
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Project-scoped CLI for SupaCloud users",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",