@coldge.com/gitbase 1.0.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.
@@ -0,0 +1,113 @@
1
+ import { runQuery } from '../api/supabase.js';
2
+ import * as Queries from './queries.js';
3
+ export async function extractSchema(projectRef) {
4
+ const tables = await extractTables(projectRef);
5
+ const functions = await extractFunctions(projectRef);
6
+ const views = await extractViews(projectRef);
7
+ const triggers = await extractTriggers(projectRef);
8
+ const policies = await extractPolicies(projectRef);
9
+ const types = await extractTypes(projectRef);
10
+ return { tables, functions, views, triggers, policies, types };
11
+ }
12
+ async function extractTables(projectRef) {
13
+ const tableList = await runQuery(projectRef, Queries.LIST_TABLES);
14
+ if (!Array.isArray(tableList))
15
+ return {};
16
+ const tables = {};
17
+ for (const row of tableList) {
18
+ const tableName = row.table_name;
19
+ const rlsEnabled = row.rls_enabled;
20
+ const columns = await runQuery(projectRef, Queries.GET_COLUMNS(tableName));
21
+ let sql = `CREATE TABLE public."${tableName}" (\n`;
22
+ const colDefs = columns.map(c => {
23
+ let line = ` "${c.column_name}" ${c.data_type}`;
24
+ if (c.is_nullable === 'NO')
25
+ line += ' NOT NULL';
26
+ if (c.column_default)
27
+ line += ` DEFAULT ${c.column_default}`;
28
+ return line;
29
+ });
30
+ sql += colDefs.join(',\n');
31
+ sql += '\n);';
32
+ if (rlsEnabled) {
33
+ sql += `\nALTER TABLE public."${tableName}" ENABLE ROW LEVEL SECURITY;`;
34
+ }
35
+ tables[tableName] = sql;
36
+ }
37
+ return tables;
38
+ }
39
+ async function extractFunctions(projectRef) {
40
+ const funcs = await runQuery(projectRef, Queries.LIST_FUNCTIONS);
41
+ if (!Array.isArray(funcs))
42
+ return {};
43
+ const result = {};
44
+ for (const f of funcs) {
45
+ result[f.name] = f.definition;
46
+ }
47
+ return result;
48
+ }
49
+ async function extractViews(projectRef) {
50
+ const views = await runQuery(projectRef, Queries.LIST_VIEWS);
51
+ if (!Array.isArray(views))
52
+ return {};
53
+ const result = {};
54
+ for (const v of views) {
55
+ result[v.name] = `CREATE OR REPLACE VIEW public."${v.name}" AS\n${v.definition}`;
56
+ }
57
+ return result;
58
+ }
59
+ async function extractTriggers(projectRef) {
60
+ const triggers = await runQuery(projectRef, Queries.LIST_TRIGGERS);
61
+ if (!Array.isArray(triggers))
62
+ return {};
63
+ const result = {};
64
+ for (const t of triggers) {
65
+ result[t.name] = t.definition;
66
+ }
67
+ return result;
68
+ }
69
+ async function extractPolicies(projectRef) {
70
+ const policies = await runQuery(projectRef, Queries.LIST_POLICIES);
71
+ if (!Array.isArray(policies))
72
+ return {};
73
+ const result = {};
74
+ for (const p of policies) {
75
+ let sql = `CREATE POLICY "${p.name}"\nON public."${p.tablename}"\nFOR ${p.cmd}`;
76
+ // Handle roles
77
+ let roles = p.roles;
78
+ if (Array.isArray(roles)) {
79
+ roles = roles.join(', ');
80
+ }
81
+ else if (typeof roles === 'string' && roles.startsWith('{')) {
82
+ roles = roles.replace(/^\{|\}$/g, '').split(',').join(', ');
83
+ }
84
+ if (roles) {
85
+ sql += `\nTO ${roles}`;
86
+ }
87
+ if (p.qual)
88
+ sql += `\nUSING (${p.qual})`;
89
+ if (p.with_check)
90
+ sql += `\nWITH CHECK (${p.with_check})`;
91
+ sql += ';';
92
+ result[`${p.tablename}_${p.name}`] = sql;
93
+ }
94
+ return result;
95
+ }
96
+ async function extractTypes(projectRef) {
97
+ const types = await runQuery(projectRef, Queries.LIST_TYPES);
98
+ if (!Array.isArray(types))
99
+ return {};
100
+ const result = {};
101
+ // Group by type name since list returns rows of (name, label) for enums
102
+ const grouped = {};
103
+ for (const t of types) {
104
+ if (!grouped[t.name])
105
+ grouped[t.name] = [];
106
+ grouped[t.name].push(t.label);
107
+ }
108
+ for (const [name, labels] of Object.entries(grouped)) {
109
+ const labelsStr = labels.map(l => `'${l}'`).join(', ');
110
+ result[name] = `CREATE TYPE public."${name}" AS ENUM (${labelsStr});`;
111
+ }
112
+ return result;
113
+ }
@@ -0,0 +1,47 @@
1
+ export const LIST_TABLES = `
2
+ SELECT c.relname as table_name, c.relrowsecurity as rls_enabled
3
+ FROM pg_class c
4
+ JOIN pg_namespace n ON n.oid = c.relnamespace
5
+ WHERE n.nspname = 'public'
6
+ AND c.relkind = 'r'
7
+ ORDER BY c.relname;
8
+ `;
9
+ export const GET_COLUMNS = (tableName) => `
10
+ SELECT column_name, data_type, is_nullable, column_default
11
+ FROM information_schema.columns
12
+ WHERE table_schema = 'public' AND table_name = '${tableName}'
13
+ ORDER BY ordinal_position;
14
+ `;
15
+ export const LIST_FUNCTIONS = `
16
+ SELECT proname as name, pg_get_functiondef(oid) as definition
17
+ FROM pg_proc
18
+ WHERE pronamespace = 'public'::regnamespace
19
+ ORDER BY proname;
20
+ `;
21
+ export const LIST_VIEWS = `
22
+ SELECT table_name as name, view_definition as definition
23
+ FROM information_schema.views
24
+ WHERE table_schema = 'public'
25
+ ORDER BY table_name;
26
+ `;
27
+ export const LIST_TRIGGERS = `
28
+ SELECT tgname as name, pg_get_triggerdef(oid) as definition
29
+ FROM pg_trigger
30
+ WHERE tgrelid IN (SELECT oid FROM pg_class WHERE relnamespace = 'public'::regnamespace)
31
+ AND tgisinternal = false
32
+ ORDER BY tgname;
33
+ `;
34
+ export const LIST_POLICIES = `
35
+ SELECT policyname as name, tablename, cmd, roles, qual, with_check
36
+ FROM pg_policies
37
+ WHERE schemaname = 'public'
38
+ ORDER BY tablename, policyname;
39
+ `;
40
+ export const LIST_TYPES = `
41
+ SELECT t.typname as name, e.enumlabel as label
42
+ FROM pg_type t
43
+ JOIN pg_enum e ON t.oid = e.enumtypid
44
+ JOIN pg_namespace n ON n.oid = t.typnamespace
45
+ WHERE n.nspname = 'public'
46
+ ORDER BY t.typname, e.enumsortorder;
47
+ `;
@@ -0,0 +1,15 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ const GITBASE_DIR = '.gitbase';
4
+ const OBJECTS_DIR = path.join(GITBASE_DIR, 'objects');
5
+ export async function readObject(hash) {
6
+ return fs.readFile(path.join(OBJECTS_DIR, hash), 'utf-8');
7
+ }
8
+ export async function readTree(treeHash) {
9
+ const content = await readObject(treeHash);
10
+ return JSON.parse(content);
11
+ }
12
+ export async function readCommit(commitHash) {
13
+ const content = await readObject(commitHash);
14
+ return JSON.parse(content);
15
+ }
@@ -0,0 +1,21 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ const CONFIG_DIR = path.join(os.homedir(), '.config', 'gitbase');
5
+ const TOKEN_FILE = path.join(CONFIG_DIR, 'token.json');
6
+ export async function ensureConfigDir() {
7
+ await fs.mkdir(CONFIG_DIR, { recursive: true });
8
+ }
9
+ export async function saveToken(token) {
10
+ await ensureConfigDir();
11
+ await fs.writeFile(TOKEN_FILE, JSON.stringify({ token }), 'utf-8');
12
+ }
13
+ export async function getToken() {
14
+ try {
15
+ const data = await fs.readFile(TOKEN_FILE, 'utf-8');
16
+ return JSON.parse(data).token;
17
+ }
18
+ catch (error) {
19
+ return null;
20
+ }
21
+ }
@@ -0,0 +1,9 @@
1
+ import crypto from 'crypto';
2
+ export function hashString(content) {
3
+ return crypto.createHash('sha1').update(content).digest('hex');
4
+ }
5
+ export function canonicalize(sql) {
6
+ // Basic normalization: trim
7
+ // In a real implementation, we would parse the SQL and reprint it deterministically.
8
+ return sql.trim();
9
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@coldge.com/gitbase",
3
+ "version": "1.0.2",
4
+ "description": "The Time Machine for Supabase",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "gitb": "dist/index.js",
9
+ "gitbase": "dist/index.js"
10
+ },
11
+ "files": [
12
+ "dist/",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "start": "node --loader ts-node/esm src/index.ts",
19
+ "test": "echo \"Error: no test specified\" && exit 1"
20
+ },
21
+ "keywords": [
22
+ "supabase",
23
+ "git",
24
+ "version-control",
25
+ "cli"
26
+ ],
27
+ "author": "Coldge",
28
+ "license": "ISC",
29
+ "dependencies": {
30
+ "axios": "^1.13.5",
31
+ "chalk": "^5.6.2",
32
+ "diff": "^8.0.3",
33
+ "dotenv": "^17.3.1",
34
+ "pg": "^8.19.0",
35
+ "toposort": "^2.0.2",
36
+ "yargs": "^17.0.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/diff": "^7.0.2",
40
+ "@types/node": "^25.3.2",
41
+ "@types/pg": "^8.16.0",
42
+ "@types/toposort": "^2.0.7",
43
+ "@types/yargs": "^17.0.35",
44
+ "ts-node": "^10.9.2",
45
+ "typescript": "^5.9.3"
46
+ }
47
+ }