@classytic/mongokit 3.39.0 → 3.40.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/dist/{PaginationEngine-DQ7beTCR.mjs → PaginationEngine-Dvl8jcRY.mjs} +33 -17
- package/dist/{Repository-D7fDpyFx.mjs → Repository-BMgPSkqZ.mjs} +279 -625
- package/dist/{Repository-IxI983_m.d.mts → Repository-CR8Y9g2E.d.mts} +57 -32
- package/dist/actions/index.mjs +1 -1
- package/dist/ai/index.d.mts +1 -1
- package/dist/better-auth/index.mjs +1 -1
- package/dist/{create-repository-DOe9E_5F.mjs → create-repository-BzBUMvd1.mjs} +2 -2
- package/dist/{create-repository-CRXjDa3N.d.mts → create-repository-CAle1MSU.d.mts} +4 -4
- package/dist/idempotency/index.d.mts +23 -0
- package/dist/idempotency/index.mjs +134 -0
- package/dist/index.d.mts +58 -32
- package/dist/index.mjs +285 -675
- package/dist/indexes/verify-indexes.d.mts +14 -1
- package/dist/indexes/verify-indexes.mjs +54 -7
- package/dist/{multi-tenant.plugin-B-tzNeYL.d.mts → multi-tenant.plugin-CBgCo9im.d.mts} +18 -2
- package/dist/outbox/index.d.mts +1 -1
- package/dist/outbox/index.mjs +6 -4
- package/dist/pagination/PaginationEngine.mjs +1 -1
- package/dist/{plugin-options-BuvtOSZf.d.mts → plugin-options-Zk46lD6F.d.mts} +15 -1
- package/dist/plugins/index.d.mts +8 -3
- package/dist/plugins/index.mjs +98 -25
- package/dist/{repository-ZbLtL7Nk.d.mts → repository-odl03582.d.mts} +20 -2
- package/dist/sync/index.d.mts +40 -0
- package/dist/sync/index.mjs +334 -0
- package/dist/tenant/index.mjs +2 -0
- package/dist/testkit/index.d.mts +2 -2
- package/dist/testkit/index.mjs +1 -1
- package/dist/{timestamp.plugin-B5eiaItQ.mjs → timestamp.plugin-DF0gJB6p.mjs} +86 -2
- package/dist/{to-plain-B8ufk3Qz.d.mts → to-plain-k7SjNlSS.d.mts} +2 -2
- package/dist/transaction-B5L9glO0.mjs +533 -0
- package/dist/{update-c_01A0if.mjs → update-DG0fCwRJ.mjs} +168 -11
- package/dist/utils/index.d.mts +1 -1
- package/dist/{validation-chain.plugin-Bju1D1Sn.mjs → validation-chain.plugin-BLY2gLEk.mjs} +192 -279
- package/dist/{validation-chain.plugin-DvrLJIbJ.d.mts → validation-chain.plugin-DYp9E7K2.d.mts} +25 -3
- package/package.json +13 -4
|
@@ -602,6 +602,20 @@ function classifyFilterFields(filters) {
|
|
|
602
602
|
};
|
|
603
603
|
}
|
|
604
604
|
/**
|
|
605
|
+
* The ESR index to recommend for a keyset query: equality → sort (incl. the `_id`
|
|
606
|
+
* tiebreaker) → range, each field ONCE. A filtered field that is also a sort key
|
|
607
|
+
* takes its sort position — Mongo rejects an index naming a key twice.
|
|
608
|
+
*/
|
|
609
|
+
function recommendKeysetIndex(filters, sort) {
|
|
610
|
+
const { equality, range } = classifyFilterFields(filters);
|
|
611
|
+
const notSorted = (f) => !Object.hasOwn(sort, f);
|
|
612
|
+
return [
|
|
613
|
+
...equality.filter(notSorted).map((f) => [f, 1]),
|
|
614
|
+
...Object.entries(sort),
|
|
615
|
+
...range.filter(notSorted).map((f) => [f, 1])
|
|
616
|
+
];
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
605
619
|
* Read a Mongoose schema's declared indexes, defensively.
|
|
606
620
|
* Returns an empty array on any introspection failure.
|
|
607
621
|
*/
|
|
@@ -641,8 +655,9 @@ function readSchemaIndexes(Model) {
|
|
|
641
655
|
function hasCompatibleKeysetIndex(indexes, filterFields, sort) {
|
|
642
656
|
const sortKeys = Object.keys(sort);
|
|
643
657
|
if (sortKeys.length === 0) return true;
|
|
644
|
-
const
|
|
645
|
-
const
|
|
658
|
+
const prefixFields = filterFields.filter((f) => !Object.hasOwn(sort, f));
|
|
659
|
+
const filterSet = new Set(prefixFields);
|
|
660
|
+
const prefixLen = prefixFields.length;
|
|
646
661
|
for (const [spec] of indexes) {
|
|
647
662
|
if (!spec || typeof spec !== "object") continue;
|
|
648
663
|
const entries = Object.entries(spec);
|
|
@@ -909,9 +924,9 @@ var PaginationEngine = class {
|
|
|
909
924
|
if (readPreference) query = query.read(readPreference);
|
|
910
925
|
const hasFilters = Object.keys(filters).length > 0;
|
|
911
926
|
const useEstimated = this.config.useEstimatedCount && !hasFilters;
|
|
912
|
-
let
|
|
913
|
-
if ((countStrategy === "estimated" || useEstimated) && !hasFilters)
|
|
914
|
-
else if (countStrategy === "none")
|
|
927
|
+
let runCount;
|
|
928
|
+
if ((countStrategy === "estimated" || useEstimated) && !hasFilters && !session) runCount = () => Promise.resolve(this.Model.estimatedDocumentCount());
|
|
929
|
+
else if (countStrategy === "none") runCount = () => Promise.resolve(0);
|
|
915
930
|
else if (countStrategy === "capped") {
|
|
916
931
|
/**
|
|
917
932
|
* `.limit(n)` on a count is MongoDB's own ceiling — the server stops
|
|
@@ -921,19 +936,21 @@ var PaginationEngine = class {
|
|
|
921
936
|
*/
|
|
922
937
|
const cappedTarget = countFilters ?? filters;
|
|
923
938
|
const cappedQuery = this.Model.countDocuments(cappedTarget).limit(resolveCountLimit(countLimit)).session(session ?? null);
|
|
939
|
+
if (collation) cappedQuery.collation(collation);
|
|
924
940
|
if (hint) cappedQuery.hint(hint);
|
|
925
941
|
if (maxTimeMS) cappedQuery.maxTimeMS(maxTimeMS);
|
|
926
942
|
if (readPreference) cappedQuery.read(readPreference);
|
|
927
|
-
|
|
943
|
+
runCount = () => cappedQuery.exec();
|
|
928
944
|
} else {
|
|
929
945
|
const countTarget = countFilters ?? filters;
|
|
930
946
|
const countQuery = this.Model.countDocuments(countTarget).session(session ?? null);
|
|
947
|
+
if (collation) countQuery.collation(collation);
|
|
931
948
|
if (hint) countQuery.hint(hint);
|
|
932
949
|
if (maxTimeMS) countQuery.maxTimeMS(maxTimeMS);
|
|
933
950
|
if (readPreference) countQuery.read(readPreference);
|
|
934
|
-
|
|
951
|
+
runCount = () => countQuery.exec();
|
|
935
952
|
}
|
|
936
|
-
const [data, total] = await Promise.all([query.exec(),
|
|
953
|
+
const [data, total] = session ? [await query.exec(), await runCount()] : await Promise.all([query.exec(), runCount()]);
|
|
937
954
|
const totalPages = countStrategy === "none" ? 0 : calculateTotalPages(total, sanitizedLimit);
|
|
938
955
|
/**
|
|
939
956
|
* A capped count that came back AT its ceiling is a floor, not a total.
|
|
@@ -1025,13 +1042,9 @@ var PaginationEngine = class {
|
|
|
1025
1042
|
const effectiveSortFields = sortFieldsForMessage(normalizedSort);
|
|
1026
1043
|
if (process.env.NODE_ENV !== "test" && filterKeys.length > 0 && effectiveSortFields.length > 0) {
|
|
1027
1044
|
const indexes = this._getSchemaIndexes();
|
|
1028
|
-
const { equality
|
|
1045
|
+
const { equality } = classifyFilterFields(filters);
|
|
1029
1046
|
if (!(hasCompatibleKeysetIndex(indexes, filterKeys, normalizedSort) || equality.length !== filterKeys.length && hasCompatibleKeysetIndex(indexes, equality, normalizedSort))) {
|
|
1030
|
-
const indexFields = [
|
|
1031
|
-
...equality.map((f) => `${f}: 1`),
|
|
1032
|
-
...Object.keys(normalizedSort).map((f) => `${f}: ${normalizedSort[f]}`),
|
|
1033
|
-
...range.map((f) => `${f}: 1`)
|
|
1034
|
-
];
|
|
1047
|
+
const indexFields = recommendKeysetIndex(filters, normalizedSort).map(([f, dir]) => `${f}: ${dir}`);
|
|
1035
1048
|
warn(`[mongokit] Keyset pagination with filters [${filterKeys.join(", ")}] and sort [${effectiveSortFields.join(", ")}] has no matching schema-declared compound index. Without one, the tiebreaker forces a BLOCKING SORT over every matching row, on every page. Declare: { ${indexFields.join(", ")} }. (Collection-level indexes created outside the schema are not visible here.)`);
|
|
1036
1049
|
}
|
|
1037
1050
|
}
|
|
@@ -1084,7 +1097,8 @@ var PaginationEngine = class {
|
|
|
1084
1097
|
hasMore,
|
|
1085
1098
|
next: hasMore ? mint(data[data.length - 1]) : null,
|
|
1086
1099
|
prev: hasPrev ? mint(data[0]) : null,
|
|
1087
|
-
hasPrev
|
|
1100
|
+
hasPrev,
|
|
1101
|
+
end: mint(data[data.length - 1])
|
|
1088
1102
|
};
|
|
1089
1103
|
}
|
|
1090
1104
|
/**
|
|
@@ -1149,11 +1163,13 @@ var PaginationEngine = class {
|
|
|
1149
1163
|
{ $limit: resolveCountLimit(countLimit) },
|
|
1150
1164
|
{ $count: "count" }
|
|
1151
1165
|
] : [...pipeline, { $count: "count" }];
|
|
1152
|
-
const
|
|
1166
|
+
const runData = () => run([
|
|
1153
1167
|
...pipeline,
|
|
1154
1168
|
{ $skip: skip },
|
|
1155
1169
|
{ $limit: fetchLimit }
|
|
1156
|
-
])
|
|
1170
|
+
]);
|
|
1171
|
+
const runCount = () => countStrategy === "none" ? Promise.resolve([]) : run(countStages);
|
|
1172
|
+
const [dataRows, countRows] = session ? [await runData(), await runCount()] : await Promise.all([runData(), runCount()]);
|
|
1157
1173
|
const data = dataRows;
|
|
1158
1174
|
const total = countRows[0]?.count || 0;
|
|
1159
1175
|
const totalPages = countStrategy === "none" ? 0 : calculateTotalPages(total, sanitizedLimit);
|