@classytic/mongokit 3.18.0 → 3.20.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 (49) hide show
  1. package/README.md +66 -0
  2. package/dist/{PaginationEngine-BcvaXBQ-.mjs → PaginationEngine-BT1ywu9k.mjs} +327 -6
  3. package/dist/PaginationEngine-DYpOD2wY.d.mts +102 -0
  4. package/dist/{Repository-B01VW_fU.mjs → Repository-dbbgOGjK.mjs} +85 -11
  5. package/dist/actions/index.d.mts +1 -1
  6. package/dist/actions/index.mjs +2 -2
  7. package/dist/adapter/index.d.mts +10 -2
  8. package/dist/adapter/index.mjs +1 -1
  9. package/dist/{adapter-DYEMkLwK.mjs → adapter-CW1EL0fF.mjs} +10 -1
  10. package/dist/ai/index.d.mts +1 -2
  11. package/dist/ai/index.mjs +3 -2
  12. package/dist/better-auth/index.d.mts +0 -1
  13. package/dist/better-auth/index.mjs +2 -2
  14. package/dist/create-repository-0TkQhcv-.d.mts +1757 -0
  15. package/dist/create-repository-CJATiAKX.mjs +29 -0
  16. package/dist/{id-resolution-CnN0YA8t.mjs → id-resolution-P2OzcyRl.mjs} +10 -5
  17. package/dist/{index-fi91ycQc.d.mts → index-Cg3SWugF.d.mts} +1 -3
  18. package/dist/index.d.mts +11 -1666
  19. package/dist/index.mjs +51 -37
  20. package/dist/lock/index.d.mts +0 -1
  21. package/dist/{mongooseToJsonSchema-BdQJU39w.d.mts → mongooseToJsonSchema-D2CyAMIC.d.mts} +9 -5
  22. package/dist/{mongooseToJsonSchema-CBEQmeEa.mjs → mongooseToJsonSchema-khdzVo8x.mjs} +1 -1
  23. package/dist/multi-tenant.plugin-CTsWz0XK.d.mts +486 -0
  24. package/dist/pagination/PaginationEngine.d.mts +1 -102
  25. package/dist/pagination/PaginationEngine.mjs +1 -1
  26. package/dist/plugins/index.d.mts +3 -3
  27. package/dist/plugins/index.mjs +2 -1
  28. package/dist/query/primitives/coercion.d.mts +1 -1
  29. package/dist/query/primitives/coercion.mjs +1 -1
  30. package/dist/query/primitives/geo.d.mts +1 -1
  31. package/dist/query/primitives/indexes.d.mts +1 -1
  32. package/dist/testkit/index.d.mts +128 -0
  33. package/dist/testkit/index.mjs +139 -0
  34. package/dist/{validation-chain.plugin-DJtC0KJ-.mjs → timestamp.plugin-nG9Z1JEk.mjs} +5 -1056
  35. package/dist/{types-CU3SEDbD.d.mts → types-DNFMofHy.d.mts} +51 -22
  36. package/dist/{update-CvCMv5Cl.mjs → update-1Lq9OMD2.mjs} +5 -5
  37. package/dist/utils/index.d.mts +1 -1
  38. package/dist/utils/index.mjs +5 -5
  39. package/dist/validation-chain.plugin-Dn_wZYLA.mjs +1056 -0
  40. package/dist/{validation-chain.plugin-BpMfy18N.d.mts → validation-chain.plugin-buy3uvyk.d.mts} +2 -487
  41. package/package.json +16 -7
  42. package/dist/cursor-_SbWVAn-.mjs +0 -254
  43. package/dist/sort-Ca1BPfvE.mjs +0 -72
  44. /package/dist/{coercion-TRMmR49h.d.mts → coercion-BjdPX3YF.d.mts} +0 -0
  45. /package/dist/{error-BTeepSNL.mjs → error-BuByZwKU.mjs} +0 -0
  46. /package/dist/{field-selection-CQW3WBiK.mjs → field-selection-FSQFLpsU.mjs} +0 -0
  47. /package/dist/{indexes-CgOF7Wjd.d.mts → indexes-DIjMpYES.d.mts} +0 -0
  48. /package/dist/{logger-B8wf-Mtb.mjs → logger-K7dPmu2U.mjs} +0 -0
  49. /package/dist/{chunk-CfYAbeIz.mjs → rolldown-runtime-D7D4PA-g.mjs} +0 -0
@@ -1,254 +0,0 @@
1
- import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
2
- import mongoose from "mongoose";
3
- //#region src/pagination/utils/filter.ts
4
- /**
5
- * Builds MongoDB filter for keyset pagination
6
- * Creates compound $or condition for proper cursor-based filtering
7
- *
8
- * @param baseFilters - Existing query filters
9
- * @param sort - Normalized sort specification
10
- * @param cursorValue - Primary field value from cursor
11
- * @param cursorId - _id value from cursor
12
- * @returns MongoDB filter with keyset condition
13
- *
14
- * @example
15
- * buildKeysetFilter(
16
- * { status: 'active' },
17
- * { createdAt: -1, _id: -1 },
18
- * new Date('2024-01-01'),
19
- * new ObjectId('...')
20
- * )
21
- * // Returns:
22
- * // {
23
- * // status: 'active',
24
- * // $or: [
25
- * // { createdAt: { $lt: Date('2024-01-01') } },
26
- * // { createdAt: Date('2024-01-01'), _id: { $lt: ObjectId('...') } }
27
- * // ]
28
- * // }
29
- */
30
- function buildKeysetFilter(baseFilters, sort, cursorValue, cursorId, cursorValues) {
31
- const sortFields = Object.keys(sort).filter((k) => k !== "_id");
32
- if (sortFields.length <= 1 && !cursorValues) {
33
- const primaryField = sortFields[0] || "_id";
34
- const direction = sort[primaryField];
35
- const operator = direction === 1 ? "$gt" : "$lt";
36
- if (cursorValue === null || cursorValue === void 0) if (direction === 1) return {
37
- ...baseFilters,
38
- $or: [{
39
- [primaryField]: null,
40
- _id: { $gt: cursorId }
41
- }, { [primaryField]: { $ne: null } }]
42
- };
43
- else return {
44
- ...baseFilters,
45
- [primaryField]: null,
46
- _id: { $lt: cursorId }
47
- };
48
- return {
49
- ...baseFilters,
50
- $or: [{ [primaryField]: { [operator]: cursorValue } }, {
51
- [primaryField]: cursorValue,
52
- _id: { [operator]: cursorId }
53
- }]
54
- };
55
- }
56
- const values = cursorValues || { [sortFields[0]]: cursorValue };
57
- const allFields = [...sortFields, "_id"];
58
- const allValues = {
59
- ...values,
60
- _id: cursorId
61
- };
62
- const orConditions = [];
63
- for (let i = 0; i < allFields.length; i++) {
64
- const field = allFields[i];
65
- const operator = (sort[field] ?? sort[sortFields[0]]) === 1 ? "$gt" : "$lt";
66
- const condition = {};
67
- for (let j = 0; j < i; j++) condition[allFields[j]] = allValues[allFields[j]];
68
- condition[field] = { [operator]: allValues[field] };
69
- orConditions.push(condition);
70
- }
71
- return {
72
- ...baseFilters,
73
- $or: orConditions
74
- };
75
- }
76
- //#endregion
77
- //#region src/pagination/utils/cursor.ts
78
- /**
79
- * Cursor Utilities
80
- *
81
- * Encoding and decoding of cursor tokens for keyset pagination.
82
- * Cursors are base64-encoded JSON containing position data and metadata.
83
- */
84
- var cursor_exports = /* @__PURE__ */ __exportAll({
85
- decodeCursor: () => decodeCursor,
86
- encodeCursor: () => encodeCursor,
87
- resolveCursorFilter: () => resolveCursorFilter,
88
- validateCursorSort: () => validateCursorSort,
89
- validateCursorVersion: () => validateCursorVersion
90
- });
91
- /**
92
- * Encodes document values and sort metadata into a base64 cursor token
93
- *
94
- * @param doc - Document to extract cursor values from
95
- * @param primaryField - Primary sort field name
96
- * @param sort - Normalized sort specification
97
- * @param version - Cursor version for forward compatibility
98
- * @returns Base64-encoded cursor token
99
- */
100
- function encodeCursor(doc, primaryField, sort, version = 1) {
101
- const primaryValue = doc[primaryField];
102
- const idValue = doc._id;
103
- const sortFields = Object.keys(sort).filter((k) => k !== "_id");
104
- const vals = {};
105
- const types = {};
106
- for (const field of sortFields) {
107
- vals[field] = serializeValue(doc[field]);
108
- types[field] = getValueType(doc[field]);
109
- }
110
- const payload = {
111
- v: serializeValue(primaryValue),
112
- t: getValueType(primaryValue),
113
- id: serializeValue(idValue),
114
- idType: getValueType(idValue),
115
- sort,
116
- ver: version,
117
- ...sortFields.length > 1 && {
118
- vals,
119
- types
120
- }
121
- };
122
- return Buffer.from(JSON.stringify(payload)).toString("base64");
123
- }
124
- /**
125
- * Decodes a cursor token back into document values and sort metadata
126
- *
127
- * @param token - Base64-encoded cursor token
128
- * @returns Decoded cursor data
129
- * @throws Error if token is invalid or malformed
130
- */
131
- function decodeCursor(token) {
132
- let json;
133
- try {
134
- json = Buffer.from(token, "base64").toString("utf-8");
135
- } catch {
136
- throw new Error("Invalid cursor token: not valid base64");
137
- }
138
- let payload;
139
- try {
140
- payload = JSON.parse(json);
141
- } catch {
142
- throw new Error("Invalid cursor token: not valid JSON");
143
- }
144
- if (!payload || typeof payload !== "object" || !("v" in payload) || !("t" in payload) || !("id" in payload) || !("idType" in payload) || !payload.sort || typeof payload.sort !== "object" || typeof payload.ver !== "number") throw new Error("Invalid cursor token: malformed payload structure");
145
- const VALID_TYPES = [
146
- "date",
147
- "objectid",
148
- "boolean",
149
- "number",
150
- "string",
151
- "null",
152
- "unknown"
153
- ];
154
- if (!VALID_TYPES.includes(payload.t) || !VALID_TYPES.includes(payload.idType)) throw new Error("Invalid cursor token: unrecognized value type");
155
- try {
156
- let values;
157
- if (payload.vals && payload.types) {
158
- values = {};
159
- for (const [field, serialized] of Object.entries(payload.vals)) values[field] = rehydrateValue(serialized, payload.types[field]);
160
- }
161
- return {
162
- value: rehydrateValue(payload.v, payload.t),
163
- id: rehydrateValue(payload.id, payload.idType),
164
- sort: payload.sort,
165
- version: payload.ver,
166
- ...values && { values }
167
- };
168
- } catch {
169
- throw new Error("Invalid cursor token: failed to rehydrate values");
170
- }
171
- }
172
- /**
173
- * Validates that cursor sort matches current query sort
174
- *
175
- * @param cursorSort - Sort specification from cursor
176
- * @param currentSort - Sort specification from query
177
- * @throws Error if sorts don't match
178
- */
179
- function validateCursorSort(cursorSort, currentSort) {
180
- if (JSON.stringify(cursorSort) !== JSON.stringify(currentSort)) throw new Error("Cursor sort does not match current query sort");
181
- }
182
- /**
183
- * Validates cursor version against the server's expected range.
184
- *
185
- * - Cursors newer than `expectedVersion` are rejected (client ahead of server).
186
- * - Cursors older than `minVersion` are rejected (client cached a cursor from
187
- * a pre-breaking-change deploy). Default `minVersion = 1` keeps the legacy
188
- * "accept anything <= expected" behavior; bump it when you ship a breaking
189
- * cursor format change so old clients restart pagination cleanly instead of
190
- * silently paginating from the wrong position.
191
- */
192
- function validateCursorVersion(cursorVersion, expectedVersion, minVersion = 1) {
193
- if (cursorVersion > expectedVersion) throw new Error(`Cursor version ${cursorVersion} is newer than expected version ${expectedVersion}. Please upgrade.`);
194
- if (cursorVersion < minVersion) throw new Error(`Cursor version ${cursorVersion} is older than minimum supported ${minVersion}. Pagination must restart.`);
195
- }
196
- /**
197
- * Serializes a value for cursor storage
198
- */
199
- function serializeValue(value) {
200
- if (value === null || value === void 0) return null;
201
- if (value instanceof Date) return value.toISOString();
202
- if (value instanceof mongoose.Types.ObjectId) return value.toString();
203
- return value;
204
- }
205
- /**
206
- * Gets the type identifier for a value
207
- */
208
- function getValueType(value) {
209
- if (value === null || value === void 0) return "null";
210
- if (value instanceof Date) return "date";
211
- if (value instanceof mongoose.Types.ObjectId) return "objectid";
212
- if (typeof value === "boolean") return "boolean";
213
- if (typeof value === "number") return "number";
214
- if (typeof value === "string") return "string";
215
- return "unknown";
216
- }
217
- /**
218
- * Rehydrates a serialized value back to its original type
219
- */
220
- function rehydrateValue(serialized, type) {
221
- if (type === "null" || serialized === null) return null;
222
- switch (type) {
223
- case "date": return new Date(serialized);
224
- case "objectid": return new mongoose.Types.ObjectId(serialized);
225
- case "boolean": return serialized === true || serialized === "true";
226
- case "number": return Number(serialized);
227
- default: return serialized;
228
- }
229
- }
230
- /**
231
- * Resolves cursor token into MongoDB query filters.
232
- * Shared by PaginationEngine.stream() and Repository.lookupPopulate() keyset path.
233
- *
234
- * Handles:
235
- * - Plain 24-char hex ObjectId strings (fallback cursor)
236
- * - Base64-encoded cursor tokens (standard cursor)
237
- * - Cursor version and sort validation
238
- */
239
- function resolveCursorFilter(after, sort, cursorVersion, baseFilters = {}, minCursorVersion = 1) {
240
- if (/^[a-f0-9]{24}$/i.test(after)) {
241
- const objectId = new mongoose.Types.ObjectId(after);
242
- const idOperator = (sort._id || -1) === 1 ? "$gt" : "$lt";
243
- return {
244
- ...baseFilters,
245
- _id: { [idOperator]: objectId }
246
- };
247
- }
248
- const cursor = decodeCursor(after);
249
- validateCursorVersion(cursor.version, cursorVersion, minCursorVersion);
250
- validateCursorSort(cursor.sort, sort);
251
- return buildKeysetFilter(baseFilters, sort, cursor.value, cursor.id, cursor.values);
252
- }
253
- //#endregion
254
- export { encodeCursor as n, resolveCursorFilter as r, cursor_exports as t };
@@ -1,72 +0,0 @@
1
- import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
2
- //#region src/pagination/utils/sort.ts
3
- var sort_exports = /* @__PURE__ */ __exportAll({
4
- getPrimaryField: () => getPrimaryField,
5
- normalizeSort: () => normalizeSort,
6
- validateKeysetSort: () => validateKeysetSort
7
- });
8
- /**
9
- * Normalizes sort object to ensure stable key order
10
- * Primary fields first, _id last (not alphabetical)
11
- *
12
- * @param sort - Sort specification
13
- * @returns Normalized sort with stable key order
14
- */
15
- function normalizeSort(sort) {
16
- const normalized = {};
17
- Object.keys(sort).forEach((key) => {
18
- if (key !== "_id") normalized[key] = sort[key];
19
- });
20
- if (sort._id !== void 0) normalized._id = sort._id;
21
- return normalized;
22
- }
23
- /**
24
- * Validates and normalizes sort for keyset pagination.
25
- * Auto-adds `_id` tie-breaker if needed; ensures `_id` direction matches the
26
- * primary field.
27
- *
28
- * **Known limitation (MongoDB mixed-type sort):** keyset pagination across a
29
- * field that can hold both typed values (Date, Number) and `null`/`undefined`
30
- * is lossy — `$lt/$gt` don't cross the type boundary in BSON comparison, so
31
- * some docs at the null/non-null boundary are unreachable. If your sort field
32
- * is nullable, either:
33
- * - sort by `_id` alone (always safe), or
34
- * - use `allowedPrimaryFields` to lock keyset sort to fields your schema
35
- * guarantees non-null (see `PaginationConfig.strictKeysetSortFields`).
36
- *
37
- * @param sort - Sort specification
38
- * @param allowedPrimaryFields - Optional allowlist of primary (non-_id) sort
39
- * field names. When set, rejects sorts whose primary field isn't listed.
40
- * `_id` is always allowed.
41
- * @returns Validated and normalized sort
42
- * @throws Error if sort is invalid for keyset pagination
43
- */
44
- function validateKeysetSort(sort, allowedPrimaryFields) {
45
- const keys = Object.keys(sort);
46
- if (keys.length === 0) throw new Error("Keyset pagination requires at least one sort field");
47
- if (keys.length === 1 && keys[0] === "_id") return normalizeSort(sort);
48
- for (const key of keys) if (sort[key] !== 1 && sort[key] !== -1) throw new Error(`Invalid sort direction for "${key}": must be 1 or -1, got ${sort[key]}`);
49
- const nonIdKeys = keys.filter((k) => k !== "_id");
50
- const primaryDirection = sort[nonIdKeys[0]];
51
- if (allowedPrimaryFields && allowedPrimaryFields.length > 0) {
52
- for (const key of nonIdKeys) if (!allowedPrimaryFields.includes(key)) throw new Error(`Keyset sort field "${key}" is not in the strictKeysetSortFields allowlist. Allowed: ${allowedPrimaryFields.join(", ")}. (See PaginationConfig.strictKeysetSortFields — protects against lossy null/non-null keyset boundaries.)`);
53
- }
54
- for (const key of nonIdKeys) if (sort[key] !== primaryDirection) throw new Error("All sort fields must share the same direction for keyset pagination");
55
- if (keys.includes("_id") && sort._id !== primaryDirection) throw new Error("_id direction must match primary field direction");
56
- if (!keys.includes("_id")) return normalizeSort({
57
- ...sort,
58
- _id: primaryDirection
59
- });
60
- return normalizeSort(sort);
61
- }
62
- /**
63
- * Extracts primary sort field (first non-_id field)
64
- *
65
- * @param sort - Sort specification
66
- * @returns Primary field name
67
- */
68
- function getPrimaryField(sort) {
69
- return Object.keys(sort).find((k) => k !== "_id") || "_id";
70
- }
71
- //#endregion
72
- export { sort_exports as n, validateKeysetSort as r, getPrimaryField as t };
File without changes