openbase-js 0.1.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 (4) hide show
  1. package/index.d.ts +28 -0
  2. package/index.js +148 -0
  3. package/package.json +16 -0
  4. package/test.js +46 -0
package/index.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ export interface OpenbaseResponse<T> {
2
+ data: T | null;
3
+ error: string | null;
4
+ }
5
+
6
+ export declare class QueryBuilder<T = Record<string, unknown>> {
7
+ select(columns?: string): this;
8
+ eq(column: string, value: unknown): this;
9
+ limit(n: number): this;
10
+ single(): this;
11
+ insert(data: Partial<T>): this;
12
+ update(data: Partial<T>): this;
13
+ delete(): this;
14
+ then<R>(
15
+ resolve: (value: OpenbaseResponse<T[]>) => R,
16
+ reject?: (reason: unknown) => R
17
+ ): Promise<R>;
18
+ }
19
+
20
+ export declare class OpenbaseClient {
21
+ from<T = Record<string, unknown>>(table: string): QueryBuilder<T>;
22
+ }
23
+
24
+ export declare function createClient(
25
+ baseUrl: string,
26
+ apiKey: string,
27
+ dbName: string
28
+ ): OpenbaseClient;
package/index.js ADDED
@@ -0,0 +1,148 @@
1
+ const axios = require('axios');
2
+
3
+ // ─── Query Builder ────────────────────────────────────────────────────────────
4
+
5
+ class QueryBuilder {
6
+ constructor(baseUrl, apiKey, dbName, table) {
7
+ this._baseUrl = baseUrl;
8
+ this._apiKey = apiKey;
9
+ this._dbName = dbName;
10
+ this._table = table;
11
+ this._filters = {};
12
+ this._columns = '*';
13
+ this._limitVal = null;
14
+ this._single = false;
15
+ this._operation = 'select';
16
+ this._insertData = null;
17
+ this._updateData = null;
18
+ }
19
+
20
+ // ─── Read ──────────────────────────────────────────────────────────────────
21
+
22
+ select(columns = '*') {
23
+ this._columns = columns;
24
+ this._operation = 'select';
25
+ return this;
26
+ }
27
+
28
+ eq(column, value) {
29
+ this._filters[column] = value;
30
+ return this;
31
+ }
32
+
33
+ limit(n) {
34
+ this._limitVal = n;
35
+ return this;
36
+ }
37
+
38
+ single() {
39
+ this._single = true;
40
+ this._limitVal = 1;
41
+ return this;
42
+ }
43
+
44
+ // ─── Write ─────────────────────────────────────────────────────────────────
45
+
46
+ insert(data) {
47
+ this._operation = 'insert';
48
+ this._insertData = data;
49
+ return this;
50
+ }
51
+
52
+ update(data) {
53
+ this._operation = 'update';
54
+ this._updateData = data;
55
+ return this;
56
+ }
57
+
58
+ delete() {
59
+ this._operation = 'delete';
60
+ return this;
61
+ }
62
+
63
+ // ─── Execute ───────────────────────────────────────────────────────────────
64
+
65
+ async _buildAndRun() {
66
+ let sql = '';
67
+
68
+ if (this._operation === 'select') {
69
+ sql = `SELECT ${this._columns} FROM "${this._table}"`;
70
+ const whereParts = Object.entries(this._filters).map(([col, val]) =>
71
+ typeof val === 'string' ? `"${col}" = '${val}'` : `"${col}" = ${val}`
72
+ );
73
+ if (whereParts.length) sql += ` WHERE ${whereParts.join(' AND ')}`;
74
+ if (this._limitVal) sql += ` LIMIT ${this._limitVal}`;
75
+
76
+ } else if (this._operation === 'insert') {
77
+ const cols = Object.keys(this._insertData).map(c => `"${c}"`).join(', ');
78
+ const vals = Object.values(this._insertData).map(v =>
79
+ typeof v === 'string' ? `'${v}'` : v
80
+ ).join(', ');
81
+ sql = `INSERT INTO "${this._table}" (${cols}) VALUES (${vals}) RETURNING *`;
82
+
83
+ } else if (this._operation === 'update') {
84
+ const setClauses = Object.entries(this._updateData).map(([col, val]) =>
85
+ typeof val === 'string' ? `"${col}" = '${val}'` : `"${col}" = ${val}`
86
+ ).join(', ');
87
+ const whereParts = Object.entries(this._filters).map(([col, val]) =>
88
+ typeof val === 'string' ? `"${col}" = '${val}'` : `"${col}" = ${val}`
89
+ );
90
+ sql = `UPDATE "${this._table}" SET ${setClauses}`;
91
+ if (whereParts.length) sql += ` WHERE ${whereParts.join(' AND ')}`;
92
+ sql += ` RETURNING *`;
93
+
94
+ } else if (this._operation === 'delete') {
95
+ const whereParts = Object.entries(this._filters).map(([col, val]) =>
96
+ typeof val === 'string' ? `"${col}" = '${val}'` : `"${col}" = ${val}`
97
+ );
98
+ sql = `DELETE FROM "${this._table}"`;
99
+ if (whereParts.length) sql += ` WHERE ${whereParts.join(' AND ')}`;
100
+ sql += ` RETURNING *`;
101
+ }
102
+
103
+ try {
104
+ const res = await axios.post(
105
+ `${this._baseUrl}/query`,
106
+ { sql, db_name: this._dbName },
107
+ { headers: { Authorization: `Bearer ${this._apiKey}` } }
108
+ );
109
+
110
+ const { data, error } = res.data;
111
+ if (error) return { data: null, error };
112
+
113
+ if (this._single) {
114
+ return { data: data[0] || null, error: null };
115
+ }
116
+
117
+ return { data, error: null };
118
+ } catch (err) {
119
+ return { data: null, error: err.message };
120
+ }
121
+ }
122
+
123
+ then(resolve, reject) {
124
+ return this._buildAndRun().then(resolve, reject);
125
+ }
126
+ }
127
+
128
+ // ─── Client ───────────────────────────────────────────────────────────────────
129
+
130
+ class OpenbaseClient {
131
+ constructor(baseUrl, apiKey, dbName) {
132
+ this._baseUrl = baseUrl.replace(/\/$/, '');
133
+ this._apiKey = apiKey;
134
+ this._dbName = dbName;
135
+ }
136
+
137
+ from(table) {
138
+ return new QueryBuilder(this._baseUrl, this._apiKey, this._dbName, table);
139
+ }
140
+ }
141
+
142
+ // ─── Factory ──────────────────────────────────────────────────────────────────
143
+
144
+ function createClient(baseUrl, apiKey, dbName) {
145
+ return new OpenbaseClient(baseUrl, apiKey, dbName);
146
+ }
147
+
148
+ module.exports = { createClient };
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "openbase-js",
3
+ "version": "0.1.0",
4
+ "description": "JavaScript client for Openbase — a self-hosted Supabase alternative",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "scripts": {
8
+ "test": "node test.js"
9
+ },
10
+ "keywords": ["openbase", "supabase", "postgres", "database"],
11
+ "author": "Omkar Patil",
12
+ "license": "MIT",
13
+ "dependencies": {
14
+ "axios": "^1.0.0"
15
+ }
16
+ }
package/test.js ADDED
@@ -0,0 +1,46 @@
1
+ const { createClient } = require('./index');
2
+
3
+ const client = createClient(
4
+ 'http://localhost:3002',
5
+ 'Yur7U6O5DHzC7Sjtyx_tI7EYM6lpMBO_I9vWWrN4YdQ', // replace with your actual anon key
6
+ 'mydb'
7
+ );
8
+
9
+ async function test() {
10
+ console.log('\n── SELECT all ──────────────────────');
11
+ const { data, error } = await client.from('leads').select('*');
12
+ console.log('data:', data);
13
+ console.log('error:', error);
14
+
15
+ console.log('\n── SELECT with .eq ─────────────────');
16
+ const { data: single } = await client
17
+ .from('leads')
18
+ .select('company_name, slug')
19
+ .eq('slug', 'google')
20
+ .single();
21
+ console.log('single:', single);
22
+
23
+ console.log('\n── INSERT ──────────────────────────');
24
+ const { data: inserted } = await client.from('leads').insert({
25
+ company_name: 'Test Co',
26
+ slug: 'test-co',
27
+ custom_pitch: 'Hello from SDK',
28
+ });
29
+ console.log('inserted:', inserted);
30
+
31
+ console.log('\n── UPDATE ──────────────────────────');
32
+ const { data: updated } = await client
33
+ .from('leads')
34
+ .update({ custom_pitch: 'Updated via SDK' })
35
+ .eq('slug', 'test-co');
36
+ console.log('updated:', updated);
37
+
38
+ console.log('\n── DELETE ──────────────────────────');
39
+ const { data: deleted } = await client
40
+ .from('leads')
41
+ .delete()
42
+ .eq('slug', 'test-co');
43
+ console.log('deleted:', deleted);
44
+ }
45
+
46
+ test().catch(console.error);