forj 0.1.6 → 0.1.8
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 +10 -18
- package/src/clause-builder.ts +26 -26
- package/src/d1/model.ts +7 -6
- package/src/dynamodb/repository.ts +2 -2
- package/src/dynamodb/schema.ts +50 -22
- package/src/model.ts +270 -14
- package/src/query-builder.ts +245 -43
- package/src/types.ts +78 -58
- package/src/utils.ts +130 -40
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forj",
|
|
3
3
|
"description": "SQLite ORM and Query Builder whitout dependencies",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.8",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"files": ["src"],
|
|
@@ -13,38 +13,30 @@
|
|
|
13
13
|
"./dynamodb/types": "./src/dynamodb/types.ts",
|
|
14
14
|
"./migrate": "./src/migrations/index.ts"
|
|
15
15
|
},
|
|
16
|
-
"-exports": {
|
|
17
|
-
".": {
|
|
18
|
-
"import": "./dist/index.js",
|
|
19
|
-
"types": "./dist/index.d.ts"
|
|
20
|
-
},
|
|
21
|
-
"./d1": {
|
|
22
|
-
"import": "./dist/d1.js",
|
|
23
|
-
"types": "./dist/d1.d.ts"
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
16
|
"scripts": {
|
|
27
17
|
"bench": "tsx tests/benchmark/*.bench.ts",
|
|
28
18
|
"build": "tsup",
|
|
29
19
|
"-prepublishOnly": "bun run build"
|
|
30
20
|
},
|
|
31
21
|
"dependencies": {
|
|
32
|
-
"@aws-sdk/client-dynamodb": "3.
|
|
33
|
-
"@aws-sdk/lib-dynamodb": "3.
|
|
22
|
+
"@aws-sdk/client-dynamodb": "^3.1003.0",
|
|
23
|
+
"@aws-sdk/lib-dynamodb": "^3.1003.0",
|
|
34
24
|
"pluralize": "^8.0",
|
|
35
|
-
"t0n": "^0.1",
|
|
36
|
-
"zod": "^3.
|
|
25
|
+
"t0n": "^0.1.12",
|
|
26
|
+
"zod": "^4.3.6"
|
|
37
27
|
},
|
|
38
28
|
"devDependencies": {
|
|
29
|
+
"@types/node": "^25.1.0",
|
|
39
30
|
"@types/pluralize": "^0.0.33",
|
|
40
31
|
"@cloudflare/workers-types": "^4.20260113.0",
|
|
41
|
-
"bun-types": "^1.
|
|
32
|
+
"bun-types": "^1.3.8",
|
|
42
33
|
"terser": "^5.46.0",
|
|
43
34
|
"tiny-glob": "^0.2",
|
|
44
|
-
"tsup": "^8.5.
|
|
35
|
+
"tsup": "^8.5.0",
|
|
45
36
|
"tsx": "^4.19.4",
|
|
46
|
-
"typescript": "^5.
|
|
37
|
+
"typescript": "^5.9.3"
|
|
47
38
|
},
|
|
39
|
+
"packageManager": "bun@1.3.10",
|
|
48
40
|
"engines": {
|
|
49
41
|
"node": ">=18.0.0"
|
|
50
42
|
},
|
package/src/clause-builder.ts
CHANGED
|
@@ -39,7 +39,7 @@ export default class ClauseBuilder<
|
|
|
39
39
|
this.#schema = schema
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
#nested(fn: WhereFn<T
|
|
42
|
+
#nested(fn: WhereFn<T>, operator: ClauseOperator = 'AND') {
|
|
43
43
|
const nested = new ClauseBuilder<T, C>(this.#table, this.#schema)
|
|
44
44
|
fn(nested)
|
|
45
45
|
|
|
@@ -80,7 +80,7 @@ export default class ClauseBuilder<
|
|
|
80
80
|
column = parseColumn(String(column), this.#table)
|
|
81
81
|
|
|
82
82
|
if (this.#schema && !zSame(column.replace(/"/g, ''), 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}'.`)
|
|
83
|
+
throw new Error(`Table column '${String(column.replace(/"/g, ''))}' of type '${zType(column.replace(/"/g, ''), this.#schema)}' is not assignable as type of '${typeof value}'.`)
|
|
84
84
|
|
|
85
85
|
return isJoinCompare(value, this.#schema) // @ts-ignore
|
|
86
86
|
? this.#clause(`${column} ${operator} ${value}`, [], logical) // @ts-ignore
|
|
@@ -111,31 +111,31 @@ export default class ClauseBuilder<
|
|
|
111
111
|
return this.#clause(parseColumn(column, this.#table) + ` ${operator} (${values.map(() => '?').join(', ')})`, values, logical)
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
whereIn(column:
|
|
114
|
+
whereIn<K extends keyof T>(column: K, values: T[K][]) { // @ts-ignore
|
|
115
115
|
return this.#in(column, values, 'IN')
|
|
116
116
|
}
|
|
117
|
-
in(column:
|
|
117
|
+
in<K extends keyof T>(column: K, values: T[K][]) {
|
|
118
118
|
return this.whereIn(column, values)
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
whereNotIn(column:
|
|
121
|
+
whereNotIn<K extends keyof T>(column: K, values: T[K][]) { // @ts-ignore
|
|
122
122
|
return this.#in(column, values, 'NOT IN')
|
|
123
123
|
}
|
|
124
|
-
notIn(column:
|
|
124
|
+
notIn<K extends keyof T>(column: K, values: T[K][]) {
|
|
125
125
|
return this.whereNotIn(column, values)
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
orWhereIn(column:
|
|
128
|
+
orWhereIn<K extends keyof T>(column: K, values: T[K][]) { // @ts-ignore
|
|
129
129
|
return this.#in(column, values, 'IN', 'OR')
|
|
130
130
|
}
|
|
131
|
-
orIn(column:
|
|
131
|
+
orIn<K extends keyof T>(column: K, values: T[K][]) {
|
|
132
132
|
return this.orWhereIn(column, values)
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
orWhereNotIn(column:
|
|
135
|
+
orWhereNotIn<K extends keyof T>(column: K, values: T[K][]) { // @ts-ignore
|
|
136
136
|
return this.#in(column, values, 'NOT IN', 'OR')
|
|
137
137
|
}
|
|
138
|
-
orNotIn(column:
|
|
138
|
+
orNotIn<K extends keyof T>(column: K, values: T[K][]) {
|
|
139
139
|
return this.orWhereNotIn(column, values)
|
|
140
140
|
}
|
|
141
141
|
|
|
@@ -149,31 +149,31 @@ export default class ClauseBuilder<
|
|
|
149
149
|
return this.#clause(parseColumn(column, this.#table) + ` ${operator} ? AND ?`, [one, two], logical)
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
whereBetween(column:
|
|
152
|
+
whereBetween<K extends keyof T>(column: K, one: T[K], two: T[K]) { // @ts-ignore
|
|
153
153
|
return this.#between(column, one, two, 'BETWEEN')
|
|
154
154
|
}
|
|
155
|
-
between(column:
|
|
155
|
+
between<K extends keyof T>(column: K, one: T[K], two: T[K]) {
|
|
156
156
|
return this.whereBetween(column, one, two)
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
orWhereBetween(column:
|
|
159
|
+
orWhereBetween<K extends keyof T>(column: K, one: T[K], two: T[K]) { // @ts-ignore
|
|
160
160
|
return this.#between(column, one, two, 'BETWEEN', 'OR')
|
|
161
161
|
}
|
|
162
|
-
orBetween(column:
|
|
162
|
+
orBetween<K extends keyof T>(column: K, one: T[K], two: T[K]) {
|
|
163
163
|
return this.orWhereBetween(column, one, two)
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
whereNotBetween(column:
|
|
166
|
+
whereNotBetween<K extends keyof T>(column: K, one: T[K], two: T[K]) { // @ts-ignore
|
|
167
167
|
return this.#between(column, one, two, 'NOT BETWEEN')
|
|
168
168
|
}
|
|
169
|
-
notBetween(column:
|
|
169
|
+
notBetween<K extends keyof T>(column: K, one: T[K], two: T[K]) {
|
|
170
170
|
return this.whereNotBetween(column, one, two)
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
orWhereNotBetween(column:
|
|
173
|
+
orWhereNotBetween<K extends keyof T>(column: K, one: T[K], two: T[K]) { // @ts-ignore
|
|
174
174
|
return this.#between(column, one, two, 'NOT BETWEEN', 'OR')
|
|
175
175
|
}
|
|
176
|
-
orNotBetween(column:
|
|
176
|
+
orNotBetween<K extends keyof T>(column: K, one: T[K], two: T[K]) {
|
|
177
177
|
return this.orWhereNotBetween(column, one, two)
|
|
178
178
|
}
|
|
179
179
|
|
|
@@ -185,31 +185,31 @@ export default class ClauseBuilder<
|
|
|
185
185
|
return this.#clause(parseColumn(column, this.#table) +` ${operator} NULL`, [], logical)
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
whereNull(column:
|
|
188
|
+
whereNull<K extends keyof T>(column: K) {
|
|
189
189
|
return this.#null(column as string)
|
|
190
190
|
}
|
|
191
|
-
onNull(column:
|
|
191
|
+
onNull<K extends keyof T>(column: K) {
|
|
192
192
|
return this.whereNull(column)
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
orWhereNull(column:
|
|
195
|
+
orWhereNull<K extends keyof T>(column: K) {
|
|
196
196
|
return this.#null(column as string, 'IS', 'OR')
|
|
197
197
|
}
|
|
198
|
-
orOnNull(column:
|
|
198
|
+
orOnNull<K extends keyof T>(column: K) {
|
|
199
199
|
return this.orWhereNull(column)
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
whereNotNull(column:
|
|
202
|
+
whereNotNull<K extends keyof T>(column: K) {
|
|
203
203
|
return this.#null(column as string, 'IS NOT')
|
|
204
204
|
}
|
|
205
|
-
onNotNull(column:
|
|
205
|
+
onNotNull<K extends keyof T>(column: K) {
|
|
206
206
|
return this.whereNotNull(column)
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
-
orWhereNotNull(column:
|
|
209
|
+
orWhereNotNull<K extends keyof T>(column: K) {
|
|
210
210
|
return this.#null(column as string, 'IS NOT', 'OR')
|
|
211
211
|
}
|
|
212
|
-
orNotNull(column:
|
|
212
|
+
orNotNull<K extends keyof T>(column: K) {
|
|
213
213
|
return this.orWhereNotNull(column)
|
|
214
214
|
}
|
|
215
215
|
}
|
package/src/d1/model.ts
CHANGED
|
@@ -4,22 +4,22 @@ import type {
|
|
|
4
4
|
// D1Result, D1ExecResult, D1Meta,
|
|
5
5
|
} from './types'
|
|
6
6
|
|
|
7
|
-
import z from 'zod'
|
|
7
|
+
import type * as z from 'zod'
|
|
8
8
|
import { Envir } from 't0n'
|
|
9
9
|
|
|
10
10
|
import QueryBuilder from '../query-builder'
|
|
11
11
|
import BModel from '../model'
|
|
12
12
|
import type {
|
|
13
|
-
DBSchema,
|
|
13
|
+
DBSchema,
|
|
14
14
|
Item,
|
|
15
15
|
Pipe, Result, RunFn,
|
|
16
16
|
} from '../types'
|
|
17
17
|
|
|
18
18
|
export function Model<
|
|
19
19
|
TSchema extends DBSchema,
|
|
20
|
-
TBase extends
|
|
20
|
+
TBase extends keyof z.infer<TSchema>
|
|
21
21
|
>(schema: TSchema, base: TBase) {
|
|
22
|
-
type S = z.infer<
|
|
22
|
+
type S = z.infer<TSchema>
|
|
23
23
|
return class extends BaseModel<TBase, S> {
|
|
24
24
|
static $table = String(base)
|
|
25
25
|
static $schema = schema
|
|
@@ -55,8 +55,9 @@ export abstract class BaseModel<TB extends keyof DB, DB> extends BModel<TB, DB>
|
|
|
55
55
|
): Promise<Result<T, C>> => {
|
|
56
56
|
let stmt = db.prepare(qb.query)
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
const args = qb.args
|
|
59
|
+
if (args?.length)
|
|
60
|
+
stmt = stmt.bind(...args)
|
|
60
61
|
|
|
61
62
|
const resp = await stmt.run<Item<T, C>>()
|
|
62
63
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type * as z from 'zod'
|
|
2
2
|
import { Dynamodb } from './client'
|
|
3
3
|
import { Schema } from './schema'
|
|
4
4
|
import { _model } from './decorators'
|
|
5
5
|
import type { ModelOpts } from './types'
|
|
6
6
|
|
|
7
7
|
export function Repository<
|
|
8
|
-
S extends ZodTypeAny,
|
|
8
|
+
S extends z.ZodTypeAny,
|
|
9
9
|
B extends new (...args: any[]) => any
|
|
10
10
|
>(
|
|
11
11
|
schema: S,
|
package/src/dynamodb/schema.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as z from 'zod'
|
|
2
2
|
import type { SchemaStructure } from './types'
|
|
3
3
|
|
|
4
4
|
const m = Symbol('a')
|
|
@@ -12,17 +12,26 @@ export function arraySchema(v: any): any {
|
|
|
12
12
|
return v
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
function getArrayItem(schema: z.ZodArray<any>): z.ZodTypeAny {
|
|
16
|
+
const def: any = schema._def
|
|
17
|
+
return (def.element ?? def.type ?? def.innerType) as z.ZodTypeAny
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function extractZodKeys(schema: z.ZodTypeAny): SchemaStructure {
|
|
16
21
|
if (schema instanceof z.ZodObject) {
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
const shape = schema.shape
|
|
23
|
+
|
|
24
|
+
return Object.entries(shape).map(([key, value]) => {
|
|
25
|
+
const inner = unwrap(value as z.ZodTypeAny)
|
|
19
26
|
|
|
20
27
|
if (inner instanceof z.ZodObject)
|
|
21
28
|
return notEmpty(key, extractZodKeys(inner))
|
|
22
29
|
|
|
23
30
|
if (inner instanceof z.ZodArray) {
|
|
24
|
-
const item = unwrap(inner
|
|
25
|
-
return item instanceof z.ZodObject
|
|
31
|
+
const item = unwrap(getArrayItem(inner))
|
|
32
|
+
return item instanceof z.ZodObject
|
|
33
|
+
? notEmpty(key, extractZodKeys(item))
|
|
34
|
+
: key
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
return key
|
|
@@ -30,7 +39,8 @@ export function extractZodKeys(schema: ZodTypeAny): SchemaStructure {
|
|
|
30
39
|
}
|
|
31
40
|
|
|
32
41
|
if (schema instanceof z.ZodArray) {
|
|
33
|
-
const item = unwrap(schema
|
|
42
|
+
const item = unwrap(getArrayItem(schema))
|
|
43
|
+
|
|
34
44
|
if (item instanceof z.ZodObject)
|
|
35
45
|
return arraySchema(extractZodKeys(item))
|
|
36
46
|
|
|
@@ -40,24 +50,42 @@ export function extractZodKeys(schema: ZodTypeAny): SchemaStructure {
|
|
|
40
50
|
return []
|
|
41
51
|
}
|
|
42
52
|
|
|
43
|
-
export function unwrap(schema: ZodTypeAny): ZodTypeAny {
|
|
44
|
-
|
|
45
|
-
|
|
53
|
+
export function unwrap(schema: z.ZodTypeAny): z.ZodTypeAny {
|
|
54
|
+
while (true) {
|
|
55
|
+
if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) {
|
|
56
|
+
schema = (schema._def as any).innerType
|
|
57
|
+
continue
|
|
58
|
+
}
|
|
46
59
|
|
|
47
|
-
|
|
48
|
-
|
|
60
|
+
if (schema instanceof z.ZodDefault) {
|
|
61
|
+
schema = (schema._def as any).innerType
|
|
62
|
+
continue
|
|
63
|
+
}
|
|
49
64
|
|
|
50
|
-
|
|
51
|
-
|
|
65
|
+
if (schema instanceof z.ZodUnion) {
|
|
66
|
+
const options = (schema._def as any).options as z.ZodTypeAny[]
|
|
67
|
+
const nonEmpty = options.find(
|
|
68
|
+
opt => !(opt instanceof z.ZodUndefined) && !(opt instanceof z.ZodNull)
|
|
69
|
+
)
|
|
70
|
+
schema = nonEmpty ?? options[0]
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
52
73
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
74
|
+
const def: any = schema._def
|
|
75
|
+
|
|
76
|
+
// unwrap transforms / pipes do v4
|
|
77
|
+
if (def?.schema) {
|
|
78
|
+
schema = def.schema
|
|
79
|
+
continue
|
|
80
|
+
}
|
|
58
81
|
|
|
59
|
-
|
|
60
|
-
|
|
82
|
+
if (def?.innerType) {
|
|
83
|
+
schema = def.innerType
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
break
|
|
88
|
+
}
|
|
61
89
|
|
|
62
90
|
return schema
|
|
63
91
|
}
|
|
@@ -67,7 +95,7 @@ function notEmpty(key: string, schema: SchemaStructure): string | Record<string,
|
|
|
67
95
|
}
|
|
68
96
|
|
|
69
97
|
export function Schema<
|
|
70
|
-
T extends ZodTypeAny,
|
|
98
|
+
T extends z.ZodTypeAny,
|
|
71
99
|
B extends object
|
|
72
100
|
>(
|
|
73
101
|
schema: T,
|