openbase-js 0.1.1 → 0.1.3
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/index.cjs +156 -0
- package/index.js +30 -24
- package/package.json +1 -1
package/index.cjs
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// ─── Query Builder ────────────────────────────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
class QueryBuilder {
|
|
4
|
+
constructor(baseUrl, apiKey, dbName, table) {
|
|
5
|
+
this._baseUrl = baseUrl;
|
|
6
|
+
this._apiKey = apiKey;
|
|
7
|
+
this._dbName = dbName;
|
|
8
|
+
this._table = table;
|
|
9
|
+
this._filters = {};
|
|
10
|
+
this._columns = '*';
|
|
11
|
+
this._limitVal = null;
|
|
12
|
+
this._single = false;
|
|
13
|
+
this._operation = 'select';
|
|
14
|
+
this._insertData = null;
|
|
15
|
+
this._updateData = null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
select(columns = '*') {
|
|
19
|
+
this._columns = columns;
|
|
20
|
+
this._operation = 'select';
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
eq(column, value) {
|
|
25
|
+
this._filters[column] = value;
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
limit(n) {
|
|
30
|
+
this._limitVal = n;
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
single() {
|
|
35
|
+
this._single = true;
|
|
36
|
+
this._limitVal = 1;
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
insert(data) {
|
|
41
|
+
this._operation = 'insert';
|
|
42
|
+
this._insertData = data;
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
update(data) {
|
|
47
|
+
this._operation = 'update';
|
|
48
|
+
this._updateData = data;
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
delete() {
|
|
53
|
+
this._operation = 'delete';
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
_buildSQL() {
|
|
58
|
+
let sql = '';
|
|
59
|
+
|
|
60
|
+
if (this._operation === 'select') {
|
|
61
|
+
sql = `SELECT ${this._columns} FROM "${this._table}"`;
|
|
62
|
+
const whereParts = Object.entries(this._filters).map(([col, val]) =>
|
|
63
|
+
typeof val === 'string' ? `"${col}" = '${val}'` : `"${col}" = ${val}`
|
|
64
|
+
);
|
|
65
|
+
if (whereParts.length) sql += ` WHERE ${whereParts.join(' AND ')}`;
|
|
66
|
+
if (this._limitVal) sql += ` LIMIT ${this._limitVal}`;
|
|
67
|
+
|
|
68
|
+
} else if (this._operation === 'insert') {
|
|
69
|
+
const cols = Object.keys(this._insertData).map(c => `"${c}"`).join(', ');
|
|
70
|
+
const vals = Object.values(this._insertData).map(v =>
|
|
71
|
+
typeof v === 'string' ? `'${v.replace(/'/g, "''")}'` : v
|
|
72
|
+
).join(', ');
|
|
73
|
+
sql = `INSERT INTO "${this._table}" (${cols}) VALUES (${vals}) RETURNING *`;
|
|
74
|
+
|
|
75
|
+
} else if (this._operation === 'update') {
|
|
76
|
+
const setClauses = Object.entries(this._updateData).map(([col, val]) =>
|
|
77
|
+
typeof val === 'string' ? `"${col}" = '${val.replace(/'/g, "''")}'` : `"${col}" = ${val}`
|
|
78
|
+
).join(', ');
|
|
79
|
+
const whereParts = Object.entries(this._filters).map(([col, val]) =>
|
|
80
|
+
typeof val === 'string' ? `"${col}" = '${val}'` : `"${col}" = ${val}`
|
|
81
|
+
);
|
|
82
|
+
sql = `UPDATE "${this._table}" SET ${setClauses}`;
|
|
83
|
+
if (whereParts.length) sql += ` WHERE ${whereParts.join(' AND ')}`;
|
|
84
|
+
sql += ` RETURNING *`;
|
|
85
|
+
|
|
86
|
+
} else if (this._operation === 'delete') {
|
|
87
|
+
const whereParts = Object.entries(this._filters).map(([col, val]) =>
|
|
88
|
+
typeof val === 'string' ? `"${col}" = '${val}'` : `"${col}" = ${val}`
|
|
89
|
+
);
|
|
90
|
+
sql = `DELETE FROM "${this._table}"`;
|
|
91
|
+
if (whereParts.length) sql += ` WHERE ${whereParts.join(' AND ')}`;
|
|
92
|
+
sql += ` RETURNING *`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return sql;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async _buildAndRun() {
|
|
99
|
+
const sql = this._buildSQL();
|
|
100
|
+
try {
|
|
101
|
+
const res = await fetch(`${this._baseUrl}/query`, {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
headers: {
|
|
104
|
+
'Content-Type': 'application/json',
|
|
105
|
+
'Authorization': `Bearer ${this._apiKey}`,
|
|
106
|
+
},
|
|
107
|
+
body: JSON.stringify({ sql, db_name: this._dbName }),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const json = await res.json();
|
|
111
|
+
const { data, error } = json;
|
|
112
|
+
if (error) return { data: null, error };
|
|
113
|
+
if (this._single) return { data: data[0] || null, error: null };
|
|
114
|
+
return { data, error: null };
|
|
115
|
+
} catch (err) {
|
|
116
|
+
return { data: null, error: err.message };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
then(resolve, reject) {
|
|
121
|
+
return this._buildAndRun().then(resolve, reject);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ─── Client ───────────────────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
class OpenbaseClient {
|
|
128
|
+
constructor(baseUrl, apiKey, dbName) {
|
|
129
|
+
this._baseUrl = baseUrl.replace(/\/$/, '');
|
|
130
|
+
this._apiKey = apiKey;
|
|
131
|
+
this._dbName = dbName;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
from(table) {
|
|
135
|
+
return new QueryBuilder(this._baseUrl, this._apiKey, this._dbName, table);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ─── Factory ──────────────────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
function createClient(baseUrl, apiKey, dbName) {
|
|
142
|
+
if (!baseUrl) throw new Error('[openbase] Missing baseUrl. Pass your backend URL as the first argument.');
|
|
143
|
+
if (!apiKey) throw new Error('[openbase] Missing apiKey. Pass your anon or service key as the second argument.');
|
|
144
|
+
if (!dbName) throw new Error('[openbase] Missing dbName. Pass your database name as the third argument.');
|
|
145
|
+
return new OpenbaseClient(baseUrl, apiKey, dbName);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (typeof module !== 'undefined') {
|
|
149
|
+
module.exports = { createClient };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (typeof window !== 'undefined') {
|
|
153
|
+
window.openbase = { createClient };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = { createClient };
|
package/index.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const axios = require('axios').default || require('axios');
|
|
2
|
-
|
|
3
1
|
// ─── Query Builder ────────────────────────────────────────────────────────────
|
|
4
2
|
|
|
5
3
|
class QueryBuilder {
|
|
@@ -17,8 +15,6 @@ class QueryBuilder {
|
|
|
17
15
|
this._updateData = null;
|
|
18
16
|
}
|
|
19
17
|
|
|
20
|
-
// ─── Read ──────────────────────────────────────────────────────────────────
|
|
21
|
-
|
|
22
18
|
select(columns = '*') {
|
|
23
19
|
this._columns = columns;
|
|
24
20
|
this._operation = 'select';
|
|
@@ -41,8 +37,6 @@ class QueryBuilder {
|
|
|
41
37
|
return this;
|
|
42
38
|
}
|
|
43
39
|
|
|
44
|
-
// ─── Write ─────────────────────────────────────────────────────────────────
|
|
45
|
-
|
|
46
40
|
insert(data) {
|
|
47
41
|
this._operation = 'insert';
|
|
48
42
|
this._insertData = data;
|
|
@@ -60,9 +54,7 @@ class QueryBuilder {
|
|
|
60
54
|
return this;
|
|
61
55
|
}
|
|
62
56
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
async _buildAndRun() {
|
|
57
|
+
_buildSQL() {
|
|
66
58
|
let sql = '';
|
|
67
59
|
|
|
68
60
|
if (this._operation === 'select') {
|
|
@@ -76,13 +68,13 @@ class QueryBuilder {
|
|
|
76
68
|
} else if (this._operation === 'insert') {
|
|
77
69
|
const cols = Object.keys(this._insertData).map(c => `"${c}"`).join(', ');
|
|
78
70
|
const vals = Object.values(this._insertData).map(v =>
|
|
79
|
-
typeof v === 'string' ? `'${v}'` : v
|
|
71
|
+
typeof v === 'string' ? `'${v.replace(/'/g, "''")}'` : v
|
|
80
72
|
).join(', ');
|
|
81
73
|
sql = `INSERT INTO "${this._table}" (${cols}) VALUES (${vals}) RETURNING *`;
|
|
82
74
|
|
|
83
75
|
} else if (this._operation === 'update') {
|
|
84
76
|
const setClauses = Object.entries(this._updateData).map(([col, val]) =>
|
|
85
|
-
typeof val === 'string' ? `"${col}" = '${val}'` : `"${col}" = ${val}`
|
|
77
|
+
typeof val === 'string' ? `"${col}" = '${val.replace(/'/g, "''")}'` : `"${col}" = ${val}`
|
|
86
78
|
).join(', ');
|
|
87
79
|
const whereParts = Object.entries(this._filters).map(([col, val]) =>
|
|
88
80
|
typeof val === 'string' ? `"${col}" = '${val}'` : `"${col}" = ${val}`
|
|
@@ -100,20 +92,25 @@ class QueryBuilder {
|
|
|
100
92
|
sql += ` RETURNING *`;
|
|
101
93
|
}
|
|
102
94
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
`${this._baseUrl}/query`,
|
|
106
|
-
{ sql, db_name: this._dbName },
|
|
107
|
-
{ headers: { Authorization: `Bearer ${this._apiKey}` } }
|
|
108
|
-
);
|
|
95
|
+
return sql;
|
|
96
|
+
}
|
|
109
97
|
|
|
110
|
-
|
|
98
|
+
async _buildAndRun() {
|
|
99
|
+
const sql = this._buildSQL();
|
|
100
|
+
try {
|
|
101
|
+
const res = await fetch(`${this._baseUrl}/query`, {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
headers: {
|
|
104
|
+
'Content-Type': 'application/json',
|
|
105
|
+
'Authorization': `Bearer ${this._apiKey}`,
|
|
106
|
+
},
|
|
107
|
+
body: JSON.stringify({ sql, db_name: this._dbName }),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const json = await res.json();
|
|
111
|
+
const { data, error } = json;
|
|
111
112
|
if (error) return { data: null, error };
|
|
112
|
-
|
|
113
|
-
if (this._single) {
|
|
114
|
-
return { data: data[0] || null, error: null };
|
|
115
|
-
}
|
|
116
|
-
|
|
113
|
+
if (this._single) return { data: data[0] || null, error: null };
|
|
117
114
|
return { data, error: null };
|
|
118
115
|
} catch (err) {
|
|
119
116
|
return { data: null, error: err.message };
|
|
@@ -142,7 +139,16 @@ class OpenbaseClient {
|
|
|
142
139
|
// ─── Factory ──────────────────────────────────────────────────────────────────
|
|
143
140
|
|
|
144
141
|
function createClient(baseUrl, apiKey, dbName) {
|
|
142
|
+
if (!baseUrl) throw new Error('[openbase] Missing baseUrl. Pass your backend URL as the first argument.');
|
|
143
|
+
if (!apiKey) throw new Error('[openbase] Missing apiKey. Pass your anon or service key as the second argument.');
|
|
144
|
+
if (!dbName) throw new Error('[openbase] Missing dbName. Pass your database name as the third argument.');
|
|
145
145
|
return new OpenbaseClient(baseUrl, apiKey, dbName);
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
module
|
|
148
|
+
if (typeof module !== 'undefined') {
|
|
149
|
+
module.exports = { createClient };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (typeof window !== 'undefined') {
|
|
153
|
+
window.openbase = { createClient };
|
|
154
|
+
}
|