@supacloud/cli 0.2.0 → 0.3.1

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 +49 -21
  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)
@@ -4369,7 +4354,7 @@ function migrationVersionFromFilename(file, fallbackIndex = 0) {
4369
4354
  return Date.now() * 1000 + fallbackIndex;
4370
4355
  }
4371
4356
  function appliedMigrationKeys(data) {
4372
- const rows = Array.isArray(data) ? data : data?.rows || [];
4357
+ const rows = data?.rows || [];
4373
4358
  const keys = new Set;
4374
4359
  for (const row of rows) {
4375
4360
  if (!row || typeof row !== "object")
@@ -4382,6 +4367,34 @@ 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
+ const shaped = data;
4378
+ const rows = shaped?.rows || [];
4379
+ return Array.isArray(rows) ? rows : [];
4380
+ }
4381
+ function vectorWarningsForPendingMigrations(migrations, vectorEnabled) {
4382
+ const warnings = [];
4383
+ const vectorUsers = migrations.filter(({ sql }) => sqlReferencesVector(sql));
4384
+ const vectorCreators = migrations.filter(({ sql }) => sqlCreatesVectorExtension(sql));
4385
+ if (!vectorUsers.length && !vectorCreators.length)
4386
+ return warnings;
4387
+ if (vectorEnabled === false && vectorUsers.length && !vectorCreators.length) {
4388
+ warnings.push(`vector extension is not enabled, but pending migrations use vector types/operators: ${vectorUsers.map(({ file }) => file).join(", ")}`);
4389
+ }
4390
+ if (vectorEnabled === false && vectorCreators.length) {
4391
+ warnings.push(`pending migrations will enable pgvector: ${vectorCreators.map(({ file }) => file).join(", ")}`);
4392
+ }
4393
+ if (vectorEnabled === null) {
4394
+ warnings.push("could not verify whether vector extension is enabled");
4395
+ }
4396
+ return warnings;
4397
+ }
4385
4398
  function registerDatabaseTools(server, http, config = {}) {
4386
4399
  const { readOnly = false, projectRef } = config;
4387
4400
  const actions = [
@@ -4431,7 +4444,7 @@ Actions: ${allActions.join(", ")}${readOnly ? " (read-only mode)" : ""}`, {
4431
4444
  return { content: [{ type: "text", text: `❌ Failed to read file ${args.file}: ${e.message}` }] };
4432
4445
  }
4433
4446
  }
4434
- const execSql = async (sql) => normalizeSqlResponse(await http.post(`/v1/projects/${ref}/database/sql`, { sql }));
4447
+ const execSql = async (sql) => http.post(`/v1/projects/${ref}/database/sql`, { sql });
4435
4448
  let text;
4436
4449
  switch (action) {
4437
4450
  case "query": {
@@ -4578,13 +4591,26 @@ Actions: ${allActions.join(", ")}${readOnly ? " (read-only mode)" : ""}`, {
4578
4591
  const appliedKeys = appliedMigrationKeys(r.data);
4579
4592
  const pending = migrationFiles.filter(({ name, version }) => !appliedKeys.has(name) && !appliedKeys.has(String(version)));
4580
4593
  const alreadyApplied = migrationFiles.filter(({ name, version }) => appliedKeys.has(name) || appliedKeys.has(String(version)));
4594
+ const pendingWithSql = pending.map((migration) => ({
4595
+ ...migration,
4596
+ sql: readFileSync2(join(dir, migration.file), "utf-8")
4597
+ }));
4598
+ let vectorEnabled = null;
4599
+ if (pendingWithSql.some(({ sql }) => sqlReferencesVector(sql) || sqlCreatesVectorExtension(sql))) {
4600
+ const extResult = await execSql("SELECT extname AS name FROM pg_extension WHERE extname = 'vector';");
4601
+ vectorEnabled = extResult.ok ? extensionRows(extResult.data).some((row) => row.name === "vector" || row.extname === "vector") : null;
4602
+ }
4603
+ const warnings = vectorWarningsForPendingMigrations(pendingWithSql, vectorEnabled);
4581
4604
  text = [
4582
4605
  `Migration dry run for ${dir}`,
4583
4606
  `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})`)] : []
4607
+ "",
4608
+ "Pending:",
4609
+ ...pending.length ? pending.map(({ file, version }) => ` - ${file} (${version})`) : [" - none"],
4610
+ "",
4611
+ "Already applied:",
4612
+ ...alreadyApplied.length ? alreadyApplied.map(({ file, version }) => ` - ${file} (${version})`) : [" - none"],
4613
+ ...warnings.length ? ["", "Warnings:", ...warnings.map((warning) => ` - ${warning}`)] : []
4588
4614
  ].join(`
4589
4615
  `);
4590
4616
  break;
@@ -5671,6 +5697,8 @@ EXAMPLES
5671
5697
  supacloud project logs --log_type database
5672
5698
  supacloud frontend list --ref abc123
5673
5699
  supacloud database query --sql "select now()"
5700
+ supacloud database query --ref abc123 --file ./queries/vector-search.sql
5701
+ supacloud database push_migrations --ref abc123 --dir supabase/migrations --dry_run
5674
5702
  supacloud edge_functions deploy --ref abc123 --slug hello --path ./supabase/functions/hello
5675
5703
  supacloud edge_functions config --ref abc123 --slug hello --verify_jwt false --background_routes "/queue/*,/render/*"
5676
5704
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supacloud/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Project-scoped CLI for SupaCloud users",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",