prisma-flare 1.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 (45) hide show
  1. package/dist/cli/db-create.cjs +240 -0
  2. package/dist/cli/db-create.d.cts +1 -0
  3. package/dist/cli/db-create.d.ts +1 -0
  4. package/dist/cli/db-create.js +217 -0
  5. package/dist/cli/db-drop.cjs +263 -0
  6. package/dist/cli/db-drop.d.cts +1 -0
  7. package/dist/cli/db-drop.d.ts +1 -0
  8. package/dist/cli/db-drop.js +240 -0
  9. package/dist/cli/db-migrate.cjs +318 -0
  10. package/dist/cli/db-migrate.d.cts +1 -0
  11. package/dist/cli/db-migrate.d.ts +1 -0
  12. package/dist/cli/db-migrate.js +295 -0
  13. package/dist/cli/db-reset.cjs +110 -0
  14. package/dist/cli/db-reset.d.cts +1 -0
  15. package/dist/cli/db-reset.d.ts +1 -0
  16. package/dist/cli/db-reset.js +87 -0
  17. package/dist/cli/db-seed.cjs +87 -0
  18. package/dist/cli/db-seed.d.cts +1 -0
  19. package/dist/cli/db-seed.d.ts +1 -0
  20. package/dist/cli/db-seed.js +64 -0
  21. package/dist/cli/index.cjs +352 -0
  22. package/dist/cli/index.d.cts +1 -0
  23. package/dist/cli/index.d.ts +1 -0
  24. package/dist/cli/index.js +328 -0
  25. package/dist/core/flareBuilder.cjs +681 -0
  26. package/dist/core/flareBuilder.d.cts +402 -0
  27. package/dist/core/flareBuilder.d.ts +402 -0
  28. package/dist/core/flareBuilder.js +658 -0
  29. package/dist/core/hooks.cjs +243 -0
  30. package/dist/core/hooks.d.cts +13 -0
  31. package/dist/core/hooks.d.ts +13 -0
  32. package/dist/core/hooks.js +209 -0
  33. package/dist/generated.cjs +31 -0
  34. package/dist/generated.d.cts +4 -0
  35. package/dist/generated.d.ts +4 -0
  36. package/dist/generated.js +6 -0
  37. package/dist/index.cjs +1315 -0
  38. package/dist/index.d.cts +237 -0
  39. package/dist/index.d.ts +237 -0
  40. package/dist/index.js +1261 -0
  41. package/dist/prisma.types-nGNe1CG8.d.cts +201 -0
  42. package/dist/prisma.types-nGNe1CG8.d.ts +201 -0
  43. package/license.md +21 -0
  44. package/package.json +115 -0
  45. package/readme.md +957 -0
@@ -0,0 +1,681 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/core/flareBuilder.ts
21
+ var flareBuilder_exports = {};
22
+ __export(flareBuilder_exports, {
23
+ default: () => FlareBuilder
24
+ });
25
+ module.exports = __toCommonJS(flareBuilder_exports);
26
+
27
+ // src/core/modelRegistry.ts
28
+ var ModelRegistry = class {
29
+ constructor() {
30
+ this.models = /* @__PURE__ */ new Map();
31
+ }
32
+ /**
33
+ * Register a custom model class for a given model name.
34
+ * The model name should match the Prisma model name (e.g., 'user', 'post', 'enrollment')
35
+ * @param modelName - The lowercase model name (matching Prisma delegate name)
36
+ * @param modelClass - The custom class that extends FlareBuilder
37
+ */
38
+ register(modelName, modelClass) {
39
+ this.models.set(modelName.toLowerCase(), modelClass);
40
+ }
41
+ /**
42
+ * Register multiple models at once
43
+ * @param models - Object mapping model names to their classes
44
+ */
45
+ registerMany(models) {
46
+ for (const [name, modelClass] of Object.entries(models)) {
47
+ this.register(name, modelClass);
48
+ }
49
+ }
50
+ /**
51
+ * Get a custom model class by name
52
+ * @param modelName - The model name to look up
53
+ * @returns The model class or undefined if not registered
54
+ */
55
+ get(modelName) {
56
+ return this.models.get(modelName.toLowerCase());
57
+ }
58
+ /**
59
+ * Check if a model is registered
60
+ * @param modelName - The model name to check
61
+ */
62
+ has(modelName) {
63
+ return this.models.has(modelName.toLowerCase());
64
+ }
65
+ /**
66
+ * Create an instance of a registered model
67
+ * @param modelName - The model name to instantiate
68
+ * @returns A new instance of the custom model class, or undefined if not registered
69
+ */
70
+ create(modelName) {
71
+ const ModelClass = this.get(modelName);
72
+ if (ModelClass) {
73
+ return new ModelClass();
74
+ }
75
+ return void 0;
76
+ }
77
+ /**
78
+ * Clear all registered models (useful for testing)
79
+ */
80
+ clear() {
81
+ this.models.clear();
82
+ }
83
+ /**
84
+ * Get all registered model names
85
+ */
86
+ getRegisteredModels() {
87
+ return Array.from(this.models.keys());
88
+ }
89
+ };
90
+ var modelRegistry = new ModelRegistry();
91
+
92
+ // src/core/flareBuilder.ts
93
+ function deepClone(obj) {
94
+ if (obj === null || typeof obj !== "object") {
95
+ return obj;
96
+ }
97
+ if (typeof obj === "bigint") {
98
+ return obj;
99
+ }
100
+ if (typeof structuredClone === "function") {
101
+ try {
102
+ return structuredClone(obj);
103
+ } catch {
104
+ }
105
+ }
106
+ if (obj instanceof Date) {
107
+ return new Date(obj.getTime());
108
+ }
109
+ if (obj instanceof RegExp) {
110
+ return new RegExp(obj.source, obj.flags);
111
+ }
112
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(obj)) {
113
+ return Buffer.from(obj);
114
+ }
115
+ if (obj instanceof ArrayBuffer) {
116
+ return obj.slice(0);
117
+ }
118
+ if (ArrayBuffer.isView(obj) && !(obj instanceof DataView)) {
119
+ const TypedArrayConstructor = obj.constructor;
120
+ const buffer = obj.buffer instanceof ArrayBuffer ? obj.buffer.slice(0) : obj.buffer;
121
+ return new TypedArrayConstructor(buffer);
122
+ }
123
+ if (obj instanceof Map) {
124
+ const clonedMap = /* @__PURE__ */ new Map();
125
+ obj.forEach((value, key) => {
126
+ clonedMap.set(deepClone(key), deepClone(value));
127
+ });
128
+ return clonedMap;
129
+ }
130
+ if (obj instanceof Set) {
131
+ const clonedSet = /* @__PURE__ */ new Set();
132
+ obj.forEach((value) => {
133
+ clonedSet.add(deepClone(value));
134
+ });
135
+ return clonedSet;
136
+ }
137
+ if (Array.isArray(obj)) {
138
+ return obj.map((item) => deepClone(item));
139
+ }
140
+ if (typeof obj.toDecimalPlaces === "function") {
141
+ return obj;
142
+ }
143
+ const prototype = Object.getPrototypeOf(obj);
144
+ const cloned = Object.create(prototype);
145
+ for (const key in obj) {
146
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
147
+ cloned[key] = deepClone(obj[key]);
148
+ }
149
+ }
150
+ return cloned;
151
+ }
152
+ var FlareBuilder = class _FlareBuilder {
153
+ constructor(model, query = {}) {
154
+ this.model = model;
155
+ this.query = query;
156
+ }
157
+ /**
158
+ * Adds a where condition to the query with type safety from Prisma.
159
+ * Multiple where() calls are composed using AND logic to avoid silent overwrites.
160
+ * @param condition - Where filter matching your Prisma model
161
+ *
162
+ * @example
163
+ * // These conditions are AND-ed together:
164
+ * DB.posts
165
+ * .where({ published: true })
166
+ * .where({ authorId: 1 })
167
+ * .findMany()
168
+ * // Equivalent to: { AND: [{ published: true }, { authorId: 1 }] }
169
+ */
170
+ where(condition) {
171
+ if (!this.query.where || Object.keys(this.query.where).length === 0) {
172
+ this.query.where = condition;
173
+ } else {
174
+ const prevWhere = this.query.where;
175
+ this.query.where = { AND: [prevWhere, condition] };
176
+ }
177
+ return this;
178
+ }
179
+ /**
180
+ * Adds a where condition using AND logic (explicit alias for where())
181
+ * @param condition - Where filter matching your Prisma model
182
+ *
183
+ * @example
184
+ * DB.posts
185
+ * .where({ published: true })
186
+ * .andWhere({ createdAt: { gte: new Date('2024-01-01') } })
187
+ * .findMany()
188
+ */
189
+ andWhere(condition) {
190
+ return this.where(condition);
191
+ }
192
+ /**
193
+ * Adds a where condition using OR logic.
194
+ *
195
+ * ⚠️ **IMPORTANT**: `orWhere()` wraps the *entire* accumulated where clause:
196
+ * `OR([prevWhere, condition])`. This means:
197
+ *
198
+ * ```ts
199
+ * .where(A).orWhere(B).where(C) // becomes: (A OR B) AND C
200
+ * ```
201
+ *
202
+ * For complex boolean logic, prefer `whereGroup()` / `orWhereGroup()` for explicit control.
203
+ *
204
+ * @param condition - Where filter matching your Prisma model
205
+ *
206
+ * @example
207
+ * // Simple case - OK:
208
+ * DB.posts
209
+ * .where({ published: true })
210
+ * .orWhere({ featured: true })
211
+ * .findMany()
212
+ * // Result: published OR featured
213
+ *
214
+ * @example
215
+ * // For complex logic, use whereGroup instead:
216
+ * DB.posts
217
+ * .where({ published: true })
218
+ * .whereGroup(qb => qb
219
+ * .where({ category: 'news' })
220
+ * .orWhere({ category: 'tech' })
221
+ * )
222
+ * .findMany()
223
+ * // Result: published AND (category='news' OR category='tech')
224
+ */
225
+ orWhere(condition) {
226
+ if (!this.query.where || Object.keys(this.query.where).length === 0) {
227
+ this.query.where = condition;
228
+ } else {
229
+ const prevWhere = this.query.where;
230
+ this.query.where = { OR: [prevWhere, condition] };
231
+ }
232
+ return this;
233
+ }
234
+ /**
235
+ * Creates a grouped where condition using a callback.
236
+ * Use this for explicit control over boolean logic grouping.
237
+ * The callback receives a fresh builder - its accumulated where becomes a single group.
238
+ *
239
+ * @param callback - Function that builds the grouped condition
240
+ * @param mode - How to combine with existing where: 'AND' (default) or 'OR'
241
+ *
242
+ * @example
243
+ * // (status = 'active') AND (name LIKE 'A%' OR name LIKE 'B%')
244
+ * DB.users
245
+ * .where({ status: 'active' })
246
+ * .whereGroup(qb => qb
247
+ * .where({ name: { startsWith: 'A' } })
248
+ * .orWhere({ name: { startsWith: 'B' } })
249
+ * )
250
+ * .findMany()
251
+ *
252
+ * @example
253
+ * // (status = 'active') OR (role = 'admin' AND verified = true)
254
+ * DB.users
255
+ * .where({ status: 'active' })
256
+ * .whereGroup(qb => qb
257
+ * .where({ role: 'admin' })
258
+ * .where({ verified: true })
259
+ * , 'OR')
260
+ * .findMany()
261
+ */
262
+ whereGroup(callback, mode = "AND") {
263
+ const groupBuilder = new _FlareBuilder(this.model, {});
264
+ callback(groupBuilder);
265
+ const groupWhere = groupBuilder.getQuery().where;
266
+ if (!groupWhere || Object.keys(groupWhere).length === 0) {
267
+ return this;
268
+ }
269
+ if (!this.query.where || Object.keys(this.query.where).length === 0) {
270
+ this.query.where = groupWhere;
271
+ } else {
272
+ const prevWhere = this.query.where;
273
+ this.query.where = { [mode]: [prevWhere, groupWhere] };
274
+ }
275
+ return this;
276
+ }
277
+ /**
278
+ * Alias for whereGroup with OR mode.
279
+ * Creates a grouped condition that's OR-ed with existing where.
280
+ *
281
+ * @param callback - Function that builds the grouped condition
282
+ *
283
+ * @example
284
+ * // (published = true) OR (authorId = 1 AND draft = true)
285
+ * DB.posts
286
+ * .where({ published: true })
287
+ * .orWhereGroup(qb => qb
288
+ * .where({ authorId: 1 })
289
+ * .where({ draft: true })
290
+ * )
291
+ * .findMany()
292
+ */
293
+ orWhereGroup(callback) {
294
+ return this.whereGroup(callback, "OR");
295
+ }
296
+ /**
297
+ * Adds a where condition to the query for the specified id.
298
+ * Uses the same AND composition as where() for consistency.
299
+ * @param id - The id to search for
300
+ */
301
+ withId(id) {
302
+ if (!id) {
303
+ throw new Error("Id is required");
304
+ }
305
+ if (!this.query.where || Object.keys(this.query.where).length === 0) {
306
+ this.query.where = { id };
307
+ } else {
308
+ const prevWhere = this.query.where;
309
+ this.query.where = { AND: [prevWhere, { id }] };
310
+ }
311
+ return this;
312
+ }
313
+ /**
314
+ * Adds an order by condition to the query
315
+ * @param orderBy - OrderBy object matching your Prisma model
316
+ */
317
+ order(orderBy) {
318
+ this.query.orderBy = orderBy;
319
+ return this;
320
+ }
321
+ /**
322
+ * Gets the last record sorted by the specified field
323
+ * @param key - Field to sort by (defaults to 'createdAt')
324
+ */
325
+ last(key = "createdAt") {
326
+ return this.order({ [key]: "desc" }).limit(1);
327
+ }
328
+ /**
329
+ * Gets the first record sorted by the specified field
330
+ * @param key - Field to sort by (defaults to 'createdAt')
331
+ */
332
+ first(key = "createdAt") {
333
+ return this.order({ [key]: "asc" }).limit(1);
334
+ }
335
+ /**
336
+ * Sets a limit on the number of records to retrieve
337
+ * @param limit - Maximum number of records
338
+ */
339
+ limit(limit) {
340
+ this.query.take = limit;
341
+ return this;
342
+ }
343
+ /**
344
+ * Sets distinct fields for the query
345
+ * @param distinct - Fields to be distinct
346
+ */
347
+ distinct(distinct) {
348
+ this.query.distinct = distinct;
349
+ return this;
350
+ }
351
+ /**
352
+ * Selects specific fields to retrieve
353
+ * @param fields - Select object matching your Prisma model
354
+ */
355
+ select(fields) {
356
+ this.query.select = fields;
357
+ return this;
358
+ }
359
+ /**
360
+ * Selects only the specified field and returns its value
361
+ * @param field - Field name to retrieve
362
+ */
363
+ async only(field) {
364
+ this.query.select = { [field]: true };
365
+ const result = await this.model.findFirst(this.query);
366
+ if (!result) {
367
+ return null;
368
+ }
369
+ return result[field];
370
+ }
371
+ /**
372
+ * Returns the current query object
373
+ */
374
+ getQuery() {
375
+ return this.query;
376
+ }
377
+ include(relation, callback) {
378
+ let relationQuery = true;
379
+ if (callback) {
380
+ const builder = modelRegistry.create(relation) ?? new _FlareBuilder(null);
381
+ callback(builder);
382
+ relationQuery = builder.getQuery();
383
+ if (Object.keys(relationQuery).length === 0) {
384
+ relationQuery = true;
385
+ }
386
+ }
387
+ this.query.include = {
388
+ ...this.query.include,
389
+ [relation]: relationQuery
390
+ };
391
+ return this;
392
+ }
393
+ /**
394
+ * Groups results by specified fields
395
+ * @param groupBy - Fields to group by
396
+ */
397
+ groupBy(groupBy) {
398
+ this.query.by = groupBy;
399
+ return this;
400
+ }
401
+ /**
402
+ * Adds a having condition to the query
403
+ * @param condition - Having condition
404
+ */
405
+ having(condition) {
406
+ this.query.having = condition;
407
+ return this;
408
+ }
409
+ /**
410
+ * Skips the specified number of records
411
+ * @param offset - Number of records to skip
412
+ */
413
+ skip(offset) {
414
+ this.query.skip = offset;
415
+ return this;
416
+ }
417
+ /**
418
+ * Checks if any record exists matching the current query
419
+ * @param existenceKey - Key to check for existence (defaults to 'id')
420
+ */
421
+ async exists(existenceKey = "id") {
422
+ const result = await this.model.findFirst({
423
+ where: this.query.where,
424
+ select: { [existenceKey]: true }
425
+ });
426
+ return Boolean(result);
427
+ }
428
+ /**
429
+ * Paginates the results
430
+ * @param page - Page number (1-based)
431
+ * @param perPage - Number of records per page
432
+ */
433
+ async paginate(page = 1, perPage = 15) {
434
+ const skip = (page - 1) * perPage;
435
+ const take = perPage;
436
+ this.query.skip = skip;
437
+ this.query.take = take;
438
+ const [data, total] = await Promise.all([
439
+ this.model.findMany(this.query),
440
+ this.model.count({ where: this.query.where })
441
+ ]);
442
+ const lastPage = Math.ceil(total / perPage);
443
+ return {
444
+ data,
445
+ meta: {
446
+ total,
447
+ lastPage,
448
+ currentPage: page,
449
+ perPage,
450
+ prev: page > 1 ? page - 1 : null,
451
+ next: page < lastPage ? page + 1 : null
452
+ }
453
+ };
454
+ }
455
+ /**
456
+ * Conditionally executes a callback on the query builder
457
+ * @param condition - Boolean or function returning boolean
458
+ * @param callback - Function to execute if condition is true
459
+ */
460
+ when(condition, callback) {
461
+ const isTrue = typeof condition === "function" ? condition() : condition;
462
+ if (isTrue) {
463
+ callback(this);
464
+ }
465
+ return this;
466
+ }
467
+ /**
468
+ * Processes results in chunks to avoid memory issues
469
+ * @param size - Size of each chunk
470
+ * @param callback - Function to process each chunk
471
+ */
472
+ async chunk(size, callback) {
473
+ let page = 1;
474
+ let hasMore = true;
475
+ const originalSkip = this.query.skip;
476
+ const originalTake = this.query.take;
477
+ while (hasMore) {
478
+ this.query.skip = (page - 1) * size;
479
+ this.query.take = size;
480
+ const results = await this.model.findMany(this.query);
481
+ if (results.length > 0) {
482
+ await callback(results);
483
+ page++;
484
+ if (results.length < size) {
485
+ hasMore = false;
486
+ }
487
+ } else {
488
+ hasMore = false;
489
+ }
490
+ }
491
+ this.query.skip = originalSkip;
492
+ this.query.take = originalTake;
493
+ }
494
+ /**
495
+ * Clones the current query builder instance.
496
+ * Uses structuredClone for proper handling of Date, BigInt, etc.
497
+ */
498
+ clone() {
499
+ const queryCopy = deepClone(this.query);
500
+ return new _FlareBuilder(this.model, queryCopy);
501
+ }
502
+ /**
503
+ * Finds the first record matching the query or throws an error if none found
504
+ * Throws a Prisma NotFoundError if no record matches the query
505
+ * @throws {Prisma.NotFoundError} When no record matches the query
506
+ * @returns Promise resolving to the found record
507
+ */
508
+ async findFirstOrThrow() {
509
+ return this.model.findFirstOrThrow(this.query);
510
+ }
511
+ /**
512
+ * Finds a unique record by primary key or throws an error if not found
513
+ * Requires a unique constraint (typically the id field)
514
+ * Throws a Prisma NotFoundError if no record matches
515
+ * @throws {Prisma.NotFoundError} When no record is found
516
+ * @returns Promise resolving to the found record
517
+ */
518
+ async findUniqueOrThrow() {
519
+ return this.model.findUniqueOrThrow(this.query);
520
+ }
521
+ /**
522
+ * Finds all records matching the query
523
+ * Respects all previously set query conditions (where, orderBy, take, skip, include, select, distinct)
524
+ * @returns Promise resolving to an array of records matching the query
525
+ */
526
+ async findMany() {
527
+ return this.model.findMany(this.query);
528
+ }
529
+ /**
530
+ * Finds the first record matching the query
531
+ * Returns null if no record matches. To throw an error instead, use findFirstOrThrow()
532
+ * @returns Promise resolving to the first matching record or null
533
+ */
534
+ async findFirst() {
535
+ return this.model.findFirst(this.query);
536
+ }
537
+ /**
538
+ * Finds a unique record by primary key
539
+ * Returns null if no record is found. To throw an error instead, use findUniqueOrThrow()
540
+ * Requires a unique constraint in the where condition (typically the id field)
541
+ * @returns Promise resolving to the found record or null
542
+ */
543
+ async findUnique() {
544
+ return this.model.findUnique(this.query);
545
+ }
546
+ /**
547
+ * Creates a new record with the provided data
548
+ * Any hooks registered for 'create' operations will be triggered
549
+ * @param data - Data matching your Prisma model's create input
550
+ * @returns Promise resolving to the newly created record
551
+ */
552
+ async create(data) {
553
+ const query = { ...this.query, data };
554
+ return this.model.create(query);
555
+ }
556
+ /**
557
+ * Creates multiple records in a single operation
558
+ * More efficient than creating records individually
559
+ * Any hooks registered for 'create' operations will be triggered for each record
560
+ * @param data - Array of data objects matching your Prisma model's create input
561
+ * @returns Promise resolving to the count of created records
562
+ */
563
+ async createMany(data) {
564
+ const query = { ...this.query, data };
565
+ return this.model.createMany(query);
566
+ }
567
+ /**
568
+ * Deletes a single record matching the current query conditions
569
+ * Requires at least one unique constraint in the where condition (typically id)
570
+ * Any hooks registered for 'delete' operations will be triggered
571
+ * @param args - Optional additional delete arguments to override query conditions
572
+ * @returns Promise resolving to the deleted record
573
+ */
574
+ async delete(args) {
575
+ const query = args ? { ...this.query, ...args } : this.query;
576
+ return this.model.delete(query);
577
+ }
578
+ /**
579
+ * Deletes multiple records matching the current query conditions
580
+ * More efficient than deleting records individually
581
+ * Any hooks registered for 'delete' operations will be triggered for each record
582
+ * @param args - Optional additional delete arguments to override query conditions
583
+ * @returns Promise resolving to the count of deleted records
584
+ */
585
+ async deleteMany(args) {
586
+ const query = args ? { ...this.query, ...args } : this.query;
587
+ return this.model.deleteMany(query);
588
+ }
589
+ /**
590
+ * Updates a single record matching the current query conditions
591
+ * Requires at least one unique constraint in the where condition (typically id)
592
+ * Any hooks registered for 'update' operations will be triggered
593
+ * @param data - Data to update, matching your Prisma model's update input
594
+ * @returns Promise resolving to the updated record
595
+ */
596
+ async update(data) {
597
+ const query = { ...this.query, data };
598
+ return this.model.update(query);
599
+ }
600
+ /**
601
+ * Updates multiple records matching the current query conditions
602
+ * More efficient than updating records individually
603
+ * Any hooks registered for 'update' operations will be triggered for each record
604
+ * @param data - Data to update, matching your Prisma model's update input
605
+ * @returns Promise resolving to the count of updated records
606
+ */
607
+ async updateMany(data) {
608
+ const query = { ...this.query, data };
609
+ return this.model.updateMany(query);
610
+ }
611
+ /**
612
+ * Updates a record if it exists, otherwise creates a new record
613
+ * The record is uniquely identified by the where condition (typically id)
614
+ * Any hooks registered for 'update' or 'create' operations will be triggered accordingly
615
+ * @param args - Optional upsert arguments including where, update, and create data
616
+ * @returns Promise resolving to the upserted record
617
+ */
618
+ async upsert(args) {
619
+ const query = args ? { ...this.query, ...args } : this.query;
620
+ return this.model.upsert(query);
621
+ }
622
+ /**
623
+ * Counts the number of records matching the query
624
+ */
625
+ async count() {
626
+ return this.model.count(this.query);
627
+ }
628
+ /**
629
+ * Sums the specified numeric field
630
+ * @param field - Field name to sum
631
+ */
632
+ async sum(field) {
633
+ const result = await this.model.aggregate({
634
+ _sum: { [field]: true },
635
+ where: this.query.where
636
+ });
637
+ return result._sum[field];
638
+ }
639
+ /**
640
+ * Calculates the average of the specified numeric field
641
+ * @param field - Field name to average
642
+ */
643
+ async avg(field) {
644
+ const result = await this.model.aggregate({
645
+ _avg: { [field]: true },
646
+ where: this.query.where
647
+ });
648
+ return result._avg[field];
649
+ }
650
+ /**
651
+ * Finds the minimum value of the specified field
652
+ * @param field - Field name to find minimum
653
+ */
654
+ async min(field) {
655
+ const result = await this.model.aggregate({
656
+ _min: { [field]: true },
657
+ where: this.query.where
658
+ });
659
+ return result._min[field];
660
+ }
661
+ /**
662
+ * Finds the maximum value of the specified field
663
+ * @param field - Field name to find maximum
664
+ */
665
+ async max(field) {
666
+ const result = await this.model.aggregate({
667
+ _max: { [field]: true },
668
+ where: this.query.where
669
+ });
670
+ return result._max[field];
671
+ }
672
+ /**
673
+ * Plucks the specified field from all results
674
+ * @param field - Field name to pluck
675
+ */
676
+ async pluck(field) {
677
+ this.query.select = { [field]: true };
678
+ const results = await this.model.findMany(this.query);
679
+ return results.map((result) => result[field]);
680
+ }
681
+ };