@classytic/mongokit 1.0.2 → 2.1.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 (64) hide show
  1. package/README.md +772 -151
  2. package/dist/actions/index.cjs +479 -0
  3. package/dist/actions/index.cjs.map +1 -0
  4. package/dist/actions/index.d.cts +3 -0
  5. package/dist/actions/index.d.ts +3 -0
  6. package/dist/actions/index.js +473 -0
  7. package/dist/actions/index.js.map +1 -0
  8. package/dist/index-BfVJZF-3.d.cts +337 -0
  9. package/dist/index-CgOJ2pqz.d.ts +337 -0
  10. package/dist/index.cjs +2142 -0
  11. package/dist/index.cjs.map +1 -0
  12. package/dist/index.d.cts +239 -0
  13. package/dist/index.d.ts +239 -0
  14. package/dist/index.js +2108 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/memory-cache-DG2oSSbx.d.ts +142 -0
  17. package/dist/memory-cache-DqfFfKes.d.cts +142 -0
  18. package/dist/pagination/PaginationEngine.cjs +375 -0
  19. package/dist/pagination/PaginationEngine.cjs.map +1 -0
  20. package/dist/pagination/PaginationEngine.d.cts +117 -0
  21. package/dist/pagination/PaginationEngine.d.ts +117 -0
  22. package/dist/pagination/PaginationEngine.js +369 -0
  23. package/dist/pagination/PaginationEngine.js.map +1 -0
  24. package/dist/plugins/index.cjs +874 -0
  25. package/dist/plugins/index.cjs.map +1 -0
  26. package/dist/plugins/index.d.cts +275 -0
  27. package/dist/plugins/index.d.ts +275 -0
  28. package/dist/plugins/index.js +857 -0
  29. package/dist/plugins/index.js.map +1 -0
  30. package/dist/types-Nxhmi1aI.d.cts +510 -0
  31. package/dist/types-Nxhmi1aI.d.ts +510 -0
  32. package/dist/utils/index.cjs +667 -0
  33. package/dist/utils/index.cjs.map +1 -0
  34. package/dist/utils/index.d.cts +189 -0
  35. package/dist/utils/index.d.ts +189 -0
  36. package/dist/utils/index.js +643 -0
  37. package/dist/utils/index.js.map +1 -0
  38. package/package.json +54 -24
  39. package/src/Repository.js +0 -225
  40. package/src/actions/aggregate.js +0 -191
  41. package/src/actions/create.js +0 -59
  42. package/src/actions/delete.js +0 -88
  43. package/src/actions/index.js +0 -11
  44. package/src/actions/read.js +0 -156
  45. package/src/actions/update.js +0 -176
  46. package/src/hooks/lifecycle.js +0 -146
  47. package/src/index.js +0 -60
  48. package/src/plugins/aggregate-helpers.plugin.js +0 -71
  49. package/src/plugins/audit-log.plugin.js +0 -60
  50. package/src/plugins/batch-operations.plugin.js +0 -66
  51. package/src/plugins/field-filter.plugin.js +0 -27
  52. package/src/plugins/index.js +0 -19
  53. package/src/plugins/method-registry.plugin.js +0 -140
  54. package/src/plugins/mongo-operations.plugin.js +0 -313
  55. package/src/plugins/soft-delete.plugin.js +0 -46
  56. package/src/plugins/subdocument.plugin.js +0 -66
  57. package/src/plugins/timestamp.plugin.js +0 -19
  58. package/src/plugins/validation-chain.plugin.js +0 -145
  59. package/src/utils/field-selection.js +0 -156
  60. package/src/utils/index.js +0 -12
  61. package/types/actions/index.d.ts +0 -121
  62. package/types/index.d.ts +0 -104
  63. package/types/plugins/index.d.ts +0 -88
  64. package/types/utils/index.d.ts +0 -24
@@ -1,156 +0,0 @@
1
- /**
2
- * Field Selection Utilities
3
- *
4
- * Provides explicit, performant field filtering using Mongoose projections.
5
- *
6
- * Philosophy:
7
- * - Explicit is better than implicit
8
- * - Filter at DB level (10x faster than in-memory)
9
- * - Progressive disclosure (show more fields as trust increases)
10
- *
11
- * Usage:
12
- * ```javascript
13
- * // For Mongoose queries (PREFERRED - 90% of cases)
14
- * const projection = getMongooseProjection(request.user, fieldPresets.gymPlans);
15
- * const plans = await GymPlan.find().select(projection).lean();
16
- *
17
- * // For complex data (10% of cases - aggregations, multiple sources)
18
- * const filtered = filterResponseData(complexData, fieldPresets.gymPlans, request.user);
19
- * ```
20
- */
21
-
22
- /**
23
- * Get allowed fields for a user based on their context
24
- *
25
- * @param {Object} user - User object from request.user (or null for public)
26
- * @param {Object} preset - Field preset configuration
27
- * @param {string[]} preset.public - Fields visible to everyone
28
- * @param {string[]} preset.authenticated - Additional fields for authenticated users
29
- * @param {string[]} preset.admin - Additional fields for admins
30
- * @returns {string[]} Array of allowed field names
31
- */
32
- export const getFieldsForUser = (user, preset) => {
33
- if (!preset) {
34
- throw new Error('Field preset is required');
35
- }
36
-
37
- // Start with public fields
38
- let fields = [...(preset.public || [])];
39
-
40
- // Add authenticated fields if user is logged in
41
- if (user) {
42
- fields.push(...(preset.authenticated || []));
43
-
44
- // Add admin fields if user is admin/superadmin
45
- const roles = Array.isArray(user.roles) ? user.roles : (user.roles ? [user.roles] : []);
46
- if (roles.includes('admin') || roles.includes('superadmin')) {
47
- fields.push(...(preset.admin || []));
48
- }
49
- }
50
-
51
- // Remove duplicates
52
- return [...new Set(fields)];
53
- };
54
-
55
- /**
56
- * Get Mongoose projection string for query .select()
57
- *
58
- * @param {Object} user - User object from request.user
59
- * @param {Object} preset - Field preset configuration
60
- * @returns {string} Space-separated field names for Mongoose .select()
61
- *
62
- * @example
63
- * const projection = getMongooseProjection(request.user, fieldPresets.gymPlans);
64
- * const plans = await GymPlan.find({ organizationId }).select(projection).lean();
65
- */
66
- export const getMongooseProjection = (user, preset) => {
67
- const fields = getFieldsForUser(user, preset);
68
- return fields.join(' ');
69
- };
70
-
71
- /**
72
- * Filter response data to include only allowed fields
73
- *
74
- * Use this for complex responses where Mongoose projections aren't applicable:
75
- * - Aggregation pipeline results
76
- * - Data from multiple sources
77
- * - Custom computed fields
78
- *
79
- * For simple DB queries, prefer getMongooseProjection() (10x faster)
80
- *
81
- * @param {Object|Array} data - Data to filter
82
- * @param {Object} preset - Field preset configuration
83
- * @param {Object} user - User object from request.user
84
- * @returns {Object|Array} Filtered data
85
- *
86
- * @example
87
- * const stats = await calculateComplexStats();
88
- * const filtered = filterResponseData(stats, fieldPresets.dashboard, request.user);
89
- * return reply.send(filtered);
90
- */
91
- export const filterResponseData = (data, preset, user = null) => {
92
- const allowedFields = getFieldsForUser(user, preset);
93
-
94
- // Handle arrays
95
- if (Array.isArray(data)) {
96
- return data.map(item => filterObject(item, allowedFields));
97
- }
98
-
99
- // Handle single object
100
- return filterObject(data, allowedFields);
101
- };
102
-
103
- /**
104
- * Filter a single object to include only allowed fields
105
- *
106
- * @private
107
- * @param {Object} obj - Object to filter
108
- * @param {string[]} allowedFields - Array of allowed field names
109
- * @returns {Object} Filtered object
110
- */
111
- const filterObject = (obj, allowedFields) => {
112
- if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
113
- return obj;
114
- }
115
-
116
- const filtered = {};
117
-
118
- for (const field of allowedFields) {
119
- if (field in obj) {
120
- filtered[field] = obj[field];
121
- }
122
- }
123
-
124
- return filtered;
125
- };
126
-
127
- /**
128
- * Helper to create field presets (module-level)
129
- *
130
- * Each module should define its own field preset in its own directory.
131
- * This keeps modules independent and self-contained.
132
- *
133
- * @param {Object} config - Field configuration
134
- * @returns {Object} Field preset
135
- *
136
- * @example
137
- * // In modules/gym-plan/gym-plan.fields.js
138
- * import { createFieldPreset } from '#common/utils/field-selection.js';
139
- *
140
- * export const gymPlanFieldPreset = createFieldPreset({
141
- * public: ['id', 'name', 'price'],
142
- * authenticated: ['features', 'description'],
143
- * admin: ['createdAt', 'updatedAt', 'internalNotes']
144
- * });
145
- *
146
- * // Then in controller:
147
- * import { gymPlanFieldPreset } from './gym-plan.fields.js';
148
- * super(gymPlanRepository, { fieldPreset: gymPlanFieldPreset });
149
- */
150
- export const createFieldPreset = (config) => {
151
- return {
152
- public: config.public || [],
153
- authenticated: config.authenticated || [],
154
- admin: config.admin || [],
155
- };
156
- };
@@ -1,12 +0,0 @@
1
- /**
2
- * Utility Functions for MongoKit
3
- * Reusable helpers for field selection, filtering, and more
4
- */
5
-
6
- export {
7
- getFieldsForUser,
8
- getMongooseProjection,
9
- filterResponseData,
10
- createFieldPreset,
11
- } from './field-selection.js';
12
-
@@ -1,121 +0,0 @@
1
- import { Model, Document, UpdateQuery, ClientSession } from 'mongoose';
2
-
3
- // Compatibility alias: QueryFilter was introduced in Mongoose 9 and replaces FilterQuery.
4
- // @ts-ignore - QueryFilter only exists in mongoose >= 9
5
- type QueryFilterV9<T> = import('mongoose').QueryFilter<T>;
6
- // @ts-ignore - FilterQuery was removed in mongoose >= 9
7
- type QueryFilterV8<T> = import('mongoose').FilterQuery<T>;
8
- type CompatibleQueryFilter<T> = QueryFilterV9<T> | QueryFilterV8<T>;
9
-
10
- export interface ActionOptions {
11
- session?: ClientSession;
12
- updatePipeline?: boolean;
13
- [key: string]: any;
14
- }
15
-
16
- // Create actions
17
- export function create<T extends Document>(
18
- Model: Model<T>,
19
- data: Partial<T>,
20
- options?: ActionOptions
21
- ): Promise<T>;
22
-
23
- export function createMany<T extends Document>(
24
- Model: Model<T>,
25
- dataArray: Partial<T>[],
26
- options?: ActionOptions
27
- ): Promise<T[]>;
28
-
29
- export function createDefault<T extends Document>(
30
- Model: Model<T>,
31
- overrides?: Partial<T>,
32
- options?: ActionOptions
33
- ): Promise<T>;
34
-
35
- export function upsert<T extends Document>(
36
- Model: Model<T>,
37
- query: CompatibleQueryFilter<T>,
38
- data: Partial<T>,
39
- options?: ActionOptions
40
- ): Promise<T>;
41
-
42
- // Read actions
43
- export function getById<T extends Document>(
44
- Model: Model<T>,
45
- id: string,
46
- options?: ActionOptions
47
- ): Promise<T | null>;
48
-
49
- export function getByQuery<T extends Document>(
50
- Model: Model<T>,
51
- query: CompatibleQueryFilter<T>,
52
- options?: ActionOptions
53
- ): Promise<T | null>;
54
-
55
- export function getOrCreate<T extends Document>(
56
- Model: Model<T>,
57
- query: CompatibleQueryFilter<T>,
58
- createData: Partial<T>,
59
- options?: ActionOptions
60
- ): Promise<T>;
61
-
62
- export function count<T extends Document>(
63
- Model: Model<T>,
64
- query?: CompatibleQueryFilter<T>,
65
- options?: ActionOptions
66
- ): Promise<number>;
67
-
68
- export function exists<T extends Document>(
69
- Model: Model<T>,
70
- query: CompatibleQueryFilter<T>,
71
- options?: ActionOptions
72
- ): Promise<boolean>;
73
-
74
- // Update actions
75
- export function update<T extends Document>(
76
- Model: Model<T>,
77
- id: string,
78
- data: UpdateQuery<T>,
79
- options?: ActionOptions
80
- ): Promise<T | null>;
81
-
82
- export function updateMany<T extends Document>(
83
- Model: Model<T>,
84
- query: CompatibleQueryFilter<T>,
85
- data: UpdateQuery<T>,
86
- options?: ActionOptions
87
- ): Promise<any>;
88
-
89
- // Delete actions
90
- export function deleteById<T extends Document>(
91
- Model: Model<T>,
92
- id: string,
93
- options?: ActionOptions
94
- ): Promise<T | null>;
95
-
96
- export function deleteMany<T extends Document>(
97
- Model: Model<T>,
98
- query: CompatibleQueryFilter<T>,
99
- options?: ActionOptions
100
- ): Promise<any>;
101
-
102
- // Aggregate actions
103
- export function aggregate<T extends Document>(
104
- Model: Model<T>,
105
- pipeline: any[],
106
- options?: ActionOptions
107
- ): Promise<any[]>;
108
-
109
- export function aggregatePaginate<T extends Document>(
110
- Model: Model<T>,
111
- pipeline: any[],
112
- options?: ActionOptions
113
- ): Promise<any>;
114
-
115
- export function distinct<T extends Document>(
116
- Model: Model<T>,
117
- field: string,
118
- query?: CompatibleQueryFilter<T>,
119
- options?: ActionOptions
120
- ): Promise<any[]>;
121
-
package/types/index.d.ts DELETED
@@ -1,104 +0,0 @@
1
- import { Model, Document, ClientSession, PaginateOptions, PaginateResult, UpdateQuery, AggregateOptions } from 'mongoose';
2
-
3
- // Compatibility alias: QueryFilter was introduced in Mongoose 9 and replaces FilterQuery.
4
- // @ts-ignore - QueryFilter only exists in mongoose >= 9
5
- type QueryFilterV9<T> = import('mongoose').QueryFilter<T>;
6
- // @ts-ignore - FilterQuery was removed in mongoose >= 9
7
- type QueryFilterV8<T> = import('mongoose').FilterQuery<T>;
8
- type CompatibleQueryFilter<T> = QueryFilterV9<T> | QueryFilterV8<T>;
9
-
10
- export interface RepositoryOptions {
11
- session?: ClientSession;
12
- populate?: string | string[] | any;
13
- select?: string | any;
14
- lean?: boolean;
15
- throwOnNotFound?: boolean;
16
- updatePipeline?: boolean;
17
- }
18
-
19
- export interface QueryParams {
20
- pagination?: {
21
- page: number;
22
- limit: number;
23
- };
24
- search?: string;
25
- sort?: string;
26
- filters?: Record<string, any>;
27
- }
28
-
29
- export interface RepositoryContext {
30
- operation: string;
31
- model: string;
32
- data?: any;
33
- dataArray?: any[];
34
- id?: string;
35
- query?: any;
36
- queryParams?: QueryParams;
37
- context?: any;
38
- user?: any;
39
- organizationId?: string;
40
- [key: string]: any;
41
- }
42
-
43
- export type EventListener = (data: any) => void | Promise<void>;
44
-
45
- export interface Plugin {
46
- name: string;
47
- apply(repository: Repository<any>): void;
48
- }
49
-
50
- export type PluginFactory = (options?: any) => Plugin;
51
-
52
- export class Repository<T extends Document> {
53
- Model: Model<T>;
54
- model: string;
55
- protected _hooks: Map<string, EventListener[]>;
56
-
57
- constructor(Model: Model<T>, plugins?: (Plugin | PluginFactory)[]);
58
-
59
- // Plugin system
60
- use(plugin: Plugin | PluginFactory): this;
61
- on(event: string, listener: EventListener): this;
62
- emit(event: string, data: any): void;
63
-
64
- // CRUD operations
65
- create(data: Partial<T>, options?: RepositoryOptions): Promise<T>;
66
- createMany(dataArray: Partial<T>[], options?: RepositoryOptions): Promise<T[]>;
67
-
68
- getById(id: string, options?: RepositoryOptions): Promise<T | null>;
69
- getByQuery(query: CompatibleQueryFilter<T>, options?: RepositoryOptions): Promise<T | null>;
70
- getAll(queryParams?: QueryParams, options?: RepositoryOptions): Promise<PaginateResult<T>>;
71
- getOrCreate(query: CompatibleQueryFilter<T>, createData: Partial<T>, options?: RepositoryOptions): Promise<T>;
72
-
73
- count(query?: CompatibleQueryFilter<T>, options?: RepositoryOptions): Promise<number>;
74
- exists(query: CompatibleQueryFilter<T>, options?: RepositoryOptions): Promise<boolean>;
75
-
76
- update(id: string, data: UpdateQuery<T>, options?: RepositoryOptions): Promise<T | null>;
77
- delete(id: string, options?: RepositoryOptions): Promise<T | null>;
78
-
79
- // Aggregation
80
- aggregate(pipeline: any[], options?: AggregateOptions): Promise<any[]>;
81
- aggregatePaginate(pipeline: any[], options?: PaginateOptions): Promise<any>;
82
- distinct(field: string, query?: CompatibleQueryFilter<T>, options?: RepositoryOptions): Promise<any[]>;
83
-
84
- // Transaction support
85
- withTransaction<R>(callback: (session: ClientSession) => Promise<R>): Promise<R>;
86
-
87
- // Internal methods
88
- protected _executeQuery(buildQuery: (model: Model<T>) => Promise<any>): Promise<any>;
89
- protected _buildContext(operation: string, options: any): Promise<RepositoryContext>;
90
- protected _parseSort(sort: string | object): object;
91
- protected _parsePopulate(populate: string | string[] | any): any[];
92
- protected _handleError(error: any): Error;
93
- }
94
-
95
- export function createRepository<T extends Document>(
96
- Model: Model<T>,
97
- plugins?: (Plugin | PluginFactory)[]
98
- ): Repository<T>;
99
-
100
- // Plugin exports
101
- export * from './plugins/index.js';
102
- export * from './utils/index.js';
103
- export * as actions from './actions/index.js';
104
-
@@ -1,88 +0,0 @@
1
- import { Plugin, PluginFactory, Repository, RepositoryContext } from '../index.js';
2
- import { Document } from 'mongoose';
3
-
4
- // Field Filter Plugin
5
- export interface FieldPreset {
6
- public?: string[];
7
- authenticated?: string[];
8
- admin?: string[];
9
- }
10
-
11
- export function fieldFilterPlugin(fieldPreset: FieldPreset): Plugin;
12
-
13
- // Soft Delete Plugin
14
- export interface SoftDeleteOptions {
15
- deletedField?: string;
16
- deletedByField?: string;
17
- }
18
-
19
- export function softDeletePlugin(options?: SoftDeleteOptions): Plugin;
20
-
21
- // Timestamp Plugin
22
- export interface TimestampOptions {
23
- createdAtField?: string;
24
- updatedAtField?: string;
25
- }
26
-
27
- export function timestampPlugin(options?: TimestampOptions): Plugin;
28
-
29
- // Audit Log Plugin
30
- export interface Logger {
31
- info(message: string, meta?: any): void;
32
- error(message: string, meta?: any): void;
33
- warn(message: string, meta?: any): void;
34
- }
35
-
36
- export function auditLogPlugin(logger: Logger): Plugin;
37
-
38
- // Validation Chain Plugin
39
- export interface Validator {
40
- name: string;
41
- operations?: string[];
42
- validate(context: RepositoryContext, repo: Repository<any>): void | Promise<void>;
43
- }
44
-
45
- export interface ValidationChainOptions {
46
- stopOnFirstError?: boolean;
47
- }
48
-
49
- export function validationChainPlugin(
50
- validators: Validator[],
51
- options?: ValidationChainOptions
52
- ): Plugin;
53
-
54
- // Validator helpers
55
- export function blockIf(
56
- name: string,
57
- operations: string[],
58
- condition: (context: RepositoryContext) => boolean,
59
- errorMessage: string
60
- ): Validator;
61
-
62
- export function requireField(field: string, operations?: string[]): Validator;
63
-
64
- export function autoInject(
65
- field: string,
66
- getter: (context: RepositoryContext) => any,
67
- operations?: string[]
68
- ): Validator;
69
-
70
- export function immutableField(field: string): Validator;
71
-
72
- export function uniqueField(field: string, errorMessage?: string): Validator;
73
-
74
- // Method Registry Plugin
75
- export function methodRegistryPlugin(): Plugin;
76
-
77
- // Mongo Operations Plugin
78
- export function mongoOperationsPlugin(): Plugin;
79
-
80
- // Batch Operations Plugin
81
- export function batchOperationsPlugin(): Plugin;
82
-
83
- // Aggregate Helpers Plugin
84
- export function aggregateHelpersPlugin(): Plugin;
85
-
86
- // Subdocument Plugin
87
- export function subdocumentPlugin(): Plugin;
88
-
@@ -1,24 +0,0 @@
1
- import { FieldPreset } from '../plugins/index.js';
2
-
3
- export interface User {
4
- roles?: string | string[];
5
- [key: string]: any;
6
- }
7
-
8
- // Field Selection utilities
9
- export function getFieldsForUser(user: User | null, preset: FieldPreset): string[];
10
-
11
- export function getMongooseProjection(user: User | null, preset: FieldPreset): string;
12
-
13
- export function filterResponseData<T>(
14
- data: T | T[],
15
- preset: FieldPreset,
16
- user?: User | null
17
- ): T | T[];
18
-
19
- export function createFieldPreset(config: {
20
- public?: string[];
21
- authenticated?: string[];
22
- admin?: string[];
23
- }): FieldPreset;
24
-