@apisr/drizzle-model 0.0.4 → 2.0.0

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.
Files changed (42) hide show
  1. package/DISCLAIMER.md +5 -0
  2. package/TODO.md +8 -61
  3. package/package.json +2 -1
  4. package/src/core/dialect.ts +81 -0
  5. package/src/core/index.ts +24 -0
  6. package/src/core/query/error.ts +15 -0
  7. package/src/core/query/joins.ts +596 -0
  8. package/src/core/query/projection.ts +136 -0
  9. package/src/core/query/where.ts +449 -0
  10. package/src/core/result.ts +297 -0
  11. package/src/core/runtime.ts +612 -0
  12. package/src/core/transform.ts +119 -0
  13. package/src/model/builder.ts +40 -6
  14. package/src/model/config.ts +9 -9
  15. package/src/model/format.ts +20 -8
  16. package/src/model/methods/exclude.ts +1 -7
  17. package/src/model/methods/return.ts +11 -11
  18. package/src/model/methods/select.ts +2 -8
  19. package/src/model/model.ts +10 -16
  20. package/src/model/query/error.ts +1 -0
  21. package/src/model/result.ts +134 -21
  22. package/src/types.ts +38 -0
  23. package/tests/base/count.test.ts +47 -0
  24. package/tests/base/delete.test.ts +90 -0
  25. package/tests/base/find.test.ts +209 -0
  26. package/tests/base/insert.test.ts +152 -0
  27. package/tests/base/safe.test.ts +91 -0
  28. package/tests/base/update.test.ts +88 -0
  29. package/tests/base/upsert.test.ts +121 -0
  30. package/tests/base.ts +21 -0
  31. package/src/model/core/joins.ts +0 -364
  32. package/src/model/core/projection.ts +0 -61
  33. package/src/model/core/runtime.ts +0 -334
  34. package/src/model/core/thenable.ts +0 -94
  35. package/src/model/core/transform.ts +0 -65
  36. package/src/model/core/where.ts +0 -249
  37. package/src/model/core/with.ts +0 -28
  38. package/tests/builder-v2-mysql.type-test.ts +0 -51
  39. package/tests/builder-v2.type-test.ts +0 -336
  40. package/tests/builder.test.ts +0 -63
  41. package/tests/find.test.ts +0 -166
  42. package/tests/insert.test.ts +0 -247
@@ -1,334 +0,0 @@
1
- import { and } from "drizzle-orm";
2
- import type { ModelDialect, ReturningIdDialects } from "../dialect.ts";
3
- import type { MethodWhereValue } from "../methods/query/where.ts";
4
- import type { MethodUpsertValue } from "../methods/upsert.ts";
5
- import type { MethodWithValue } from "../methods/with.ts";
6
- import type { ModelOptions } from "../options.ts";
7
- import { buildSelectProjection } from "./projection.ts";
8
- import type { MutateState, QueryState } from "./thenable.ts";
9
- import { MutateResult, QueryResult } from "./thenable.ts";
10
- import { applyExclude, applyFormat, applySelect } from "./transform.ts";
11
- import { compileWhere } from "./where.ts";
12
- import { runWithJoins } from "./with.ts";
13
-
14
- type AnyObj = Record<string, any>;
15
-
16
- type MutateKind = "insert" | "update" | "delete" | "upsert";
17
-
18
- type BaseState = {
19
- db: any;
20
- where: unknown;
21
- };
22
-
23
- // TODO: can be broken...
24
- function compileEffectiveWhere(
25
- table: AnyObj,
26
- optionsWhere: unknown,
27
- stateWhere: unknown
28
- ) {
29
- const base = compileWhere(table, optionsWhere);
30
- const extra = compileWhere(table, stateWhere);
31
- if (base && extra) {
32
- return and(base as any, extra as any);
33
- }
34
- return (base ?? extra) as any;
35
- }
36
-
37
- function isReturningIdDialect(dialect: string): dialect is ReturningIdDialects {
38
- return (
39
- dialect === "MySQL" ||
40
- dialect === "SingleStore" ||
41
- dialect === "CockroachDB"
42
- );
43
- }
44
-
45
- async function execReturn(
46
- q: any,
47
- mState: MutateState,
48
- dialect: string
49
- ): Promise<any> {
50
- if (typeof q?.returning === "function") {
51
- return await (mState.returnSelect
52
- ? q.returning(mState.returnSelect)
53
- : q.returning());
54
- }
55
- if (isReturningIdDialect(dialect) && typeof q?.$returningId === "function") {
56
- return await q.$returningId();
57
- }
58
- return await q;
59
- }
60
-
61
- function normalizeUpsertTarget(table: AnyObj, target: any): any {
62
- if (!target) {
63
- return target;
64
- }
65
- if (typeof target === "string") {
66
- return (table as any)[target] ?? target;
67
- }
68
- if (Array.isArray(target)) {
69
- return target.map((t) =>
70
- typeof t === "string" ? ((table as any)[t] ?? t) : t
71
- );
72
- }
73
- return target;
74
- }
75
-
76
- export function makeModelRuntime(config: {
77
- db: any;
78
- schema: Record<string, any>;
79
- relations: Record<string, any>;
80
- tableName: string;
81
- dialect: ModelDialect;
82
- options: ModelOptions<any, any, any, any>;
83
- }): any {
84
- const baseState: BaseState = {
85
- db: config.db,
86
- where: undefined as unknown,
87
- };
88
-
89
- const build = (state: BaseState): any => {
90
- const modelObj: AnyObj = {
91
- $model: "model",
92
- $modelName: config.tableName,
93
- $format: config.options.format,
94
- $formatValue: undefined,
95
- where(value: MethodWhereValue<any, any>) {
96
- return build({ ...state, where: value });
97
- },
98
- include(value: MethodWithValue<any, any>) {
99
- return value;
100
- },
101
- extend(nextOptions: any) {
102
- return makeModelRuntime({
103
- ...config,
104
- options: {
105
- ...config.options,
106
- ...nextOptions,
107
- methods: {
108
- ...(nextOptions?.methods ?? {}),
109
- ...(config.options?.methods ?? {}),
110
- },
111
- format: nextOptions?.format ?? config.options?.format,
112
- },
113
- });
114
- },
115
- db(db: any) {
116
- return makeModelRuntime({ ...config, db });
117
- },
118
- };
119
-
120
- const attachMethods = (methods: AnyObj | undefined) => {
121
- if (!methods) {
122
- return;
123
- }
124
- for (const [key, fn] of Object.entries(methods)) {
125
- if (typeof fn === "function") {
126
- (modelObj as any)[key] = fn.bind(modelObj);
127
- }
128
- }
129
- };
130
-
131
- attachMethods(config.options.methods);
132
-
133
- modelObj.findMany = () => {
134
- const runner = async (qState: QueryState) => {
135
- const table = (config.schema as any)[config.tableName];
136
- const whereSql = compileEffectiveWhere(
137
- table as AnyObj,
138
- config.options.where,
139
- state.where
140
- );
141
-
142
- let result: any;
143
- if (qState.with) {
144
- result = await runWithJoins({
145
- db: config.db,
146
- schema: config.schema,
147
- relations: config.relations,
148
- tableName: config.tableName,
149
- table,
150
- dialect: config.dialect,
151
- whereSql,
152
- qState,
153
- kind: "many",
154
- });
155
- } else {
156
- const { selectMap } = buildSelectProjection(
157
- table as AnyObj,
158
- qState.select as any,
159
- qState.exclude as any
160
- );
161
- let q = (config.db as any).select(selectMap).from(table);
162
- if (whereSql) {
163
- q = q.where(whereSql);
164
- }
165
- result = await q;
166
- }
167
-
168
- let out: any = result;
169
- if (qState.select) {
170
- out = applySelect(out, qState.select);
171
- }
172
- if (qState.exclude) {
173
- out = applyExclude(out, qState.exclude);
174
- }
175
- if (!qState.raw) {
176
- out = applyFormat(out, config.options.format);
177
- }
178
- return out;
179
- };
180
-
181
- return new QueryResult({} as QueryState, runner) as any;
182
- };
183
-
184
- modelObj.findFirst = () => {
185
- const runner = async (qState: QueryState) => {
186
- const table = (config.schema as any)[config.tableName];
187
- const whereSql = compileEffectiveWhere(
188
- table as AnyObj,
189
- config.options.where,
190
- state.where
191
- );
192
-
193
- let result: any;
194
- if (qState.with) {
195
- result = await runWithJoins({
196
- db: config.db,
197
- schema: config.schema,
198
- relations: config.relations,
199
- tableName: config.tableName,
200
- table,
201
- dialect: config.dialect,
202
- whereSql,
203
- qState,
204
- kind: "one",
205
- });
206
- } else {
207
- const { selectMap } = buildSelectProjection(
208
- table as AnyObj,
209
- qState.select as any,
210
- qState.exclude as any
211
- );
212
- let q = (config.db as any).select(selectMap).from(table);
213
- if (whereSql) {
214
- q = q.where(whereSql);
215
- }
216
- q = q.limit(1);
217
- const rows = await q;
218
- result = rows[0];
219
- }
220
-
221
- let out: any = result;
222
- if (qState.select) {
223
- out = applySelect(out, qState.select);
224
- }
225
- if (qState.exclude) {
226
- out = applyExclude(out, qState.exclude);
227
- }
228
- if (!qState.raw) {
229
- out = applyFormat(out, config.options.format);
230
- }
231
- return out;
232
- };
233
-
234
- return new QueryResult({} as QueryState, runner) as any;
235
- };
236
-
237
- modelObj.insert = (value: any) => {
238
- const runner = async (mState: MutateState) => {
239
- const table = (config.schema as any)[config.tableName];
240
- const q = (config.db as any).insert(table).values(mState.value);
241
- const result = await execReturn(q, mState, config.dialect);
242
- if (!Array.isArray(mState.value) && Array.isArray(result)) {
243
- return result[0];
244
- }
245
- return result;
246
- };
247
-
248
- return new MutateResult(
249
- { kind: "insert" as MutateKind, value } as MutateState,
250
- runner
251
- ) as any;
252
- };
253
-
254
- modelObj.update = (value: any) => {
255
- const runner = async (mState: MutateState) => {
256
- const table = (config.schema as any)[config.tableName];
257
- const whereSql = compileEffectiveWhere(
258
- table as AnyObj,
259
- config.options.where,
260
- state.where
261
- );
262
- let q = (config.db as any).update(table).set(mState.value);
263
- if (whereSql) {
264
- q = q.where(whereSql);
265
- }
266
- return await execReturn(q, mState, config.dialect);
267
- };
268
-
269
- return new MutateResult(
270
- { kind: "update" as MutateKind, value } as MutateState,
271
- runner
272
- ) as any;
273
- };
274
-
275
- modelObj.delete = () => {
276
- const runner = async (mState: MutateState) => {
277
- const table = (config.schema as any)[config.tableName];
278
- const whereSql = compileEffectiveWhere(
279
- table as AnyObj,
280
- config.options.where,
281
- state.where
282
- );
283
- let q = (config.db as any).delete(table);
284
- if (whereSql) {
285
- q = q.where(whereSql);
286
- }
287
- return await execReturn(q, mState, config.dialect);
288
- };
289
-
290
- return new MutateResult(
291
- { kind: "delete" as MutateKind } as MutateState,
292
- runner
293
- ) as any;
294
- };
295
-
296
- modelObj.upsert = (value: MethodUpsertValue<any>) => {
297
- const runner = async (mState: MutateState) => {
298
- const table = (config.schema as any)[config.tableName];
299
- const insertValues = (mState.value as any).insert;
300
- const updateCfg = (mState.value as any).update;
301
- const target = normalizeUpsertTarget(
302
- table as AnyObj,
303
- (mState.value as any).target
304
- );
305
- let updateSet = updateCfg;
306
- if (typeof updateCfg === "function") {
307
- updateSet = updateCfg({
308
- excluded: (field: string) => (table as any)[field],
309
- inserted: (field: string) => (table as any)[field],
310
- });
311
- }
312
-
313
- let q = (config.db as any).insert(table).values(insertValues);
314
- if (q.onConflictDoUpdate) {
315
- q = q.onConflictDoUpdate({
316
- target,
317
- set: updateSet,
318
- });
319
- }
320
-
321
- return await execReturn(q, mState, config.dialect);
322
- };
323
-
324
- return new MutateResult(
325
- { kind: "upsert" as MutateKind, value } as MutateState,
326
- runner
327
- ) as any;
328
- };
329
-
330
- return modelObj;
331
- };
332
-
333
- return build(baseState);
334
- }
@@ -1,94 +0,0 @@
1
- import type { MethodExcludeValue } from "../methods/exclude.ts";
2
- import type { MethodSelectValue } from "../methods/select.ts";
3
- import type { MethodWithValue } from "../methods/with.ts";
4
-
5
- type AnyObj = Record<string, any>;
6
-
7
- type QueryState = {
8
- where?: unknown;
9
- with?: unknown;
10
- raw?: boolean;
11
- select?: AnyObj;
12
- exclude?: AnyObj;
13
- };
14
-
15
- type MutateKind = "insert" | "update" | "delete" | "upsert";
16
-
17
- type MutateState = {
18
- kind: MutateKind;
19
- where?: unknown;
20
- value?: unknown;
21
- returnSelect?: AnyObj;
22
- };
23
-
24
- export class ThenableResult<T> implements PromiseLike<T> {
25
- protected _execute: () => Promise<T>;
26
-
27
- constructor(execute: () => Promise<T>) {
28
- this._execute = execute;
29
- }
30
-
31
- then<TResult1 = T, TResult2 = never>(
32
- onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
33
- onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
34
- ): Promise<TResult1 | TResult2> {
35
- return this._execute().then(onfulfilled as any, onrejected as any);
36
- }
37
- }
38
-
39
- export class QueryResult<T> extends ThenableResult<T> {
40
- private state: QueryState;
41
- private runner: (state: QueryState) => Promise<T>;
42
-
43
- constructor(state: QueryState, runner: (state: QueryState) => Promise<T>) {
44
- super(() => runner(state));
45
- this.state = state;
46
- this.runner = runner;
47
- }
48
-
49
- with(value: MethodWithValue<any, any>): any {
50
- return new QueryResult({ ...this.state, with: value }, this.runner) as any;
51
- }
52
-
53
- select(value: MethodSelectValue<any>): any {
54
- return new QueryResult(
55
- { ...this.state, select: value as any },
56
- this.runner
57
- ) as any;
58
- }
59
-
60
- exclude(value: MethodExcludeValue<any>): any {
61
- return new QueryResult(
62
- { ...this.state, exclude: value as any },
63
- this.runner
64
- ) as any;
65
- }
66
-
67
- raw(): any {
68
- return new QueryResult({ ...this.state, raw: true }, this.runner) as any;
69
- }
70
-
71
- debug(): any {
72
- return this.state;
73
- }
74
- }
75
-
76
- export class MutateResult<T> extends ThenableResult<T> {
77
- private state: MutateState;
78
- private runner: (state: MutateState) => Promise<T>;
79
-
80
- constructor(state: MutateState, runner: (state: MutateState) => Promise<T>) {
81
- super(() => runner(state));
82
- this.state = state;
83
- this.runner = runner;
84
- }
85
-
86
- return(value?: AnyObj): any {
87
- return new MutateResult(
88
- { ...this.state, returnSelect: value },
89
- this.runner
90
- ) as any;
91
- }
92
- }
93
-
94
- export type { QueryState, MutateState, MutateKind };
@@ -1,65 +0,0 @@
1
- type AnyObj = Record<string, any>;
2
-
3
- export function applySelect(value: any, select: AnyObj): any {
4
- if (value == null) {
5
- return value;
6
- }
7
- if (Array.isArray(value)) {
8
- return value.map((v) => applySelect(v, select));
9
- }
10
- if (typeof value !== "object") {
11
- return value;
12
- }
13
-
14
- const out: AnyObj = {};
15
- for (const [key, sel] of Object.entries(select)) {
16
- if (sel === true) {
17
- out[key] = (value as any)[key];
18
- continue;
19
- }
20
- if (sel && typeof sel === "object") {
21
- out[key] = applySelect((value as any)[key], sel as AnyObj);
22
- }
23
- }
24
- return out;
25
- }
26
-
27
- export function applyExclude(value: any, exclude: AnyObj): any {
28
- if (value == null) {
29
- return value;
30
- }
31
- if (Array.isArray(value)) {
32
- return value.map((v) => applyExclude(v, exclude));
33
- }
34
- if (typeof value !== "object") {
35
- return value;
36
- }
37
-
38
- const out: AnyObj = { ...(value as AnyObj) };
39
- for (const [key, ex] of Object.entries(exclude)) {
40
- if (ex === true) {
41
- delete out[key];
42
- continue;
43
- }
44
- if (ex && typeof ex === "object" && key in out) {
45
- out[key] = applyExclude(out[key], ex as AnyObj);
46
- }
47
- }
48
- return out;
49
- }
50
-
51
- export function applyFormat(value: any, format: any): any {
52
- if (!format) {
53
- return value;
54
- }
55
- if (value == null) {
56
- return value;
57
- }
58
- if (Array.isArray(value)) {
59
- return value.map((v) => applyFormat(v, format));
60
- }
61
- if (typeof value !== "object") {
62
- return value;
63
- }
64
- return format(value);
65
- }