@classytic/mongokit 3.18.0 → 3.19.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 (32) hide show
  1. package/README.md +66 -0
  2. package/dist/PaginationEngine-4SjzfBQE.d.mts +103 -0
  3. package/dist/actions/index.d.mts +1 -1
  4. package/dist/adapter/index.mjs +1 -1
  5. package/dist/ai/index.d.mts +1 -1
  6. package/dist/better-auth/index.mjs +1 -1
  7. package/dist/create-repository-D5YX0wGs.mjs +29 -0
  8. package/dist/create-repository-K0yft2ht.d.mts +1663 -0
  9. package/dist/{index-fi91ycQc.d.mts → index-BgSBwlWu.d.mts} +1 -1
  10. package/dist/index.d.mts +11 -1665
  11. package/dist/index.mjs +5 -29
  12. package/dist/{mongooseToJsonSchema-BdQJU39w.d.mts → mongooseToJsonSchema-Bz47aMfH.d.mts} +1 -1
  13. package/dist/multi-tenant.plugin-D6lw3BsI.d.mts +487 -0
  14. package/dist/pagination/PaginationEngine.d.mts +1 -102
  15. package/dist/plugins/index.d.mts +3 -2
  16. package/dist/plugins/index.mjs +2 -1
  17. package/dist/query/primitives/coercion.d.mts +1 -1
  18. package/dist/query/primitives/indexes.d.mts +1 -1
  19. package/dist/testkit/index.d.mts +129 -0
  20. package/dist/testkit/index.mjs +139 -0
  21. package/dist/{validation-chain.plugin-DJtC0KJ-.mjs → timestamp.plugin-B1A9U-P7.mjs} +1 -1052
  22. package/dist/utils/index.d.mts +1 -1
  23. package/dist/utils/index.mjs +2 -2
  24. package/dist/{validation-chain.plugin-BpMfy18N.d.mts → validation-chain.plugin-CYOPfFGF.d.mts} +2 -486
  25. package/dist/validation-chain.plugin-DHZbpb4t.mjs +1056 -0
  26. package/package.json +9 -1
  27. /package/dist/{adapter-DYEMkLwK.mjs → adapter-BSxhT0Ip.mjs} +0 -0
  28. /package/dist/{coercion-TRMmR49h.d.mts → coercion-BxxHyLjP.d.mts} +0 -0
  29. /package/dist/{field-selection-CQW3WBiK.mjs → field-selection-d1qdRiMr.mjs} +0 -0
  30. /package/dist/{indexes-CgOF7Wjd.d.mts → indexes-3eHBWNZ2.d.mts} +0 -0
  31. /package/dist/{mongooseToJsonSchema-CBEQmeEa.mjs → mongooseToJsonSchema-ChtXSrLT.mjs} +0 -0
  32. /package/dist/{types-CU3SEDbD.d.mts → types-BA8oXjFe.d.mts} +0 -0
package/README.md CHANGED
@@ -351,6 +351,72 @@ npm run build
351
351
 
352
352
  The test suite uses `mongodb-memory-server` by default. Set `MONGODB_URI` to run against an external MongoDB deployment.
353
353
 
354
+ ## Test Harness — `@classytic/mongokit/testkit`
355
+
356
+ A batteries-included harness for testing your own repositories. Spins an ephemeral MongoDB (standalone, or a single-node replica set for transactions), opens an **isolated** connection, and gives you a live mongokit `Repository` in one call.
357
+
358
+ `mongodb-memory-server` is an **optional peer** — install it where you use the testkit; it is never a production dependency and is dynamically imported, so it never enters your app bundle:
359
+
360
+ ```bash
361
+ npm i -D mongodb-memory-server
362
+ ```
363
+
364
+ ```ts
365
+ import { createTestRepository } from '@classytic/mongokit/testkit';
366
+
367
+ const t = await createTestRepository({
368
+ name: 'Order',
369
+ schema: orderSchema,
370
+ config: { softDelete: true, timestamps: true }, // full CreateRepositoryConfig
371
+ });
372
+
373
+ await t.repository.create({ total: 10 });
374
+ await t.clear(); // empty the collection between tests
375
+ await t.close(); // close connection + stop server (idempotent)
376
+ ```
377
+
378
+ ### Run against your own local or cloud DB
379
+
380
+ Pass a `uri` (or set `MONGODB_URI`) and **no in-memory server starts** — the testkit connects to your real database and `close()`/`stop()` become no-ops, so it never drops it:
381
+
382
+ ```ts
383
+ const t = await createTestRepository({
384
+ name: 'Order', schema: orderSchema,
385
+ uri: process.env.ATLAS_TEST_URI ?? 'mongodb://localhost:27017/myapp-test',
386
+ });
387
+ ```
388
+
389
+ ```bash
390
+ # or for the whole suite, no code change:
391
+ MONGODB_URI="mongodb+srv://…/myapp-test" vitest run
392
+ ```
393
+
394
+ > ⚠️ `clear()` empties **every collection** on the connection. Against a real database, always point at a **dedicated test DB** — never production.
395
+
396
+ ### Testing indexes
397
+
398
+ `createTestRepository` runs `model.init()`, so declared indexes are really built — unique-constraint enforcement (`E11000`), TTL, compound, text, and geo indexes all behave for real on the in-memory server. Use the exposed `model` for runtime index work:
399
+
400
+ ```ts
401
+ await t.model.syncIndexes();
402
+ await t.model.collection.indexes(); // inspect
403
+ await t.model.collection.dropIndex('tag_1'); // drop
404
+ ```
405
+
406
+ Atlas **`$search` / `$vectorSearch`** indexes are Atlas-only and don't exist on any non-Atlas server — test those by pointing `uri` at a real Atlas test cluster.
407
+
408
+ ### Helpers
409
+
410
+ | Helper | Use |
411
+ | --- | --- |
412
+ | `createTestRepository` | server + connection + live `Repository` in one call |
413
+ | `createTestConnection` | isolated connection + `clear()` / `close()` |
414
+ | `withMongoMemory(fn)` | scoped setup → run → teardown (`finally`) |
415
+ | `createMongoMemory` | raw server lifecycle (`uri` + `stop`) |
416
+ | `mongoMemoryBackend()` | a `TestBackend` seam for `@classytic/arc-testkit` |
417
+
418
+ All accept `{ replset?, dbName?, uri? }`.
419
+
354
420
  ## License
355
421
 
356
422
  MIT
@@ -0,0 +1,103 @@
1
+ import { A as KeysetPaginationOptions, U as PaginationConfig, V as OffsetPaginationOptions, i as AnyDocument, n as AggregatePaginationOptions } from "./types-BA8oXjFe.mjs";
2
+ import { Model } from "mongoose";
3
+ import { AggregatePaginationResult, KeysetPaginationResult, OffsetPaginationResult } from "@classytic/repo-core/pagination";
4
+
5
+ //#region src/pagination/PaginationEngine.d.ts
6
+ /**
7
+ * Internal pagination config with required values
8
+ */
9
+ interface ResolvedPaginationConfig {
10
+ defaultLimit: number;
11
+ maxLimit: number;
12
+ maxPage: number;
13
+ deepPageThreshold: number;
14
+ cursorVersion: number;
15
+ minCursorVersion: number;
16
+ strictKeysetSortFields: string[] | undefined;
17
+ useEstimatedCount: boolean;
18
+ }
19
+ /**
20
+ * Production-grade pagination engine for MongoDB
21
+ * Supports offset, keyset (cursor), and aggregate pagination
22
+ */
23
+ declare class PaginationEngine<TDoc = AnyDocument> {
24
+ readonly Model: Model<TDoc>;
25
+ readonly config: ResolvedPaginationConfig;
26
+ /**
27
+ * Lazily-cached schema index snapshot used by stream() to decide whether
28
+ * to emit the "missing compound index" warning. Computed on first use.
29
+ */
30
+ private _cachedSchemaIndexes;
31
+ /**
32
+ * Create a new pagination engine
33
+ *
34
+ * @param Model - Mongoose model to paginate
35
+ * @param config - Pagination configuration
36
+ */
37
+ constructor(Model: Model<TDoc, any, any, any>, config?: PaginationConfig);
38
+ /** Memoized schema index lookup — avoids re-walking schema on every stream(). */
39
+ private _getSchemaIndexes;
40
+ /**
41
+ * Offset-based pagination using skip/limit
42
+ * Best for small datasets and when users need random page access
43
+ * O(n) performance - slower for deep pages
44
+ *
45
+ * @param options - Pagination options
46
+ * @returns Pagination result with total count
47
+ *
48
+ * @example
49
+ * const result = await engine.paginate({
50
+ * filters: { status: 'active' },
51
+ * sort: { createdAt: -1 },
52
+ * page: 1,
53
+ * limit: 20
54
+ * });
55
+ * console.log(result.data, result.total, result.hasNext);
56
+ */
57
+ paginate(options?: OffsetPaginationOptions): Promise<OffsetPaginationResult<TDoc>>;
58
+ /**
59
+ * Keyset (cursor-based) pagination for high-performance streaming
60
+ * Best for large datasets, infinite scroll, real-time feeds
61
+ * O(1) performance - consistent speed regardless of position
62
+ *
63
+ * @param options - Pagination options (sort is required)
64
+ * @returns Pagination result with next cursor
65
+ *
66
+ * @example
67
+ * // First page
68
+ * const page1 = await engine.stream({
69
+ * sort: { createdAt: -1 },
70
+ * limit: 20
71
+ * });
72
+ *
73
+ * // Next page using cursor
74
+ * const page2 = await engine.stream({
75
+ * sort: { createdAt: -1 },
76
+ * after: page1.next,
77
+ * limit: 20
78
+ * });
79
+ */
80
+ stream(options: KeysetPaginationOptions): Promise<KeysetPaginationResult<TDoc>>;
81
+ /**
82
+ * Aggregate pipeline with pagination
83
+ * Best for complex queries requiring aggregation stages
84
+ * Uses $facet to combine results and count in single query
85
+ *
86
+ * @param options - Aggregation options
87
+ * @returns Pagination result with total count
88
+ *
89
+ * @example
90
+ * const result = await engine.aggregatePaginate({
91
+ * pipeline: [
92
+ * { $match: { status: 'active' } },
93
+ * { $group: { _id: '$category', count: { $sum: 1 } } },
94
+ * { $sort: { count: -1 } }
95
+ * ],
96
+ * page: 1,
97
+ * limit: 20
98
+ * });
99
+ */
100
+ aggregatePaginate(options?: AggregatePaginationOptions): Promise<AggregatePaginationResult<TDoc>>;
101
+ }
102
+ //#endregion
103
+ export { PaginationEngine as t };
@@ -1,2 +1,2 @@
1
- import { a as create_d_exports, i as delete_d_exports, n as update_d_exports, o as aggregate_d_exports, r as read_d_exports } from "../index-fi91ycQc.mjs";
1
+ import { a as create_d_exports, i as delete_d_exports, n as update_d_exports, o as aggregate_d_exports, r as read_d_exports } from "../index-BgSBwlWu.mjs";
2
2
  export { aggregate_d_exports as aggregate, create_d_exports as create, delete_d_exports as deleteActions, read_d_exports as read, update_d_exports as update };
@@ -1,2 +1,2 @@
1
- import { n as createMongooseAdapter, r as isMongooseModel, t as MongooseAdapter } from "../adapter-DYEMkLwK.mjs";
1
+ import { n as createMongooseAdapter, r as isMongooseModel, t as MongooseAdapter } from "../adapter-BSxhT0Ip.mjs";
2
2
  export { MongooseAdapter, createMongooseAdapter, isMongooseModel };
@@ -1,4 +1,4 @@
1
- import { G as Plugin } from "../types-CU3SEDbD.mjs";
1
+ import { G as Plugin } from "../types-BA8oXjFe.mjs";
2
2
  import { PipelineStage } from "mongoose";
3
3
 
4
4
  //#region src/ai/types.d.ts
@@ -1,5 +1,5 @@
1
1
  import { n as Repository } from "../Repository-B01VW_fU.mjs";
2
- import { n as createMongooseAdapter } from "../adapter-DYEMkLwK.mjs";
2
+ import { n as createMongooseAdapter } from "../adapter-BSxhT0Ip.mjs";
3
3
  import { pluralizeBetterAuthCollection, resolveBetterAuthCollections } from "@classytic/repo-core/better-auth";
4
4
  //#region src/better-auth/index.ts
5
5
  /**
@@ -0,0 +1,29 @@
1
+ import { n as Repository } from "./Repository-B01VW_fU.mjs";
2
+ import { a as methodRegistryPlugin, d as cachePlugin, f as batchOperationsPlugin, h as auditLogPlugin, i as multiTenantPlugin, m as auditTrailPlugin, n as softDeletePlugin, o as customIdPlugin, t as timestampPlugin } from "./timestamp.plugin-B1A9U-P7.mjs";
3
+ //#region src/create-repository.ts
4
+ function isLoggerAudit(value) {
5
+ const v = value;
6
+ return typeof v.info === "function" || typeof v.warn === "function" || typeof v.error === "function" || typeof v.debug === "function";
7
+ }
8
+ /**
9
+ * Build a `Repository` from a declarative config. See module JSDoc for
10
+ * ordering guarantees and an example.
11
+ */
12
+ function createRepository(model, config = {}) {
13
+ const { tenant, softDelete, timestamps, batch, cache, audit, customId, plugins = [], pagination = {}, ...repositoryOptions } = config;
14
+ const stack = [methodRegistryPlugin()];
15
+ if (tenant) stack.push(multiTenantPlugin(tenant));
16
+ if (softDelete) stack.push(softDeletePlugin(softDelete === true ? {} : softDelete));
17
+ if (timestamps) stack.push(timestampPlugin());
18
+ if (customId) stack.push(customIdPlugin(customId));
19
+ if (cache) stack.push(cachePlugin(cache));
20
+ if (audit) stack.push(isLoggerAudit(audit) ? auditLogPlugin(audit) : auditTrailPlugin(audit));
21
+ if (batch) stack.push(batchOperationsPlugin());
22
+ stack.push(...plugins);
23
+ return new Repository(model, stack, pagination, {
24
+ pluginOrderChecks: repositoryOptions.pluginOrderChecks ?? "throw",
25
+ ...repositoryOptions
26
+ });
27
+ }
28
+ //#endregion
29
+ export { createRepository as t };