@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.
- package/README.md +20 -0
- package/dist/index.js +50 -20
- 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) =>
|
|
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
|
-
|
|
4585
|
-
|
|
4586
|
-
...pending.length ?
|
|
4587
|
-
|
|
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
|
|