@deessejs/collections 0.0.1
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 +60 -0
- package/src/adapter.ts +38 -0
- package/src/collection.ts +138 -0
- package/src/config.ts +134 -0
- package/src/field-type.ts +40 -0
- package/src/field.ts +46 -0
- package/src/fields/f.ts +192 -0
- package/src/fields/index.ts +1 -0
- package/src/index.ts +27 -0
- package/src/migrations.ts +49 -0
- package/src/operations/collection-operations.ts +808 -0
- package/src/operations/index.ts +2 -0
- package/src/operations/types.ts +141 -0
- package/src/schema.ts +62 -0
- package/tests/adapter.test.ts +35 -0
- package/tests/collection.test.ts +205 -0
- package/tests/config.test.ts +58 -0
- package/tests/field-type.test.ts +181 -0
- package/tests/field.test.ts +201 -0
- package/tests/fixtures.ts +44 -0
- package/tests/hooks.test.ts +1076 -0
- package/tests/integration/hooks.test.ts +329 -0
- package/tests/metadata.test.ts +200 -0
- package/tests/schema.test.ts +58 -0
- package/tests/type-inference.test.ts +108 -0
- package/tsconfig.json +32 -0
- package/tsup.config.ts +11 -0
- package/vitest.config.ts +29 -0
- package/vitest.integration.config.ts +9 -0
|
@@ -0,0 +1,808 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { eq, and, like, gt, gte, lt, lte, isNull, inArray, not, desc, asc } from 'drizzle-orm'
|
|
3
|
+
|
|
4
|
+
import type { Collection, CollectionHooks, CreateHookContext, UpdateHookContext, DeleteHookContext, ReadHookContext, OperationHookContext } from '../collection'
|
|
5
|
+
import type {
|
|
6
|
+
FindManyOptions,
|
|
7
|
+
FindUniqueOptions,
|
|
8
|
+
FindFirstOptions,
|
|
9
|
+
CreateOptions,
|
|
10
|
+
CreateManyOptions,
|
|
11
|
+
UpdateOptions,
|
|
12
|
+
UpdateManyOptions,
|
|
13
|
+
DeleteOptions,
|
|
14
|
+
DeleteManyOptions,
|
|
15
|
+
CountOptions,
|
|
16
|
+
ExistsOptions,
|
|
17
|
+
WhereOperator
|
|
18
|
+
} from './types'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Collection operations interface
|
|
22
|
+
*/
|
|
23
|
+
export interface CollectionOperations {
|
|
24
|
+
findMany<T>(options?: FindManyOptions): Promise<T[]>
|
|
25
|
+
findUnique<T>(options: FindUniqueOptions): Promise<T | undefined>
|
|
26
|
+
findFirst<T>(options: FindFirstOptions): Promise<T | undefined>
|
|
27
|
+
create<T>(options: CreateOptions<T>): Promise<T | undefined>
|
|
28
|
+
createMany<T>(options: CreateManyOptions<T>): Promise<number>
|
|
29
|
+
update<T>(options: UpdateOptions<T>): Promise<T | undefined>
|
|
30
|
+
updateMany<T>(options: UpdateManyOptions<T>): Promise<number>
|
|
31
|
+
delete<T>(options: DeleteOptions): Promise<T | undefined>
|
|
32
|
+
deleteMany(options: DeleteManyOptions): Promise<number>
|
|
33
|
+
count(options?: CountOptions): Promise<number>
|
|
34
|
+
exists(options: ExistsOptions): Promise<boolean>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Build where conditions from options
|
|
39
|
+
*/
|
|
40
|
+
const buildWhereClause = (
|
|
41
|
+
tableColumns: Record<string, any>,
|
|
42
|
+
where?: Record<string, unknown>
|
|
43
|
+
): any => {
|
|
44
|
+
if (!where) return undefined
|
|
45
|
+
|
|
46
|
+
const conditions: any[] = []
|
|
47
|
+
|
|
48
|
+
for (const [key, value] of Object.entries(where)) {
|
|
49
|
+
const column = tableColumns[key]
|
|
50
|
+
if (!column) continue
|
|
51
|
+
|
|
52
|
+
if (value === null || typeof value !== 'object') {
|
|
53
|
+
conditions.push(eq(column, value))
|
|
54
|
+
} else {
|
|
55
|
+
const operator = value as WhereOperator<unknown>
|
|
56
|
+
if ('eq' in operator) {
|
|
57
|
+
conditions.push(eq(column, operator.eq))
|
|
58
|
+
} else if ('neq' in operator) {
|
|
59
|
+
conditions.push(not(eq(column, operator.neq)))
|
|
60
|
+
} else if ('gt' in operator) {
|
|
61
|
+
conditions.push(gt(column, operator.gt as number))
|
|
62
|
+
} else if ('gte' in operator) {
|
|
63
|
+
conditions.push(gte(column, operator.gte as number))
|
|
64
|
+
} else if ('lt' in operator) {
|
|
65
|
+
conditions.push(lt(column, operator.lt as number))
|
|
66
|
+
} else if ('lte' in operator) {
|
|
67
|
+
conditions.push(lte(column, operator.lte as number))
|
|
68
|
+
} else if ('in' in operator) {
|
|
69
|
+
conditions.push(inArray(column, operator.in))
|
|
70
|
+
} else if ('notIn' in operator) {
|
|
71
|
+
conditions.push(not(inArray(column, operator.notIn)))
|
|
72
|
+
} else if ('contains' in operator) {
|
|
73
|
+
conditions.push(like(column, `%${operator.contains}%`))
|
|
74
|
+
} else if ('startsWith' in operator) {
|
|
75
|
+
conditions.push(like(column, `${operator.startsWith}%`))
|
|
76
|
+
} else if ('endsWith' in operator) {
|
|
77
|
+
conditions.push(like(column, `%${operator.endsWith}`))
|
|
78
|
+
} else if ('isNull' in operator) {
|
|
79
|
+
if (operator.isNull) {
|
|
80
|
+
conditions.push(isNull(column))
|
|
81
|
+
}
|
|
82
|
+
} else if ('not' in operator) {
|
|
83
|
+
conditions.push(not(eq(column, operator.not)))
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (conditions.length === 0) return undefined
|
|
89
|
+
if (conditions.length === 1) return conditions[0]
|
|
90
|
+
return and(...conditions)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build orderBy from options
|
|
95
|
+
*/
|
|
96
|
+
const buildOrderBy = (
|
|
97
|
+
tableColumns: Record<string, any>,
|
|
98
|
+
orderBy?: Record<string, unknown> | Record<string, unknown>[]
|
|
99
|
+
): any[] => {
|
|
100
|
+
if (!orderBy) return []
|
|
101
|
+
|
|
102
|
+
const orders = Array.isArray(orderBy) ? orderBy : [orderBy]
|
|
103
|
+
return orders.map((order) => {
|
|
104
|
+
for (const [key, direction] of Object.entries(order)) {
|
|
105
|
+
const column = tableColumns[key]
|
|
106
|
+
if (!column) continue
|
|
107
|
+
return direction === 'desc' ? desc(column) : asc(column)
|
|
108
|
+
}
|
|
109
|
+
return undefined
|
|
110
|
+
}).filter(Boolean)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Execute before operation hooks
|
|
115
|
+
*/
|
|
116
|
+
const executeBeforeOperationHooks = async (
|
|
117
|
+
hooks: CollectionHooks | undefined,
|
|
118
|
+
context: OperationHookContext
|
|
119
|
+
): Promise<void> => {
|
|
120
|
+
if (!hooks?.beforeOperation) return
|
|
121
|
+
for (const hook of hooks.beforeOperation) {
|
|
122
|
+
await hook(context)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Execute after operation hooks
|
|
128
|
+
*/
|
|
129
|
+
const executeAfterOperationHooks = async (
|
|
130
|
+
hooks: CollectionHooks | undefined,
|
|
131
|
+
context: OperationHookContext
|
|
132
|
+
): Promise<void> => {
|
|
133
|
+
if (!hooks?.afterOperation) return
|
|
134
|
+
for (const hook of hooks.afterOperation) {
|
|
135
|
+
await hook(context)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Execute before create hooks
|
|
141
|
+
*/
|
|
142
|
+
const executeBeforeCreateHooks = async (
|
|
143
|
+
hooks: CollectionHooks | undefined,
|
|
144
|
+
context: CreateHookContext
|
|
145
|
+
): Promise<void> => {
|
|
146
|
+
if (!hooks?.beforeCreate) return
|
|
147
|
+
for (const hook of hooks.beforeCreate) {
|
|
148
|
+
await hook(context)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Execute after create hooks
|
|
154
|
+
*/
|
|
155
|
+
const executeAfterCreateHooks = async (
|
|
156
|
+
hooks: CollectionHooks | undefined,
|
|
157
|
+
context: CreateHookContext
|
|
158
|
+
): Promise<void> => {
|
|
159
|
+
if (!hooks?.afterCreate) return
|
|
160
|
+
for (const hook of hooks.afterCreate) {
|
|
161
|
+
await hook(context)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Execute before update hooks
|
|
167
|
+
*/
|
|
168
|
+
const executeBeforeUpdateHooks = async (
|
|
169
|
+
hooks: CollectionHooks | undefined,
|
|
170
|
+
context: UpdateHookContext
|
|
171
|
+
): Promise<void> => {
|
|
172
|
+
if (!hooks?.beforeUpdate) return
|
|
173
|
+
for (const hook of hooks.beforeUpdate) {
|
|
174
|
+
await hook(context)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Execute after update hooks
|
|
180
|
+
*/
|
|
181
|
+
const executeAfterUpdateHooks = async (
|
|
182
|
+
hooks: CollectionHooks | undefined,
|
|
183
|
+
context: UpdateHookContext
|
|
184
|
+
): Promise<void> => {
|
|
185
|
+
if (!hooks?.afterUpdate) return
|
|
186
|
+
for (const hook of hooks.afterUpdate) {
|
|
187
|
+
await hook(context)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Execute before delete hooks
|
|
193
|
+
*/
|
|
194
|
+
const executeBeforeDeleteHooks = async (
|
|
195
|
+
hooks: CollectionHooks | undefined,
|
|
196
|
+
context: DeleteHookContext
|
|
197
|
+
): Promise<void> => {
|
|
198
|
+
if (!hooks?.beforeDelete) return
|
|
199
|
+
for (const hook of hooks.beforeDelete) {
|
|
200
|
+
await hook(context)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Execute after delete hooks
|
|
206
|
+
*/
|
|
207
|
+
const executeAfterDeleteHooks = async (
|
|
208
|
+
hooks: CollectionHooks | undefined,
|
|
209
|
+
context: DeleteHookContext
|
|
210
|
+
): Promise<void> => {
|
|
211
|
+
if (!hooks?.afterDelete) return
|
|
212
|
+
for (const hook of hooks.afterDelete) {
|
|
213
|
+
await hook(context)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Execute before read hooks
|
|
219
|
+
*/
|
|
220
|
+
const executeBeforeReadHooks = async (
|
|
221
|
+
hooks: CollectionHooks | undefined,
|
|
222
|
+
context: ReadHookContext
|
|
223
|
+
): Promise<void> => {
|
|
224
|
+
if (!hooks?.beforeRead) return
|
|
225
|
+
for (const hook of hooks.beforeRead) {
|
|
226
|
+
await hook(context)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Execute after read hooks
|
|
232
|
+
*/
|
|
233
|
+
const executeAfterReadHooks = async (
|
|
234
|
+
hooks: CollectionHooks | undefined,
|
|
235
|
+
context: ReadHookContext
|
|
236
|
+
): Promise<void> => {
|
|
237
|
+
if (!hooks?.afterRead) return
|
|
238
|
+
for (const hook of hooks.afterRead) {
|
|
239
|
+
await hook(context)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Creates collection operations with Drizzle
|
|
245
|
+
*/
|
|
246
|
+
export const createCollectionOperations = (
|
|
247
|
+
_collection: Collection,
|
|
248
|
+
_slug: string,
|
|
249
|
+
_db: any,
|
|
250
|
+
_table: any,
|
|
251
|
+
_hooks?: CollectionHooks
|
|
252
|
+
): CollectionOperations => {
|
|
253
|
+
const tableColumns = _table as Record<string, any>
|
|
254
|
+
const db = _db as any
|
|
255
|
+
const hooks = _hooks as CollectionHooks | undefined
|
|
256
|
+
|
|
257
|
+
// If no db instance, return placeholder operations
|
|
258
|
+
if (!db) {
|
|
259
|
+
return {
|
|
260
|
+
findMany: async <T>(): Promise<T[]> => [],
|
|
261
|
+
findUnique: async <T>(): Promise<T | undefined> => undefined,
|
|
262
|
+
findFirst: async <T>(): Promise<T | undefined> => undefined,
|
|
263
|
+
create: async <T>(): Promise<T | undefined> => undefined,
|
|
264
|
+
createMany: async (): Promise<number> => 0,
|
|
265
|
+
update: async <T>(): Promise<T | undefined> => undefined,
|
|
266
|
+
updateMany: async (): Promise<number> => 0,
|
|
267
|
+
delete: async <T>(): Promise<T | undefined> => undefined,
|
|
268
|
+
deleteMany: async (): Promise<number> => 0,
|
|
269
|
+
count: async (): Promise<number> => 0,
|
|
270
|
+
exists: async (): Promise<boolean> => false
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
findMany: async <T>(options?: FindManyOptions): Promise<T[]> => {
|
|
276
|
+
const whereClause = buildWhereClause(tableColumns, options?.where)
|
|
277
|
+
const orderByClause = buildOrderBy(tableColumns, options?.orderBy)
|
|
278
|
+
|
|
279
|
+
// Execute before operation hooks
|
|
280
|
+
await executeBeforeOperationHooks(hooks, {
|
|
281
|
+
collection: _slug,
|
|
282
|
+
operation: 'read',
|
|
283
|
+
where: options?.where
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
// Execute before read hooks
|
|
287
|
+
await executeBeforeReadHooks(hooks, {
|
|
288
|
+
collection: _slug,
|
|
289
|
+
operation: 'read',
|
|
290
|
+
query: options as unknown as Record<string, unknown>,
|
|
291
|
+
db
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
let query = db.select().from(_table)
|
|
295
|
+
|
|
296
|
+
if (whereClause) {
|
|
297
|
+
query = query.where(whereClause)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (orderByClause.length > 0) {
|
|
301
|
+
query = query.orderBy(...orderByClause)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (options?.offset) {
|
|
305
|
+
query = query.offset(options.offset)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (options?.limit) {
|
|
309
|
+
query = query.limit(options.limit)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const result = await query
|
|
313
|
+
|
|
314
|
+
// Execute after read hooks
|
|
315
|
+
await executeAfterReadHooks(hooks, {
|
|
316
|
+
collection: _slug,
|
|
317
|
+
operation: 'read',
|
|
318
|
+
query: options as unknown as Record<string, unknown>,
|
|
319
|
+
result: result as unknown[],
|
|
320
|
+
db
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
// Execute after operation hooks
|
|
324
|
+
await executeAfterOperationHooks(hooks, {
|
|
325
|
+
collection: _slug,
|
|
326
|
+
operation: 'read',
|
|
327
|
+
where: options?.where,
|
|
328
|
+
result
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
return result as T[]
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
findUnique: async <T>(options: FindUniqueOptions): Promise<T | undefined> => {
|
|
335
|
+
const whereClause = buildWhereClause(tableColumns, options.where)
|
|
336
|
+
if (!whereClause) return undefined
|
|
337
|
+
|
|
338
|
+
// Execute before operation hooks
|
|
339
|
+
await executeBeforeOperationHooks(hooks, {
|
|
340
|
+
collection: _slug,
|
|
341
|
+
operation: 'read',
|
|
342
|
+
where: options.where
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
// Execute before read hooks
|
|
346
|
+
await executeBeforeReadHooks(hooks, {
|
|
347
|
+
collection: _slug,
|
|
348
|
+
operation: 'read',
|
|
349
|
+
query: options as unknown as Record<string, unknown>,
|
|
350
|
+
db
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
const result = await db.select().from(_table).where(whereClause).limit(1)
|
|
354
|
+
const returnValue = result[0] as T | undefined
|
|
355
|
+
|
|
356
|
+
// Execute after read hooks
|
|
357
|
+
await executeAfterReadHooks(hooks, {
|
|
358
|
+
collection: _slug,
|
|
359
|
+
operation: 'read',
|
|
360
|
+
query: options as unknown as Record<string, unknown>,
|
|
361
|
+
result: returnValue ? [returnValue] : [],
|
|
362
|
+
db
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
// Execute after operation hooks
|
|
366
|
+
await executeAfterOperationHooks(hooks, {
|
|
367
|
+
collection: _slug,
|
|
368
|
+
operation: 'read',
|
|
369
|
+
where: options.where,
|
|
370
|
+
result: returnValue
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
return returnValue
|
|
374
|
+
},
|
|
375
|
+
|
|
376
|
+
findFirst: async <T>(options: FindFirstOptions): Promise<T | undefined> => {
|
|
377
|
+
const whereClause = buildWhereClause(tableColumns, options.where)
|
|
378
|
+
const orderByClause = buildOrderBy(tableColumns, options.orderBy)
|
|
379
|
+
|
|
380
|
+
// Execute before operation hooks
|
|
381
|
+
await executeBeforeOperationHooks(hooks, {
|
|
382
|
+
collection: _slug,
|
|
383
|
+
operation: 'read',
|
|
384
|
+
where: options.where
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
// Execute before read hooks
|
|
388
|
+
await executeBeforeReadHooks(hooks, {
|
|
389
|
+
collection: _slug,
|
|
390
|
+
operation: 'read',
|
|
391
|
+
query: options as unknown as Record<string, unknown>,
|
|
392
|
+
db
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
let query = db.select().from(_table)
|
|
396
|
+
|
|
397
|
+
if (whereClause) {
|
|
398
|
+
query = query.where(whereClause)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (orderByClause.length > 0) {
|
|
402
|
+
query = query.orderBy(...orderByClause)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const result = await query.limit(1)
|
|
406
|
+
const returnValue = result[0] as T | undefined
|
|
407
|
+
|
|
408
|
+
// Execute after read hooks
|
|
409
|
+
await executeAfterReadHooks(hooks, {
|
|
410
|
+
collection: _slug,
|
|
411
|
+
operation: 'read',
|
|
412
|
+
query: options as unknown as Record<string, unknown>,
|
|
413
|
+
result: returnValue ? [returnValue] : [],
|
|
414
|
+
db
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
// Execute after operation hooks
|
|
418
|
+
await executeAfterOperationHooks(hooks, {
|
|
419
|
+
collection: _slug,
|
|
420
|
+
operation: 'read',
|
|
421
|
+
where: options.where,
|
|
422
|
+
result: returnValue
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
return returnValue
|
|
426
|
+
},
|
|
427
|
+
|
|
428
|
+
create: async <T>(options: CreateOptions<T>): Promise<T | undefined> => {
|
|
429
|
+
const data = Array.isArray(options.data) ? options.data : [options.data]
|
|
430
|
+
const firstData = data[0] as Record<string, unknown>
|
|
431
|
+
|
|
432
|
+
// Execute before operation hooks
|
|
433
|
+
await executeBeforeOperationHooks(hooks, {
|
|
434
|
+
collection: _slug,
|
|
435
|
+
operation: 'create',
|
|
436
|
+
data: firstData,
|
|
437
|
+
where: undefined
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
// Execute before create hooks
|
|
441
|
+
await executeBeforeCreateHooks(hooks, {
|
|
442
|
+
collection: _slug,
|
|
443
|
+
operation: 'create',
|
|
444
|
+
data: firstData,
|
|
445
|
+
db
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
const result = await db.insert(_table).values(data).returning()
|
|
449
|
+
|
|
450
|
+
const returnValue = options.returning ? result[0] as T : undefined
|
|
451
|
+
|
|
452
|
+
// Execute after create hooks
|
|
453
|
+
await executeAfterCreateHooks(hooks, {
|
|
454
|
+
collection: _slug,
|
|
455
|
+
operation: 'create',
|
|
456
|
+
data: firstData,
|
|
457
|
+
result: returnValue,
|
|
458
|
+
db
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
// Execute after operation hooks
|
|
462
|
+
await executeAfterOperationHooks(hooks, {
|
|
463
|
+
collection: _slug,
|
|
464
|
+
operation: 'create',
|
|
465
|
+
data: firstData,
|
|
466
|
+
result: returnValue
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
return returnValue
|
|
470
|
+
},
|
|
471
|
+
|
|
472
|
+
createMany: async <T>(options: CreateManyOptions<T>): Promise<number> => {
|
|
473
|
+
const dataArray = Array.isArray(options.data) ? options.data : [options.data]
|
|
474
|
+
|
|
475
|
+
// Execute before operation hooks for each item
|
|
476
|
+
for (const data of dataArray) {
|
|
477
|
+
await executeBeforeOperationHooks(hooks, {
|
|
478
|
+
collection: _slug,
|
|
479
|
+
operation: 'create',
|
|
480
|
+
data: data as Record<string, unknown>,
|
|
481
|
+
where: undefined
|
|
482
|
+
})
|
|
483
|
+
|
|
484
|
+
await executeBeforeCreateHooks(hooks, {
|
|
485
|
+
collection: _slug,
|
|
486
|
+
operation: 'create',
|
|
487
|
+
data: data as Record<string, unknown>,
|
|
488
|
+
db
|
|
489
|
+
})
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const result = await db.insert(_table).values(options.data as any)
|
|
493
|
+
|
|
494
|
+
// Execute after operation hooks for each item
|
|
495
|
+
for (let i = 0; i < dataArray.length; i++) {
|
|
496
|
+
await executeAfterCreateHooks(hooks, {
|
|
497
|
+
collection: _slug,
|
|
498
|
+
operation: 'create',
|
|
499
|
+
data: dataArray[i] as Record<string, unknown>,
|
|
500
|
+
result: result[i],
|
|
501
|
+
db
|
|
502
|
+
})
|
|
503
|
+
|
|
504
|
+
await executeAfterOperationHooks(hooks, {
|
|
505
|
+
collection: _slug,
|
|
506
|
+
operation: 'create',
|
|
507
|
+
data: dataArray[i] as Record<string, unknown>,
|
|
508
|
+
result: result[i]
|
|
509
|
+
})
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return result.length || 0
|
|
513
|
+
},
|
|
514
|
+
|
|
515
|
+
update: async <T>(options: UpdateOptions<T>): Promise<T | undefined> => {
|
|
516
|
+
const whereClause = buildWhereClause(tableColumns, options.where)
|
|
517
|
+
if (!whereClause) return undefined
|
|
518
|
+
|
|
519
|
+
// Get previous data for hooks
|
|
520
|
+
const previousResult = await db.select().from(_table).where(whereClause).limit(1)
|
|
521
|
+
const previousData = previousResult[0] as Record<string, unknown> | undefined
|
|
522
|
+
|
|
523
|
+
// Execute before operation hooks
|
|
524
|
+
await executeBeforeOperationHooks(hooks, {
|
|
525
|
+
collection: _slug,
|
|
526
|
+
operation: 'update',
|
|
527
|
+
data: options.data as Record<string, unknown>,
|
|
528
|
+
where: options.where
|
|
529
|
+
})
|
|
530
|
+
|
|
531
|
+
// Execute before update hooks
|
|
532
|
+
await executeBeforeUpdateHooks(hooks, {
|
|
533
|
+
collection: _slug,
|
|
534
|
+
operation: 'update',
|
|
535
|
+
data: options.data as Record<string, unknown>,
|
|
536
|
+
where: options.where,
|
|
537
|
+
previousData,
|
|
538
|
+
db
|
|
539
|
+
})
|
|
540
|
+
|
|
541
|
+
const result = await db.update(_table)
|
|
542
|
+
.set(options.data as any)
|
|
543
|
+
.where(whereClause)
|
|
544
|
+
.returning()
|
|
545
|
+
|
|
546
|
+
const returnValue = options.returning ? result[0] as T : undefined
|
|
547
|
+
|
|
548
|
+
// Execute after update hooks
|
|
549
|
+
await executeAfterUpdateHooks(hooks, {
|
|
550
|
+
collection: _slug,
|
|
551
|
+
operation: 'update',
|
|
552
|
+
data: options.data as Record<string, unknown>,
|
|
553
|
+
where: options.where,
|
|
554
|
+
previousData,
|
|
555
|
+
result: returnValue,
|
|
556
|
+
db
|
|
557
|
+
})
|
|
558
|
+
|
|
559
|
+
// Execute after operation hooks
|
|
560
|
+
await executeAfterOperationHooks(hooks, {
|
|
561
|
+
collection: _slug,
|
|
562
|
+
operation: 'update',
|
|
563
|
+
data: options.data as Record<string, unknown>,
|
|
564
|
+
where: options.where,
|
|
565
|
+
result: returnValue
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
return returnValue
|
|
569
|
+
},
|
|
570
|
+
|
|
571
|
+
updateMany: async <T>(options: UpdateManyOptions<T>): Promise<number> => {
|
|
572
|
+
const whereClause = buildWhereClause(tableColumns, options.where)
|
|
573
|
+
if (!whereClause) return 0
|
|
574
|
+
|
|
575
|
+
// Get previous data for hooks
|
|
576
|
+
const previousResults = await db.select().from(_table).where(whereClause)
|
|
577
|
+
|
|
578
|
+
// Execute before operation hooks
|
|
579
|
+
await executeBeforeOperationHooks(hooks, {
|
|
580
|
+
collection: _slug,
|
|
581
|
+
operation: 'update',
|
|
582
|
+
data: options.data as Record<string, unknown>,
|
|
583
|
+
where: options.where
|
|
584
|
+
})
|
|
585
|
+
|
|
586
|
+
// Execute before update hooks (for each previous record)
|
|
587
|
+
for (const previousData of previousResults) {
|
|
588
|
+
await executeBeforeUpdateHooks(hooks, {
|
|
589
|
+
collection: _slug,
|
|
590
|
+
operation: 'update',
|
|
591
|
+
data: options.data as Record<string, unknown>,
|
|
592
|
+
where: options.where,
|
|
593
|
+
previousData: previousData as Record<string, unknown>,
|
|
594
|
+
db
|
|
595
|
+
})
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
const result = await db.update(_table)
|
|
599
|
+
.set(options.data as any)
|
|
600
|
+
.where(whereClause)
|
|
601
|
+
|
|
602
|
+
// Execute after update hooks
|
|
603
|
+
for (const previousData of previousResults) {
|
|
604
|
+
await executeAfterUpdateHooks(hooks, {
|
|
605
|
+
collection: _slug,
|
|
606
|
+
operation: 'update',
|
|
607
|
+
data: options.data as Record<string, unknown>,
|
|
608
|
+
where: options.where,
|
|
609
|
+
previousData: previousData as Record<string, unknown>,
|
|
610
|
+
db
|
|
611
|
+
})
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Execute after operation hooks
|
|
615
|
+
await executeAfterOperationHooks(hooks, {
|
|
616
|
+
collection: _slug,
|
|
617
|
+
operation: 'update',
|
|
618
|
+
data: options.data as Record<string, unknown>,
|
|
619
|
+
where: options.where
|
|
620
|
+
})
|
|
621
|
+
|
|
622
|
+
return result.length || 0
|
|
623
|
+
},
|
|
624
|
+
|
|
625
|
+
delete: async <T>(options: DeleteOptions): Promise<T | undefined> => {
|
|
626
|
+
const whereClause = buildWhereClause(tableColumns, options.where)
|
|
627
|
+
if (!whereClause) return undefined
|
|
628
|
+
|
|
629
|
+
// Get previous data for hooks
|
|
630
|
+
const previousResult = await db.select().from(_table).where(whereClause).limit(1)
|
|
631
|
+
const previousData = previousResult[0] as Record<string, unknown> | undefined
|
|
632
|
+
|
|
633
|
+
// Execute before operation hooks
|
|
634
|
+
await executeBeforeOperationHooks(hooks, {
|
|
635
|
+
collection: _slug,
|
|
636
|
+
operation: 'delete',
|
|
637
|
+
where: options.where
|
|
638
|
+
})
|
|
639
|
+
|
|
640
|
+
// Execute before delete hooks
|
|
641
|
+
await executeBeforeDeleteHooks(hooks, {
|
|
642
|
+
collection: _slug,
|
|
643
|
+
operation: 'delete',
|
|
644
|
+
where: options.where,
|
|
645
|
+
previousData,
|
|
646
|
+
db
|
|
647
|
+
})
|
|
648
|
+
|
|
649
|
+
const result = await db.delete(_table)
|
|
650
|
+
.where(whereClause)
|
|
651
|
+
.returning()
|
|
652
|
+
|
|
653
|
+
const returnValue = options.returning ? result[0] as T : undefined
|
|
654
|
+
|
|
655
|
+
// Execute after delete hooks
|
|
656
|
+
await executeAfterDeleteHooks(hooks, {
|
|
657
|
+
collection: _slug,
|
|
658
|
+
operation: 'delete',
|
|
659
|
+
where: options.where,
|
|
660
|
+
previousData,
|
|
661
|
+
result: returnValue,
|
|
662
|
+
db
|
|
663
|
+
})
|
|
664
|
+
|
|
665
|
+
// Execute after operation hooks
|
|
666
|
+
await executeAfterOperationHooks(hooks, {
|
|
667
|
+
collection: _slug,
|
|
668
|
+
operation: 'delete',
|
|
669
|
+
where: options.where,
|
|
670
|
+
result: returnValue
|
|
671
|
+
})
|
|
672
|
+
|
|
673
|
+
return returnValue
|
|
674
|
+
},
|
|
675
|
+
|
|
676
|
+
deleteMany: async (options: DeleteManyOptions): Promise<number> => {
|
|
677
|
+
const whereClause = buildWhereClause(tableColumns, options.where)
|
|
678
|
+
if (!whereClause) return 0
|
|
679
|
+
|
|
680
|
+
// Get previous data for hooks
|
|
681
|
+
const previousResults = await db.select().from(_table).where(whereClause)
|
|
682
|
+
|
|
683
|
+
// Execute before operation hooks
|
|
684
|
+
await executeBeforeOperationHooks(hooks, {
|
|
685
|
+
collection: _slug,
|
|
686
|
+
operation: 'delete',
|
|
687
|
+
where: options.where
|
|
688
|
+
})
|
|
689
|
+
|
|
690
|
+
// Execute before delete hooks
|
|
691
|
+
for (const previousData of previousResults) {
|
|
692
|
+
await executeBeforeDeleteHooks(hooks, {
|
|
693
|
+
collection: _slug,
|
|
694
|
+
operation: 'delete',
|
|
695
|
+
where: options.where,
|
|
696
|
+
previousData: previousData as Record<string, unknown>,
|
|
697
|
+
db
|
|
698
|
+
})
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
const result = await db.delete(_table).where(whereClause)
|
|
702
|
+
|
|
703
|
+
// Execute after delete hooks
|
|
704
|
+
for (const previousData of previousResults) {
|
|
705
|
+
await executeAfterDeleteHooks(hooks, {
|
|
706
|
+
collection: _slug,
|
|
707
|
+
operation: 'delete',
|
|
708
|
+
where: options.where,
|
|
709
|
+
previousData: previousData as Record<string, unknown>,
|
|
710
|
+
db
|
|
711
|
+
})
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// Execute after operation hooks
|
|
715
|
+
await executeAfterOperationHooks(hooks, {
|
|
716
|
+
collection: _slug,
|
|
717
|
+
operation: 'delete',
|
|
718
|
+
where: options.where
|
|
719
|
+
})
|
|
720
|
+
|
|
721
|
+
return result.length || 0
|
|
722
|
+
},
|
|
723
|
+
|
|
724
|
+
count: async (options?: CountOptions): Promise<number> => {
|
|
725
|
+
const whereClause = buildWhereClause(tableColumns, options?.where)
|
|
726
|
+
|
|
727
|
+
// Execute before operation hooks
|
|
728
|
+
await executeBeforeOperationHooks(hooks, {
|
|
729
|
+
collection: _slug,
|
|
730
|
+
operation: 'read',
|
|
731
|
+
where: options?.where
|
|
732
|
+
})
|
|
733
|
+
|
|
734
|
+
// Execute before read hooks
|
|
735
|
+
await executeBeforeReadHooks(hooks, {
|
|
736
|
+
collection: _slug,
|
|
737
|
+
operation: 'read',
|
|
738
|
+
query: options as unknown as Record<string, unknown>,
|
|
739
|
+
db
|
|
740
|
+
})
|
|
741
|
+
|
|
742
|
+
const result = whereClause
|
|
743
|
+
? await db.select().from(_table).where(whereClause)
|
|
744
|
+
: await db.select().from(_table)
|
|
745
|
+
|
|
746
|
+
// Execute after read hooks
|
|
747
|
+
await executeAfterReadHooks(hooks, {
|
|
748
|
+
collection: _slug,
|
|
749
|
+
operation: 'read',
|
|
750
|
+
query: options as unknown as Record<string, unknown>,
|
|
751
|
+
result,
|
|
752
|
+
db
|
|
753
|
+
})
|
|
754
|
+
|
|
755
|
+
// Execute after operation hooks
|
|
756
|
+
await executeAfterOperationHooks(hooks, {
|
|
757
|
+
collection: _slug,
|
|
758
|
+
operation: 'read',
|
|
759
|
+
where: options?.where,
|
|
760
|
+
result
|
|
761
|
+
})
|
|
762
|
+
|
|
763
|
+
return result.length
|
|
764
|
+
},
|
|
765
|
+
|
|
766
|
+
exists: async (options: ExistsOptions): Promise<boolean> => {
|
|
767
|
+
const whereClause = buildWhereClause(tableColumns, options.where)
|
|
768
|
+
if (!whereClause) return false
|
|
769
|
+
|
|
770
|
+
// Execute before operation hooks
|
|
771
|
+
await executeBeforeOperationHooks(hooks, {
|
|
772
|
+
collection: _slug,
|
|
773
|
+
operation: 'read',
|
|
774
|
+
where: options.where
|
|
775
|
+
})
|
|
776
|
+
|
|
777
|
+
// Execute before read hooks
|
|
778
|
+
await executeBeforeReadHooks(hooks, {
|
|
779
|
+
collection: _slug,
|
|
780
|
+
operation: 'read',
|
|
781
|
+
query: options as unknown as Record<string, unknown>,
|
|
782
|
+
db
|
|
783
|
+
})
|
|
784
|
+
|
|
785
|
+
const result = await db.select().from(_table).where(whereClause).limit(1)
|
|
786
|
+
const returnValue = result.length > 0
|
|
787
|
+
|
|
788
|
+
// Execute after read hooks
|
|
789
|
+
await executeAfterReadHooks(hooks, {
|
|
790
|
+
collection: _slug,
|
|
791
|
+
operation: 'read',
|
|
792
|
+
query: options as unknown as Record<string, unknown>,
|
|
793
|
+
result: result,
|
|
794
|
+
db
|
|
795
|
+
})
|
|
796
|
+
|
|
797
|
+
// Execute after operation hooks
|
|
798
|
+
await executeAfterOperationHooks(hooks, {
|
|
799
|
+
collection: _slug,
|
|
800
|
+
operation: 'read',
|
|
801
|
+
where: options.where,
|
|
802
|
+
result: returnValue
|
|
803
|
+
})
|
|
804
|
+
|
|
805
|
+
return returnValue
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|