forj 0.0.1 → 0.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,375 @@
1
+ import ClauseBuilder from './clause-builder'
2
+ import {
3
+ isOperator,
4
+ parseColumn, parseSelectColumn,
5
+ formatValue,
6
+ isJoinCompare,
7
+ zSame, zType,
8
+ } from './utils'
9
+ import type {
10
+ IJoinBuilder, IClauseBuilder,
11
+ OrderDirection,
12
+ JoinType,
13
+ WhereArgs,
14
+ Values,
15
+ Item,
16
+ JoinArgs,
17
+ DBSchema,
18
+ Pipe,
19
+ } from './types'
20
+
21
+ export class QueryBuilder<
22
+ S,
23
+ T,
24
+ C extends keyof T = keyof T //,
25
+ // J extends keyof S = keyof S
26
+ // T, // = any,,
27
+ // C extends keyof T = keyof T
28
+ // > {
29
+ > implements IJoinBuilder<S>, IClauseBuilder<T> {
30
+ #table!: string
31
+ #schema?: DBSchema
32
+ #selects: string[] = []
33
+ #clauses!: ClauseBuilder<T>
34
+ #groups: string[] = []
35
+ #orders: string[] = []
36
+
37
+ #distinct = false
38
+ #hasJoin = false
39
+ #limit?: number
40
+ #offset?: number
41
+
42
+ #joins: string[] = []
43
+
44
+ #pipe?: Pipe<S, T, C>
45
+ // #runFn?: RunFn<S, T, C>
46
+
47
+ constructor(
48
+ table: string,
49
+ schema?: DBSchema,
50
+ pipe?: Pipe<S, T, C>
51
+ // runFn?: RunFn<S, T, C>
52
+ ) {
53
+ this.#table = table
54
+ this.#schema = schema
55
+ this.#pipe = pipe
56
+ // this.#runFn = runFn
57
+ this.#clauses = new ClauseBuilder<T>(table, schema)
58
+ }
59
+
60
+ async run() {
61
+ // if (!this.#runFn)
62
+ if (!this.#pipe?.run)
63
+ throw new Error(`No database connection.`)
64
+
65
+ return await this.#pipe?.run(this)
66
+ }
67
+
68
+ async first<K extends keyof T>(...columns: K[] | K[][]): Promise<null | Item<T, C>> {
69
+ columns?.length && this.select(...columns)
70
+ const resp = await this.run()
71
+ return resp.results?.length ? resp.results[0] : null
72
+ }
73
+
74
+ async all<K extends keyof T>(...columns: K[] | K[][]): Promise<Item<T, C>[]> {
75
+ columns?.length && this.select(...columns)
76
+ const resp = await this.run()
77
+ return resp.results
78
+ }
79
+
80
+ select<K extends keyof T>(...columns: K[] | K[][]): QueryBuilder<S, T, K> {
81
+ this.#selects.push(...columns.flat(Infinity) as string[])
82
+ return this as any
83
+ }
84
+
85
+ distinct() {
86
+ this.#distinct = true
87
+ return this
88
+ }
89
+
90
+ #join<J extends keyof S>(
91
+ type: JoinType | undefined,
92
+ table: J,
93
+ ...args: JoinArgs<S, J>
94
+ ) {
95
+ this.#hasJoin = true
96
+ const query = (type ? type + ' ' : '') + `JOIN ${table as string} ON `
97
+
98
+ if (typeof args[0] == 'function') {
99
+ const join = new ClauseBuilder<S[J]>(table as string, this.#schema)
100
+ args[0](join)
101
+
102
+ this.#joins.push(query + join.clauses.join(' '))
103
+ this.#clauses.args = join.args
104
+ return this
105
+ }
106
+
107
+ const length = args.length
108
+ let [column, operator, value, value2] = args
109
+
110
+ if (length == 2) { // @ts-ignore
111
+ value = operator
112
+ operator = '='
113
+ } else if (length == 3 && !isOperator(operator)) { // @ts-ignore
114
+ // console.log(column, operator, value, value2) // @ts-ignore
115
+ value = parseColumn(value as string, operator as string) // TODO: check if value is a valid column
116
+
117
+ if (this.#schema && !isJoinCompare(value, this.#schema))
118
+ throw new Error(`Table column '${value}' doesn't exists.`)
119
+
120
+ operator = '='
121
+ } else if (length == 4) { // @ts-ignore
122
+ // console.log(column, operator, value, value2) // @ts-ignore
123
+ value = parseColumn(value2 as string, value as string)
124
+ operator = '='
125
+ }
126
+
127
+ const col = parseColumn(String(column), String(table))
128
+ if (this.#schema && !zSame(col, value, this.#schema))
129
+ throw new Error(`Table column '${col}' of type '${zType(col, this.#schema)}' is not assignable as type of '${typeof value}'.`)
130
+
131
+ if (!isJoinCompare(value, this.#schema)) { // @ts-ignore
132
+ this.#clauses.args = [value] // @ts-ignore // TODO: https://developers.cloudflare.com/d1/worker-api/#type-conversion
133
+ value = '?'
134
+ }
135
+
136
+ // @ts-ignore
137
+ this.#joins.push(query + col + ` ${operator} ${value}`)
138
+ return this
139
+ }
140
+
141
+ join<J extends keyof S>(table: J, ...args: JoinArgs<S, J>) {
142
+ return this.#join(undefined, table, ...args)
143
+ }
144
+
145
+ innerJoin<J extends keyof S>(table: J, ...args: JoinArgs<S, J>) {
146
+ return this.#join('INNER', table, ...args)
147
+ }
148
+
149
+ leftJoin<J extends keyof S>(table: J, ...args: JoinArgs<S, J>) {
150
+ return this.#join('LEFT', table, ...args)
151
+ }
152
+
153
+ rightJoin<J extends keyof S>(table: J, ...args: JoinArgs<S, J>) {
154
+ return this.#join('RIGHT', table, ...args)
155
+ }
156
+
157
+ crossJoin<J extends keyof S>(table: J, ...args: JoinArgs<S, J>) {
158
+ return this.#join('CROSS', table, ...args)
159
+ }
160
+
161
+ where(...args: WhereArgs<T>) {
162
+ this.#clauses.where(...args)
163
+ return this
164
+ }
165
+ on(...args: WhereArgs<T>) {
166
+ return this.where(...args)
167
+ }
168
+
169
+ orWhere(...args: WhereArgs<T>) {
170
+ this.#clauses.orWhere(...args)
171
+ return this
172
+ }
173
+ orOn(...args: WhereArgs<T>) {
174
+ return this.orWhere(...args)
175
+ }
176
+
177
+ whereIn(column: C, values: T[C][]) {
178
+ this.#clauses.whereIn(column, values)
179
+ return this
180
+ }
181
+ in(column: C, values: T[C][]) {
182
+ return this.whereIn(column, values)
183
+ }
184
+
185
+ whereNotIn(column: C, values: T[C][]) {
186
+ this.#clauses.whereNotIn(column, values)
187
+ return this
188
+ }
189
+ notIn(column: C, values: T[C][]) {
190
+ return this.whereNotIn(column, values)
191
+ }
192
+
193
+ orWhereIn(column: C, values: T[C][]) {
194
+ this.#clauses.orWhereIn(column, values)
195
+ return this
196
+ }
197
+ orIn(column: C, values: T[C][]) {
198
+ return this.orWhereIn(column, values)
199
+ }
200
+
201
+ orWhereNotIn(column: C, values: T[C][]) {
202
+ this.#clauses.orWhereNotIn(column, values)
203
+ return this
204
+ }
205
+ orNotIn(column: C, values: T[C][]) {
206
+ return this.orWhereNotIn(column, values)
207
+ }
208
+
209
+ whereBetween(column: C, one: T[C], two: T[C]) {
210
+ this.#clauses.whereBetween(column, one, two)
211
+ return this
212
+ }
213
+ between(column: C, one: T[C], two: T[C]) {
214
+ return this.whereBetween(column, one, two)
215
+ }
216
+
217
+ orWhereBetween(column: C, one: T[C], two: T[C]) {
218
+ this.#clauses.orWhereBetween(column, one, two)
219
+ return this
220
+ }
221
+ orBetween(column: C, one: T[C], two: T[C]) {
222
+ return this.orWhereBetween(column, one, two)
223
+ }
224
+
225
+ whereNotBetween(column: C, one: T[C], two: T[C]) {
226
+ this.#clauses.whereNotBetween(column, one, two)
227
+ return this
228
+ }
229
+ notBetween(column: C, one: T[C], two: T[C]) {
230
+ return this.whereNotBetween(column, one, two)
231
+ }
232
+
233
+ orWhereNotBetween(column: C, one: T[C], two: T[C]) {
234
+ this.#clauses.orWhereNotBetween(column, one, two)
235
+ return this
236
+ }
237
+ orNotBetween(column: C, one: T[C], two: T[C]) {
238
+ return this.orWhereNotBetween(column, one, two)
239
+ }
240
+
241
+ whereNull(column: C) {
242
+ this.#clauses.whereNull(column)
243
+ return this
244
+ }
245
+ onNull(column: C) {
246
+ return this.whereNull(column)
247
+ }
248
+
249
+ orWhereNull(column: C) {
250
+ this.#clauses.orWhereNull(column)
251
+ return this
252
+ }
253
+ orOnNull(column: C) {
254
+ return this.orWhereNull(column)
255
+ }
256
+
257
+ whereNotNull(column: C) {
258
+ this.#clauses.whereNotNull(column)
259
+ return this
260
+ }
261
+ onNotNull(column: C) {
262
+ return this.whereNotNull(column)
263
+ }
264
+
265
+ orWhereNotNull(column: C) {
266
+ this.#clauses.orWhereNotNull(column)
267
+ return this
268
+ }
269
+ orNotNull(column: C) {
270
+ return this.orWhereNotNull(column)
271
+ }
272
+
273
+ groupBy(...columns: string[]) {
274
+ this.#groups.push(...columns)
275
+ return this
276
+ }
277
+
278
+ order(column: C, direction: OrderDirection = 'ASC') {
279
+ this.#orders.push(parseColumn(column as string, this.#table, this.#hasJoin) +' '+ direction.toUpperCase())
280
+ return this
281
+ }
282
+ orderBy(column: C, direction: OrderDirection = 'ASC') {
283
+ return this.order(column, direction)
284
+ }
285
+ asc(column: C) {
286
+ return this.order(column, 'ASC')
287
+ }
288
+ desc(column: C) {
289
+ return this.order(column, 'DESC')
290
+ }
291
+
292
+ limit(val: number | string) {
293
+ val = parseInt(String(val)) || 0
294
+ if (val) this.#limit = val
295
+ return this
296
+ }
297
+
298
+ offset(val: number | string) {
299
+ this.#offset = parseInt(String(val)) || 0
300
+ return this
301
+ }
302
+
303
+ #bind(sql: string, values: Values) {
304
+ let i = 0
305
+ let out = ''
306
+ let last = 0
307
+
308
+ for (let pos = sql.indexOf('?'); pos !== -1; pos = sql.indexOf('?', pos + 1)) {
309
+ if (i >= values.length)
310
+ throw new Error(`Missing bind value at position ${i}`)
311
+
312
+ out += sql.slice(last, pos)
313
+ out += formatValue(values[i++])
314
+ last = pos + 1
315
+ }
316
+
317
+ if (i < values.length)
318
+ throw new Error(`Too many bind values: expected ${i}, got ${values.length}`)
319
+
320
+ return out + sql.slice(last)
321
+ }
322
+
323
+ get args() {
324
+ return this.#clauses.args
325
+ }
326
+ get arguments() {
327
+ return this.args
328
+ }
329
+ get bindings() {
330
+ return this.args
331
+ }
332
+
333
+ get query() {
334
+ let sql = ''
335
+
336
+ const selects = new Set<string>()
337
+ this.#selects.forEach(column => {
338
+ column = parseSelectColumn(column, this.#table, this.#hasJoin)
339
+ !selects.has(column) && selects.add(column)
340
+ })
341
+
342
+ sql += `SELECT ${this.#distinct ? 'DISTINCT ' : ''}${
343
+ selects.size ? [...selects].join(', ') : '*'
344
+ }`
345
+
346
+ sql += ' FROM '+ this.#table
347
+
348
+ if (this.#joins.length)
349
+ sql += ' '+ this.#joins.join(' ')
350
+
351
+ if (this.#clauses.length)
352
+ sql += ' WHERE '+ this.#clauses.clauses.join(' ')
353
+
354
+ if (this.#groups.length)
355
+ sql += ' GROUP BY '+ this.#groups.join(', ')
356
+
357
+ if (this.#orders.length)
358
+ sql += ' ORDER BY '+ this.#orders.join(', ')
359
+
360
+ if (this.#limit != undefined)
361
+ sql += ' LIMIT '+ this.#limit
362
+
363
+ if (this.#offset != undefined)
364
+ sql += ' OFFSET '+ this.#offset
365
+
366
+ return sql
367
+ }
368
+
369
+ get sql() {
370
+ return this.#bind(this.query, this.#clauses.args)
371
+ }
372
+ get raw() {
373
+ return this.sql
374
+ }
375
+ }
package/src/types.ts ADDED
@@ -0,0 +1,260 @@
1
+ import z from 'zod'
2
+ import { QueryBuilder } from './query-builder'
3
+
4
+ export type text = string
5
+ export type real = number
6
+ export type integer = number
7
+ export type bool = boolean | 0 | 1
8
+
9
+ export type Primitive = null | number | string | boolean
10
+ export type Primitives = Primitive[]
11
+
12
+ export type Value = Primitive | undefined
13
+ export type Values = Value[]
14
+
15
+ // export type WriteType = Primitive | ArrayBuffer | ArrayBufferView | undefined
16
+ // export type ReadType = Primitive | any[]
17
+
18
+ export type Operator = '=' | '!=' | '<' | '>' | '<=' | '>=' | 'LIKE' // | 'IN' | 'NOT IN' | 'BETWEEN' | 'IS NULL' | 'IS NOT NULL'
19
+ export type OrderDirection = 'ASC' | 'DESC' | 'asc' | 'desc'
20
+
21
+ export type JoinType = 'INNER' | 'LEFT' | 'RIGHT' | 'CROSS'
22
+
23
+ export type DBSchema = z.ZodObject<any>
24
+
25
+ export type SchemaObject = Record<string, z.ZodTypeAny>
26
+ export type SchemaKeys<TSchema extends DBSchema> =
27
+ TSchema extends z.ZodObject<infer TShape>
28
+ ? keyof TShape
29
+ : TSchema extends SchemaObject
30
+ ? keyof TSchema
31
+ : never
32
+
33
+ // TODO: transform QueryBuilder<S, T, C> into a interface
34
+ export type RunFn<S, T, C extends keyof T = keyof T> = (qb: QueryBuilder<S, T, C>) => Promise<Result<T, C>>
35
+ // export type RunBatchFn<S, T, C extends keyof T = keyof T> = (qb: QueryBuilder<S, T, C>[]) => Promise<Result<T, C>>[]
36
+
37
+ export type Pipe<S, T, C extends keyof T = keyof T> = {
38
+ run: RunFn<S, T, C>,
39
+ // batch: RunBatchFn<S, T, C>,
40
+ }
41
+
42
+ export type Result<T, C extends keyof T> = {
43
+ changes: number,
44
+ duration: number,
45
+ lastId?: number | string,
46
+ rowsRead: number,
47
+ rowsWritten: number,
48
+ success: boolean,
49
+ results: Item<T, C>[],
50
+ }
51
+
52
+ export type Item<B, S extends keyof B, T = Pick<B, S>> = { [K in keyof T]: T[K] } & {}
53
+
54
+ export type ClauseOperator = 'AND' | 'OR'
55
+
56
+ export type WhereFn<T, C extends keyof T = keyof T> = (q: IClauseBuilder<T, C>) => void
57
+ export type WhereArgs<T, C extends keyof T = keyof T> = [WhereFn<T, C>] | [C, T[C]] | [C, Operator, T[C]]
58
+
59
+ export interface IClauseBuilder<T, C extends keyof T = keyof T> {
60
+ where(fn: WhereFn<T, C>): this
61
+ where(column: C, value: T[C]): this
62
+ where(column: C, operator: Operator, value: T[C]): this
63
+ where(...args: WhereArgs<T>): this
64
+
65
+ on(fn: WhereFn<T, C>): this
66
+ on(column: C, value: T[C]): this
67
+ on(column: C, operator: Operator, value: T[C]): this
68
+ on(...args: WhereArgs<T>): this
69
+
70
+ orWhere(fn: WhereFn<T, C>): this
71
+ orWhere(column: C, value: T[C]): this
72
+ orWhere(column: C, operator: Operator, value: T[C]): this
73
+ orWhere(...args: WhereArgs<T>): this
74
+
75
+ orOn(fn: WhereFn<T, C>): this
76
+ orOn(column: C, value: T[C]): this
77
+ orOn(column: C, operator: Operator, value: T[C]): this
78
+ orOn(...args: WhereArgs<T>): this
79
+
80
+ whereIn(column: C, values: T[C][]): this
81
+ in(column: C, values: T[C][]): this
82
+
83
+ whereNotIn(column: C, values: T[C][]): this
84
+ notIn(column: C, values: T[C][]): this
85
+
86
+ orWhereIn(column: C, values: T[C][]): this
87
+ orIn(column: C, values: T[C][]): this
88
+
89
+ orWhereNotIn(column: C, values: T[C][]): this
90
+ orNotIn(column: C, values: T[C][]): this
91
+
92
+ whereBetween(column: C, one: T[C], two: T[C]): this
93
+ between(column: C, one: T[C], two: T[C]): this
94
+
95
+ orWhereBetween(column: C, one: T[C], two: T[C]): this
96
+ orBetween(column: C, one: T[C], two: T[C]): this
97
+
98
+ whereNotBetween(column: C, one: T[C], two: T[C]): this
99
+ notBetween(column: C, one: T[C], two: T[C]): this
100
+
101
+ orWhereNotBetween(column: C, one: T[C], two: T[C]): this
102
+ orNotBetween(column: C, one: T[C], two: T[C]): this
103
+
104
+ whereNull(column: C): this
105
+ onNull(column: C): this
106
+
107
+ orWhereNull(column: C): this
108
+ orOnNull(column: C): this
109
+
110
+ whereNotNull(column: C): this
111
+ onNotNull(column: C): this
112
+
113
+ orWhereNotNull(column: C): this
114
+ orNotNull(column: C): this
115
+ }
116
+
117
+ export type JoinArgs<S, J extends keyof S> =
118
+ [WhereFn<S[J]>]
119
+ | [keyof S[J], S[J][keyof S[J]]]
120
+ | [keyof S[J], Operator, S[J][keyof S[J]]]
121
+ | [keyof S[J], keyof S, keyof S[keyof S]]
122
+ | [keyof S[J], Operator, S[J][keyof S[J]]]
123
+ | [keyof S[J], Operator, keyof S, keyof S[keyof S]]
124
+
125
+ export interface IJoinBuilder<S> {
126
+ join<J extends keyof S>(table: J, fn: WhereFn<S[J]>): this
127
+ join<
128
+ J extends keyof S,
129
+ T extends S[J],
130
+ C extends keyof T
131
+ >(table: J, column: C, value: T[C]): this
132
+ join<
133
+ J extends keyof S,
134
+ T extends S[J],
135
+ C extends keyof T
136
+ >(table: J, column: C, operator: Operator, value: T[C]): this
137
+ join<
138
+ J extends keyof S,
139
+ T extends S[J],
140
+ C extends keyof T,
141
+ J2 extends keyof S,
142
+ C2 extends keyof S[J2],
143
+ >(table: J, column: C, table2: J2, column2: C2): this
144
+ join<
145
+ J extends keyof S,
146
+ T extends S[J],
147
+ C extends keyof T,
148
+ J2 extends keyof S,
149
+ C2 extends keyof S[J2],
150
+ >(table: J, column: C, operator: Operator, table2: J2, column2: C2): this
151
+ join<J extends keyof S>(table: J, ...args: JoinArgs<S, J>): this
152
+
153
+ innerJoin<J extends keyof S>(table: J, fn: WhereFn<S[J]>): this
154
+ innerJoin<
155
+ J extends keyof S,
156
+ T extends S[J],
157
+ C extends keyof T
158
+ >(table: J, column: C, value: T[C]): this
159
+ innerJoin<
160
+ J extends keyof S,
161
+ T extends S[J],
162
+ C extends keyof T
163
+ >(table: J, column: C, operator: Operator, value: T[C]): this
164
+ innerJoin<
165
+ J extends keyof S,
166
+ T extends S[J],
167
+ C extends keyof T,
168
+ J2 extends keyof S,
169
+ C2 extends keyof S[J2],
170
+ >(table: J, column: C, table2: J2, column2: C2): this
171
+ innerJoin<
172
+ J extends keyof S,
173
+ T extends S[J],
174
+ C extends keyof T,
175
+ J2 extends keyof S,
176
+ C2 extends keyof S[J2],
177
+ >(table: J, column: C, operator: Operator, table2: J2, column2: C2): this
178
+ innerJoin<J extends keyof S>(table: J, ...args: JoinArgs<S, J>): this
179
+
180
+ leftJoin<J extends keyof S>(table: J, fn: WhereFn<S[J]>): this
181
+ leftJoin<
182
+ J extends keyof S,
183
+ T extends S[J],
184
+ C extends keyof T
185
+ >(table: J, column: C, value: T[C]): this
186
+ leftJoin<
187
+ J extends keyof S,
188
+ T extends S[J],
189
+ C extends keyof T
190
+ >(table: J, column: C, operator: Operator, value: T[C]): this
191
+ leftJoin<
192
+ J extends keyof S,
193
+ T extends S[J],
194
+ C extends keyof T,
195
+ J2 extends keyof S,
196
+ C2 extends keyof S[J2],
197
+ >(table: J, column: C, table2: J2, column2: C2): this
198
+ leftJoin<
199
+ J extends keyof S,
200
+ T extends S[J],
201
+ C extends keyof T,
202
+ J2 extends keyof S,
203
+ C2 extends keyof S[J2],
204
+ >(table: J, column: C, operator: Operator, table2: J2, column2: C2): this
205
+ leftJoin<J extends keyof S>(table: J, ...args: JoinArgs<S, J>): this
206
+
207
+ rightJoin<J extends keyof S>(table: J, fn: WhereFn<S[J]>): this
208
+ rightJoin<
209
+ J extends keyof S,
210
+ T extends S[J],
211
+ C extends keyof T
212
+ >(table: J, column: C, value: T[C]): this
213
+ rightJoin<
214
+ J extends keyof S,
215
+ T extends S[J],
216
+ C extends keyof T
217
+ >(table: J, column: C, operator: Operator, value: T[C]): this
218
+ rightJoin<
219
+ J extends keyof S,
220
+ T extends S[J],
221
+ C extends keyof T,
222
+ J2 extends keyof S,
223
+ C2 extends keyof S[J2],
224
+ >(table: J, column: C, table2: J2, column2: C2): this
225
+ rightJoin<
226
+ J extends keyof S,
227
+ T extends S[J],
228
+ C extends keyof T,
229
+ J2 extends keyof S,
230
+ C2 extends keyof S[J2],
231
+ >(table: J, column: C, operator: Operator, table2: J2, column2: C2): this
232
+ rightJoin<J extends keyof S>(table: J, ...args: JoinArgs<S, J>): this
233
+
234
+ crossJoin<J extends keyof S>(table: J, fn: WhereFn<S[J]>): this
235
+ crossJoin<
236
+ J extends keyof S,
237
+ T extends S[J],
238
+ C extends keyof T
239
+ >(table: J, column: C, value: T[C]): this
240
+ crossJoin<
241
+ J extends keyof S,
242
+ T extends S[J],
243
+ C extends keyof T
244
+ >(table: J, column: C, operator: Operator, value: T[C]): this
245
+ crossJoin<
246
+ J extends keyof S,
247
+ T extends S[J],
248
+ C extends keyof T,
249
+ J2 extends keyof S,
250
+ C2 extends keyof S[J2],
251
+ >(table: J, column: C, table2: J2, column2: C2): this
252
+ crossJoin<
253
+ J extends keyof S,
254
+ T extends S[J],
255
+ C extends keyof T,
256
+ J2 extends keyof S,
257
+ C2 extends keyof S[J2],
258
+ >(table: J, column: C, operator: Operator, table2: J2, column2: C2): this
259
+ crossJoin<J extends keyof S>(table: J, ...args: JoinArgs<S, J>): this
260
+ }