@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.
- package/README.md +66 -0
- package/dist/PaginationEngine-4SjzfBQE.d.mts +103 -0
- package/dist/actions/index.d.mts +1 -1
- package/dist/adapter/index.mjs +1 -1
- package/dist/ai/index.d.mts +1 -1
- package/dist/better-auth/index.mjs +1 -1
- package/dist/create-repository-D5YX0wGs.mjs +29 -0
- package/dist/create-repository-K0yft2ht.d.mts +1663 -0
- package/dist/{index-fi91ycQc.d.mts → index-BgSBwlWu.d.mts} +1 -1
- package/dist/index.d.mts +11 -1665
- package/dist/index.mjs +5 -29
- package/dist/{mongooseToJsonSchema-BdQJU39w.d.mts → mongooseToJsonSchema-Bz47aMfH.d.mts} +1 -1
- package/dist/multi-tenant.plugin-D6lw3BsI.d.mts +487 -0
- package/dist/pagination/PaginationEngine.d.mts +1 -102
- package/dist/plugins/index.d.mts +3 -2
- package/dist/plugins/index.mjs +2 -1
- package/dist/query/primitives/coercion.d.mts +1 -1
- package/dist/query/primitives/indexes.d.mts +1 -1
- package/dist/testkit/index.d.mts +129 -0
- package/dist/testkit/index.mjs +139 -0
- package/dist/{validation-chain.plugin-DJtC0KJ-.mjs → timestamp.plugin-B1A9U-P7.mjs} +1 -1052
- package/dist/utils/index.d.mts +1 -1
- package/dist/utils/index.mjs +2 -2
- package/dist/{validation-chain.plugin-BpMfy18N.d.mts → validation-chain.plugin-CYOPfFGF.d.mts} +2 -486
- package/dist/validation-chain.plugin-DHZbpb4t.mjs +1056 -0
- package/package.json +9 -1
- /package/dist/{adapter-DYEMkLwK.mjs → adapter-BSxhT0Ip.mjs} +0 -0
- /package/dist/{coercion-TRMmR49h.d.mts → coercion-BxxHyLjP.d.mts} +0 -0
- /package/dist/{field-selection-CQW3WBiK.mjs → field-selection-d1qdRiMr.mjs} +0 -0
- /package/dist/{indexes-CgOF7Wjd.d.mts → indexes-3eHBWNZ2.d.mts} +0 -0
- /package/dist/{mongooseToJsonSchema-CBEQmeEa.mjs → mongooseToJsonSchema-ChtXSrLT.mjs} +0 -0
- /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 };
|
package/dist/actions/index.d.mts
CHANGED
|
@@ -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-
|
|
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 };
|
package/dist/adapter/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as createMongooseAdapter, r as isMongooseModel, t as MongooseAdapter } from "../adapter-
|
|
1
|
+
import { n as createMongooseAdapter, r as isMongooseModel, t as MongooseAdapter } from "../adapter-BSxhT0Ip.mjs";
|
|
2
2
|
export { MongooseAdapter, createMongooseAdapter, isMongooseModel };
|
package/dist/ai/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as Repository } from "../Repository-B01VW_fU.mjs";
|
|
2
|
-
import { n as createMongooseAdapter } from "../adapter-
|
|
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 };
|