@classytic/mongokit 3.11.1 → 3.13.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 (38) hide show
  1. package/README.md +75 -5
  2. package/dist/{PaginationEngine-CvDFfVXT.mjs → PaginationEngine-CfQRM1yz.mjs} +16 -16
  3. package/dist/Repository-BY40uhcv.mjs +3603 -0
  4. package/dist/actions/index.d.mts +1 -1
  5. package/dist/actions/index.mjs +1 -1
  6. package/dist/adapter/index.d.mts +190 -0
  7. package/dist/adapter/index.mjs +2 -0
  8. package/dist/adapter-DTDMnUOH.mjs +158 -0
  9. package/dist/ai/index.d.mts +1 -1
  10. package/dist/ai/index.mjs +3 -3
  11. package/dist/better-auth/index.d.mts +149 -0
  12. package/dist/better-auth/index.mjs +81 -0
  13. package/dist/field-selection-Br3WPOEb.mjs +99 -0
  14. package/dist/{index-dNTmpgK8.d.mts → index-BbHaKwi5.d.mts} +33 -13
  15. package/dist/index.d.mts +678 -36
  16. package/dist/index.mjs +240 -17
  17. package/dist/{logger-DJ7egMnd.mjs → logger-DprijP5h.mjs} +1 -5
  18. package/dist/{mongooseToJsonSchema-D1sdCOmj.mjs → mongooseToJsonSchema-BYbG894y.mjs} +68 -20
  19. package/dist/{mongooseToJsonSchema-DXxjFkLB.d.mts → mongooseToJsonSchema-BgRAaXX-.d.mts} +10 -2
  20. package/dist/pagination/PaginationEngine.d.mts +3 -2
  21. package/dist/pagination/PaginationEngine.mjs +1 -1
  22. package/dist/plugins/index.d.mts +2 -2
  23. package/dist/plugins/index.mjs +2 -2
  24. package/dist/query/primitives/coercion.d.mts +1 -1
  25. package/dist/query/primitives/coercion.mjs +1 -1
  26. package/dist/query/primitives/indexes.d.mts +1 -1
  27. package/dist/{types-Dc2aDVUi.d.mts → types-BbaPZleN.d.mts} +419 -205
  28. package/dist/{update-DEJYcgfV.mjs → update-DLcSSa5b.mjs} +184 -45
  29. package/dist/utils/index.d.mts +2 -91
  30. package/dist/utils/index.mjs +5 -5
  31. package/dist/{validation-chain.plugin-B0VSdcwa.d.mts → validation-chain.plugin-CAPYCj_T.d.mts} +228 -98
  32. package/dist/validation-chain.plugin-_-WuNml2.mjs +2399 -0
  33. package/package.json +25 -5
  34. package/dist/field-selection-dI0m4RBs.mjs +0 -221
  35. package/dist/validation-chain.plugin-Dllv-cml.mjs +0 -5122
  36. /package/dist/{coercion-oM8G4b6m.d.mts → coercion-TRMmR49h.d.mts} +0 -0
  37. /package/dist/{id-resolution-e4BT9zAV.mjs → id-resolution-B0Odvvou.mjs} +0 -0
  38. /package/dist/{indexes-C26GNZRU.d.mts → indexes-CgOF7Wjd.d.mts} +0 -0
@@ -0,0 +1,99 @@
1
+ //#region src/utils/field-selection.ts
2
+ /**
3
+ * Get allowed fields for a user based on their context
4
+ *
5
+ * @param user - User object from request.user (or null for public)
6
+ * @param preset - Field preset configuration
7
+ * @returns Array of allowed field names
8
+ *
9
+ * @example
10
+ * const fields = getFieldsForUser(request.user, {
11
+ * public: ['id', 'name', 'price'],
12
+ * authenticated: ['description', 'features'],
13
+ * admin: ['createdAt', 'internalNotes']
14
+ * });
15
+ */
16
+ function getFieldsForUser(user, preset) {
17
+ if (!preset) throw new Error("Field preset is required");
18
+ const fields = [...preset.public || []];
19
+ if (user) {
20
+ fields.push(...preset.authenticated || []);
21
+ const roles = Array.isArray(user.roles) ? user.roles : user.roles ? [user.roles] : [];
22
+ if (roles.includes("admin") || roles.includes("superadmin")) fields.push(...preset.admin || []);
23
+ }
24
+ return [...new Set(fields)];
25
+ }
26
+ /**
27
+ * Get Mongoose projection string for query .select()
28
+ *
29
+ * @param user - User object from request.user
30
+ * @param preset - Field preset configuration
31
+ * @returns Space-separated field names for Mongoose .select()
32
+ *
33
+ * @example
34
+ * const projection = getMongooseProjection(request.user, fieldPresets.gymPlans);
35
+ * const plans = await GymPlan.find({ organizationId }).select(projection).lean();
36
+ */
37
+ function getMongooseProjection(user, preset) {
38
+ return getFieldsForUser(user, preset).join(" ");
39
+ }
40
+ /**
41
+ * Filter a single object to include only allowed fields
42
+ */
43
+ function filterObject(obj, allowedFields) {
44
+ if (!obj || typeof obj !== "object" || Array.isArray(obj)) return obj;
45
+ const filtered = {};
46
+ for (const field of allowedFields) if (field in obj) filtered[field] = obj[field];
47
+ return filtered;
48
+ }
49
+ /**
50
+ * Filter response data to include only allowed fields
51
+ *
52
+ * Use this for complex responses where Mongoose projections aren't applicable:
53
+ * - Aggregation pipeline results
54
+ * - Data from multiple sources
55
+ * - Custom computed fields
56
+ *
57
+ * For simple DB queries, prefer getMongooseProjection() (10x faster)
58
+ *
59
+ * @param data - Data to filter
60
+ * @param preset - Field preset configuration
61
+ * @param user - User object from request.user
62
+ * @returns Filtered data
63
+ *
64
+ * @example
65
+ * const stats = await calculateComplexStats();
66
+ * const filtered = filterResponseData(stats, fieldPresets.dashboard, request.user);
67
+ * return reply.send(filtered);
68
+ */
69
+ function filterResponseData(data, preset, user = null) {
70
+ const allowedFields = getFieldsForUser(user, preset);
71
+ if (Array.isArray(data)) return data.map((item) => filterObject(item, allowedFields));
72
+ return filterObject(data, allowedFields);
73
+ }
74
+ /**
75
+ * Helper to create field presets (module-level)
76
+ *
77
+ * Each module should define its own field preset in its own directory.
78
+ * This keeps modules independent and self-contained.
79
+ *
80
+ * @param config - Field configuration
81
+ * @returns Field preset
82
+ *
83
+ * @example
84
+ * // In modules/gym-plan/gym-plan.fields.ts
85
+ * export const gymPlanFieldPreset = createFieldPreset({
86
+ * public: ['id', 'name', 'price'],
87
+ * authenticated: ['features', 'description'],
88
+ * admin: ['createdAt', 'updatedAt', 'internalNotes']
89
+ * });
90
+ */
91
+ function createFieldPreset(config) {
92
+ return {
93
+ public: config.public || [],
94
+ authenticated: config.authenticated || [],
95
+ admin: config.admin || []
96
+ };
97
+ }
98
+ //#endregion
99
+ export { getMongooseProjection as i, filterResponseData as n, getFieldsForUser as r, createFieldPreset as t };
@@ -1,4 +1,4 @@
1
- import { $ as ReadPreferenceType, B as ObjectId, D as GroupResult, E as FindOneAndUpdateOptions, R as MinMaxResult, U as OperationOptions, X as PopulateSpec, a as AnyDocument, b as DeleteResult, ft as SortSpec, g as CreateOptions, gt as UpdateOptions, ht as UpdateManyResult, kt as LookupOptions, ot as SelectSpec, vt as UpdateWithValidationResult } from "./types-Dc2aDVUi.mjs";
1
+ import { B as ObjectId, Dt as LookupOptions, H as OperationOptions, I as MinMaxResult, J as PopulateSpec, T as GroupResult, Z as ReadPreferenceType, ft as UpdateManyResult, ht as UpdateWithValidationResult, i as AnyDocument, it as SelectSpec, p as CreateOptions, pt as UpdateOptions, ut as SortSpec, v as DeleteResult, w as FindOneAndUpdateOptions } from "./types-BbaPZleN.mjs";
2
2
  import { Model, PipelineStage } from "mongoose";
3
3
 
4
4
  //#region src/actions/aggregate.d.ts
@@ -21,7 +21,7 @@ declare function aggregatePaginate<TDoc = AnyDocument>(Model: Model<TDoc>, pipel
21
21
  limit?: number;
22
22
  session?: unknown;
23
23
  }): Promise<{
24
- docs: TDoc[];
24
+ data: TDoc[];
25
25
  total: number;
26
26
  page: number;
27
27
  limit: number;
@@ -116,38 +116,43 @@ declare namespace delete_d_exports {
116
116
  export { deleteById, deleteByQuery, deleteMany, restore, softDelete };
117
117
  }
118
118
  /**
119
- * Delete by ID
119
+ * Delete by ID. Returns `null` on miss (matches `update()` convention) —
120
+ * a second delete on the same id is a no-op, not an error.
120
121
  */
121
122
  declare function deleteById<TDoc = AnyDocument>(Model: Model<TDoc>, id: string | ObjectId, options?: {
122
123
  session?: unknown;
123
124
  query?: Record<string, unknown>;
124
- }): Promise<DeleteResult>;
125
+ }): Promise<DeleteResult | null>;
125
126
  /**
126
- * Delete many documents
127
+ * Delete many documents. Always returns a {@link DeleteResult} (never
128
+ * null) — a deleteMany with zero matches is a successful op with
129
+ * `count: 0`, not a miss.
127
130
  */
128
131
  declare function deleteMany<TDoc = AnyDocument>(Model: Model<TDoc>, query: Record<string, unknown>, options?: {
129
132
  session?: unknown;
130
133
  }): Promise<DeleteResult>;
131
134
  /**
132
- * Delete by query
135
+ * Delete by query. Returns `null` on miss unless `throwOnNotFound: true`.
133
136
  */
134
137
  declare function deleteByQuery(Model: Model<any>, query: Record<string, unknown>, options?: {
135
138
  session?: unknown;
136
139
  throwOnNotFound?: boolean;
137
- }): Promise<DeleteResult>;
140
+ }): Promise<DeleteResult | null>;
138
141
  /**
139
- * Soft delete (set deleted flag)
142
+ * Soft delete (set deleted flag). Returns `null` on miss for parity with
143
+ * `deleteById`; callers that prefer a throw can wrap or use `throwOnNotFound`
144
+ * pattern at the call site.
140
145
  */
141
146
  declare function softDelete<TDoc = AnyDocument>(Model: Model<TDoc>, id: string | ObjectId, options?: {
142
147
  session?: unknown;
143
148
  userId?: string;
144
- }): Promise<DeleteResult>;
149
+ }): Promise<DeleteResult | null>;
145
150
  /**
146
- * Restore soft deleted document
151
+ * Restore soft-deleted document. Returns `null` on miss.
147
152
  */
148
153
  declare function restore<TDoc = AnyDocument>(Model: Model<TDoc>, id: string | ObjectId, options?: {
149
154
  session?: unknown;
150
- }): Promise<DeleteResult>;
155
+ }): Promise<DeleteResult | null>;
151
156
  declare namespace read_d_exports {
152
157
  export { count, exists, getAll, getById, getByQuery, getOrCreate, tryGetByQuery };
153
158
  }
@@ -190,12 +195,27 @@ declare function getAll<TDoc = AnyDocument>(Model: Model<TDoc>, query?: Record<s
190
195
  readPreference?: ReadPreferenceType;
191
196
  }): Promise<TDoc[]>;
192
197
  /**
193
- * Get or create document (upsert)
198
+ * Get or create document (atomic upsert).
199
+ *
200
+ * Returns `{ doc, created }` so callers can disambiguate the two
201
+ * outcomes without a follow-up read — load-bearing for race-detection
202
+ * in idempotency stores, lock acquisition, and "ensure-exists" flows.
203
+ *
204
+ * `created: true` ⇔ this caller's `createData` won the race and was
205
+ * just inserted; `created: false` ⇔ a doc matching `query` already
206
+ * existed and was returned unchanged.
207
+ *
208
+ * Mongo: `includeResultMetadata: true` returns the raw `ModifyResult`
209
+ * (`{ value, lastErrorObject, ok }`). `lastErrorObject.upserted` is
210
+ * the `_id` of a freshly-inserted row — present iff this call inserted.
194
211
  */
195
212
  declare function getOrCreate<TDoc = AnyDocument>(Model: Model<TDoc>, query: Record<string, unknown>, createData: Record<string, unknown>, options?: {
196
213
  session?: unknown;
197
214
  updatePipeline?: boolean;
198
- }): Promise<TDoc | null>;
215
+ }): Promise<{
216
+ doc: TDoc;
217
+ created: boolean;
218
+ }>;
199
219
  /**
200
220
  * Count documents matching query
201
221
  */