@rwillians/qx 0.1.2 → 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/dist/cjs/bun-sqlite.d.ts +47 -0
- package/dist/cjs/bun-sqlite.js +483 -0
- package/dist/cjs/index.d.ts +1174 -0
- package/dist/cjs/index.js +666 -0
- package/dist/cjs/pretty-logger.d.ts +7 -0
- package/dist/cjs/pretty-logger.js +14 -0
- package/dist/cjs/standard-schema.d.ts +157 -0
- package/dist/cjs/standard-schema.js +236 -0
- package/dist/cjs/utils.d.ts +50 -0
- package/dist/cjs/utils.js +76 -0
- package/dist/esm/bun-sqlite.d.ts +47 -0
- package/dist/esm/bun-sqlite.js +483 -0
- package/dist/esm/index.d.ts +1174 -0
- package/dist/esm/index.js +660 -0
- package/dist/esm/pretty-logger.d.ts +7 -0
- package/dist/esm/pretty-logger.js +11 -0
- package/dist/esm/standard-schema.d.ts +157 -0
- package/dist/esm/standard-schema.js +233 -0
- package/dist/esm/utils.d.ts +50 -0
- package/dist/esm/utils.js +65 -0
- package/package.json +1 -1
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
import * as std from './standard-schema';
|
|
2
|
+
import * as u from './utils';
|
|
3
|
+
// // // // // // // // // // // // // // // // // // // // // // // //
|
|
4
|
+
// SYMBOLS //
|
|
5
|
+
// // // // // // // // // // // // // // // // // // // // // // // //
|
|
6
|
+
/**
|
|
7
|
+
* @private Symbol used to store the table name on aliased tables.
|
|
8
|
+
* @since 0.1.0
|
|
9
|
+
* @version 1
|
|
10
|
+
*/
|
|
11
|
+
const TABLE_NAME = Symbol.for('~exto.table-name');
|
|
12
|
+
/**
|
|
13
|
+
* @private Symbol used to store the table alias on aliased tables.
|
|
14
|
+
* @since 0.1.0
|
|
15
|
+
* @version 1
|
|
16
|
+
*/
|
|
17
|
+
const TABLE_ALIAS = Symbol.for('~exto.table-alias');
|
|
18
|
+
/**
|
|
19
|
+
* @private A builder for column properties.
|
|
20
|
+
* @since 0.1.0
|
|
21
|
+
* @version 1
|
|
22
|
+
*/
|
|
23
|
+
class ColumnPropsBuilder {
|
|
24
|
+
props;
|
|
25
|
+
constructor(props) {
|
|
26
|
+
this.props = props;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* @public Marks the column as autoincrementing.
|
|
30
|
+
* @since 0.1.0
|
|
31
|
+
* @version 1
|
|
32
|
+
*/
|
|
33
|
+
autoincrement() {
|
|
34
|
+
if (this.props.type !== 'INTEGER')
|
|
35
|
+
throw new Error('Autoincrement can only be set on integer columns');
|
|
36
|
+
return new ColumnPropsBuilder({ ...this.props, autoincrement: true });
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* @public Sets a default value for the column.
|
|
40
|
+
* @since 0.1.0
|
|
41
|
+
* @version 1
|
|
42
|
+
*/
|
|
43
|
+
default(value) {
|
|
44
|
+
return new ColumnPropsBuilder({ ...this.props, default: value });
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* @public Marks the column as nullable.
|
|
48
|
+
* @since 0.1.0
|
|
49
|
+
* @version 1
|
|
50
|
+
* @throws {Error} if the column is a primary key.
|
|
51
|
+
*/
|
|
52
|
+
nullable() {
|
|
53
|
+
if (this.props.primaryKey)
|
|
54
|
+
throw new Error('Cannot set nullable on a primary key column');
|
|
55
|
+
const { schema, ...props } = this.props;
|
|
56
|
+
return new ColumnPropsBuilder({ ...props, nullable: true, schema: std.nullable(schema) });
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* @public Marks the column as a primary key.
|
|
60
|
+
* @since 0.1.0
|
|
61
|
+
* @version 1
|
|
62
|
+
* @throws {Error} if the column is nullable.
|
|
63
|
+
*/
|
|
64
|
+
primaryKey() {
|
|
65
|
+
if (this.props.nullable)
|
|
66
|
+
throw new Error('Cannot make a nullable column a primary key');
|
|
67
|
+
return new ColumnPropsBuilder({ ...this.props, primaryKey: true });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* @public A way to define a column with a custom standard schema.
|
|
72
|
+
* @since 0.1.0
|
|
73
|
+
* @version 1
|
|
74
|
+
*/
|
|
75
|
+
const defineColumn = (baseProps) => new ColumnPropsBuilder(baseProps);
|
|
76
|
+
/**
|
|
77
|
+
* @public Built-in column types.
|
|
78
|
+
* @since 0.1.0
|
|
79
|
+
* @version 1
|
|
80
|
+
*/
|
|
81
|
+
const types = {
|
|
82
|
+
/**
|
|
83
|
+
* @public Defines a column type that accepts binary data.
|
|
84
|
+
* @since 0.1.0
|
|
85
|
+
* @version 1
|
|
86
|
+
*/
|
|
87
|
+
binary: () => defineColumn({
|
|
88
|
+
type: 'BINARY',
|
|
89
|
+
schema: std.instanceOf(Uint8Array),
|
|
90
|
+
}),
|
|
91
|
+
/**
|
|
92
|
+
* @public Defines a column type that accepts boolean values.
|
|
93
|
+
* @since 0.1.0
|
|
94
|
+
* @version 1
|
|
95
|
+
*/
|
|
96
|
+
boolean: () => defineColumn({
|
|
97
|
+
type: 'BOOLEAN',
|
|
98
|
+
schema: std.boolean(),
|
|
99
|
+
}),
|
|
100
|
+
/**
|
|
101
|
+
* @public Defines a column type that accepts a Date object, an
|
|
102
|
+
* ISO 8601 date string, or a Unix timestamp in milliseconds.
|
|
103
|
+
* @since 0.1.0
|
|
104
|
+
* @version 1
|
|
105
|
+
*/
|
|
106
|
+
datetime: () => defineColumn({
|
|
107
|
+
type: 'DATETIME',
|
|
108
|
+
schema: std.date(),
|
|
109
|
+
}),
|
|
110
|
+
/**
|
|
111
|
+
* @public Defines a column type that accepts floating-point numbers.
|
|
112
|
+
* @since 0.1.0
|
|
113
|
+
* @version 1
|
|
114
|
+
*/
|
|
115
|
+
float: () => defineColumn({
|
|
116
|
+
type: 'FLOAT',
|
|
117
|
+
schema: std.number({ min: Number.MIN_VALUE, max: Number.MAX_VALUE }),
|
|
118
|
+
}),
|
|
119
|
+
/**
|
|
120
|
+
* @public Defines a column type that accepts integer numbers.
|
|
121
|
+
* @since 0.1.0
|
|
122
|
+
* @version 1
|
|
123
|
+
*/
|
|
124
|
+
integer: () => defineColumn({
|
|
125
|
+
type: 'INTEGER',
|
|
126
|
+
schema: std.integer({ min: Number.MIN_SAFE_INTEGER, max: Number.MAX_SAFE_INTEGER }),
|
|
127
|
+
}),
|
|
128
|
+
/**
|
|
129
|
+
* @public Defines a column type that accepts small strings (VARCHAR,
|
|
130
|
+
* up to 255 characters).
|
|
131
|
+
* @since 0.1.0
|
|
132
|
+
* @version 1
|
|
133
|
+
* @throws {Error} if the specified size is greater than 255.
|
|
134
|
+
*/
|
|
135
|
+
string: ({ size = 255 } = {}) => defineColumn({
|
|
136
|
+
type: 'VARCHAR',
|
|
137
|
+
schema: std.string({ max: u.lte(size, 255) }),
|
|
138
|
+
size,
|
|
139
|
+
}),
|
|
140
|
+
/**
|
|
141
|
+
* @public Defines a column type that accepts large strings (TEXT).
|
|
142
|
+
* @since 0.1.0
|
|
143
|
+
* @version 1
|
|
144
|
+
*/
|
|
145
|
+
text: () => defineColumn({
|
|
146
|
+
type: 'TEXT',
|
|
147
|
+
schema: std.string(),
|
|
148
|
+
}),
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* @private Creates the `as` function for aliasing a table.
|
|
152
|
+
* @since 0.1.0
|
|
153
|
+
* @version 1
|
|
154
|
+
*/
|
|
155
|
+
const aliasedTableFn = (table) => (alias) => {
|
|
156
|
+
const columns = Object.fromEntries(Object
|
|
157
|
+
.entries(table.columns)
|
|
158
|
+
.map(([cname, col]) => [cname, { ...col, table: alias }]));
|
|
159
|
+
return { [TABLE_NAME]: table.name, [TABLE_ALIAS]: alias, ...columns };
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* @public Defines a new table.
|
|
163
|
+
* @since 0.1.0
|
|
164
|
+
* @version 1
|
|
165
|
+
*/
|
|
166
|
+
const defineTable = (tname, shapeFn) => {
|
|
167
|
+
const columns = Object.fromEntries(Object
|
|
168
|
+
.entries(shapeFn(types))
|
|
169
|
+
.map(([cname, { props }]) => [cname, { ...props, name: cname, table: tname }]));
|
|
170
|
+
const table = { name: tname, columns };
|
|
171
|
+
return { ...table, as: aliasedTableFn(table) };
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* @public Expression builders.
|
|
175
|
+
* @since 0.1.0
|
|
176
|
+
* @version 1
|
|
177
|
+
*/
|
|
178
|
+
const expr = {
|
|
179
|
+
// // // // // // // // // // // // // // // // // // // // // // //
|
|
180
|
+
// BINARY OP EXPRESSIONS //
|
|
181
|
+
// // // // // // // // // // // // // // // // // // // // // // //
|
|
182
|
+
/**
|
|
183
|
+
* @public Builds an equality expression.
|
|
184
|
+
* @since 0.1.0
|
|
185
|
+
* @version 1
|
|
186
|
+
*/
|
|
187
|
+
eq: (lhs, rhs) => ({ lhs, op: '=', rhs }),
|
|
188
|
+
/**
|
|
189
|
+
* @public Builds a not-equal expression.
|
|
190
|
+
* @since 0.1.0
|
|
191
|
+
* @version 1
|
|
192
|
+
*/
|
|
193
|
+
ne: (lhs, rhs) => ({ lhs, op: '!=', rhs }),
|
|
194
|
+
/**
|
|
195
|
+
* @public Builds a less-than expression.
|
|
196
|
+
* @since 0.1.0
|
|
197
|
+
* @version 1
|
|
198
|
+
*/
|
|
199
|
+
lt: (lhs, rhs) => ({ lhs, op: '<', rhs }),
|
|
200
|
+
/**
|
|
201
|
+
* @public Builds a less-than-or-equal expression.
|
|
202
|
+
* @since 0.1.0
|
|
203
|
+
* @version 1
|
|
204
|
+
*/
|
|
205
|
+
lte: (lhs, rhs) => ({ lhs, op: '<=', rhs }),
|
|
206
|
+
/**
|
|
207
|
+
* @public Builds a greater-than expression.
|
|
208
|
+
* @since 0.1.0
|
|
209
|
+
* @version 1
|
|
210
|
+
*/
|
|
211
|
+
gt: (lhs, rhs) => ({ lhs, op: '>', rhs }),
|
|
212
|
+
/**
|
|
213
|
+
* @public Builds a greater-than-or-equal expression.
|
|
214
|
+
* @since 0.1.0
|
|
215
|
+
* @version 1
|
|
216
|
+
*/
|
|
217
|
+
gte: (lhs, rhs) => ({ lhs, op: '>=', rhs }),
|
|
218
|
+
/**
|
|
219
|
+
* @public Builds a LIKE expression.
|
|
220
|
+
* @since 0.1.0
|
|
221
|
+
* @version 1
|
|
222
|
+
*/
|
|
223
|
+
like: (lhs, rhs) => ({ lhs, op: 'LIKE', rhs }),
|
|
224
|
+
/**
|
|
225
|
+
* @public Builds a NOT LIKE expression.
|
|
226
|
+
* @since 0.1.0
|
|
227
|
+
* @version 1
|
|
228
|
+
*/
|
|
229
|
+
notLike: (lhs, rhs) => ({ lhs, op: 'NOT LIKE', rhs }),
|
|
230
|
+
/**
|
|
231
|
+
* @public Builds an IN expression.
|
|
232
|
+
* @since 0.1.0
|
|
233
|
+
* @version 1
|
|
234
|
+
*/
|
|
235
|
+
in: (lhs, rhs) => ({ lhs, op: 'IN', rhs }),
|
|
236
|
+
/**
|
|
237
|
+
* @public Builds a NOT IN expression.
|
|
238
|
+
* @since 0.1.0
|
|
239
|
+
* @version 1
|
|
240
|
+
*/
|
|
241
|
+
notIn: (lhs, rhs) => ({ lhs, op: 'NOT IN', rhs }),
|
|
242
|
+
/**
|
|
243
|
+
* @public Builds an IS expression.
|
|
244
|
+
* @since 0.1.0
|
|
245
|
+
* @version 1
|
|
246
|
+
*/
|
|
247
|
+
is: (lhs, rhs) => ({ lhs, op: 'IS', rhs }),
|
|
248
|
+
/**
|
|
249
|
+
* @public Builds an IS NOT expression.
|
|
250
|
+
* @since 0.1.0
|
|
251
|
+
* @version 1
|
|
252
|
+
*/
|
|
253
|
+
isNot: (lhs, rhs) => ({ lhs, op: 'IS NOT', rhs }),
|
|
254
|
+
// // // // // // // // // // // // // // // // // // // // // // //
|
|
255
|
+
// BOOLEAN OP EXPRESSIONS //
|
|
256
|
+
// // // // // // // // // // // // // // // // // // // // // // //
|
|
257
|
+
/**
|
|
258
|
+
* @public Builds an AND expression.
|
|
259
|
+
* @since 0.1.0
|
|
260
|
+
* @version 1
|
|
261
|
+
*/
|
|
262
|
+
and: (exprs) => ({ and: exprs }),
|
|
263
|
+
/**
|
|
264
|
+
* @public Builds an OR expression.
|
|
265
|
+
* @since 0.1.0
|
|
266
|
+
* @version 1
|
|
267
|
+
*/
|
|
268
|
+
or: (exprs) => ({ or: exprs }),
|
|
269
|
+
/**
|
|
270
|
+
* @public Builds a NOT expression.
|
|
271
|
+
* @since 0.1.0
|
|
272
|
+
* @version 1
|
|
273
|
+
*/
|
|
274
|
+
not: (expr) => ({ not: expr }),
|
|
275
|
+
};
|
|
276
|
+
/**
|
|
277
|
+
* @public Expression type guards.
|
|
278
|
+
* @since 0.1.0
|
|
279
|
+
* @version 1
|
|
280
|
+
*/
|
|
281
|
+
const is = {
|
|
282
|
+
// // // // // // // // // // // // // // // // // // // // // // //
|
|
283
|
+
// BINARY OP EXPRESSIONS //
|
|
284
|
+
// // // // // // // // // // // // // // // // // // // // // // //
|
|
285
|
+
/**
|
|
286
|
+
* @public Checks whether the given value is a binary op expression.
|
|
287
|
+
* @since 0.1.0
|
|
288
|
+
* @version 1
|
|
289
|
+
*/
|
|
290
|
+
binaryOp: (expr) => u.isPlainObject(expr) && 'op' in expr && 'lhs' in expr && 'rhs' in expr,
|
|
291
|
+
/**
|
|
292
|
+
* @public Checks whether the given value is an equality expression.
|
|
293
|
+
* @since 0.1.0
|
|
294
|
+
* @version 1
|
|
295
|
+
*/
|
|
296
|
+
eq: (expr) => u.isPlainObject(expr) && 'op' in expr && expr.op === '=',
|
|
297
|
+
/**
|
|
298
|
+
* @public Checks whether the given value is a not-equal expression.
|
|
299
|
+
* @since 0.1.0
|
|
300
|
+
* @version 1
|
|
301
|
+
*/
|
|
302
|
+
ne: (expr) => u.isPlainObject(expr) && 'op' in expr && expr.op === '!=',
|
|
303
|
+
/**
|
|
304
|
+
* @public Checks whether the given value is a less-than expression.
|
|
305
|
+
* @since 0.1.0
|
|
306
|
+
* @version 1
|
|
307
|
+
*/
|
|
308
|
+
lt: (expr) => u.isPlainObject(expr) && 'op' in expr && expr.op === '<',
|
|
309
|
+
/**
|
|
310
|
+
* @public Checks whether the given value is a less-than-or-equal expression.
|
|
311
|
+
* @since 0.1.0
|
|
312
|
+
* @version 1
|
|
313
|
+
*/
|
|
314
|
+
lte: (expr) => u.isPlainObject(expr) && 'op' in expr && expr.op === '<=',
|
|
315
|
+
/**
|
|
316
|
+
* @public Checks whether the given value is a greater-than expression.
|
|
317
|
+
* @since 0.1.0
|
|
318
|
+
* @version 1
|
|
319
|
+
*/
|
|
320
|
+
gt: (expr) => u.isPlainObject(expr) && 'op' in expr && expr.op === '>',
|
|
321
|
+
/**
|
|
322
|
+
* @public Checks whether the given value is a greater-than-or-equal expression.
|
|
323
|
+
* @since 0.1.0
|
|
324
|
+
* @version 1
|
|
325
|
+
*/
|
|
326
|
+
gte: (expr) => u.isPlainObject(expr) && 'op' in expr && expr.op === '>=',
|
|
327
|
+
/**
|
|
328
|
+
* @public Checks whether the given value is a LIKE expression.
|
|
329
|
+
* @since 0.1.0
|
|
330
|
+
* @version 1
|
|
331
|
+
*/
|
|
332
|
+
like: (expr) => u.isPlainObject(expr) && 'op' in expr && expr.op === 'LIKE',
|
|
333
|
+
/**
|
|
334
|
+
* @public Checks whether the given value is a NOT LIKE expression.
|
|
335
|
+
* @since 0.1.0
|
|
336
|
+
* @version 1
|
|
337
|
+
*/
|
|
338
|
+
notLike: (expr) => u.isPlainObject(expr) && 'op' in expr && expr.op === 'NOT LIKE',
|
|
339
|
+
/**
|
|
340
|
+
* @public Checks whether the given value is an IN expression.
|
|
341
|
+
* @since 0.1.0
|
|
342
|
+
* @version 1
|
|
343
|
+
*/
|
|
344
|
+
in: (expr) => u.isPlainObject(expr) && 'op' in expr && expr.op === 'IN',
|
|
345
|
+
/**
|
|
346
|
+
* @public Checks whether the given value is a NOT IN expression.
|
|
347
|
+
* @since 0.1.0
|
|
348
|
+
* @version 1
|
|
349
|
+
*/
|
|
350
|
+
notIn: (expr) => u.isPlainObject(expr) && 'op' in expr && expr.op === 'NOT IN',
|
|
351
|
+
/**
|
|
352
|
+
* @public Checks whether the given value is an IS expression.
|
|
353
|
+
* @since 0.1.0
|
|
354
|
+
* @version 1
|
|
355
|
+
*/
|
|
356
|
+
is: (expr) => u.isPlainObject(expr) && 'is' in expr,
|
|
357
|
+
// // // // // // // // // // // // // // // // // // // // // // //
|
|
358
|
+
// BOOLEAN OP EXPRESSIONS //
|
|
359
|
+
// // // // // // // // // // // // // // // // // // // // // // //
|
|
360
|
+
/**
|
|
361
|
+
* @public Checks whether the given value is an AND expression.
|
|
362
|
+
* @since 0.1.0
|
|
363
|
+
* @version 1
|
|
364
|
+
*/
|
|
365
|
+
and: (expr) => u.isPlainObject(expr) && 'and' in expr,
|
|
366
|
+
/**
|
|
367
|
+
* @public Checks whether the given value is an OR expression.
|
|
368
|
+
* @since 0.1.0
|
|
369
|
+
* @version 1
|
|
370
|
+
*/
|
|
371
|
+
or: (expr) => u.isPlainObject(expr) && 'or' in expr,
|
|
372
|
+
/**
|
|
373
|
+
* @public Checks whether the given value is a NOT expression.
|
|
374
|
+
* @since 0.1.0
|
|
375
|
+
* @version 1
|
|
376
|
+
*/
|
|
377
|
+
not: (expr) => u.isPlainObject(expr) && 'not' in expr,
|
|
378
|
+
// // // // // // // // // // // // // // // // // // // // // // //
|
|
379
|
+
// LITERALS //
|
|
380
|
+
// // // // // // // // // // // // // // // // // // // // // // //
|
|
381
|
+
/**
|
|
382
|
+
* @public Checks whether the given value is a boolean literal.
|
|
383
|
+
* @since 0.1.0
|
|
384
|
+
* @version 1
|
|
385
|
+
*/
|
|
386
|
+
boolean: (value) => typeof value === 'boolean',
|
|
387
|
+
/**
|
|
388
|
+
* @public Checks whether the given value is a date literal.
|
|
389
|
+
* @since 0.1.0
|
|
390
|
+
* @version 1
|
|
391
|
+
*/
|
|
392
|
+
date: (value) => value instanceof Date,
|
|
393
|
+
/**
|
|
394
|
+
* @public Checks whether the given value is a null literal.
|
|
395
|
+
* @since 0.1.0
|
|
396
|
+
* @version 1
|
|
397
|
+
*/
|
|
398
|
+
null: (expr) => expr === null,
|
|
399
|
+
/**
|
|
400
|
+
* @public Checks whether the given value is a number literal.
|
|
401
|
+
* @since 0.1.0
|
|
402
|
+
* @version 1
|
|
403
|
+
*/
|
|
404
|
+
number: (expr) => typeof expr === 'number',
|
|
405
|
+
/**
|
|
406
|
+
* @public Checks whether the given value is a string literal.
|
|
407
|
+
* @since 0.1.0
|
|
408
|
+
* @version 1
|
|
409
|
+
*/
|
|
410
|
+
string: (expr) => typeof expr === 'string',
|
|
411
|
+
// // // // // // // // // // // // // // // // // // // // // // //
|
|
412
|
+
// OTHERS //
|
|
413
|
+
// // // // // // // // // // // // // // // // // // // // // // //
|
|
414
|
+
/**
|
|
415
|
+
* @public Checks whether the given value is a literal expression.
|
|
416
|
+
* @since 0.1.0
|
|
417
|
+
* @version 1
|
|
418
|
+
*/
|
|
419
|
+
literal: (expr) => expr === null || expr instanceof Date || ['boolean', 'number', 'string'].includes(typeof expr),
|
|
420
|
+
/**
|
|
421
|
+
* @public Checks whether the given value is a column expression.
|
|
422
|
+
* @since 0.1.0
|
|
423
|
+
* @version 1
|
|
424
|
+
*/
|
|
425
|
+
column: (expr) => u.isPlainObject(expr) && 'type' in expr && 'schema' in expr,
|
|
426
|
+
};
|
|
427
|
+
;
|
|
428
|
+
;
|
|
429
|
+
// // // // // // // // // // // // // // // // // // // // // // // //
|
|
430
|
+
// CREATE STATEMENTS //
|
|
431
|
+
// // // // // // // // // // // // // // // // // // // // // // // //
|
|
432
|
+
/**
|
|
433
|
+
* @public Create statement builder.
|
|
434
|
+
* @since 0.1.0
|
|
435
|
+
* @version 1
|
|
436
|
+
*/
|
|
437
|
+
const create = {
|
|
438
|
+
/**
|
|
439
|
+
* @public Prepares a create table statement.
|
|
440
|
+
* @since 0.1.0
|
|
441
|
+
* @version 1
|
|
442
|
+
*/
|
|
443
|
+
table: (table, options = {}) => ({
|
|
444
|
+
/**
|
|
445
|
+
* @public Executes the create table statement onto the given
|
|
446
|
+
* database.
|
|
447
|
+
* @since 0.1.0
|
|
448
|
+
* @version 1
|
|
449
|
+
*/
|
|
450
|
+
onto: async (db) => db.createTable({
|
|
451
|
+
...options,
|
|
452
|
+
table: table.name,
|
|
453
|
+
columns: Object.values(table.columns),
|
|
454
|
+
}),
|
|
455
|
+
}),
|
|
456
|
+
};
|
|
457
|
+
// // // // // // // // // // // // // // // // // // // // // // // //
|
|
458
|
+
// INSERT STATEMENT //
|
|
459
|
+
// // // // // // // // // // // // // // // // // // // // // // // //
|
|
460
|
+
/**
|
|
461
|
+
* @private Resolves the value of a column before insertion. This is
|
|
462
|
+
* where default values are applied if the column is either
|
|
463
|
+
* missing or nullish.
|
|
464
|
+
* @since 0.1.0
|
|
465
|
+
* @version 1
|
|
466
|
+
*/
|
|
467
|
+
const resolveInsertValue = (column, row) => {
|
|
468
|
+
if (column.default)
|
|
469
|
+
return row[column.name] ?? u.resolve(column.default);
|
|
470
|
+
// ↓ it's ok to omit nullable columns, let's make it null if undefined
|
|
471
|
+
if (column.nullable)
|
|
472
|
+
return row[column.name] ?? null;
|
|
473
|
+
return row[column.name];
|
|
474
|
+
};
|
|
475
|
+
/**
|
|
476
|
+
* @private Prepares a row for insertion. Only known fields are kept,
|
|
477
|
+
* additional fields get disposed of.
|
|
478
|
+
* @since 0.1.0
|
|
479
|
+
* @version 1
|
|
480
|
+
*/
|
|
481
|
+
const prepareForInsert = (shape) => (row) => Object.fromEntries(Object
|
|
482
|
+
.entries(shape)
|
|
483
|
+
.map(([key, column]) => [key, resolveInsertValue(column, row)]));
|
|
484
|
+
/**
|
|
485
|
+
* @private Takes from the given table only the columns that can be
|
|
486
|
+
* present on insertion. It excludes, for example,
|
|
487
|
+
* autoincrement columns.
|
|
488
|
+
* @since 0.1.0
|
|
489
|
+
* @version 1
|
|
490
|
+
*/
|
|
491
|
+
const getInsertShape = (table) => Object.fromEntries(Object
|
|
492
|
+
.entries(table.columns)
|
|
493
|
+
.filter(([_, col]) => !col.autoincrement));
|
|
494
|
+
/**
|
|
495
|
+
* @private Builds the standard schema to be used to parse / validate
|
|
496
|
+
* rows before insertion.
|
|
497
|
+
* @since 0.1.0
|
|
498
|
+
* @version 1
|
|
499
|
+
*/
|
|
500
|
+
const buildInsertSchema = (shape) => std.strictObject(u.mapValues(shape, (column) => column.schema));
|
|
501
|
+
/**
|
|
502
|
+
* @public Insert statement builder.
|
|
503
|
+
* @since 0.1.0
|
|
504
|
+
* @version 1
|
|
505
|
+
*/
|
|
506
|
+
class InsertBuilder {
|
|
507
|
+
table;
|
|
508
|
+
rows;
|
|
509
|
+
constructor(table, rows = []) {
|
|
510
|
+
this.table = table;
|
|
511
|
+
this.rows = rows;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* @public Adds one or more rows to be inserted.
|
|
515
|
+
* @since 0.1.0
|
|
516
|
+
* @version 1
|
|
517
|
+
*/
|
|
518
|
+
insert(rows) {
|
|
519
|
+
this.rows.push(...(u.wrap(rows)));
|
|
520
|
+
return this;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* @public Executes the insert statement onto the given database.
|
|
524
|
+
* @since 0.1.0
|
|
525
|
+
* @version 1
|
|
526
|
+
*/
|
|
527
|
+
async run(db) {
|
|
528
|
+
if (this.rows.length === 0)
|
|
529
|
+
return [];
|
|
530
|
+
const insertShape = getInsertShape(this.table);
|
|
531
|
+
const schema = std.array(buildInsertSchema(insertShape));
|
|
532
|
+
const result = std.parse(schema, this.rows.map(prepareForInsert(insertShape)));
|
|
533
|
+
if (result.issues)
|
|
534
|
+
throw new Error('Failed validation: ' + JSON.stringify(result.issues, null, 2));
|
|
535
|
+
const rows = await db.insert({
|
|
536
|
+
table: this.table.name,
|
|
537
|
+
records: result.value,
|
|
538
|
+
insertShape,
|
|
539
|
+
returnShape: this.table.columns,
|
|
540
|
+
});
|
|
541
|
+
return rows;
|
|
542
|
+
}
|
|
543
|
+
;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* @public Starts an insert statement for the given table.
|
|
547
|
+
* @since 0.1.0
|
|
548
|
+
* @version 1
|
|
549
|
+
*/
|
|
550
|
+
const into = (table) => new InsertBuilder(table);
|
|
551
|
+
/**
|
|
552
|
+
* @private Transforms a {@link Query} into a {@link SelectStatement}.
|
|
553
|
+
* @since 0.1.0
|
|
554
|
+
* @version 1
|
|
555
|
+
*/
|
|
556
|
+
const toSelectStatement = (query) => ({
|
|
557
|
+
...query,
|
|
558
|
+
registry: u.mapValues(query.registry, table => table[TABLE_NAME]),
|
|
559
|
+
});
|
|
560
|
+
/**
|
|
561
|
+
* @public Select statement builder with a fluent API.
|
|
562
|
+
*
|
|
563
|
+
* We only need to keep track of two types in here:
|
|
564
|
+
* - the registry of aliased tables, so they can be
|
|
565
|
+
* referenced in expressions and selection; and
|
|
566
|
+
* - the selected columns, so we can infer the returning
|
|
567
|
+
* type of the query.
|
|
568
|
+
* @since 0.1.0
|
|
569
|
+
* @version 1
|
|
570
|
+
*/
|
|
571
|
+
class QueryBuilder {
|
|
572
|
+
query;
|
|
573
|
+
constructor(query) {
|
|
574
|
+
this.query = query;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* @public Executes the select statement against the given database,
|
|
578
|
+
* returning all matching rows.
|
|
579
|
+
* @since 0.1.0
|
|
580
|
+
* @version 1
|
|
581
|
+
*/
|
|
582
|
+
async all(db) {
|
|
583
|
+
// @TODO calls query engine with the query object
|
|
584
|
+
return db.query(toSelectStatement(this.query));
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* @public Sets a limit on the number of rows to be returned.
|
|
588
|
+
* @since 0.1.0
|
|
589
|
+
* @version 1
|
|
590
|
+
*/
|
|
591
|
+
limit(n) {
|
|
592
|
+
return new QueryBuilder({ ...this.query, limit: n });
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* @public Sets an offset for the rows to be returned.
|
|
596
|
+
* @since 0.1.0
|
|
597
|
+
* @version 1
|
|
598
|
+
*/
|
|
599
|
+
offset(n) {
|
|
600
|
+
return new QueryBuilder({ ...this.query, offset: n });
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* @public Executes the select statement against the given database,
|
|
604
|
+
* returning the first matching row.
|
|
605
|
+
* @since 0.1.0
|
|
606
|
+
* @version 1
|
|
607
|
+
*/
|
|
608
|
+
async one(db) {
|
|
609
|
+
// @TODO calls query engine with the query object
|
|
610
|
+
const op = toSelectStatement({ ...this.query, limit: 1, offset: 0 });
|
|
611
|
+
const rows = await db.query(op);
|
|
612
|
+
if (rows.length === 0)
|
|
613
|
+
return null;
|
|
614
|
+
return rows[0];
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* @public Defines the order by clause of the query.
|
|
618
|
+
* @since 0.1.0
|
|
619
|
+
* @version 1
|
|
620
|
+
*/
|
|
621
|
+
orderBy(fn) {
|
|
622
|
+
return new QueryBuilder({ ...this.query, orderBy: fn(this.query.registry) });
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* @public Defines the selection of the query.
|
|
626
|
+
* @since 0.1.0
|
|
627
|
+
* @version 1
|
|
628
|
+
*/
|
|
629
|
+
select(fn) {
|
|
630
|
+
return new QueryBuilder({ ...this.query, select: fn(this.query.registry) });
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* @public Defines the where clause of the query. If there's already
|
|
634
|
+
* a WHERE clause in the query, the new clause will be
|
|
635
|
+
* ANDed to the existing one.
|
|
636
|
+
* @since 0.1.0
|
|
637
|
+
* @version 1
|
|
638
|
+
*/
|
|
639
|
+
where(fn) {
|
|
640
|
+
const value = fn(this.query.registry);
|
|
641
|
+
const where = this.query.where === undefined ? value
|
|
642
|
+
: is.and(this.query.where) ? expr.and([...this.query.where.and, value])
|
|
643
|
+
: expr.and([this.query.where, value]);
|
|
644
|
+
return new QueryBuilder({ ...this.query, where });
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* @public Starts a select statement from the given table.
|
|
649
|
+
* @since 0.1.0
|
|
650
|
+
* @version 1
|
|
651
|
+
*/
|
|
652
|
+
const from = (table) => new QueryBuilder({
|
|
653
|
+
registry: { [table[TABLE_ALIAS]]: table },
|
|
654
|
+
from: table[TABLE_ALIAS],
|
|
655
|
+
select: { ...table },
|
|
656
|
+
});
|
|
657
|
+
// // // // // // // // // // // // // // // // // // // // // // // //
|
|
658
|
+
// EXPORTS //
|
|
659
|
+
// // // // // // // // // // // // // // // // // // // // // // // //
|
|
660
|
+
export { create, defineTable as table, expr, from, into, is, types as t, };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { inspect } from 'node:util';
|
|
2
|
+
import { highlight } from 'sql-highlight';
|
|
3
|
+
import {} from './index';
|
|
4
|
+
/**
|
|
5
|
+
* @public Creates a basic console logger that logs all queries.
|
|
6
|
+
* @since 0.1.0
|
|
7
|
+
* @version 1
|
|
8
|
+
*/
|
|
9
|
+
export const createPrettyLogger = () => ({
|
|
10
|
+
debug: (sql, params) => { process.stdout.write(highlight(sql) + ' ' + inspect(params, false, null, true) + '\n'); },
|
|
11
|
+
});
|