openbase-js 0.1.3 → 0.1.4
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 +110 -15
- package/index.d.ts +10 -0
- package/index.js +110 -14
- package/package.json +1 -1
package/index.cjs
CHANGED
|
@@ -6,9 +6,12 @@ class QueryBuilder {
|
|
|
6
6
|
this._apiKey = apiKey;
|
|
7
7
|
this._dbName = dbName;
|
|
8
8
|
this._table = table;
|
|
9
|
-
this._filters = {}
|
|
9
|
+
this._filters = []; // changed: was {} now array of {col, op, val}
|
|
10
10
|
this._columns = '*';
|
|
11
11
|
this._limitVal = null;
|
|
12
|
+
this._offsetVal = null; // new
|
|
13
|
+
this._orderCol = null; // new
|
|
14
|
+
this._orderAsc = true; // new
|
|
12
15
|
this._single = false;
|
|
13
16
|
this._operation = 'select';
|
|
14
17
|
this._insertData = null;
|
|
@@ -21,8 +24,59 @@ class QueryBuilder {
|
|
|
21
24
|
return this;
|
|
22
25
|
}
|
|
23
26
|
|
|
27
|
+
// ─── Filter operators ──────────────────────────────────────────────────────
|
|
28
|
+
|
|
24
29
|
eq(column, value) {
|
|
25
|
-
this._filters
|
|
30
|
+
this._filters.push({ col: column, op: 'eq', val: value });
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
gt(column, value) {
|
|
35
|
+
this._filters.push({ col: column, op: 'gt', val: value });
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
lt(column, value) {
|
|
40
|
+
this._filters.push({ col: column, op: 'lt', val: value });
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
gte(column, value) {
|
|
45
|
+
this._filters.push({ col: column, op: 'gte', val: value });
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
lte(column, value) {
|
|
50
|
+
this._filters.push({ col: column, op: 'lte', val: value });
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
like(column, pattern) {
|
|
55
|
+
this._filters.push({ col: column, op: 'like', val: pattern });
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
ilike(column, pattern) {
|
|
60
|
+
this._filters.push({ col: column, op: 'ilike', val: pattern });
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
in(column, values) {
|
|
65
|
+
this._filters.push({ col: column, op: 'in', val: values });
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
is(column, value) {
|
|
70
|
+
// value should be null, true, or false
|
|
71
|
+
this._filters.push({ col: column, op: 'is', val: value });
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─── Modifiers ─────────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
order(column, { ascending = true } = {}) {
|
|
78
|
+
this._orderCol = column;
|
|
79
|
+
this._orderAsc = ascending;
|
|
26
80
|
return this;
|
|
27
81
|
}
|
|
28
82
|
|
|
@@ -31,12 +85,21 @@ class QueryBuilder {
|
|
|
31
85
|
return this;
|
|
32
86
|
}
|
|
33
87
|
|
|
88
|
+
range(from, to) {
|
|
89
|
+
// e.g. range(0, 9) => LIMIT 10 OFFSET 0
|
|
90
|
+
this._limitVal = to - from + 1;
|
|
91
|
+
this._offsetVal = from;
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
|
|
34
95
|
single() {
|
|
35
96
|
this._single = true;
|
|
36
97
|
this._limitVal = 1;
|
|
37
98
|
return this;
|
|
38
99
|
}
|
|
39
100
|
|
|
101
|
+
// ─── Mutation operators ────────────────────────────────────────────────────
|
|
102
|
+
|
|
40
103
|
insert(data) {
|
|
41
104
|
this._operation = 'insert';
|
|
42
105
|
this._insertData = data;
|
|
@@ -54,16 +117,47 @@ class QueryBuilder {
|
|
|
54
117
|
return this;
|
|
55
118
|
}
|
|
56
119
|
|
|
120
|
+
// ─── SQL Builder ───────────────────────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
_filterToSQL(f) {
|
|
123
|
+
const col = `"${f.col}"`;
|
|
124
|
+
switch (f.op) {
|
|
125
|
+
case 'eq': return typeof f.val === 'string' ? `${col} = '${f.val}'` : `${col} = ${f.val}`;
|
|
126
|
+
case 'gt': return `${col} > ${f.val}`;
|
|
127
|
+
case 'lt': return `${col} < ${f.val}`;
|
|
128
|
+
case 'gte': return `${col} >= ${f.val}`;
|
|
129
|
+
case 'lte': return `${col} <= ${f.val}`;
|
|
130
|
+
case 'like': return `${col} LIKE '${f.val}'`;
|
|
131
|
+
case 'ilike': return `${col} ILIKE '${f.val}'`;
|
|
132
|
+
case 'in': {
|
|
133
|
+
const list = f.val.map(v => typeof v === 'string' ? `'${v}'` : v).join(', ');
|
|
134
|
+
return `${col} IN (${list})`;
|
|
135
|
+
}
|
|
136
|
+
case 'is': {
|
|
137
|
+
if (f.val === null) return `${col} IS NULL`;
|
|
138
|
+
if (f.val === true) return `${col} IS TRUE`;
|
|
139
|
+
if (f.val === false) return `${col} IS FALSE`;
|
|
140
|
+
return `${col} IS NULL`;
|
|
141
|
+
}
|
|
142
|
+
default: return `${col} = '${f.val}'`;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
57
146
|
_buildSQL() {
|
|
58
147
|
let sql = '';
|
|
59
148
|
|
|
60
149
|
if (this._operation === 'select') {
|
|
61
150
|
sql = `SELECT ${this._columns} FROM "${this._table}"`;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
151
|
+
|
|
152
|
+
if (this._filters.length) {
|
|
153
|
+
const where = this._filters.map(f => this._filterToSQL(f)).join(' AND ');
|
|
154
|
+
sql += ` WHERE ${where}`;
|
|
155
|
+
}
|
|
156
|
+
if (this._orderCol) {
|
|
157
|
+
sql += ` ORDER BY "${this._orderCol}" ${this._orderAsc ? 'ASC' : 'DESC'}`;
|
|
158
|
+
}
|
|
66
159
|
if (this._limitVal) sql += ` LIMIT ${this._limitVal}`;
|
|
160
|
+
if (this._offsetVal) sql += ` OFFSET ${this._offsetVal}`;
|
|
67
161
|
|
|
68
162
|
} else if (this._operation === 'insert') {
|
|
69
163
|
const cols = Object.keys(this._insertData).map(c => `"${c}"`).join(', ');
|
|
@@ -76,25 +170,27 @@ class QueryBuilder {
|
|
|
76
170
|
const setClauses = Object.entries(this._updateData).map(([col, val]) =>
|
|
77
171
|
typeof val === 'string' ? `"${col}" = '${val.replace(/'/g, "''")}'` : `"${col}" = ${val}`
|
|
78
172
|
).join(', ');
|
|
79
|
-
const whereParts = Object.entries(this._filters).map(([col, val]) =>
|
|
80
|
-
typeof val === 'string' ? `"${col}" = '${val}'` : `"${col}" = ${val}`
|
|
81
|
-
);
|
|
82
173
|
sql = `UPDATE "${this._table}" SET ${setClauses}`;
|
|
83
|
-
if (
|
|
174
|
+
if (this._filters.length) {
|
|
175
|
+
const where = this._filters.map(f => this._filterToSQL(f)).join(' AND ');
|
|
176
|
+
sql += ` WHERE ${where}`;
|
|
177
|
+
}
|
|
84
178
|
sql += ` RETURNING *`;
|
|
85
179
|
|
|
86
180
|
} 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
181
|
sql = `DELETE FROM "${this._table}"`;
|
|
91
|
-
if (
|
|
182
|
+
if (this._filters.length) {
|
|
183
|
+
const where = this._filters.map(f => this._filterToSQL(f)).join(' AND ');
|
|
184
|
+
sql += ` WHERE ${where}`;
|
|
185
|
+
}
|
|
92
186
|
sql += ` RETURNING *`;
|
|
93
187
|
}
|
|
94
188
|
|
|
95
189
|
return sql;
|
|
96
190
|
}
|
|
97
191
|
|
|
192
|
+
// ─── Executor ──────────────────────────────────────────────────────────────
|
|
193
|
+
|
|
98
194
|
async _buildAndRun() {
|
|
99
195
|
const sql = this._buildSQL();
|
|
100
196
|
try {
|
|
@@ -152,5 +248,4 @@ if (typeof module !== 'undefined') {
|
|
|
152
248
|
if (typeof window !== 'undefined') {
|
|
153
249
|
window.openbase = { createClient };
|
|
154
250
|
}
|
|
155
|
-
|
|
156
251
|
module.exports = { createClient };
|
package/index.d.ts
CHANGED
|
@@ -6,6 +6,16 @@ export interface OpenbaseResponse<T> {
|
|
|
6
6
|
export declare class QueryBuilder<T = Record<string, unknown>> {
|
|
7
7
|
select(columns?: string): this;
|
|
8
8
|
eq(column: string, value: unknown): this;
|
|
9
|
+
gt(column: string, value: number): this;
|
|
10
|
+
lt(column: string, value: number): this;
|
|
11
|
+
gte(column: string, value: number): this;
|
|
12
|
+
lte(column: string, value: number): this;
|
|
13
|
+
like(column: string, pattern: string): this;
|
|
14
|
+
ilike(column: string, pattern: string): this;
|
|
15
|
+
in(column: string, values: unknown[]): this;
|
|
16
|
+
is(column: string, value: null | boolean): this;
|
|
17
|
+
order(column: string, options?: { ascending?: boolean }): this;
|
|
18
|
+
range(from: number, to: number): this;
|
|
9
19
|
limit(n: number): this;
|
|
10
20
|
single(): this;
|
|
11
21
|
insert(data: Partial<T>): this;
|
package/index.js
CHANGED
|
@@ -6,9 +6,12 @@ class QueryBuilder {
|
|
|
6
6
|
this._apiKey = apiKey;
|
|
7
7
|
this._dbName = dbName;
|
|
8
8
|
this._table = table;
|
|
9
|
-
this._filters = {}
|
|
9
|
+
this._filters = []; // changed: was {} now array of {col, op, val}
|
|
10
10
|
this._columns = '*';
|
|
11
11
|
this._limitVal = null;
|
|
12
|
+
this._offsetVal = null; // new
|
|
13
|
+
this._orderCol = null; // new
|
|
14
|
+
this._orderAsc = true; // new
|
|
12
15
|
this._single = false;
|
|
13
16
|
this._operation = 'select';
|
|
14
17
|
this._insertData = null;
|
|
@@ -21,8 +24,59 @@ class QueryBuilder {
|
|
|
21
24
|
return this;
|
|
22
25
|
}
|
|
23
26
|
|
|
27
|
+
// ─── Filter operators ──────────────────────────────────────────────────────
|
|
28
|
+
|
|
24
29
|
eq(column, value) {
|
|
25
|
-
this._filters
|
|
30
|
+
this._filters.push({ col: column, op: 'eq', val: value });
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
gt(column, value) {
|
|
35
|
+
this._filters.push({ col: column, op: 'gt', val: value });
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
lt(column, value) {
|
|
40
|
+
this._filters.push({ col: column, op: 'lt', val: value });
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
gte(column, value) {
|
|
45
|
+
this._filters.push({ col: column, op: 'gte', val: value });
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
lte(column, value) {
|
|
50
|
+
this._filters.push({ col: column, op: 'lte', val: value });
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
like(column, pattern) {
|
|
55
|
+
this._filters.push({ col: column, op: 'like', val: pattern });
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
ilike(column, pattern) {
|
|
60
|
+
this._filters.push({ col: column, op: 'ilike', val: pattern });
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
in(column, values) {
|
|
65
|
+
this._filters.push({ col: column, op: 'in', val: values });
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
is(column, value) {
|
|
70
|
+
// value should be null, true, or false
|
|
71
|
+
this._filters.push({ col: column, op: 'is', val: value });
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─── Modifiers ─────────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
order(column, { ascending = true } = {}) {
|
|
78
|
+
this._orderCol = column;
|
|
79
|
+
this._orderAsc = ascending;
|
|
26
80
|
return this;
|
|
27
81
|
}
|
|
28
82
|
|
|
@@ -31,12 +85,21 @@ class QueryBuilder {
|
|
|
31
85
|
return this;
|
|
32
86
|
}
|
|
33
87
|
|
|
88
|
+
range(from, to) {
|
|
89
|
+
// e.g. range(0, 9) => LIMIT 10 OFFSET 0
|
|
90
|
+
this._limitVal = to - from + 1;
|
|
91
|
+
this._offsetVal = from;
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
|
|
34
95
|
single() {
|
|
35
96
|
this._single = true;
|
|
36
97
|
this._limitVal = 1;
|
|
37
98
|
return this;
|
|
38
99
|
}
|
|
39
100
|
|
|
101
|
+
// ─── Mutation operators ────────────────────────────────────────────────────
|
|
102
|
+
|
|
40
103
|
insert(data) {
|
|
41
104
|
this._operation = 'insert';
|
|
42
105
|
this._insertData = data;
|
|
@@ -54,16 +117,47 @@ class QueryBuilder {
|
|
|
54
117
|
return this;
|
|
55
118
|
}
|
|
56
119
|
|
|
120
|
+
// ─── SQL Builder ───────────────────────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
_filterToSQL(f) {
|
|
123
|
+
const col = `"${f.col}"`;
|
|
124
|
+
switch (f.op) {
|
|
125
|
+
case 'eq': return typeof f.val === 'string' ? `${col} = '${f.val}'` : `${col} = ${f.val}`;
|
|
126
|
+
case 'gt': return `${col} > ${f.val}`;
|
|
127
|
+
case 'lt': return `${col} < ${f.val}`;
|
|
128
|
+
case 'gte': return `${col} >= ${f.val}`;
|
|
129
|
+
case 'lte': return `${col} <= ${f.val}`;
|
|
130
|
+
case 'like': return `${col} LIKE '${f.val}'`;
|
|
131
|
+
case 'ilike': return `${col} ILIKE '${f.val}'`;
|
|
132
|
+
case 'in': {
|
|
133
|
+
const list = f.val.map(v => typeof v === 'string' ? `'${v}'` : v).join(', ');
|
|
134
|
+
return `${col} IN (${list})`;
|
|
135
|
+
}
|
|
136
|
+
case 'is': {
|
|
137
|
+
if (f.val === null) return `${col} IS NULL`;
|
|
138
|
+
if (f.val === true) return `${col} IS TRUE`;
|
|
139
|
+
if (f.val === false) return `${col} IS FALSE`;
|
|
140
|
+
return `${col} IS NULL`;
|
|
141
|
+
}
|
|
142
|
+
default: return `${col} = '${f.val}'`;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
57
146
|
_buildSQL() {
|
|
58
147
|
let sql = '';
|
|
59
148
|
|
|
60
149
|
if (this._operation === 'select') {
|
|
61
150
|
sql = `SELECT ${this._columns} FROM "${this._table}"`;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
151
|
+
|
|
152
|
+
if (this._filters.length) {
|
|
153
|
+
const where = this._filters.map(f => this._filterToSQL(f)).join(' AND ');
|
|
154
|
+
sql += ` WHERE ${where}`;
|
|
155
|
+
}
|
|
156
|
+
if (this._orderCol) {
|
|
157
|
+
sql += ` ORDER BY "${this._orderCol}" ${this._orderAsc ? 'ASC' : 'DESC'}`;
|
|
158
|
+
}
|
|
66
159
|
if (this._limitVal) sql += ` LIMIT ${this._limitVal}`;
|
|
160
|
+
if (this._offsetVal) sql += ` OFFSET ${this._offsetVal}`;
|
|
67
161
|
|
|
68
162
|
} else if (this._operation === 'insert') {
|
|
69
163
|
const cols = Object.keys(this._insertData).map(c => `"${c}"`).join(', ');
|
|
@@ -76,25 +170,27 @@ class QueryBuilder {
|
|
|
76
170
|
const setClauses = Object.entries(this._updateData).map(([col, val]) =>
|
|
77
171
|
typeof val === 'string' ? `"${col}" = '${val.replace(/'/g, "''")}'` : `"${col}" = ${val}`
|
|
78
172
|
).join(', ');
|
|
79
|
-
const whereParts = Object.entries(this._filters).map(([col, val]) =>
|
|
80
|
-
typeof val === 'string' ? `"${col}" = '${val}'` : `"${col}" = ${val}`
|
|
81
|
-
);
|
|
82
173
|
sql = `UPDATE "${this._table}" SET ${setClauses}`;
|
|
83
|
-
if (
|
|
174
|
+
if (this._filters.length) {
|
|
175
|
+
const where = this._filters.map(f => this._filterToSQL(f)).join(' AND ');
|
|
176
|
+
sql += ` WHERE ${where}`;
|
|
177
|
+
}
|
|
84
178
|
sql += ` RETURNING *`;
|
|
85
179
|
|
|
86
180
|
} 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
181
|
sql = `DELETE FROM "${this._table}"`;
|
|
91
|
-
if (
|
|
182
|
+
if (this._filters.length) {
|
|
183
|
+
const where = this._filters.map(f => this._filterToSQL(f)).join(' AND ');
|
|
184
|
+
sql += ` WHERE ${where}`;
|
|
185
|
+
}
|
|
92
186
|
sql += ` RETURNING *`;
|
|
93
187
|
}
|
|
94
188
|
|
|
95
189
|
return sql;
|
|
96
190
|
}
|
|
97
191
|
|
|
192
|
+
// ─── Executor ──────────────────────────────────────────────────────────────
|
|
193
|
+
|
|
98
194
|
async _buildAndRun() {
|
|
99
195
|
const sql = this._buildSQL();
|
|
100
196
|
try {
|