@classytic/mongokit 3.23.0 → 3.25.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 +11 -2
- package/dist/{PaginationEngine-BnZ24ehC.d.mts → PaginationEngine-CBS1XGzp.d.mts} +2 -1
- package/dist/{PaginationEngine-BT1ywu9k.mjs → PaginationEngine-CczbvCAq.mjs} +64 -3
- package/dist/{Repository-CLQA3sEz.mjs → Repository-KKR8jxfD.mjs} +74 -45
- package/dist/actions/index.d.mts +1 -1
- package/dist/adapter/index.d.mts +1 -1
- package/dist/adapter/index.mjs +1 -1
- package/dist/{adapter-BWgJ_Jdc.mjs → adapter-CLvUHiTW.mjs} +1 -1
- package/dist/ai/index.d.mts +1 -1
- package/dist/better-auth/index.mjs +2 -2
- package/dist/core-62oo3N1C.d.mts +50 -0
- package/dist/{create-repository-Bx1PpcdF.mjs → create-repository-DA5VJh-3.mjs} +2 -2
- package/dist/{create-repository-Cn1LkvL7.d.mts → create-repository-HjMD9h9_.d.mts} +18 -5
- package/dist/{id-resolution-P2OzcyRl.mjs → id-resolution-BqDrk0S2.mjs} +12 -1
- package/dist/{index-DLQ6pjal.d.mts → index-DceInCTX.d.mts} +3 -1
- package/dist/index.d.mts +53 -213
- package/dist/index.mjs +1072 -923
- package/dist/{mongooseToJsonSchema-CVnrKFDq.mjs → mongooseToJsonSchema-CByt1VhQ.mjs} +1 -1
- package/dist/{multi-tenant.plugin-BjXXuc_g.d.mts → multi-tenant.plugin-DVyVGREb.d.mts} +117 -18
- package/dist/operations-C8IcCHF9.d.mts +463 -0
- package/dist/pagination/PaginationEngine.d.mts +1 -1
- package/dist/pagination/PaginationEngine.mjs +1 -1
- package/dist/pagination-jSUdrACK.d.mts +158 -0
- package/dist/plugin-options-vFufQZMZ.d.mts +125 -0
- package/dist/plugins/index.d.mts +4 -4
- package/dist/plugins/index.mjs +3 -3
- package/dist/query/primitives/coercion.mjs +1 -1
- package/dist/query/primitives/geo.d.mts +1 -1
- package/dist/repository-DCHRWRXw.d.mts +511 -0
- package/dist/testkit/index.d.mts +1 -1
- package/dist/testkit/index.mjs +1 -1
- package/dist/{timestamp.plugin-BnuuIDqJ.mjs → timestamp.plugin-B8252GuQ.mjs} +192 -46
- package/dist/{to-plain-g8-gsVxZ.d.mts → to-plain-DkF5px0R.d.mts} +12 -16
- package/dist/type-utils-CLzRFtIA.d.mts +163 -0
- package/dist/utils/index.d.mts +2 -2
- package/dist/utils/index.mjs +3 -3
- package/dist/{validation-chain.plugin-ZFDHxXLn.mjs → validation-chain.plugin-C3Vwp-Rk.mjs} +1 -1
- package/dist/{validation-chain.plugin-CFJbr964.d.mts → validation-chain.plugin-DxD15h-2.d.mts} +3 -1
- package/package.json +5 -3
- package/dist/types-BtMuhoX5.d.mts +0 -1830
package/README.md
CHANGED
|
@@ -212,12 +212,21 @@ const parser = new QueryParser({
|
|
|
212
212
|
searchMode: 'auto',
|
|
213
213
|
});
|
|
214
214
|
|
|
215
|
-
const parsed = parser.parse(req.query);
|
|
215
|
+
const parsed = parser.parse(req.query); // throws HttpError(400) on invalid/blocked input
|
|
216
216
|
const result = await invoiceRepo.getAll(parsed);
|
|
217
217
|
```
|
|
218
218
|
|
|
219
219
|
Supports common URL operators such as `_gt`, `_gte`, `_lt`, `_lte`, `_in`, `_nin`, `_regex`, geo filters, populate, and schema-aware coercion.
|
|
220
220
|
|
|
221
|
+
The parser is **fail-closed by default** (`invalidInput: 'throw'`): blocked
|
|
222
|
+
operators, disallowed fields, malformed values, and pathological regex input
|
|
223
|
+
raise HTTP 400 (`code: 'INVALID_QUERY_INPUT'`) instead of being silently
|
|
224
|
+
dropped — dropping a request's *only* filter would broaden the query to every
|
|
225
|
+
record in tenant scope. Literal-semantics input (`search`, `like`/`contains`)
|
|
226
|
+
always escapes rather than rejects, so `?search=c++` never 400s. Trusted
|
|
227
|
+
migration/compat tooling can opt into the legacy warn-and-drop behavior with
|
|
228
|
+
`invalidInput: 'drop'`.
|
|
229
|
+
|
|
221
230
|
## Built-In Plugins
|
|
222
231
|
|
|
223
232
|
| Plugin | Purpose |
|
|
@@ -228,7 +237,7 @@ Supports common URL operators such as `_gt`, `_gte`, `_lt`, `_lte`, `_in`, `_nin
|
|
|
228
237
|
| `timestampPlugin` | `createdAt` / `updatedAt` management |
|
|
229
238
|
| `batchOperationsPlugin` | `bulkWrite` and batch helpers |
|
|
230
239
|
| `cachePlugin` | Pluggable read-through cache |
|
|
231
|
-
| `auditLogPlugin` / `auditTrailPlugin` |
|
|
240
|
+
| `auditLogPlugin` / `auditTrailPlugin` | Audit trails — `auditTrailPlugin({ mode: 'transactional' })` gives session-joined entries that are atomic inside `withTransaction` (compliance-grade); the default `'best-effort'` mode is fire-and-forget observability. Call `ensureAuditTrailReady()` at boot so collection/index creation doesn't happen inside the first transaction |
|
|
232
241
|
| `observabilityPlugin` | Metrics hook points |
|
|
233
242
|
| `cascadePlugin` | Hook-routed cascade delete |
|
|
234
243
|
| `customIdPlugin` | Prefixed or sequential public IDs |
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as AnyDocument } from "./core-62oo3N1C.mjs";
|
|
2
|
+
import { c as PaginationConfig, o as KeysetPaginationOptions, s as OffsetPaginationOptions, t as AggregatePaginationOptions } from "./pagination-jSUdrACK.mjs";
|
|
2
3
|
import { Model } from "mongoose";
|
|
3
4
|
import { AggregatePaginationResult, KeysetPaginationResult, OffsetPaginationResult } from "@classytic/repo-core/pagination";
|
|
4
5
|
//#region src/pagination/PaginationEngine.d.ts
|
|
@@ -255,6 +255,59 @@ function resolveCursorFilter(after, sort, cursorVersion, baseFilters = {}, minCu
|
|
|
255
255
|
//#endregion
|
|
256
256
|
//#region src/pagination/utils/index-hint.ts
|
|
257
257
|
/**
|
|
258
|
+
* The only operators that give the query planner a *bounded equality* it can
|
|
259
|
+
* lead an index with. Everything else (`$ne`, `$nin`, `$gt(e)`, `$lt(e)`,
|
|
260
|
+
* `$all`, `$elemMatch`, `$regex`, `$exists`, …) is a range predicate and, per ESR,
|
|
261
|
+
* must come AFTER the sort keys — never in the leading prefix.
|
|
262
|
+
*
|
|
263
|
+
* `$all` is intentionally excluded: although it combines equality matches,
|
|
264
|
+
* it targets multikey data, whose bounded index prefix cannot generally
|
|
265
|
+
* provide a subsequent index sort. Treating it as residual avoids claiming
|
|
266
|
+
* that a multikey-prefix compound index satisfies the sort.
|
|
267
|
+
*/
|
|
268
|
+
const EQUALITY_OPERATORS = /* @__PURE__ */ new Set(["$eq", "$in"]);
|
|
269
|
+
/**
|
|
270
|
+
* Is a top-level filter value a RANGE predicate (as opposed to equality)?
|
|
271
|
+
*
|
|
272
|
+
* Only a plain operator object — one whose own keys start with `$` — can be a
|
|
273
|
+
* range. Scalars, `Date`, `ObjectId`, arrays, and embedded-doc equality
|
|
274
|
+
* matches (`{ nested: { a: 1 } }`) are all equality for index-leading
|
|
275
|
+
* purposes. An operator object is equality iff every `$`-operator it uses is
|
|
276
|
+
* in {@link EQUALITY_OPERATORS}.
|
|
277
|
+
*/
|
|
278
|
+
function isRangePredicate(value) {
|
|
279
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
280
|
+
const operatorKeys = Object.keys(value).filter((k) => k.startsWith("$"));
|
|
281
|
+
if (operatorKeys.length === 0) return false;
|
|
282
|
+
return operatorKeys.some((k) => !EQUALITY_OPERATORS.has(k));
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Split top-level filter fields into ESR equality vs range predicates.
|
|
286
|
+
*
|
|
287
|
+
* The keyset index check follows MongoDB's ESR rule (Equality → Sort →
|
|
288
|
+
* Range). Only equality predicates belong in an index's leading prefix; a
|
|
289
|
+
* range predicate placed there stops the planner from using any later key for
|
|
290
|
+
* bounds OR for satisfying the sort. So the compatibility check and the
|
|
291
|
+
* recommended-index shape must treat the two classes differently — a field
|
|
292
|
+
* filtered by a range should TRAIL the sort keys, not lead them.
|
|
293
|
+
*
|
|
294
|
+
* `$`-prefixed top-level keys (`$and`/`$or`/…) are ignored (the caller decides
|
|
295
|
+
* how to handle logical combinators).
|
|
296
|
+
*/
|
|
297
|
+
function classifyFilterFields(filters) {
|
|
298
|
+
const equality = [];
|
|
299
|
+
const range = [];
|
|
300
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
301
|
+
if (key.startsWith("$")) continue;
|
|
302
|
+
if (isRangePredicate(value)) range.push(key);
|
|
303
|
+
else equality.push(key);
|
|
304
|
+
}
|
|
305
|
+
return {
|
|
306
|
+
equality,
|
|
307
|
+
range
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
258
311
|
* Read a Mongoose schema's declared indexes, defensively.
|
|
259
312
|
* Returns an empty array on any introspection failure.
|
|
260
313
|
*/
|
|
@@ -626,9 +679,17 @@ var PaginationEngine = class {
|
|
|
626
679
|
const sortWithoutIdTail = stripTrailingIdTiebreaker(normalizedSort);
|
|
627
680
|
const filterKeys = Object.keys(filters).filter((k) => !k.startsWith("$"));
|
|
628
681
|
const effectiveSortFields = Object.keys(sortWithoutIdTail);
|
|
629
|
-
if (process.env.NODE_ENV !== "test" && filterKeys.length > 0 && effectiveSortFields.length > 0
|
|
630
|
-
const
|
|
631
|
-
|
|
682
|
+
if (process.env.NODE_ENV !== "test" && filterKeys.length > 0 && effectiveSortFields.length > 0) {
|
|
683
|
+
const indexes = this._getSchemaIndexes();
|
|
684
|
+
const { equality, range } = classifyFilterFields(filters);
|
|
685
|
+
if (!(hasCompatibleKeysetIndex(indexes, filterKeys, sortWithoutIdTail) || equality.length !== filterKeys.length && hasCompatibleKeysetIndex(indexes, equality, sortWithoutIdTail))) {
|
|
686
|
+
const indexFields = [
|
|
687
|
+
...equality.map((f) => `${f}: 1`),
|
|
688
|
+
...effectiveSortFields.map((f) => `${f}: ${sortWithoutIdTail[f]}`),
|
|
689
|
+
...range.map((f) => `${f}: 1`)
|
|
690
|
+
];
|
|
691
|
+
warn(`[mongokit] Keyset pagination with filters [${filterKeys.join(", ")}] and sort [${effectiveSortFields.join(", ")}] has no matching schema-declared compound index. For O(1) performance, declare: { ${indexFields.join(", ")} }. (Collection-level indexes created outside the schema are not visible here.)`);
|
|
692
|
+
}
|
|
632
693
|
}
|
|
633
694
|
let query = { ...filters };
|
|
634
695
|
if (after) query = resolveCursorFilter(after, normalizedSort, this.config.cursorVersion, query, this.config.minCursorVersion);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { i as isValidIdForType, t as getSchemaIdType } from "./id-resolution-
|
|
1
|
+
import { i as isValidIdForType, t as getSchemaIdType } from "./id-resolution-BqDrk0S2.mjs";
|
|
2
2
|
import { a as count, b as LookupBuilder, c as getByQuery, d as deleteById, f as deleteByQuery, h as createMany, l as getOrCreate, m as create, n as update, o as exists, r as updateByQuery, s as getById, t as findOneAndUpdate, x as compileFilterToMongo, y as distinct } from "./update-1Lq9OMD2.mjs";
|
|
3
3
|
import { n as warn } from "./logger-K7dPmu2U.mjs";
|
|
4
4
|
import { n as isDuplicateKeyError, r as parseDuplicateKeyError, t as createError } from "./error-BuByZwKU.mjs";
|
|
5
|
-
import { t as PaginationEngine } from "./PaginationEngine-
|
|
5
|
+
import { t as PaginationEngine } from "./PaginationEngine-CczbvCAq.mjs";
|
|
6
6
|
import { hasNearOperator, rewriteNearForCount } from "./query/primitives/geo.mjs";
|
|
7
7
|
import { RepositoryBase, runChunkedArchive, runChunkedPurge, throwIfAborted, validatePluginOrder, withRetry } from "@classytic/repo-core/repository";
|
|
8
8
|
import mongoose from "mongoose";
|
|
@@ -543,7 +543,7 @@ function buildAggPipeline(req) {
|
|
|
543
543
|
const match = compileFilterToMongo(baseFilter);
|
|
544
544
|
if (Object.keys(match).length > 0) stages.push({ $match: match });
|
|
545
545
|
}
|
|
546
|
-
if (req.lookups && req.lookups.length > 0) appendLookupStages(stages, req.lookups);
|
|
546
|
+
if (req.lookups && req.lookups.length > 0) appendLookupStages$1(stages, req.lookups);
|
|
547
547
|
if (joinedFilter !== void 0) {
|
|
548
548
|
const match = compileFilterToMongo(joinedFilter);
|
|
549
549
|
if (Object.keys(match).length > 0) stages.push({ $match: match });
|
|
@@ -698,7 +698,7 @@ function validateBucketAliases(bucketAliases, groupCols, measures) {
|
|
|
698
698
|
function safeIdKey(field) {
|
|
699
699
|
return field.includes(".") ? field.replace(/\./g, "__") : field;
|
|
700
700
|
}
|
|
701
|
-
function appendLookupStages(stages, lookups) {
|
|
701
|
+
function appendLookupStages$1(stages, lookups) {
|
|
702
702
|
const asLookupOptions = lookups;
|
|
703
703
|
stages.push(...LookupBuilder.multiple(asLookupOptions));
|
|
704
704
|
for (const lookup of lookups) if (lookup.single) {
|
|
@@ -1470,6 +1470,59 @@ var AggregationBuilder = class AggregationBuilder {
|
|
|
1470
1470
|
* ```
|
|
1471
1471
|
*/
|
|
1472
1472
|
//#endregion
|
|
1473
|
+
//#region src/repository/lookup-populate.ts
|
|
1474
|
+
/**
|
|
1475
|
+
* Inclusion projections must carry the keyset sort fields (plus `_id`) or
|
|
1476
|
+
* cursor encoding has no values to serialize. No-op for exclusion
|
|
1477
|
+
* projections and cursor-less calls.
|
|
1478
|
+
*/
|
|
1479
|
+
function ensureLookupProjectionIncludesCursorFields(projection, sort) {
|
|
1480
|
+
if (!projection || !sort) return projection;
|
|
1481
|
+
if (!Object.values(projection).some((value) => value === 1)) return projection;
|
|
1482
|
+
const nextProjection = { ...projection };
|
|
1483
|
+
for (const field of [...Object.keys(sort), "_id"]) nextProjection[field] = 1;
|
|
1484
|
+
return nextProjection;
|
|
1485
|
+
}
|
|
1486
|
+
/**
|
|
1487
|
+
* Build the `$project` map from a caller's select spec (string / array /
|
|
1488
|
+
* inclusion-exclusion record). For inclusion projections, every lookup's
|
|
1489
|
+
* `as` field is auto-included so `$project` doesn't strip the joined data.
|
|
1490
|
+
* Returns a fresh object (callers may add fields for cursor encoding).
|
|
1491
|
+
*/
|
|
1492
|
+
function buildLookupProjection(selectSpec, lookups) {
|
|
1493
|
+
if (!selectSpec) return void 0;
|
|
1494
|
+
let projection;
|
|
1495
|
+
if (typeof selectSpec === "string") {
|
|
1496
|
+
projection = {};
|
|
1497
|
+
for (const field of selectSpec.split(",").map((f) => f.trim())) if (field.startsWith("-")) projection[field.substring(1)] = 0;
|
|
1498
|
+
else projection[field] = 1;
|
|
1499
|
+
} else if (Array.isArray(selectSpec)) {
|
|
1500
|
+
projection = {};
|
|
1501
|
+
for (const field of selectSpec) if (field.startsWith("-")) projection[field.substring(1)] = 0;
|
|
1502
|
+
else projection[field] = 1;
|
|
1503
|
+
} else projection = { ...selectSpec };
|
|
1504
|
+
if (Object.values(projection).some((v) => v === 1)) for (const lookup of lookups) {
|
|
1505
|
+
const asField = lookup.as || lookup.from;
|
|
1506
|
+
if (!(asField in projection)) projection[asField] = 1;
|
|
1507
|
+
}
|
|
1508
|
+
return projection;
|
|
1509
|
+
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Append the shared pipeline tail: `$lookup` stages, `$ifNull` coalescing
|
|
1512
|
+
* for `single` lookups, and the final `$project` (with cursor fields kept
|
|
1513
|
+
* when a keyset sort is active). Mutates `pipeline` in place — it is the
|
|
1514
|
+
* tail-builder both the keyset and offset modes share.
|
|
1515
|
+
*/
|
|
1516
|
+
function appendLookupStages(pipeline, lookups, projection, keysetSort) {
|
|
1517
|
+
pipeline.push(...LookupBuilder.multiple(lookups));
|
|
1518
|
+
for (const lookup of lookups) if (lookup.single) {
|
|
1519
|
+
const asField = lookup.as || lookup.from;
|
|
1520
|
+
pipeline.push({ $addFields: { [asField]: { $ifNull: [`$${asField}`, null] } } });
|
|
1521
|
+
}
|
|
1522
|
+
const finalProjection = ensureLookupProjectionIncludesCursorFields(projection, keysetSort);
|
|
1523
|
+
if (finalProjection) pipeline.push({ $project: finalProjection });
|
|
1524
|
+
}
|
|
1525
|
+
//#endregion
|
|
1473
1526
|
//#region src/tx-bound.ts
|
|
1474
1527
|
/**
|
|
1475
1528
|
* Map of session-aware method name → positional index of the `{ session }`
|
|
@@ -1680,13 +1733,6 @@ function isTransactionUnsupported(error) {
|
|
|
1680
1733
|
}
|
|
1681
1734
|
//#endregion
|
|
1682
1735
|
//#region src/Repository.ts
|
|
1683
|
-
function ensureLookupProjectionIncludesCursorFields(projection, sort) {
|
|
1684
|
-
if (!projection || !sort) return projection;
|
|
1685
|
-
if (!Object.values(projection).some((value) => value === 1)) return projection;
|
|
1686
|
-
const nextProjection = { ...projection };
|
|
1687
|
-
for (const field of [...Object.keys(sort), "_id"]) nextProjection[field] = 1;
|
|
1688
|
-
return nextProjection;
|
|
1689
|
-
}
|
|
1690
1736
|
/**
|
|
1691
1737
|
* Validates that an update patch is homogeneous — either all top-level
|
|
1692
1738
|
* keys are `$`-prefixed Mongo operators OR none are. Mixed shapes (e.g.
|
|
@@ -3423,6 +3469,15 @@ var Repository = class extends RepositoryBase {
|
|
|
3423
3469
|
* Routes through the hook system for policy enforcement
|
|
3424
3470
|
* (multi-tenant, soft-delete).
|
|
3425
3471
|
*
|
|
3472
|
+
* The pipeline parameter also accepts `readonly unknown[]` (3.25.0):
|
|
3473
|
+
* real-world stage arrays are assembled from exported fragment builders
|
|
3474
|
+
* and spreads whose element type degrades to `unknown` — mongoose's
|
|
3475
|
+
* `PipelineStage` union can't be satisfied without an `as` at every
|
|
3476
|
+
* such call site. Runtime validation is MongoDB's job either way; the
|
|
3477
|
+
* single documented widening lives HERE instead of N host-side casts
|
|
3478
|
+
* (spine PACKAGE_RULES §7.1). Literal arrays still get full
|
|
3479
|
+
* `PipelineStage` contextual checking via the first union member.
|
|
3480
|
+
*
|
|
3426
3481
|
* @param pipeline - MongoDB aggregation stage array
|
|
3427
3482
|
* @param options - Aggregation options including governance controls
|
|
3428
3483
|
*/
|
|
@@ -3748,39 +3803,13 @@ var Repository = class extends RepositoryBase {
|
|
|
3748
3803
|
const pageFromContext = context.page ?? options.page;
|
|
3749
3804
|
const isKeyset = !!after || !pageFromContext && !!sort;
|
|
3750
3805
|
const countStrategy = context.countStrategy ?? options.countStrategy ?? "exact";
|
|
3751
|
-
const
|
|
3752
|
-
|
|
3753
|
-
if (selectSpec) {
|
|
3754
|
-
if (typeof selectSpec === "string") {
|
|
3755
|
-
projection = {};
|
|
3756
|
-
for (const field of selectSpec.split(",").map((f) => f.trim())) if (field.startsWith("-")) projection[field.substring(1)] = 0;
|
|
3757
|
-
else projection[field] = 1;
|
|
3758
|
-
} else if (Array.isArray(selectSpec)) {
|
|
3759
|
-
projection = {};
|
|
3760
|
-
for (const field of selectSpec) if (field.startsWith("-")) projection[field.substring(1)] = 0;
|
|
3761
|
-
else projection[field] = 1;
|
|
3762
|
-
} else projection = { ...selectSpec };
|
|
3763
|
-
if (projection) {
|
|
3764
|
-
if (Object.values(projection).some((v) => v === 1)) for (const lookup of lookups) {
|
|
3765
|
-
const asField = lookup.as || lookup.from;
|
|
3766
|
-
if (!(asField in projection)) projection[asField] = 1;
|
|
3767
|
-
}
|
|
3768
|
-
}
|
|
3769
|
-
}
|
|
3770
|
-
const appendLookupStages = (pipeline) => {
|
|
3771
|
-
pipeline.push(...LookupBuilder.multiple(lookups));
|
|
3772
|
-
for (const lookup of lookups) if (lookup.single) {
|
|
3773
|
-
const asField = lookup.as || lookup.from;
|
|
3774
|
-
pipeline.push({ $addFields: { [asField]: { $ifNull: [`$${asField}`, null] } } });
|
|
3775
|
-
}
|
|
3776
|
-
const finalProjection = ensureLookupProjectionIncludesCursorFields(projection, isKeyset && sort ? this._parseSort(sort) : void 0);
|
|
3777
|
-
if (finalProjection) pipeline.push({ $project: finalProjection });
|
|
3778
|
-
};
|
|
3806
|
+
const projection = buildLookupProjection(context.select ?? options.select, lookups);
|
|
3807
|
+
const appendLookupStages$2 = (pipeline) => appendLookupStages(pipeline, lookups, projection, isKeyset && sort ? this._parseSort(sort) : void 0);
|
|
3779
3808
|
if (isKeyset && sort) {
|
|
3780
3809
|
const parsedSort = this._parseSort(sort);
|
|
3781
|
-
const { validateKeysetSort } = await import("./PaginationEngine-
|
|
3782
|
-
const { encodeCursor, resolveCursorFilter } = await import("./PaginationEngine-
|
|
3783
|
-
const { getPrimaryField } = await import("./PaginationEngine-
|
|
3810
|
+
const { validateKeysetSort } = await import("./PaginationEngine-CczbvCAq.mjs").then((n) => n.n);
|
|
3811
|
+
const { encodeCursor, resolveCursorFilter } = await import("./PaginationEngine-CczbvCAq.mjs").then((n) => n.r);
|
|
3812
|
+
const { getPrimaryField } = await import("./PaginationEngine-CczbvCAq.mjs").then((n) => n.n);
|
|
3784
3813
|
const normalizedSort = validateKeysetSort(parsedSort, this._pagination.config.strictKeysetSortFields);
|
|
3785
3814
|
const cursorVersion = this._pagination.config.cursorVersion ?? 1;
|
|
3786
3815
|
const minCursorVersion = this._pagination.config.minCursorVersion ?? 1;
|
|
@@ -3794,7 +3823,7 @@ var Repository = class extends RepositoryBase {
|
|
|
3794
3823
|
if (Object.keys(matchFilters).length > 0) pipeline.push({ $match: matchFilters });
|
|
3795
3824
|
pipeline.push({ $sort: normalizedSort });
|
|
3796
3825
|
pipeline.push({ $limit: limit + 1 });
|
|
3797
|
-
appendLookupStages(pipeline);
|
|
3826
|
+
appendLookupStages$2(pipeline);
|
|
3798
3827
|
const aggregation = this.Model.aggregate(pipeline).session(session || null);
|
|
3799
3828
|
if (collation) aggregation.collation(collation);
|
|
3800
3829
|
if (readPref) aggregation.read(readPref);
|
|
@@ -3823,7 +3852,7 @@ var Repository = class extends RepositoryBase {
|
|
|
3823
3852
|
if (sort) dataPipeline.push({ $sort: this._parseSort(sort) });
|
|
3824
3853
|
if (countStrategy === "none") {
|
|
3825
3854
|
dataPipeline.push({ $skip: skip }, { $limit: limit + 1 });
|
|
3826
|
-
appendLookupStages(dataPipeline);
|
|
3855
|
+
appendLookupStages$2(dataPipeline);
|
|
3827
3856
|
const aggregation = this.Model.aggregate(dataPipeline).session(session || null);
|
|
3828
3857
|
if (collation) aggregation.collation(collation);
|
|
3829
3858
|
if (readPref) aggregation.read(readPref);
|
|
@@ -3847,7 +3876,7 @@ var Repository = class extends RepositoryBase {
|
|
|
3847
3876
|
return result;
|
|
3848
3877
|
}
|
|
3849
3878
|
dataPipeline.push({ $skip: skip }, { $limit: limit });
|
|
3850
|
-
appendLookupStages(dataPipeline);
|
|
3879
|
+
appendLookupStages$2(dataPipeline);
|
|
3851
3880
|
const countPipeline = [];
|
|
3852
3881
|
if (filters && Object.keys(filters).length > 0) countPipeline.push({ $match: filters });
|
|
3853
3882
|
countPipeline.push({ $count: "total" });
|
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-DceInCTX.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.d.mts
CHANGED
|
@@ -59,7 +59,7 @@ interface MongooseAdapterOptions<TDoc = unknown> {
|
|
|
59
59
|
* `@classytic/repo-core/adapter`.
|
|
60
60
|
*/
|
|
61
61
|
declare class MongooseAdapter<TDoc = unknown> implements DataAdapter<TDoc> {
|
|
62
|
-
readonly type:
|
|
62
|
+
readonly type: "mongoose";
|
|
63
63
|
readonly name: string;
|
|
64
64
|
readonly model: Model<TDoc>;
|
|
65
65
|
readonly repository: RepositoryLike<TDoc>;
|
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-CLvUHiTW.mjs";
|
|
2
2
|
export { MongooseAdapter, createMongooseAdapter, isMongooseModel };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as warn } from "./logger-K7dPmu2U.mjs";
|
|
2
|
-
import { t as buildCrudSchemasFromModel } from "./mongooseToJsonSchema-
|
|
2
|
+
import { t as buildCrudSchemasFromModel } from "./mongooseToJsonSchema-CByt1VhQ.mjs";
|
|
3
3
|
import { matchesRecordFilter } from "@classytic/repo-core/filter";
|
|
4
4
|
import { mergeFieldRuleConstraints } from "@classytic/repo-core/schema";
|
|
5
5
|
import { asRepositoryLike, isRepository } from "@classytic/repo-core/adapter";
|
package/dist/ai/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as Repository } from "../Repository-
|
|
2
|
-
import { n as createMongooseAdapter } from "../adapter-
|
|
1
|
+
import { n as Repository } from "../Repository-KKR8jxfD.mjs";
|
|
2
|
+
import { n as createMongooseAdapter } from "../adapter-CLvUHiTW.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,50 @@
|
|
|
1
|
+
import { Document, Model, PopulateOptions, Types } from "mongoose";
|
|
2
|
+
//#region src/types/core.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Core query/document primitives shared across every mongokit module —
|
|
5
|
+
* sort/select/populate/filter specs and the mongoose-facing aliases.
|
|
6
|
+
*
|
|
7
|
+
* Also the home of the `softRequired` Mongoose declaration merge (this file
|
|
8
|
+
* is loaded by every module, so the augmentation is always in the program).
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Global declaration merge: adds `softRequired` to Mongoose's SchemaTypeOptions
|
|
12
|
+
* so consumers can annotate paths type-safely without importing anything.
|
|
13
|
+
*
|
|
14
|
+
* A path with `softRequired: true` keeps its DB-level `required: true`
|
|
15
|
+
* invariant but is excluded from the auto-generated CRUD body `required[]`
|
|
16
|
+
* array (see buildCrudSchemasFromModel).
|
|
17
|
+
*/
|
|
18
|
+
declare module 'mongoose' {
|
|
19
|
+
interface SchemaTypeOptions<T, EnforcedDocType = any, THydratedDocumentType = HydratedDocument<EnforcedDocType>> {
|
|
20
|
+
/** mongokit: omit from auto-generated CRUD body `required` array. */
|
|
21
|
+
softRequired?: boolean;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Read Preference Type for replica sets */
|
|
25
|
+
type ReadPreferenceType = 'primary' | 'primaryPreferred' | 'secondary' | 'secondaryPreferred' | 'nearest' | (string & {});
|
|
26
|
+
/** Re-export mongoose ObjectId */
|
|
27
|
+
type ObjectId = Types.ObjectId;
|
|
28
|
+
/** Generic document type */
|
|
29
|
+
type AnyDocument = Document & Record<string, unknown>;
|
|
30
|
+
/** Generic model type */
|
|
31
|
+
type AnyModel = Model<AnyDocument>;
|
|
32
|
+
/** Sort direction */
|
|
33
|
+
type SortDirection = 1 | -1;
|
|
34
|
+
/** Sort specification for MongoDB queries */
|
|
35
|
+
type SortSpec = Record<string, SortDirection>;
|
|
36
|
+
/** Populate specification */
|
|
37
|
+
type PopulateSpec = string | string[] | PopulateOptions | PopulateOptions[];
|
|
38
|
+
/**
|
|
39
|
+
* Select specification. `readonly string[]` widens the legacy `string[]`
|
|
40
|
+
* form to also accept the immutable variant repo-core's contract uses
|
|
41
|
+
* (`LookupPopulateOptions.select: readonly string[] | Record<string, 0
|
|
42
|
+
* | 1>`), so `Repository<TDoc>` assigns to `StandardRepo<TDoc>` without
|
|
43
|
+
* TS2322 on the `select` field. Any existing mutable-array caller stays
|
|
44
|
+
* compatible — `string[]` is assignable to `readonly string[]`.
|
|
45
|
+
*/
|
|
46
|
+
type SelectSpec = string | readonly string[] | Record<string, 0 | 1>;
|
|
47
|
+
/** Filter query type for MongoDB queries (compatible with Mongoose 8 & 9) */
|
|
48
|
+
type FilterQuery<_T = unknown> = Record<string, unknown>;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { PopulateSpec as a, SortDirection as c, ObjectId as i, SortSpec as l, AnyModel as n, ReadPreferenceType as o, FilterQuery as r, SelectSpec as s, AnyDocument as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as Repository } from "./Repository-
|
|
2
|
-
import { a as methodRegistryPlugin, d as cachePlugin, f as batchOperationsPlugin,
|
|
1
|
+
import { n as Repository } from "./Repository-KKR8jxfD.mjs";
|
|
2
|
+
import { a as methodRegistryPlugin, d as cachePlugin, f as batchOperationsPlugin, g as auditLogPlugin, i as multiTenantPlugin, m as auditTrailPlugin, n as softDeletePlugin, o as customIdPlugin, t as timestampPlugin } from "./timestamp.plugin-B8252GuQ.mjs";
|
|
3
3
|
//#region src/create-repository.ts
|
|
4
4
|
function isLoggerAudit(value) {
|
|
5
5
|
const v = value;
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { t as
|
|
1
|
+
import { _ as WithTransactionOptions, a as DeleteManyResult, f as OperationOptions, g as UpdateOptions, h as UpdateManyResult, i as CreateOptions, l as LookupPopulateOptions, m as SessionOptions, o as DeleteResult, p as ReadOptions, r as CacheableOptions, s as FindOneAndUpdateOptions, t as AggregateOptions, u as LookupPopulateResult, v as LookupBuilder, y as LookupOptions } from "./operations-C8IcCHF9.mjs";
|
|
2
|
+
import { a as PopulateSpec, i as ObjectId, l as SortSpec$1, o as ReadPreferenceType, s as SelectSpec } from "./core-62oo3N1C.mjs";
|
|
3
|
+
import { c as PaginationConfig, r as CollationOptions, t as AggregatePaginationOptions } from "./pagination-jSUdrACK.mjs";
|
|
4
|
+
import { _ as RepositoryOptions, d as PluginType, f as PrioritizedHook, o as Middleware, p as RepositoryContext } from "./repository-DCHRWRXw.mjs";
|
|
5
|
+
import { T as AuditTrailOptions, _ as RepositoryCachePluginOptions, i as CustomIdOptions, t as MultiTenantOptions } from "./multi-tenant.plugin-DVyVGREb.mjs";
|
|
6
|
+
import { t as PaginationEngine } from "./PaginationEngine-CBS1XGzp.mjs";
|
|
7
|
+
import { i as Logger, o as SoftDeleteOptions } from "./plugin-options-vFufQZMZ.mjs";
|
|
4
8
|
import { AggPaginationRequest, AggRequest, AggResult, ArchiveOptions, ArchiveResult, ArchiveSink, ChangeEvent, FilterInput, KeysetAggPaginationResult, QueryOptions, RepoCapabilities, RepositoryBase, TenantPurgeOptions, TenantPurgeResult, TenantPurgeStrategy, WatchOptions } from "@classytic/repo-core/repository";
|
|
5
9
|
import { ClientSession, Expression, Model, PipelineStage, PopulateOptions } from "mongoose";
|
|
6
10
|
import { Filter } from "@classytic/repo-core/filter";
|
|
7
11
|
import { UpdateInput } from "@classytic/repo-core/update";
|
|
8
|
-
import { AggregatePaginationResult, KeysetPaginationResult, OffsetPaginationResult, OffsetPaginationResultCore } from "@classytic/repo-core/pagination";
|
|
9
12
|
import { HttpError } from "@classytic/repo-core/errors";
|
|
13
|
+
import { AggregatePaginationResult, KeysetPaginationResult, OffsetPaginationResult, OffsetPaginationResultCore } from "@classytic/repo-core/pagination";
|
|
10
14
|
//#region src/query/AggregationBuilder.d.ts
|
|
11
15
|
/** Options for $vectorSearch stage (Atlas only) */
|
|
12
16
|
interface VectorSearchOptions {
|
|
@@ -1486,10 +1490,19 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
|
|
|
1486
1490
|
* Routes through the hook system for policy enforcement
|
|
1487
1491
|
* (multi-tenant, soft-delete).
|
|
1488
1492
|
*
|
|
1493
|
+
* The pipeline parameter also accepts `readonly unknown[]` (3.25.0):
|
|
1494
|
+
* real-world stage arrays are assembled from exported fragment builders
|
|
1495
|
+
* and spreads whose element type degrades to `unknown` — mongoose's
|
|
1496
|
+
* `PipelineStage` union can't be satisfied without an `as` at every
|
|
1497
|
+
* such call site. Runtime validation is MongoDB's job either way; the
|
|
1498
|
+
* single documented widening lives HERE instead of N host-side casts
|
|
1499
|
+
* (spine PACKAGE_RULES §7.1). Literal arrays still get full
|
|
1500
|
+
* `PipelineStage` contextual checking via the first union member.
|
|
1501
|
+
*
|
|
1489
1502
|
* @param pipeline - MongoDB aggregation stage array
|
|
1490
1503
|
* @param options - Aggregation options including governance controls
|
|
1491
1504
|
*/
|
|
1492
|
-
aggregatePipeline<TResult = unknown>(pipeline: PipelineStage[], options?: AggregateOptions): Promise<TResult[]>;
|
|
1505
|
+
aggregatePipeline<TResult = unknown>(pipeline: PipelineStage[] | readonly unknown[], options?: AggregateOptions): Promise<TResult[]>;
|
|
1493
1506
|
/**
|
|
1494
1507
|
* Paginated kit-native aggregation pipeline. Same mongo-specific
|
|
1495
1508
|
* scope as `aggregatePipeline` — reach for this when you need
|
|
@@ -15,6 +15,17 @@ import mongoose from "mongoose";
|
|
|
15
15
|
* Design: no `this`, no Model, no DB calls. Only reads `schema.paths._id.instance`.
|
|
16
16
|
*/
|
|
17
17
|
/**
|
|
18
|
+
* Null-tolerant `id → ObjectId` coercion — the single seam for optional
|
|
19
|
+
* reference/audit-stamp fields (`approvedBy`, `submittedBy`, …) where an
|
|
20
|
+
* absent actor is a legitimate `null`, not an error. Invalid non-empty
|
|
21
|
+
* strings still throw (mongoose's own constructor validation) — this helper
|
|
22
|
+
* only absorbs the absent case, never bad input. Hosts were hand-rolling
|
|
23
|
+
* this identically at every approval-hook write; import it instead.
|
|
24
|
+
*/
|
|
25
|
+
function toObjectId(id) {
|
|
26
|
+
return id ? new mongoose.Types.ObjectId(id) : null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
18
29
|
* Check if a Mongoose SchemaType `instance` string is an ObjectId variant.
|
|
19
30
|
* Mongoose 9.x uses `'ObjectId'`, older versions use `'ObjectID'`. This
|
|
20
31
|
* is the single source of truth — use it instead of inline
|
|
@@ -103,4 +114,4 @@ function idVariants(id) {
|
|
|
103
114
|
return [id];
|
|
104
115
|
}
|
|
105
116
|
//#endregion
|
|
106
|
-
export { isValidIdForType as i, idVariants as n, isObjectIdInstance as r, getSchemaIdType as t };
|
|
117
|
+
export { toObjectId as a, isValidIdForType as i, idVariants as n, isObjectIdInstance as r, getSchemaIdType as t };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as GroupResult, d as MinMaxResult, f as OperationOptions, g as UpdateOptions, h as UpdateManyResult, i as CreateOptions, o as DeleteResult, s as FindOneAndUpdateOptions, y as LookupOptions } from "./operations-C8IcCHF9.mjs";
|
|
2
|
+
import { a as PopulateSpec, i as ObjectId, l as SortSpec, o as ReadPreferenceType, s as SelectSpec, t as AnyDocument } from "./core-62oo3N1C.mjs";
|
|
3
|
+
import { p as UpdateWithValidationResult } from "./type-utils-CLzRFtIA.mjs";
|
|
2
4
|
import { Model, PipelineStage } from "mongoose";
|
|
3
5
|
declare namespace aggregate_d_exports {
|
|
4
6
|
export { aggregate, aggregatePaginate, average, countBy, distinct, facet, groupBy, lookup, minMax, sum, unwind };
|