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