@classytic/mongokit 2.0.0 → 3.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 (105) hide show
  1. package/README.md +221 -7
  2. package/dist/actions/index.d.ts +3 -0
  3. package/dist/actions/index.js +473 -0
  4. package/dist/actions/index.js.map +1 -0
  5. package/dist/index-CgOJ2pqz.d.ts +337 -0
  6. package/dist/index.d.ts +239 -0
  7. package/dist/index.js +2108 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/memory-cache-DG2oSSbx.d.ts +142 -0
  10. package/dist/pagination/PaginationEngine.d.ts +117 -0
  11. package/dist/pagination/PaginationEngine.js +369 -0
  12. package/dist/pagination/PaginationEngine.js.map +1 -0
  13. package/dist/plugins/index.d.ts +275 -0
  14. package/dist/plugins/index.js +857 -0
  15. package/dist/plugins/index.js.map +1 -0
  16. package/dist/types-Nxhmi1aI.d.ts +510 -0
  17. package/dist/utils/index.d.ts +189 -0
  18. package/dist/utils/index.js +643 -0
  19. package/dist/utils/index.js.map +1 -0
  20. package/package.json +38 -21
  21. package/src/Repository.js +0 -296
  22. package/src/actions/aggregate.js +0 -266
  23. package/src/actions/create.js +0 -59
  24. package/src/actions/delete.js +0 -88
  25. package/src/actions/index.js +0 -11
  26. package/src/actions/read.js +0 -188
  27. package/src/actions/update.js +0 -176
  28. package/src/hooks/lifecycle.js +0 -146
  29. package/src/index.js +0 -71
  30. package/src/pagination/PaginationEngine.js +0 -348
  31. package/src/pagination/utils/cursor.js +0 -119
  32. package/src/pagination/utils/filter.js +0 -42
  33. package/src/pagination/utils/limits.js +0 -82
  34. package/src/pagination/utils/sort.js +0 -101
  35. package/src/plugins/aggregate-helpers.plugin.js +0 -71
  36. package/src/plugins/audit-log.plugin.js +0 -60
  37. package/src/plugins/batch-operations.plugin.js +0 -66
  38. package/src/plugins/field-filter.plugin.js +0 -27
  39. package/src/plugins/index.js +0 -19
  40. package/src/plugins/method-registry.plugin.js +0 -140
  41. package/src/plugins/mongo-operations.plugin.js +0 -317
  42. package/src/plugins/soft-delete.plugin.js +0 -46
  43. package/src/plugins/subdocument.plugin.js +0 -66
  44. package/src/plugins/timestamp.plugin.js +0 -19
  45. package/src/plugins/validation-chain.plugin.js +0 -145
  46. package/src/types.d.ts +0 -87
  47. package/src/utils/error.js +0 -12
  48. package/src/utils/field-selection.js +0 -156
  49. package/src/utils/index.js +0 -12
  50. package/types/Repository.d.ts +0 -95
  51. package/types/Repository.d.ts.map +0 -1
  52. package/types/actions/aggregate.d.ts +0 -112
  53. package/types/actions/aggregate.d.ts.map +0 -1
  54. package/types/actions/create.d.ts +0 -21
  55. package/types/actions/create.d.ts.map +0 -1
  56. package/types/actions/delete.d.ts +0 -37
  57. package/types/actions/delete.d.ts.map +0 -1
  58. package/types/actions/index.d.ts +0 -6
  59. package/types/actions/index.d.ts.map +0 -1
  60. package/types/actions/read.d.ts +0 -135
  61. package/types/actions/read.d.ts.map +0 -1
  62. package/types/actions/update.d.ts +0 -58
  63. package/types/actions/update.d.ts.map +0 -1
  64. package/types/hooks/lifecycle.d.ts +0 -44
  65. package/types/hooks/lifecycle.d.ts.map +0 -1
  66. package/types/index.d.ts +0 -25
  67. package/types/index.d.ts.map +0 -1
  68. package/types/pagination/PaginationEngine.d.ts +0 -386
  69. package/types/pagination/PaginationEngine.d.ts.map +0 -1
  70. package/types/pagination/utils/cursor.d.ts +0 -40
  71. package/types/pagination/utils/cursor.d.ts.map +0 -1
  72. package/types/pagination/utils/filter.d.ts +0 -28
  73. package/types/pagination/utils/filter.d.ts.map +0 -1
  74. package/types/pagination/utils/limits.d.ts +0 -64
  75. package/types/pagination/utils/limits.d.ts.map +0 -1
  76. package/types/pagination/utils/sort.d.ts +0 -41
  77. package/types/pagination/utils/sort.d.ts.map +0 -1
  78. package/types/plugins/aggregate-helpers.plugin.d.ts +0 -6
  79. package/types/plugins/aggregate-helpers.plugin.d.ts.map +0 -1
  80. package/types/plugins/audit-log.plugin.d.ts +0 -6
  81. package/types/plugins/audit-log.plugin.d.ts.map +0 -1
  82. package/types/plugins/batch-operations.plugin.d.ts +0 -6
  83. package/types/plugins/batch-operations.plugin.d.ts.map +0 -1
  84. package/types/plugins/field-filter.plugin.d.ts +0 -6
  85. package/types/plugins/field-filter.plugin.d.ts.map +0 -1
  86. package/types/plugins/index.d.ts +0 -11
  87. package/types/plugins/index.d.ts.map +0 -1
  88. package/types/plugins/method-registry.plugin.d.ts +0 -3
  89. package/types/plugins/method-registry.plugin.d.ts.map +0 -1
  90. package/types/plugins/mongo-operations.plugin.d.ts +0 -4
  91. package/types/plugins/mongo-operations.plugin.d.ts.map +0 -1
  92. package/types/plugins/soft-delete.plugin.d.ts +0 -6
  93. package/types/plugins/soft-delete.plugin.d.ts.map +0 -1
  94. package/types/plugins/subdocument.plugin.d.ts +0 -6
  95. package/types/plugins/subdocument.plugin.d.ts.map +0 -1
  96. package/types/plugins/timestamp.plugin.d.ts +0 -6
  97. package/types/plugins/timestamp.plugin.d.ts.map +0 -1
  98. package/types/plugins/validation-chain.plugin.d.ts +0 -31
  99. package/types/plugins/validation-chain.plugin.d.ts.map +0 -1
  100. package/types/utils/error.d.ts +0 -11
  101. package/types/utils/error.d.ts.map +0 -1
  102. package/types/utils/field-selection.d.ts +0 -9
  103. package/types/utils/field-selection.d.ts.map +0 -1
  104. package/types/utils/index.d.ts +0 -2
  105. package/types/utils/index.d.ts.map +0 -1
package/README.md CHANGED
@@ -10,10 +10,12 @@
10
10
 
11
11
  - ✅ **Zero external dependencies** (only Mongoose peer dependency)
12
12
  - ✅ **Smart pagination** - auto-detects offset vs cursor-based
13
+ - ✅ **HTTP utilities** - query parser & schema generator for controllers
13
14
  - ✅ **Event-driven** hooks for every operation
14
15
  - ✅ **Plugin architecture** for reusable behaviors
15
16
  - ✅ **TypeScript** first-class support with discriminated unions
16
- - ✅ **Battle-tested** in production with 68 passing tests
17
+ - ✅ **Optional caching** - Redis/Memcached with auto-invalidation
18
+ - ✅ **Battle-tested** in production with 182 passing tests
17
19
 
18
20
  ---
19
21
 
@@ -26,7 +28,13 @@ npm install @classytic/mongokit mongoose
26
28
  > **Peer Dependencies:**
27
29
  > - `mongoose ^8.0.0 || ^9.0.0` (supports both Mongoose 8 and 9)
28
30
 
29
- **That's it.** No additional pagination libraries needed.
31
+ **Available imports:**
32
+ ```javascript
33
+ import { MongooseRepository } from '@classytic/mongokit'; // Core repository
34
+ import { queryParser, buildCrudSchemasFromModel } from '@classytic/mongokit/utils'; // HTTP utilities
35
+ ```
36
+
37
+ **That's it.** No additional pagination, validation, or query parsing libraries needed.
30
38
 
31
39
  ---
32
40
 
@@ -282,6 +290,101 @@ const feedView = await postRepo.getAll({
282
290
 
283
291
  ---
284
292
 
293
+ ## 🌐 HTTP Utilities for Controllers & Routes
294
+
295
+ MongoKit provides utilities to quickly build production-ready controllers and routes for Express, Fastify, NestJS, and other frameworks.
296
+
297
+ ### Query Parser
298
+
299
+ Parse HTTP query strings into MongoDB filters automatically:
300
+
301
+ ```javascript
302
+ import { queryParser } from '@classytic/mongokit/utils';
303
+
304
+ // Express/Fastify route
305
+ app.get('/users', async (req, res) => {
306
+ const { filters, limit, page, sort } = queryParser.parseQuery(req.query);
307
+
308
+ const result = await userRepo.getAll({ filters, limit, page, sort });
309
+ res.json(result);
310
+ });
311
+ ```
312
+
313
+ **Supported query patterns:**
314
+
315
+ ```bash
316
+ # Simple filtering
317
+ GET /users?email=john@example.com&role=admin
318
+
319
+ # Operators
320
+ GET /users?age[gte]=18&age[lte]=65 # Range queries
321
+ GET /users?email[contains]=gmail # Text search
322
+ GET /users?role[in]=admin,user # Multiple values
323
+ GET /users?status[ne]=deleted # Not equal
324
+
325
+ # Pagination
326
+ GET /users?page=2&limit=50 # Offset pagination
327
+ GET /users?after=eyJfaWQiOiI2M... # Cursor pagination
328
+
329
+ # Sorting
330
+ GET /users?sort=-createdAt,name # Multi-field sort (- = descending)
331
+
332
+ # Combined
333
+ GET /users?role=admin&createdAt[gte]=2024-01-01&sort=-createdAt&limit=20
334
+ ```
335
+
336
+ ### Schema Generator (Fastify/OpenAPI)
337
+
338
+ Generate JSON schemas from Mongoose models with field rules:
339
+
340
+ ```javascript
341
+ import { buildCrudSchemasFromModel } from '@classytic/mongokit/utils';
342
+
343
+ const { crudSchemas } = buildCrudSchemasFromModel(UserModel, {
344
+ strictAdditionalProperties: true, // Reject unknown fields
345
+ fieldRules: {
346
+ organizationId: { immutable: true }, // Cannot be updated
347
+ status: { systemManaged: true }, // Omitted from create/update
348
+ email: { optional: false }, // Required field
349
+ },
350
+ create: {
351
+ omitFields: ['verifiedAt'], // Custom omissions
352
+ },
353
+ });
354
+
355
+ // Use in Fastify routes
356
+ fastify.post('/users', {
357
+ schema: crudSchemas.create,
358
+ }, async (request, reply) => {
359
+ const user = await userRepo.create(request.body);
360
+ return reply.status(201).send(user);
361
+ });
362
+
363
+ fastify.get('/users', {
364
+ schema: crudSchemas.list,
365
+ }, async (request, reply) => {
366
+ const { filters, limit, page, sort } = queryParser.parseQuery(request.query);
367
+ const result = await userRepo.getAll({ filters, limit, page, sort });
368
+ return reply.send(result);
369
+ });
370
+ ```
371
+
372
+ **Generated schemas:**
373
+ - `crudSchemas.create` - POST validation (body only)
374
+ - `crudSchemas.update` - PATCH validation (body + params)
375
+ - `crudSchemas.get` - GET by ID validation (params)
376
+ - `crudSchemas.list` - GET list validation (query)
377
+ - `crudSchemas.remove` - DELETE validation (params)
378
+
379
+ **Field Rules:**
380
+ - `immutable` - Field cannot be updated after creation (omitted from update schema)
381
+ - `systemManaged` - System-only field (omitted from both create and update schemas)
382
+ - `optional` - Remove from required array
383
+
384
+ **See full example:** [`examples/fastify-controller-example.js`](examples/fastify-controller-example.js)
385
+
386
+ ---
387
+
285
388
  ## 📘 Complete API Reference
286
389
 
287
390
  ### CRUD Operations
@@ -391,6 +494,71 @@ const result = await repo.getAll({ page: 1, limit: 20 });
391
494
 
392
495
  ---
393
496
 
497
+ ## 📊 Indexing Guide
498
+
499
+ **Critical:** MongoDB only auto-indexes `_id`. You must create indexes for efficient pagination.
500
+
501
+ ### Single-Tenant Applications
502
+
503
+ ```javascript
504
+ const PostSchema = new mongoose.Schema({
505
+ title: String,
506
+ publishedAt: { type: Date, default: Date.now }
507
+ });
508
+
509
+ // Required for keyset pagination
510
+ PostSchema.index({ publishedAt: -1, _id: -1 });
511
+ // ^^^^^^^^^^^^^^ ^^^^^^
512
+ // Sort field Tie-breaker
513
+ ```
514
+
515
+ ### Multi-Tenant Applications
516
+
517
+ ```javascript
518
+ const UserSchema = new mongoose.Schema({
519
+ organizationId: String,
520
+ email: String,
521
+ createdAt: { type: Date, default: Date.now }
522
+ });
523
+
524
+ // Required for multi-tenant keyset pagination
525
+ UserSchema.index({ organizationId: 1, createdAt: -1, _id: -1 });
526
+ // ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^
527
+ // Tenant filter Sort field Tie-breaker
528
+ ```
529
+
530
+ ### Common Index Patterns
531
+
532
+ ```javascript
533
+ // Basic sorting
534
+ Schema.index({ createdAt: -1, _id: -1 });
535
+
536
+ // Multi-tenant
537
+ Schema.index({ tenantId: 1, createdAt: -1, _id: -1 });
538
+
539
+ // Multi-tenant + status filter
540
+ Schema.index({ tenantId: 1, status: 1, createdAt: -1, _id: -1 });
541
+
542
+ // Text search
543
+ Schema.index({ title: 'text', content: 'text' });
544
+ Schema.index({ createdAt: -1, _id: -1 }); // Still need this for sorting
545
+
546
+ // Multi-field sort
547
+ Schema.index({ priority: -1, createdAt: -1, _id: -1 });
548
+ ```
549
+
550
+ ### Performance Impact
551
+
552
+ | Scenario | Without Index | With Index |
553
+ |----------|--------------|------------|
554
+ | 10K docs | ~50ms | ~5ms |
555
+ | 1M docs | ~5000ms | ~5ms |
556
+ | 100M docs | timeout | ~5ms |
557
+
558
+ **Rule:** Index = (tenant_field +) sort_field + _id
559
+
560
+ ---
561
+
394
562
  ## 🔌 Built-in Plugins
395
563
 
396
564
  ### Field Filtering (Role-based Access)
@@ -474,6 +642,46 @@ class UserRepository extends Repository {
474
642
  // All CUD operations automatically logged
475
643
  ```
476
644
 
645
+ ### Caching (Redis, Memcached, or In-Memory)
646
+
647
+ Add caching with automatic invalidation on mutations:
648
+
649
+ ```javascript
650
+ import { Repository, cachePlugin, createMemoryCache } from '@classytic/mongokit';
651
+
652
+ const userRepo = new Repository(UserModel, [
653
+ cachePlugin({
654
+ adapter: createMemoryCache(), // or your Redis adapter
655
+ ttl: 60, // 60 seconds default
656
+ byIdTtl: 300, // 5 min for getById
657
+ queryTtl: 30, // 30s for lists
658
+ })
659
+ ]);
660
+
661
+ // Reads are cached automatically
662
+ const user = await userRepo.getById(id); // cached on second call
663
+
664
+ // Skip cache for fresh data
665
+ const fresh = await userRepo.getById(id, { skipCache: true });
666
+
667
+ // Mutations auto-invalidate cache
668
+ await userRepo.update(id, { name: 'New' });
669
+
670
+ // Manual invalidation (microservices)
671
+ await userRepo.invalidateCache(id); // single doc
672
+ await userRepo.invalidateAllCache(); // full model
673
+ ```
674
+
675
+ **Redis adapter example:**
676
+ ```javascript
677
+ const redisAdapter = {
678
+ async get(key) { return JSON.parse(await redis.get(key) || 'null'); },
679
+ async set(key, value, ttl) { await redis.setex(key, ttl, JSON.stringify(value)); },
680
+ async del(key) { await redis.del(key); },
681
+ async clear(pattern) { /* optional: bulk delete by pattern */ }
682
+ };
683
+ ```
684
+
477
685
  ### More Plugins
478
686
 
479
687
  - **`timestampPlugin()`** - Auto-manage `createdAt`/`updatedAt`
@@ -652,19 +860,24 @@ await repo.getAll({ page: 1000, limit: 50 }); // O(50000)
652
860
  await repo.getAll({ after: cursor, limit: 50 }); // O(1)
653
861
  ```
654
862
 
655
- ### 2. Create Proper Indexes
863
+ ### 2. Create Required Indexes
864
+
865
+ **IMPORTANT:** MongoDB only auto-indexes `_id`. You must manually create indexes for pagination.
656
866
 
657
867
  ```javascript
658
- // For keyset pagination with sort
868
+ // Single-Tenant: Sort field + _id
659
869
  PostSchema.index({ createdAt: -1, _id: -1 });
660
870
 
661
- // For multi-tenant keyset pagination
871
+ // Multi-Tenant: Tenant field + Sort field + _id
662
872
  UserSchema.index({ organizationId: 1, createdAt: -1, _id: -1 });
663
873
 
664
- // For text search
874
+ // Text Search: Text index
665
875
  PostSchema.index({ title: 'text', content: 'text' });
666
876
  ```
667
877
 
878
+ **Without indexes = slow (full collection scan)**
879
+ **With indexes = fast (O(1) index seek)**
880
+
668
881
  ### 3. Use Estimated Counts for Large Collections
669
882
 
670
883
  ```javascript
@@ -795,11 +1008,12 @@ npm test
795
1008
  ```
796
1009
 
797
1010
  **Test Coverage:**
798
- - 68 tests (67 passing, 1 skipped - requires replica set)
1011
+ - 184 tests (182 passing, 2 skipped - require replica set)
799
1012
  - CRUD operations
800
1013
  - Offset pagination
801
1014
  - Keyset pagination
802
1015
  - Aggregation pagination
1016
+ - Caching (hit/miss, invalidation)
803
1017
  - Multi-tenancy
804
1018
  - Text search + infinite scroll
805
1019
  - Real-world scenarios
@@ -0,0 +1,3 @@
1
+ export { a as aggregate, c as create, _ as deleteActions, r as read, u as update } from '../index-CgOJ2pqz.js';
2
+ import 'mongoose';
3
+ import '../types-Nxhmi1aI.js';