@classytic/mongokit 3.15.0 → 3.16.1
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 +193 -319
- package/dist/{PaginationEngine-CfQRM1yz.mjs → PaginationEngine-0XXTW-9b.mjs} +2 -2
- package/dist/{Repository-BGL1Pq8w.mjs → Repository-DE7D2QeS.mjs} +240 -19
- package/dist/actions/index.d.mts +1 -1
- package/dist/ai/index.d.mts +1 -1
- package/dist/ai/index.mjs +14 -5
- package/dist/better-auth/index.d.mts +15 -0
- package/dist/better-auth/index.mjs +1 -1
- package/dist/{index-Dccha9dw.d.mts → index-CV5vv-8E.d.mts} +1 -1
- package/dist/index.d.mts +1182 -1079
- package/dist/index.mjs +32 -19
- package/dist/{mongooseToJsonSchema-BYbG894y.mjs → mongooseToJsonSchema-CMM2QHzd.mjs} +28 -1
- package/dist/{mongooseToJsonSchema-BgRAaXX-.d.mts → mongooseToJsonSchema-GhvZUjRO.d.mts} +1 -1
- package/dist/pagination/PaginationEngine.d.mts +1 -1
- package/dist/pagination/PaginationEngine.mjs +1 -1
- package/dist/plugins/index.d.mts +1 -1
- package/dist/plugins/index.mjs +1 -1
- package/dist/query/primitives/coercion.mjs +1 -1
- package/dist/{types-BbaPZleN.d.mts → types-BWZILovv.d.mts} +26 -1
- package/dist/utils/index.d.mts +1 -1
- package/dist/utils/index.mjs +3 -3
- package/dist/{validation-chain.plugin-CWhvBRej.mjs → validation-chain.plugin-Crl5nQAY.mjs} +1172 -1137
- package/dist/{validation-chain.plugin-CAPYCj_T.d.mts → validation-chain.plugin-Iee2_puD.d.mts} +368 -344
- package/package.json +6 -6
- /package/dist/{cursor-J84rR6wG.mjs → cursor-4LMYa4ga.mjs} +0 -0
- /package/dist/{field-selection-Br3WPOEb.mjs → field-selection-CQW3WBiK.mjs} +0 -0
- /package/dist/{id-resolution-B0Odvvou.mjs → id-resolution-DduoO4LI.mjs} +0 -0
- /package/dist/{sort-CrZjTK25.mjs → sort-RPVz5u0w.mjs} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { n as warn } from "./logger-DprijP5h.mjs";
|
|
2
2
|
import { t as createError } from "./error-B00pI7dV.mjs";
|
|
3
|
-
import { n as encodeCursor, r as resolveCursorFilter } from "./cursor-
|
|
4
|
-
import { r as validateKeysetSort, t as getPrimaryField } from "./sort-
|
|
3
|
+
import { n as encodeCursor, r as resolveCursorFilter } from "./cursor-4LMYa4ga.mjs";
|
|
4
|
+
import { r as validateKeysetSort, t as getPrimaryField } from "./sort-RPVz5u0w.mjs";
|
|
5
5
|
//#region src/pagination/utils/index-hint.ts
|
|
6
6
|
/**
|
|
7
7
|
* Read a Mongoose schema's declared indexes, defensively.
|
|
@@ -1,15 +1,42 @@
|
|
|
1
1
|
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-BXMnCvO6.mjs";
|
|
2
2
|
import { n as warn } from "./logger-DprijP5h.mjs";
|
|
3
3
|
import { n as isDuplicateKeyError, r as parseDuplicateKeyError, t as createError } from "./error-B00pI7dV.mjs";
|
|
4
|
-
import { t as PaginationEngine } from "./PaginationEngine-
|
|
4
|
+
import { t as PaginationEngine } from "./PaginationEngine-0XXTW-9b.mjs";
|
|
5
5
|
import { hasNearOperator, rewriteNearForCount } from "./query/primitives/geo.mjs";
|
|
6
|
-
import { r as isValidIdForType, t as getSchemaIdType } from "./id-resolution-
|
|
6
|
+
import { r as isValidIdForType, t as getSchemaIdType } from "./id-resolution-DduoO4LI.mjs";
|
|
7
7
|
import { isFilter } from "@classytic/repo-core/filter";
|
|
8
8
|
import mongoose from "mongoose";
|
|
9
9
|
import { HOOK_PRIORITY } from "@classytic/repo-core/hooks";
|
|
10
|
-
import { RepositoryBase, runChunkedPurge, validatePluginOrder } from "@classytic/repo-core/repository";
|
|
10
|
+
import { RepositoryBase, runChunkedPurge, throwIfAborted, validatePluginOrder, withRetry } from "@classytic/repo-core/repository";
|
|
11
11
|
import { compileUpdateSpecToMongo, isUpdateSpec } from "@classytic/repo-core/update";
|
|
12
12
|
import { decodeAggCursor, encodeAggCursor, isKeysetMode, normalizeGroupBy, validateMeasures } from "@classytic/repo-core/aggregate";
|
|
13
|
+
//#region src/capabilities.ts
|
|
14
|
+
const MONGOKIT_CAPABILITIES = {
|
|
15
|
+
transactions: true,
|
|
16
|
+
nestedTransactions: true,
|
|
17
|
+
upsert: true,
|
|
18
|
+
duplicateKeyError: true,
|
|
19
|
+
distinct: true,
|
|
20
|
+
aggregate: true,
|
|
21
|
+
aggregateOps: {
|
|
22
|
+
percentile: true,
|
|
23
|
+
stddev: true,
|
|
24
|
+
topN: true,
|
|
25
|
+
customDateBuckets: true,
|
|
26
|
+
dateBucketSubMinute: true,
|
|
27
|
+
cache: true
|
|
28
|
+
},
|
|
29
|
+
getOrCreate: true,
|
|
30
|
+
countAndExists: true,
|
|
31
|
+
purgeByField: true,
|
|
32
|
+
arrayOperators: true,
|
|
33
|
+
regexFilter: true,
|
|
34
|
+
changeStreams: true,
|
|
35
|
+
lean: true,
|
|
36
|
+
lookupPopulate: true,
|
|
37
|
+
streaming: true
|
|
38
|
+
};
|
|
39
|
+
//#endregion
|
|
13
40
|
//#region src/operations.ts
|
|
14
41
|
/**
|
|
15
42
|
* The registry. Order intentionally groups by category for readability.
|
|
@@ -127,6 +154,11 @@ const OP_REGISTRY = {
|
|
|
127
154
|
mutates: false,
|
|
128
155
|
hasIdContext: false
|
|
129
156
|
},
|
|
157
|
+
watch: {
|
|
158
|
+
policyKey: "query",
|
|
159
|
+
mutates: false,
|
|
160
|
+
hasIdContext: false
|
|
161
|
+
},
|
|
130
162
|
getAll: {
|
|
131
163
|
policyKey: "filters",
|
|
132
164
|
mutates: false,
|
|
@@ -1649,6 +1681,34 @@ function assertNoMixedPatchShape(opName, patch) {
|
|
|
1649
1681
|
const flatKeys = patchKeys.filter((k) => !k.startsWith("$"));
|
|
1650
1682
|
throw createError(400, `[${opName}] assertNoMixedPatchShape: patch mixes Mongo operators (${operatorKeys.join(", ")}) with raw field keys (${flatKeys.join(", ")}). Mongo would silently DROP the flat keys — that's a write-loss bug we refuse to forward. Either wrap the flat keys in $set explicitly, or remove the operator keys.`);
|
|
1651
1683
|
}
|
|
1684
|
+
/** Mongo `operationType` → portable `ChangeEvent.operation`. */
|
|
1685
|
+
const CHANGE_OPERATION_MAP = {
|
|
1686
|
+
insert: "create",
|
|
1687
|
+
update: "update",
|
|
1688
|
+
replace: "replace",
|
|
1689
|
+
delete: "delete"
|
|
1690
|
+
};
|
|
1691
|
+
/**
|
|
1692
|
+
* Re-root a compiled Mongo match document under a prefix (`fullDocument`)
|
|
1693
|
+
* so a caller filter like `{ status: 'pending' }` matches the change
|
|
1694
|
+
* stream's post-image. Logical operators (`$and` / `$or` / `$nor`)
|
|
1695
|
+
* recurse; `$`-prefixed top-level operators ($expr etc.) pass through
|
|
1696
|
+
* unprefixed — they reference paths explicitly.
|
|
1697
|
+
*/
|
|
1698
|
+
function prefixMatchPaths(match, prefix) {
|
|
1699
|
+
const out = {};
|
|
1700
|
+
for (const [key, value] of Object.entries(match)) if (key === "$and" || key === "$or" || key === "$nor") out[key] = value.map((clause) => prefixMatchPaths(clause, prefix));
|
|
1701
|
+
else if (key.startsWith("$")) out[key] = value;
|
|
1702
|
+
else out[`${prefix}.${key}`] = value;
|
|
1703
|
+
return out;
|
|
1704
|
+
}
|
|
1705
|
+
/** Best-available commit timestamp for a change-stream document. */
|
|
1706
|
+
function changeEventTimestamp(change) {
|
|
1707
|
+
if (change.wallTime instanceof Date) return change.wallTime;
|
|
1708
|
+
const highBits = change.clusterTime?.getHighBits?.();
|
|
1709
|
+
if (typeof highBits === "number" && highBits > 0) return /* @__PURE__ */ new Date(highBits * 1e3);
|
|
1710
|
+
return /* @__PURE__ */ new Date();
|
|
1711
|
+
}
|
|
1652
1712
|
/**
|
|
1653
1713
|
* Plugin phase priorities (lower = runs first).
|
|
1654
1714
|
*
|
|
@@ -1673,6 +1733,12 @@ const HOOK_PRIORITY$1 = HOOK_PRIORITY;
|
|
|
1673
1733
|
* upgrading from 3.9 need no code changes.
|
|
1674
1734
|
*/
|
|
1675
1735
|
var Repository = class extends RepositoryBase {
|
|
1736
|
+
/**
|
|
1737
|
+
* Runtime capability descriptor required by `StandardRepo<TDoc>`
|
|
1738
|
+
* (repo-core 0.6.0). Hosts feature-detect once at boot instead of
|
|
1739
|
+
* try/catching `UnsupportedOperationError` per call.
|
|
1740
|
+
*/
|
|
1741
|
+
capabilities = MONGOKIT_CAPABILITIES;
|
|
1676
1742
|
Model;
|
|
1677
1743
|
/**
|
|
1678
1744
|
* Mongoose model name. Duplicated on the instance for BC — arc / catalog /
|
|
@@ -1696,7 +1762,10 @@ var Repository = class extends RepositoryBase {
|
|
|
1696
1762
|
super({
|
|
1697
1763
|
name: Model.modelName,
|
|
1698
1764
|
hooks: options.hooks ?? "async",
|
|
1699
|
-
pluginOrderChecks: "off"
|
|
1765
|
+
pluginOrderChecks: "off",
|
|
1766
|
+
schema: options.schema,
|
|
1767
|
+
updateSchema: options.updateSchema,
|
|
1768
|
+
events: options.events
|
|
1700
1769
|
});
|
|
1701
1770
|
validatePluginOrder(plugins, Model.modelName, options.pluginOrderChecks ?? "warn", (m) => {
|
|
1702
1771
|
warn(m);
|
|
@@ -1909,7 +1978,7 @@ var Repository = class extends RepositoryBase {
|
|
|
1909
1978
|
async _runOp(op, context, fn) {
|
|
1910
1979
|
return this._composeMiddleware(op, context, async () => {
|
|
1911
1980
|
try {
|
|
1912
|
-
const result = await fn
|
|
1981
|
+
const result = await this._withResilience(context, fn);
|
|
1913
1982
|
await this._emitHook(`after:${op}`, {
|
|
1914
1983
|
context,
|
|
1915
1984
|
result
|
|
@@ -1925,6 +1994,28 @@ var Repository = class extends RepositoryBase {
|
|
|
1925
1994
|
});
|
|
1926
1995
|
}
|
|
1927
1996
|
/**
|
|
1997
|
+
* Resilience envelope around the DRIVER round-trip only — the repo-core
|
|
1998
|
+
* 0.6.0 `QueryOptions.signal` / `QueryOptions.retryPolicy` contract.
|
|
1999
|
+
*
|
|
2000
|
+
* `throwIfAborted` stops a cancelled request before the next driver call;
|
|
2001
|
+
* `withRetry` retries transient failures with exponential backoff (and is
|
|
2002
|
+
* a zero-cost passthrough when no policy was passed, so we wrap
|
|
2003
|
+
* unconditionally). Crucially this wraps `fn` AFTER `_buildContext` ran —
|
|
2004
|
+
* before-hooks (validation, tenant scope, audit, events) execute exactly
|
|
2005
|
+
* once per logical call and are NEVER re-run on retry; only the driver
|
|
2006
|
+
* call repeats.
|
|
2007
|
+
*
|
|
2008
|
+
* Both knobs are read from the context (the options bag is spread into
|
|
2009
|
+
* `_buildContext` inputs), so every op routed through `_runOp` — and the
|
|
2010
|
+
* inline-try methods that call this directly — honors them uniformly.
|
|
2011
|
+
*/
|
|
2012
|
+
_withResilience(context, fn) {
|
|
2013
|
+
const signal = context.signal;
|
|
2014
|
+
const retryPolicy = context.retryPolicy;
|
|
2015
|
+
throwIfAborted(signal);
|
|
2016
|
+
return withRetry(fn, retryPolicy, signal);
|
|
2017
|
+
}
|
|
2018
|
+
/**
|
|
1928
2019
|
* Create single document
|
|
1929
2020
|
*/
|
|
1930
2021
|
async create(data, options = {}) {
|
|
@@ -1967,10 +2058,10 @@ var Repository = class extends RepositoryBase {
|
|
|
1967
2058
|
try {
|
|
1968
2059
|
const effectiveIdField = options.idField ?? this.idField;
|
|
1969
2060
|
if (effectiveIdField !== "_id") {
|
|
1970
|
-
const result = await getByQuery(this.Model, {
|
|
2061
|
+
const result = await this._withResilience(context, () => getByQuery(this.Model, {
|
|
1971
2062
|
[effectiveIdField]: id,
|
|
1972
2063
|
...context.query || {}
|
|
1973
|
-
}, context);
|
|
2064
|
+
}, context));
|
|
1974
2065
|
await this._emitHook("after:getById", {
|
|
1975
2066
|
context,
|
|
1976
2067
|
result
|
|
@@ -1982,7 +2073,7 @@ var Repository = class extends RepositoryBase {
|
|
|
1982
2073
|
if (wantsThrow) throw createError(404, "Document not found");
|
|
1983
2074
|
return null;
|
|
1984
2075
|
}
|
|
1985
|
-
const result = await getById(this.Model, id, context);
|
|
2076
|
+
const result = await this._withResilience(context, () => getById(this.Model, id, context));
|
|
1986
2077
|
if (!result && wantsThrow) throw createError(404, "Document not found");
|
|
1987
2078
|
await this._emitHook("after:getById", {
|
|
1988
2079
|
context,
|
|
@@ -2047,7 +2138,7 @@ var Repository = class extends RepositoryBase {
|
|
|
2047
2138
|
}
|
|
2048
2139
|
const finalQuery = context.query || query;
|
|
2049
2140
|
try {
|
|
2050
|
-
const result = await getByQuery(this.Model, finalQuery, context);
|
|
2141
|
+
const result = await this._withResilience(context, () => getByQuery(this.Model, finalQuery, context));
|
|
2051
2142
|
await this._emitHook("after:getOne", {
|
|
2052
2143
|
context,
|
|
2053
2144
|
result
|
|
@@ -2155,6 +2246,135 @@ var Repository = class extends RepositoryBase {
|
|
|
2155
2246
|
}
|
|
2156
2247
|
}
|
|
2157
2248
|
/**
|
|
2249
|
+
* Portable change feed — `StandardRepo.watch()` over Mongo change
|
|
2250
|
+
* streams (`Model.watch`). Requires a replica set (or mongos); a
|
|
2251
|
+
* standalone mongod rejects change streams at open time — gate on
|
|
2252
|
+
* `repo.capabilities.changeStreams` plus your deployment topology.
|
|
2253
|
+
*
|
|
2254
|
+
* Semantics:
|
|
2255
|
+
* - **Plugin-routed like every other read.** The filter goes through
|
|
2256
|
+
* the standard `before:watch` hook pipeline (`OP_REGISTRY.watch`,
|
|
2257
|
+
* policyKey `'query'`) BEFORE the change-stream pipeline is built —
|
|
2258
|
+
* `multiTenantPlugin` injects the tenant scope (and throws under
|
|
2259
|
+
* `required: true` exactly like other reads), `softDeletePlugin`
|
|
2260
|
+
* injects the deletion-state predicate. A tenant-scoped repo never
|
|
2261
|
+
* streams cross-tenant changes.
|
|
2262
|
+
* - `fullDocument: 'updateLookup'` — update events carry the post-
|
|
2263
|
+
* image document when it still exists at lookup time.
|
|
2264
|
+
* - `filter` (record or Filter IR) is compiled with the standard
|
|
2265
|
+
* filter compiler and applied against `fullDocument.*` paths, so
|
|
2266
|
+
* `watch({ status: 'pending' })` matches the post-image. Delete
|
|
2267
|
+
* events have no `fullDocument` and therefore don't match a
|
|
2268
|
+
* non-empty post-policy filter — use `bypassTenant` / an unscoped
|
|
2269
|
+
* repo without a filter to observe deletes.
|
|
2270
|
+
* - `options.signal` ends the iterator (closes the stream). A
|
|
2271
|
+
* pre-aborted signal rejects at the op boundary like every other op.
|
|
2272
|
+
* - `options.resumeAfter` forwards a previously captured Mongo
|
|
2273
|
+
* resume token for at-least-once consumption across restarts.
|
|
2274
|
+
*
|
|
2275
|
+
* Context keys for policy plugins (`organizationId`, `bypassTenant`,
|
|
2276
|
+
* `includeDeleted`, ...) ride the options bag at runtime — same as every
|
|
2277
|
+
* other op. The contract's `WatchOptions` type doesn't declare an index
|
|
2278
|
+
* signature, so typed callers widen at the call site (or rely on an
|
|
2279
|
+
* ALS `resolveContext` so no per-call tenant key is needed).
|
|
2280
|
+
*
|
|
2281
|
+
* @example
|
|
2282
|
+
* ```ts
|
|
2283
|
+
* const ac = new AbortController();
|
|
2284
|
+
* for await (const change of repo.watch({ status: 'pending' }, { signal: ac.signal })) {
|
|
2285
|
+
* if (change.operation === 'create') enqueue(change.doc!);
|
|
2286
|
+
* }
|
|
2287
|
+
* ```
|
|
2288
|
+
*/
|
|
2289
|
+
async *watch(filter, options = {}) {
|
|
2290
|
+
const context = await this._buildContext("watch", {
|
|
2291
|
+
query: filter ?? {},
|
|
2292
|
+
...options
|
|
2293
|
+
});
|
|
2294
|
+
const pipeline = [{ $match: { operationType: { $in: [
|
|
2295
|
+
"insert",
|
|
2296
|
+
"update",
|
|
2297
|
+
"replace",
|
|
2298
|
+
"delete"
|
|
2299
|
+
] } } }];
|
|
2300
|
+
const compiled = compileFilterToMongo(context.query ?? {});
|
|
2301
|
+
if (Object.keys(compiled).length > 0) pipeline.push({ $match: prefixMatchPaths(compiled, "fullDocument") });
|
|
2302
|
+
const stream = this.Model.watch(pipeline, {
|
|
2303
|
+
fullDocument: "updateLookup",
|
|
2304
|
+
...options.resumeAfter !== void 0 ? { resumeAfter: options.resumeAfter } : {}
|
|
2305
|
+
});
|
|
2306
|
+
const queue = [];
|
|
2307
|
+
let streamError = null;
|
|
2308
|
+
let ended = false;
|
|
2309
|
+
let notify = null;
|
|
2310
|
+
const wake = () => {
|
|
2311
|
+
const resolve = notify;
|
|
2312
|
+
notify = null;
|
|
2313
|
+
resolve?.();
|
|
2314
|
+
};
|
|
2315
|
+
const onChange = (change) => {
|
|
2316
|
+
queue.push(change);
|
|
2317
|
+
wake();
|
|
2318
|
+
};
|
|
2319
|
+
const onError = (error) => {
|
|
2320
|
+
streamError = error;
|
|
2321
|
+
wake();
|
|
2322
|
+
};
|
|
2323
|
+
const onClose = () => {
|
|
2324
|
+
ended = true;
|
|
2325
|
+
wake();
|
|
2326
|
+
};
|
|
2327
|
+
stream.on("change", onChange);
|
|
2328
|
+
stream.on("error", onError);
|
|
2329
|
+
stream.on("close", onClose);
|
|
2330
|
+
const closeStream = async () => {
|
|
2331
|
+
try {
|
|
2332
|
+
await stream.close();
|
|
2333
|
+
} catch {}
|
|
2334
|
+
};
|
|
2335
|
+
const signal = options.signal;
|
|
2336
|
+
const onAbort = () => {
|
|
2337
|
+
ended = true;
|
|
2338
|
+
wake();
|
|
2339
|
+
closeStream();
|
|
2340
|
+
};
|
|
2341
|
+
try {
|
|
2342
|
+
if (signal) {
|
|
2343
|
+
if (signal.aborted) return;
|
|
2344
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2345
|
+
}
|
|
2346
|
+
while (true) {
|
|
2347
|
+
if (signal?.aborted) return;
|
|
2348
|
+
const change = queue.shift();
|
|
2349
|
+
if (change) {
|
|
2350
|
+
const operation = CHANGE_OPERATION_MAP[change.operationType];
|
|
2351
|
+
if (!operation) continue;
|
|
2352
|
+
yield {
|
|
2353
|
+
operation,
|
|
2354
|
+
id: change.documentKey?._id,
|
|
2355
|
+
...change.fullDocument !== void 0 ? { doc: change.fullDocument } : {},
|
|
2356
|
+
timestamp: changeEventTimestamp(change)
|
|
2357
|
+
};
|
|
2358
|
+
continue;
|
|
2359
|
+
}
|
|
2360
|
+
if (streamError) {
|
|
2361
|
+
if (signal?.aborted) return;
|
|
2362
|
+
throw streamError;
|
|
2363
|
+
}
|
|
2364
|
+
if (ended) return;
|
|
2365
|
+
await new Promise((resolve) => {
|
|
2366
|
+
notify = resolve;
|
|
2367
|
+
});
|
|
2368
|
+
}
|
|
2369
|
+
} finally {
|
|
2370
|
+
if (signal) signal.removeEventListener("abort", onAbort);
|
|
2371
|
+
stream.removeListener("change", onChange);
|
|
2372
|
+
stream.removeListener("error", onError);
|
|
2373
|
+
stream.removeListener("close", onClose);
|
|
2374
|
+
await closeStream();
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
/**
|
|
2158
2378
|
* Unified pagination - auto-detects offset vs keyset based on params
|
|
2159
2379
|
*
|
|
2160
2380
|
* Auto-detection logic:
|
|
@@ -2428,11 +2648,11 @@ var Repository = class extends RepositoryBase {
|
|
|
2428
2648
|
}
|
|
2429
2649
|
}
|
|
2430
2650
|
let result;
|
|
2431
|
-
if (effectiveIdField !== "_id") result = await updateByQuery(this.Model, {
|
|
2651
|
+
if (effectiveIdField !== "_id") result = await this._withResilience(context, () => updateByQuery(this.Model, {
|
|
2432
2652
|
[effectiveIdField]: id,
|
|
2433
2653
|
...context.query || {}
|
|
2434
|
-
}, context.data || data, context);
|
|
2435
|
-
else result = await update(this.Model, id, context.data || data, context);
|
|
2654
|
+
}, context.data || data, context));
|
|
2655
|
+
else result = await this._withResilience(context, () => update(this.Model, id, context.data || data, context));
|
|
2436
2656
|
if (!result && wantsThrow) throw createError(404, "Document not found");
|
|
2437
2657
|
await this._emitHook("after:update", {
|
|
2438
2658
|
context,
|
|
@@ -2903,10 +3123,10 @@ var Repository = class extends RepositoryBase {
|
|
|
2903
3123
|
[effectiveIdField]: id,
|
|
2904
3124
|
...context.query || {}
|
|
2905
3125
|
} : void 0;
|
|
2906
|
-
const result = deleteQuery ?
|
|
3126
|
+
const result = await this._withResilience(context, () => deleteQuery ? deleteByQuery(this.Model, deleteQuery, { session: options.session }) : deleteById(this.Model, id, {
|
|
2907
3127
|
session: options.session,
|
|
2908
3128
|
query: context.query
|
|
2909
|
-
});
|
|
3129
|
+
}));
|
|
2910
3130
|
if (!result && wantsThrow) throw createError(404, "Document not found");
|
|
2911
3131
|
await this._emitHook("after:delete", {
|
|
2912
3132
|
context,
|
|
@@ -2999,7 +3219,7 @@ var Repository = class extends RepositoryBase {
|
|
|
2999
3219
|
}
|
|
3000
3220
|
const finalQuery = context.query || filter;
|
|
3001
3221
|
if (!finalQuery || Object.keys(finalQuery).length === 0) throw createError(400, "deleteMany requires a non-empty query filter after policy hooks.");
|
|
3002
|
-
const result = await this.Model.deleteMany(finalQuery, { session: options.session }).exec();
|
|
3222
|
+
const result = await this._withResilience(context, () => this.Model.deleteMany(finalQuery, { session: options.session }).exec());
|
|
3003
3223
|
await this._emitHook("after:deleteMany", {
|
|
3004
3224
|
context,
|
|
3005
3225
|
result
|
|
@@ -3394,9 +3614,9 @@ var Repository = class extends RepositoryBase {
|
|
|
3394
3614
|
};
|
|
3395
3615
|
if (isKeyset && sort) {
|
|
3396
3616
|
const parsedSort = this._parseSort(sort);
|
|
3397
|
-
const { validateKeysetSort } = await import("./sort-
|
|
3398
|
-
const { encodeCursor, resolveCursorFilter } = await import("./cursor-
|
|
3399
|
-
const { getPrimaryField } = await import("./sort-
|
|
3617
|
+
const { validateKeysetSort } = await import("./sort-RPVz5u0w.mjs").then((n) => n.n);
|
|
3618
|
+
const { encodeCursor, resolveCursorFilter } = await import("./cursor-4LMYa4ga.mjs").then((n) => n.t);
|
|
3619
|
+
const { getPrimaryField } = await import("./sort-RPVz5u0w.mjs").then((n) => n.n);
|
|
3400
3620
|
const normalizedSort = validateKeysetSort(parsedSort, this._pagination.config.strictKeysetSortFields);
|
|
3401
3621
|
const cursorVersion = this._pagination.config.cursorVersion ?? 1;
|
|
3402
3622
|
const minCursorVersion = this._pagination.config.minCursorVersion ?? 1;
|
|
@@ -3621,6 +3841,7 @@ var Repository = class extends RepositoryBase {
|
|
|
3621
3841
|
* 4. DEFAULT (500) — user-registered hooks
|
|
3622
3842
|
*/
|
|
3623
3843
|
async _buildContext(operation, options) {
|
|
3844
|
+
throwIfAborted(options.signal);
|
|
3624
3845
|
const normalized = this._normalizeFilterSlots(options);
|
|
3625
3846
|
return await super._buildContext(operation, normalized);
|
|
3626
3847
|
}
|
|
@@ -3795,4 +4016,4 @@ var Repository = class extends RepositoryBase {
|
|
|
3795
4016
|
}
|
|
3796
4017
|
};
|
|
3797
4018
|
//#endregion
|
|
3798
|
-
export { withTransaction as a, MUTATING_OPERATIONS as c, operationsByPolicyKey as d, isTransactionUnsupported as i, OP_REGISTRY as l, Repository as n, AggregationBuilder as o, batchTransaction as r, ALL_OPERATIONS as s, HOOK_PRIORITY$1 as t, READ_OPERATIONS as u };
|
|
4019
|
+
export { withTransaction as a, MUTATING_OPERATIONS as c, operationsByPolicyKey as d, MONGOKIT_CAPABILITIES as f, isTransactionUnsupported as i, OP_REGISTRY as l, Repository as n, AggregationBuilder as o, batchTransaction as r, ALL_OPERATIONS as s, HOOK_PRIORITY$1 as t, READ_OPERATIONS as u };
|
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-CV5vv-8E.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/ai/index.d.mts
CHANGED
package/dist/ai/index.mjs
CHANGED
|
@@ -269,7 +269,9 @@ function vectorPlugin(options) {
|
|
|
269
269
|
name: "vector",
|
|
270
270
|
apply(repo) {
|
|
271
271
|
if (!repo.registerMethod) throw new Error("[mongokit] vectorPlugin requires methodRegistryPlugin");
|
|
272
|
+
let vectorSearchUnavailable = null;
|
|
272
273
|
repo.registerMethod("searchSimilar", async function searchSimilar(params) {
|
|
274
|
+
if (vectorSearchUnavailable) throw vectorSearchUnavailable;
|
|
273
275
|
const field = resolveField(fields, params.field);
|
|
274
276
|
let queryVector;
|
|
275
277
|
if (Array.isArray(params.query)) queryVector = params.query;
|
|
@@ -281,11 +283,18 @@ function vectorPlugin(options) {
|
|
|
281
283
|
const pipeline = buildVectorSearchPipeline(field, queryVector, params);
|
|
282
284
|
const agg = repo.Model.aggregate(pipeline);
|
|
283
285
|
if (params.session) agg.session(params.session);
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
286
|
+
let results;
|
|
287
|
+
try {
|
|
288
|
+
results = await withVectorErrorHints(() => agg.exec(), {
|
|
289
|
+
indexName: field.index,
|
|
290
|
+
dimensions: field.dimensions,
|
|
291
|
+
filterPaths: params.filter ? Object.keys(params.filter) : void 0
|
|
292
|
+
});
|
|
293
|
+
} catch (err) {
|
|
294
|
+
if (err.code === "NOT_ATLAS") vectorSearchUnavailable = err;
|
|
295
|
+
throw err;
|
|
296
|
+
}
|
|
297
|
+
return results.map((doc) => {
|
|
289
298
|
const score = doc._score ?? 0;
|
|
290
299
|
const { _score, ...rest } = doc;
|
|
291
300
|
return {
|
|
@@ -119,6 +119,21 @@ interface BetterAuthOverlayOptions<TDoc = Record<string, unknown>> {
|
|
|
119
119
|
* Subclass `Repository<TDoc>` to add domain methods (e.g. `getByEmail`,
|
|
120
120
|
* `deactivate`, `getAdmins`). When omitted, the default `Repository<TDoc>`
|
|
121
121
|
* is used — the standard CRUD surface is already complete.
|
|
122
|
+
*
|
|
123
|
+
* **Plugin composition warning.** The default overlay repo is built with
|
|
124
|
+
* NO plugins, deliberately:
|
|
125
|
+
*
|
|
126
|
+
* - **Do NOT apply `multiTenantPlugin` to BA overlays.** Better Auth's
|
|
127
|
+
* tables are global by design — `user`, `session`, `account`,
|
|
128
|
+
* `verification` have no tenant column at all, and `member` /
|
|
129
|
+
* `invitation` carry `organizationId` under BA's OWN semantics
|
|
130
|
+
* (membership rows, not tenant-scoped data). A tenant plugin with
|
|
131
|
+
* `required: true` would break every read; scope BA queries with
|
|
132
|
+
* explicit filters (`{ organizationId }`) at the call site instead.
|
|
133
|
+
* - **`cachePlugin` is safe only because the data is global** — if you
|
|
134
|
+
* wire it in a custom `RepositoryClass`, remember BA's own writes go
|
|
135
|
+
* through BA's driver and will NOT invalidate mongokit's cache. Use
|
|
136
|
+
* short TTLs or skip caching BA collections entirely.
|
|
122
137
|
*/
|
|
123
138
|
RepositoryClass?: new (Model: ReturnType<MongooseLike['model']>) => RepositoryLike<TDoc>;
|
|
124
139
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as Repository } from "../Repository-
|
|
1
|
+
import { n as Repository } from "../Repository-DE7D2QeS.mjs";
|
|
2
2
|
import { n as createMongooseAdapter } from "../adapter-gG_uyFSZ.mjs";
|
|
3
3
|
import { pluralizeBetterAuthCollection, resolveBetterAuthCollections } from "@classytic/repo-core/better-auth";
|
|
4
4
|
//#region src/better-auth/index.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as ObjectId, Dt as LookupOptions, H as OperationOptions, I as MinMaxResult, J as PopulateSpec, T as GroupResult, Z as ReadPreferenceType, ft as UpdateManyResult, ht as UpdateWithValidationResult, i as AnyDocument, it as SelectSpec, p as CreateOptions, pt as UpdateOptions, ut as SortSpec, v as DeleteResult, w as FindOneAndUpdateOptions } from "./types-
|
|
1
|
+
import { B as ObjectId, Dt as LookupOptions, H as OperationOptions, I as MinMaxResult, J as PopulateSpec, T as GroupResult, Z as ReadPreferenceType, ft as UpdateManyResult, ht as UpdateWithValidationResult, i as AnyDocument, it as SelectSpec, p as CreateOptions, pt as UpdateOptions, ut as SortSpec, v as DeleteResult, w as FindOneAndUpdateOptions } from "./types-BWZILovv.mjs";
|
|
2
2
|
import { Model, PipelineStage } from "mongoose";
|
|
3
3
|
|
|
4
4
|
//#region src/actions/aggregate.d.ts
|