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.
package/package.json CHANGED
@@ -1,10 +1,15 @@
1
1
  {
2
2
  "name": "forj",
3
3
  "description": "SQLite ORM and Query Builder whitout dependencies",
4
- "version": "0.0.1",
4
+ "version": "0.0.2",
5
5
  "type": "module",
6
- "files": ["dist"],
6
+ "main": "src/index.ts",
7
+ "files": ["src"],
7
8
  "exports": {
9
+ ".": "./src/index.ts",
10
+ "./d1": "./src/d1/index.ts"
11
+ },
12
+ "-exports": {
8
13
  ".": {
9
14
  "import": "./dist/index.js",
10
15
  "types": "./dist/index.d.ts"
@@ -17,7 +22,7 @@
17
22
  "scripts": {
18
23
  "bench": "tsx tests/benchmark/*.bench.ts",
19
24
  "build": "tsup",
20
- "prepublishOnly": "bun run build"
25
+ "-prepublishOnly": "bun run build"
21
26
  },
22
27
  "dependencies": {
23
28
  "@cloudflare/workers-types": "^4.20260113.0",
@@ -0,0 +1,216 @@
1
+ import { parseColumn, isJoinCompare, zSame, zType } from './utils'
2
+ import type {
3
+ IClauseBuilder,
4
+ ClauseOperator,
5
+ Value, Values,
6
+ WhereFn, WhereArgs,
7
+ DBSchema,
8
+ } from './types'
9
+
10
+ export default class ClauseBuilder<
11
+ T,
12
+ C extends keyof T = keyof T
13
+ > implements IClauseBuilder<T> {
14
+ #table: string
15
+ #schema?: DBSchema
16
+ #clauses: string[] = []
17
+ #args: Values = []
18
+
19
+ get clauses() {
20
+ return this.#clauses
21
+ }
22
+ set clauses(clauses: string[]) {
23
+ this.#clauses.push(...clauses)
24
+ }
25
+
26
+ get args() {
27
+ return this.#args
28
+ }
29
+ set args(args: Values) {
30
+ this.#args.push(...args)
31
+ }
32
+
33
+ get length() {
34
+ return this.#clauses.length
35
+ }
36
+
37
+ constructor(table: string, schema?: DBSchema) {
38
+ this.#table = table
39
+ this.#schema = schema
40
+ }
41
+
42
+ #nested(fn: WhereFn<T, C>, operator: ClauseOperator = 'AND') {
43
+ const nested = new ClauseBuilder<T, C>(this.#table, this.#schema)
44
+ fn(nested)
45
+
46
+ if (nested.length) {
47
+ this.#clauses.push(`${this.length ? operator +' ' : ''}(${nested.clauses.join(' ')})`)
48
+ this.#args.push(...nested.args)
49
+ }
50
+
51
+ return this
52
+ }
53
+
54
+ #clause(sql: string, values: Values = [], bool: ClauseOperator = 'AND') {
55
+ if (this.length) sql = bool +' '+ sql
56
+ this.#clauses.push(sql)
57
+
58
+ if (values?.length) // TODO: https://developers.cloudflare.com/d1/worker-api/#type-conversion
59
+ this.#args.push(...values)
60
+
61
+ return this
62
+ }
63
+
64
+ #where(
65
+ logical: ClauseOperator,
66
+ ...args: WhereArgs<T>
67
+ ) {
68
+ if (typeof args[0] == 'function')
69
+ return this.#nested(args[0], logical)
70
+
71
+ const length = args.length
72
+ let [column, operator, value] = args
73
+
74
+ if (length == 2) { // @ts-ignore
75
+ value = operator
76
+ operator = '='
77
+ }
78
+
79
+ // @ts-ignore
80
+ column = parseColumn(String(column), this.#table)
81
+
82
+ if (this.#schema && !zSame(column, value, this.#schema)) {
83
+ throw new Error(`Table column '${String(column)}' of type '${zType(column, this.#schema)}' is not assignable as type of '${typeof value}'.`)
84
+ }
85
+
86
+ return isJoinCompare(value, this.#schema) // @ts-ignore
87
+ ? this.#clause(`${column} ${operator} ${value}`, [], logical) // @ts-ignore
88
+ : this.#clause(`${column} ${operator} ?`, [value], logical)
89
+ }
90
+
91
+ where(...args: WhereArgs<T>) {
92
+ return this.#where('AND', ...args)
93
+ }
94
+ on(...args: WhereArgs<T>) {
95
+ return this.where(...args)
96
+ }
97
+
98
+ orWhere(...args: WhereArgs<T>) {
99
+ return this.#where('OR', ...args)
100
+ }
101
+ orOn(...args: WhereArgs<T>) {
102
+ return this.orWhere(...args)
103
+ }
104
+
105
+ #in(
106
+ column: string,
107
+ values: Values,
108
+ operator: 'IN' | 'NOT IN',
109
+ logical: ClauseOperator = 'AND'
110
+ ) {
111
+ if (!values?.length) return this
112
+ return this.#clause(parseColumn(column, this.#table) + ` ${operator} (${values.map(() => '?').join(', ')})`, values, logical)
113
+ }
114
+
115
+ whereIn(column: C, values: T[C][]) { // @ts-ignore
116
+ return this.#in(column, values, 'IN')
117
+ }
118
+ in(column: C, values: T[C][]) {
119
+ return this.whereIn(column, values)
120
+ }
121
+
122
+ whereNotIn(column: C, values: T[C][]) { // @ts-ignore
123
+ return this.#in(column, values, 'NOT IN')
124
+ }
125
+ notIn(column: C, values: T[C][]) {
126
+ return this.whereNotIn(column, values)
127
+ }
128
+
129
+ orWhereIn(column: C, values: T[C][]) { // @ts-ignore
130
+ return this.#in(column, values, 'IN', 'OR')
131
+ }
132
+ orIn(column: C, values: T[C][]) {
133
+ return this.orWhereIn(column, values)
134
+ }
135
+
136
+ orWhereNotIn(column: C, values: T[C][]) { // @ts-ignore
137
+ return this.#in(column, values, 'NOT IN', 'OR')
138
+ }
139
+ orNotIn(column: C, values: T[C][]) {
140
+ return this.orWhereNotIn(column, values)
141
+ }
142
+
143
+ #between(
144
+ column: string,
145
+ one: Value,
146
+ two: Value,
147
+ operator: 'BETWEEN' | 'NOT BETWEEN',
148
+ logical: ClauseOperator = 'AND'
149
+ ) {
150
+ return this.#clause(parseColumn(column, this.#table) + ` ${operator} ? AND ?`, [one, two], logical)
151
+ }
152
+
153
+ whereBetween(column: C, one: T[C], two: T[C]) { // @ts-ignore
154
+ return this.#between(column, one, two, 'BETWEEN')
155
+ }
156
+ between(column: C, one: T[C], two: T[C]) {
157
+ return this.whereBetween(column, one, two)
158
+ }
159
+
160
+ orWhereBetween(column: C, one: T[C], two: T[C]) { // @ts-ignore
161
+ return this.#between(column, one, two, 'BETWEEN', 'OR')
162
+ }
163
+ orBetween(column: C, one: T[C], two: T[C]) {
164
+ return this.orWhereBetween(column, one, two)
165
+ }
166
+
167
+ whereNotBetween(column: C, one: T[C], two: T[C]) { // @ts-ignore
168
+ return this.#between(column, one, two, 'NOT BETWEEN')
169
+ }
170
+ notBetween(column: C, one: T[C], two: T[C]) {
171
+ return this.whereNotBetween(column, one, two)
172
+ }
173
+
174
+ orWhereNotBetween(column: C, one: T[C], two: T[C]) { // @ts-ignore
175
+ return this.#between(column, one, two, 'NOT BETWEEN', 'OR')
176
+ }
177
+ orNotBetween(column: C, one: T[C], two: T[C]) {
178
+ return this.orWhereNotBetween(column, one, two)
179
+ }
180
+
181
+ #null(
182
+ column: string,
183
+ operator: 'IS' | 'IS NOT' = 'IS',
184
+ logical: ClauseOperator = 'AND'
185
+ ) {
186
+ return this.#clause(parseColumn(column, this.#table) +` ${operator} NULL`, [], logical)
187
+ }
188
+
189
+ whereNull(column: C) {
190
+ return this.#null(column as string)
191
+ }
192
+ onNull(column: C) {
193
+ return this.whereNull(column)
194
+ }
195
+
196
+ orWhereNull(column: C) {
197
+ return this.#null(column as string, 'IS', 'OR')
198
+ }
199
+ orOnNull(column: C) {
200
+ return this.orWhereNull(column)
201
+ }
202
+
203
+ whereNotNull(column: C) {
204
+ return this.#null(column as string, 'IS NOT')
205
+ }
206
+ onNotNull(column: C) {
207
+ return this.whereNotNull(column)
208
+ }
209
+
210
+ orWhereNotNull(column: C) {
211
+ return this.#null(column as string, 'IS NOT', 'OR')
212
+ }
213
+ orNotNull(column: C) {
214
+ return this.orWhereNotNull(column)
215
+ }
216
+ }
@@ -0,0 +1,4 @@
1
+
2
+ // export * from './migration'
3
+ // export * from './seeder'
4
+ export * from './model'
@@ -0,0 +1,79 @@
1
+ import type {
2
+ D1Database,
3
+ D1PreparedStatement,
4
+ D1Result, D1ExecResult, D1Meta,
5
+ } from '@cloudflare/workers-types'
6
+
7
+ import z from 'zod'
8
+
9
+ import { QueryBuilder } from '../query-builder'
10
+ import { default as BaseModel } from '../model'
11
+ import type {
12
+ DBSchema, SchemaKeys,
13
+ Item,
14
+ Pipe, Result, RunFn,
15
+ } from '../types'
16
+
17
+ export function ModelBuilder<
18
+ TSchema extends DBSchema,
19
+ TBase extends SchemaKeys<TSchema>
20
+ >(schema: TSchema, base: TBase) {
21
+ type S = z.infer<typeof schema>
22
+ return class extends Model<TBase, S> {
23
+ static $table = String(base)
24
+ static $schema = schema
25
+ }
26
+ }
27
+
28
+ export default ModelBuilder
29
+
30
+ export abstract class Model<TB extends keyof DB, DB> extends BaseModel<TB, DB> {
31
+ static $db: string | D1Database = 'DB'
32
+
33
+ static pipe<S, T>(): Pipe<S, T> {
34
+ const db = this.DB()
35
+
36
+ return {
37
+ run: this.run<S, T>(db)
38
+ }
39
+ }
40
+
41
+ static DB() {
42
+ if (typeof this.$db == 'string') { // TODO: improv compatibility without nodejs_compat
43
+ if (!(this.$db in process.env))
44
+ throw new Error(`Database '${this.$db}' instance not provided.`)
45
+
46
+ // @ts-ignore
47
+ return process.env[this.$db] as D1Database
48
+ }
49
+
50
+ return this.$db
51
+ }
52
+
53
+ static run<S, T>(db: D1Database): RunFn<S, T> {
54
+ return async <S, T, C extends keyof T = keyof T>(
55
+ qb: QueryBuilder<S, T, C>
56
+ ): Promise<Result<T, C>> => {
57
+ let stmt = db.prepare(qb.query)
58
+
59
+ if (qb.args?.length)
60
+ stmt = stmt.bind(...qb.args)
61
+
62
+ const resp = await stmt.run<Item<T, C>>()
63
+
64
+ const meta = resp.meta as any
65
+
66
+ return {
67
+ changes: meta?.changes,
68
+ duration: meta?.duration,
69
+ lastId: meta?.last_row_id,
70
+ // served_by: meta?.served_by,
71
+ rowsRead: meta?.rows_read,
72
+ rowsWritten: meta?.rows_written,
73
+ // meta: resp.meta,
74
+ success: resp.success,
75
+ results: resp.results,
76
+ }
77
+ }
78
+ }
79
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './query-builder'
package/src/model.ts ADDED
@@ -0,0 +1,258 @@
1
+ import pluralize from 'pluralize'
2
+ import { QueryBuilder } from './query-builder'
3
+ import type {
4
+ Operator, OrderDirection,
5
+ WhereFn, WhereArgs,
6
+ DBSchema, Pipe,
7
+ } from './types'
8
+
9
+ export default abstract class Model<TB extends keyof DB, DB> {
10
+ // Property only for the compiler (does not exist at runtime)
11
+ readonly $DBShape!: DB
12
+ readonly $TShape!: DB[TB]
13
+
14
+ static $table: string = ''
15
+ static $schema?: DBSchema
16
+
17
+ static pipe<S, T>(): Pipe<S, T> {
18
+ throw new Error(`Database connection not provided.`) // improv this message
19
+ }
20
+
21
+ static builder<S, T>() {
22
+ const table = this.$table || pluralize(this.name.toLowerCase())
23
+
24
+ return new QueryBuilder<S, T>(table, this.$schema, this.pipe<S, T>())
25
+ }
26
+
27
+ static select< // @ts-ignore
28
+ M extends typeof Model<TB, DB>,
29
+ I extends InstanceType<M>,
30
+ T extends I['$TShape'],
31
+ C extends keyof T
32
+ >(this: M, ...columns: C[] | C[][]) {
33
+ return this.builder<I['$DBShape'], T>().select(...columns)
34
+ }
35
+
36
+ static distinct< // @ts-ignore
37
+ M extends typeof Model<TB, DB>,
38
+ I extends InstanceType<M>,
39
+ T extends I['$TShape']
40
+ >(this: M) {
41
+ return this.builder<I['$DBShape'], T>().distinct()
42
+ }
43
+
44
+ static where< // @ts-ignore
45
+ M extends typeof Model<TB, DB>,
46
+ I extends InstanceType<M>,
47
+ T extends I['$TShape'],
48
+ C extends keyof T
49
+ >(this: M, fn: WhereFn<T>): QueryBuilder<I['$DBShape'], T, C>
50
+ static where< // @ts-ignore
51
+ M extends typeof Model<TB, DB>,
52
+ I extends InstanceType<M>,
53
+ T extends I['$TShape'],
54
+ C extends keyof T
55
+ >(this: M, column: C, value: T[C]): QueryBuilder<I['$DBShape'], T, C>
56
+ static where< // @ts-ignore
57
+ M extends typeof Model<TB, DB>,
58
+ I extends InstanceType<M>,
59
+ T extends I['$TShape'],
60
+ C extends keyof T
61
+ >(this: M, column: C, operator: Operator, value: T[C]): QueryBuilder<I['$DBShape'], T, C>
62
+ static where< // @ts-ignore
63
+ M extends typeof Model<TB, DB>,
64
+ I extends InstanceType<M>,
65
+ T extends I['$TShape']
66
+ >(this: M, ...args: WhereArgs<T>) {
67
+ return this.builder<I['$DBShape'], T>().where(...args)
68
+ }
69
+
70
+ static on< // @ts-ignore
71
+ M extends typeof Model<TB, DB>,
72
+ I extends InstanceType<M>,
73
+ T extends I['$TShape'],
74
+ C extends keyof T
75
+ >(this: M, fn: WhereFn<T>): QueryBuilder<I['$DBShape'], T, C>
76
+ static on< // @ts-ignore
77
+ M extends typeof Model<TB, DB>,
78
+ I extends InstanceType<M>,
79
+ T extends I['$TShape'],
80
+ C extends keyof T
81
+ >(this: M, column: C, value: T[C]): QueryBuilder<I['$DBShape'], T, C>
82
+ static on< // @ts-ignore
83
+ M extends typeof Model<TB, DB>,
84
+ I extends InstanceType<M>,
85
+ T extends I['$TShape'],
86
+ C extends keyof T
87
+ >(this: M, column: C, operator: Operator, value: T[C]): QueryBuilder<I['$DBShape'], T, C>
88
+ static on< // @ts-ignore
89
+ M extends typeof Model<TB, DB>,
90
+ I extends InstanceType<M>,
91
+ T extends I['$TShape']
92
+ >(this: M, ...args: WhereArgs<T>) {
93
+ return this.builder<I['$DBShape'], T>().where(...args)
94
+ }
95
+
96
+ static whereIn< // @ts-ignore
97
+ M extends typeof Model<TB, DB>,
98
+ I extends InstanceType<M>,
99
+ T extends I['$TShape'],
100
+ C extends keyof T
101
+ >(this: M, column: C, value: T[C][]) {
102
+ return this.builder<I['$DBShape'], T>().whereIn(column, value)
103
+ }
104
+
105
+ static in< // @ts-ignore
106
+ M extends typeof Model<TB, DB>,
107
+ I extends InstanceType<M>,
108
+ T extends I['$TShape'],
109
+ C extends keyof T
110
+ >(this: M, column: C, value: T[C][]) {
111
+ return this.builder<I['$DBShape'], T>().whereIn(column, value)
112
+ }
113
+
114
+ static whereNotIn< // @ts-ignore
115
+ M extends typeof Model<TB, DB>,
116
+ I extends InstanceType<M>,
117
+ T extends I['$TShape'],
118
+ C extends keyof T
119
+ >(this: M, column: C, value: T[C][]) {
120
+ return this.builder<I['$DBShape'], T>().whereNotIn(column, value)
121
+ }
122
+
123
+ static notIn< // @ts-ignore
124
+ M extends typeof Model<TB, DB>,
125
+ I extends InstanceType<M>,
126
+ T extends I['$TShape'],
127
+ C extends keyof T
128
+ >(this: M, column: C, value: T[C][]) {
129
+ return this.builder<I['$DBShape'], T>().whereNotIn(column, value)
130
+ }
131
+
132
+ static whereBetween< // @ts-ignore
133
+ M extends typeof Model<TB, DB>,
134
+ I extends InstanceType<M>,
135
+ T extends I['$TShape'],
136
+ C extends keyof T
137
+ >(this: M, column: C, one: T[C], two: T[C]) {
138
+ return this.builder<I['$DBShape'], T>().whereBetween(column, one, two)
139
+ }
140
+
141
+ static between< // @ts-ignore
142
+ M extends typeof Model<TB, DB>,
143
+ I extends InstanceType<M>,
144
+ T extends I['$TShape'],
145
+ C extends keyof T
146
+ >(this: M, column: C, one: T[C], two: T[C]) {
147
+ return this.builder<I['$DBShape'], T>().whereBetween(column, one, two)
148
+ }
149
+
150
+ static whereNotBetween< // @ts-ignore
151
+ M extends typeof Model<TB, DB>,
152
+ I extends InstanceType<M>,
153
+ T extends I['$TShape'],
154
+ C extends keyof T
155
+ >(this: M, column: C, one: T[C], two: T[C]) {
156
+ return this.builder<I['$DBShape'], T>().whereNotBetween(column, one, two)
157
+ }
158
+
159
+ static notBetween< // @ts-ignore
160
+ M extends typeof Model<TB, DB>,
161
+ I extends InstanceType<M>,
162
+ T extends I['$TShape'],
163
+ C extends keyof T
164
+ >(this: M, column: C, one: T[C], two: T[C]) {
165
+ return this.builder<I['$DBShape'], T>().whereNotBetween(column, one, two)
166
+ }
167
+
168
+ static whereNull< // @ts-ignore
169
+ M extends typeof Model<TB, DB>,
170
+ I extends InstanceType<M>,
171
+ T extends I['$TShape'],
172
+ C extends keyof T
173
+ >(this: M, column: C) {
174
+ return this.builder<I['$DBShape'], T>().whereNull(column)
175
+ }
176
+
177
+ static onNull< // @ts-ignore
178
+ M extends typeof Model<TB, DB>,
179
+ I extends InstanceType<M>,
180
+ T extends I['$TShape'],
181
+ C extends keyof T
182
+ >(this: M, column: C) {
183
+ return this.builder<I['$DBShape'], T>().whereNull(column)
184
+ }
185
+
186
+ static whereNotNull< // @ts-ignore
187
+ M extends typeof Model<TB, DB>,
188
+ I extends InstanceType<M>,
189
+ T extends I['$TShape'],
190
+ C extends keyof T
191
+ >(this: M, column: C) {
192
+ return this.builder<I['$DBShape'], T>().whereNotNull(column)
193
+ }
194
+
195
+ static onNotNull< // @ts-ignore
196
+ M extends typeof Model<TB, DB>,
197
+ I extends InstanceType<M>,
198
+ T extends I['$TShape'],
199
+ C extends keyof T
200
+ >(this: M, column: C) {
201
+ return this.builder<I['$DBShape'], T>().whereNotNull(column)
202
+ }
203
+
204
+ // TODO: groupBy(...columns: string[])
205
+
206
+ static order< // @ts-ignore
207
+ M extends typeof Model<TB, DB>,
208
+ I extends InstanceType<M>,
209
+ T extends I['$TShape'],
210
+ C extends keyof T
211
+ >(this: M, column: C, direction: OrderDirection = 'ASC') {
212
+ return this.builder<I['$DBShape'], T>().order(column, direction)
213
+ }
214
+
215
+ static orderBy< // @ts-ignore
216
+ M extends typeof Model<TB, DB>,
217
+ I extends InstanceType<M>,
218
+ T extends I['$TShape'],
219
+ C extends keyof T
220
+ >(this: M, column: C, direction: OrderDirection = 'ASC') {
221
+ return this.builder<I['$DBShape'], T>().order(column, direction)
222
+ }
223
+
224
+ static asc< // @ts-ignore
225
+ M extends typeof Model<TB, DB>,
226
+ I extends InstanceType<M>,
227
+ T extends I['$TShape'],
228
+ C extends keyof T
229
+ >(this: M, column: C) {
230
+ return this.builder<I['$DBShape'], T>().asc(column)
231
+ }
232
+
233
+ static desc< // @ts-ignore
234
+ M extends typeof Model<TB, DB>,
235
+ I extends InstanceType<M>,
236
+ T extends I['$TShape'],
237
+ C extends keyof T
238
+ >(this: M, column: C) {
239
+ return this.builder<I['$DBShape'], T>().desc(column)
240
+ }
241
+
242
+ static limit< // @ts-ignore
243
+ M extends typeof Model<TB, DB>,
244
+ I extends InstanceType<M>,
245
+ T extends I['$TShape']
246
+ >(this: M, val: number | string) {
247
+ return this.builder<I['$DBShape'], T>().limit(val)
248
+ }
249
+
250
+ static offset< // @ts-ignore
251
+ M extends typeof Model<TB, DB>,
252
+ I extends InstanceType<M>,
253
+ T extends I['$TShape']
254
+ >(this: M, val: number | string) {
255
+ return this.builder<I['$DBShape'], T>().offset(val)
256
+ }
257
+
258
+ }